- makes fileHandler calling resemble Time more closely ENH: provide natural_sort less/greater functions - more succinct than (compare < 0) or (compare > 0). The corresponding wrappers for UList renamed as list_less, etc but they were only used in unit tests ENH: handle (-allRegions | -all-regions, ..) directly when retrieving args - makes handling independent of aliasing order STYLE: include <string_view> when including <string> - the standard does not guarantee which headers (if any) actually include string_view
784 lines
22 KiB
C++
784 lines
22 KiB
C++
/*---------------------------------------------------------------------------*\
|
|
========= |
|
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
|
\\ / O peration |
|
|
\\ / A nd | www.openfoam.com
|
|
\\/ M anipulation |
|
|
-------------------------------------------------------------------------------
|
|
Copyright (C) 2016-2025 OpenCFD Ltd.
|
|
-------------------------------------------------------------------------------
|
|
License
|
|
This file is part of OpenFOAM.
|
|
|
|
OpenFOAM is free software: you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
Class
|
|
Foam::memorybuf
|
|
|
|
Description
|
|
A std::streambuf used for memory buffer streams such as
|
|
ispanstream, ocharstream, etc.
|
|
|
|
\*---------------------------------------------------------------------------*/
|
|
|
|
#ifndef Foam_memoryStreamBuffer_H
|
|
#define Foam_memoryStreamBuffer_H
|
|
|
|
#include "DynamicList.H"
|
|
|
|
#include <memory>
|
|
#include <string_view>
|
|
#include <string>
|
|
#include <type_traits>
|
|
|
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
|
|
|
namespace Foam
|
|
{
|
|
|
|
/*---------------------------------------------------------------------------*\
|
|
Class memorybuf Declaration
|
|
\*---------------------------------------------------------------------------*/
|
|
|
|
//- A streambuf for memory similar to std::spanbuf (C++23)
|
|
class memorybuf
|
|
:
|
|
public std::streambuf
|
|
{
|
|
protected:
|
|
|
|
//- Set position pointer to relative position
|
|
virtual std::streampos seekoff
|
|
(
|
|
std::streamoff off,
|
|
std::ios_base::seekdir way,
|
|
std::ios_base::openmode which = std::ios_base::in|std::ios_base::out
|
|
)
|
|
{
|
|
const bool testin = which & std::ios_base::in;
|
|
const bool testout = which & std::ios_base::out;
|
|
|
|
if (way == std::ios_base::beg)
|
|
{
|
|
if (testin)
|
|
{
|
|
setg(eback(), eback(), egptr());
|
|
gbump(off);
|
|
}
|
|
if (testout)
|
|
{
|
|
setp(pbase(), epptr());
|
|
pbump(off);
|
|
}
|
|
}
|
|
else if (way == std::ios_base::cur)
|
|
{
|
|
if (testin)
|
|
{
|
|
gbump(off);
|
|
}
|
|
if (testout)
|
|
{
|
|
pbump(off);
|
|
}
|
|
}
|
|
else if (way == std::ios_base::end)
|
|
{
|
|
if (testin)
|
|
{
|
|
setg(eback(), eback(), egptr());
|
|
gbump(egptr() - eback() - off);
|
|
}
|
|
if (testout)
|
|
{
|
|
setp(pbase(), epptr());
|
|
pbump(epptr() - pbase() - off);
|
|
}
|
|
}
|
|
|
|
if (testin)
|
|
{
|
|
return (gptr() - eback()); // span_tellg()
|
|
}
|
|
if (testout)
|
|
{
|
|
return (pptr() - pbase()); // span_tellp()
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
//- Set position pointer to absolute position
|
|
virtual std::streampos seekpos
|
|
(
|
|
std::streampos pos,
|
|
std::ios_base::openmode which = std::ios_base::in|std::ios_base::out
|
|
)
|
|
{
|
|
return seekoff(pos, std::ios_base::beg, which);
|
|
}
|
|
|
|
public:
|
|
|
|
// Forward Declarations
|
|
class in_base;
|
|
class in_dynamic;
|
|
class out_base;
|
|
class out_dynamic;
|
|
};
|
|
|
|
|
|
/*---------------------------------------------------------------------------*\
|
|
Class memorybuf::in_base Declaration
|
|
\*---------------------------------------------------------------------------*/
|
|
|
|
//- The base input streambuf with memory access
|
|
class memorybuf::in_base
|
|
:
|
|
public memorybuf
|
|
{
|
|
protected:
|
|
|
|
//- Get sequence of characters from a fixed region
|
|
virtual std::streamsize xsgetn(char* s, std::streamsize n)
|
|
{
|
|
std::streamsize count = 0;
|
|
while (count < n && gptr() < egptr())
|
|
{
|
|
*(s + count++) = *(gptr());
|
|
gbump(1);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
public:
|
|
|
|
// Constructors
|
|
|
|
//- Default construct
|
|
in_base() = default;
|
|
|
|
//- Construct for character array (can be nullptr) and number of bytes
|
|
in_base(char* s, std::streamsize n)
|
|
{
|
|
resetg(s, n);
|
|
}
|
|
|
|
|
|
// Member Functions
|
|
|
|
// //- Reset get buffer pointer to the beginning of existing span
|
|
// void rewind() { setg(eback(), eback(), egptr()); }
|
|
|
|
//- Reset get buffer with character data (can be nullptr) and count
|
|
// Sets get pointer to the begin.
|
|
void resetg(char* s, std::streamsize n)
|
|
{
|
|
if (s)
|
|
{
|
|
setg(s, s, s + n);
|
|
}
|
|
else
|
|
{
|
|
setg(nullptr, nullptr, nullptr);
|
|
}
|
|
}
|
|
|
|
//- The current buffer get position
|
|
std::streamsize span_tellg() const { return (gptr() - eback()); }
|
|
|
|
//- The get buffer capacity
|
|
std::streamsize span_capacity() const { return (egptr() - eback()); }
|
|
|
|
//- The number of characters remaining in the get area
|
|
std::streamsize span_remaining() const
|
|
{
|
|
return (gptr() < egptr()) ? (egptr() - gptr()) : 0;
|
|
}
|
|
|
|
//- The span data (start of input characters)
|
|
char* data_bytes() const { return eback(); }
|
|
|
|
//- The span size (number of input characters)
|
|
std::streamsize size_bytes() const { return (egptr() - eback()); }
|
|
|
|
//- True if position is within the current input range
|
|
bool in_range(std::streampos pos) const
|
|
{
|
|
return (pos >= 0 && pos < span_capacity());
|
|
}
|
|
|
|
//- A string view of the current input region
|
|
auto view() const
|
|
{
|
|
return std::string_view(data_bytes(), size_bytes());
|
|
}
|
|
|
|
//- A sub-slice string view of the current input region
|
|
auto view(size_t pos, size_t len) const
|
|
{
|
|
// Restrict intersection count to the current content range
|
|
if (len && in_range(pos))
|
|
{
|
|
if ((len == std::string::npos) || (len > (size_bytes()-pos)))
|
|
{
|
|
len = (size_bytes()-pos);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
len = 0; // Ignore out-of-range
|
|
}
|
|
|
|
if (len)
|
|
{
|
|
return std::string_view(data_bytes()+pos, len);
|
|
}
|
|
else
|
|
{
|
|
return std::string_view();
|
|
}
|
|
}
|
|
|
|
//- Some information about the input buffer position/capacity
|
|
void info(Ostream& os) const
|
|
{
|
|
os << "get=" << span_tellg() << '/' << span_capacity();
|
|
}
|
|
};
|
|
|
|
|
|
/*---------------------------------------------------------------------------*\
|
|
Class memorybuf::in_dynamic Declaration
|
|
\*---------------------------------------------------------------------------*/
|
|
|
|
//- An output streambuf for memory access
|
|
class memorybuf::in_dynamic
|
|
:
|
|
public memorybuf::in_base
|
|
{
|
|
private:
|
|
|
|
//- Character storage
|
|
List<char> storage_;
|
|
|
|
|
|
public:
|
|
|
|
// Constructors
|
|
|
|
//- Default construct - empty
|
|
in_dynamic() = default;
|
|
|
|
//- Copy construct from content
|
|
in_dynamic(const char* s, std::streamsize n)
|
|
{
|
|
if (s && n)
|
|
{
|
|
storage_.resize_nocopy(n);
|
|
std::copy(s, (s + n), storage_.data());
|
|
}
|
|
sync_gbuffer();
|
|
}
|
|
|
|
//- Move construct from List
|
|
in_dynamic(::Foam::List<char>&& buffer)
|
|
:
|
|
storage_(std::move(buffer))
|
|
{
|
|
sync_gbuffer();
|
|
}
|
|
|
|
//- Move construct from DynamicList (added length only)
|
|
template<int SizeMin>
|
|
in_dynamic(::Foam::DynamicList<char,SizeMin>&& buffer)
|
|
{
|
|
storage_.transfer(buffer); // Implies shrink_to_fit
|
|
sync_gbuffer();
|
|
}
|
|
|
|
|
|
// Member Functions
|
|
|
|
//- Sync get buffer pointers to agree with list dimensions
|
|
// Sets get pointer to the begin (rewind).
|
|
void sync_gbuffer()
|
|
{
|
|
resetg(storage_.data(), storage_.size());
|
|
}
|
|
|
|
//- Reset content (copy)
|
|
void reset(const char* s, std::streamsize n)
|
|
{
|
|
if (s && n)
|
|
{
|
|
storage_.resize_nocopy(n);
|
|
std::copy(s, (s + n), storage_.data());
|
|
}
|
|
else
|
|
{
|
|
storage_.clear();
|
|
}
|
|
sync_gbuffer();
|
|
}
|
|
|
|
//- Exchange buffer content and parameter contents, reset positions
|
|
void swap(List<char>& other)
|
|
{
|
|
other.swap(storage_); // Swap contents
|
|
sync_gbuffer();
|
|
}
|
|
|
|
//- Exchange buffer content and parameter contents, reset positions
|
|
template<int SizeMin>
|
|
void swap(DynamicList<char,SizeMin>& other)
|
|
{
|
|
// NB: not storage_.swap(other)! - incorrect slicing
|
|
other.swap(storage_); // Swap contents: implies shrink_to_fit
|
|
sync_gbuffer();
|
|
}
|
|
|
|
//- Reset buffer and return contents.
|
|
//- The list size and capacity are identical
|
|
DynamicList<char> release()
|
|
{
|
|
DynamicList<char> chars(std::move(storage_));
|
|
sync_gbuffer();
|
|
return chars;
|
|
}
|
|
};
|
|
|
|
|
|
/*---------------------------------------------------------------------------*\
|
|
Class memorybuf::out_base Declaration
|
|
\*---------------------------------------------------------------------------*/
|
|
|
|
//- An output streambuf for memory access
|
|
class memorybuf::out_base
|
|
:
|
|
public memorybuf
|
|
{
|
|
protected:
|
|
|
|
//- Put sequence of characters to a fixed region
|
|
virtual std::streamsize xsputn(const char* s, std::streamsize n)
|
|
{
|
|
std::streamsize count = 0;
|
|
while (count < n && pptr() < epptr())
|
|
{
|
|
*(pptr()) = *(s + count++);
|
|
pbump(1);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
public:
|
|
|
|
// Constructors
|
|
|
|
//- Default construct
|
|
out_base() = default;
|
|
|
|
//- Construct for character array (can be nullptr) and number of bytes
|
|
out_base(char* s, std::streamsize n)
|
|
{
|
|
resetp(s, n);
|
|
}
|
|
|
|
|
|
// Member Functions
|
|
|
|
// //- Reset put buffer pointer to the beginning of existing span
|
|
// void rewind() { setp(pbase(), epptr()); }
|
|
|
|
//- Reset put buffer with character data (can be nullptr) and count
|
|
// Sets put pointer to the begin.
|
|
inline void resetp(char* s, std::streamsize n)
|
|
{
|
|
if (s)
|
|
{
|
|
// As per (std::ios_base::out && !std::ios_base::ate)
|
|
setp(s, s + n);
|
|
// No treatment for (std::ios_base::out && std::ios_base::ate)
|
|
}
|
|
else
|
|
{
|
|
setp(nullptr, nullptr);
|
|
}
|
|
}
|
|
|
|
//- The current buffer put position
|
|
std::streamsize span_tellp() const { return (pptr() - pbase()); }
|
|
|
|
//- The put buffer capacity
|
|
std::streamsize span_capacity() const { return (epptr() - pbase()); }
|
|
|
|
//- The span data (start of output characters)
|
|
char* data_bytes() const { return pbase(); }
|
|
|
|
//- The span size (size of output buffer)
|
|
std::streamsize size_bytes() const { return (pptr() - pbase()); }
|
|
|
|
//- True if position is within the current output range
|
|
bool in_range(std::streampos pos) const
|
|
{
|
|
return (pos >= 0 && pos < span_tellp());
|
|
}
|
|
|
|
//- A string view of the current output region
|
|
auto view() const
|
|
{
|
|
return std::string_view(data_bytes(), size_bytes());
|
|
}
|
|
|
|
//- A sub-slice string view of the current output region
|
|
auto view(size_t pos, size_t len) const
|
|
{
|
|
// Restrict intersection count to the current content range
|
|
if (len && in_range(pos))
|
|
{
|
|
if ((len == std::string::npos) || (len > (size_bytes()-pos)))
|
|
{
|
|
len = (size_bytes()-pos);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
len = 0; // Ignore out-of-range
|
|
}
|
|
|
|
if (len)
|
|
{
|
|
return std::string_view(data_bytes()+pos, len);
|
|
}
|
|
else
|
|
{
|
|
return std::string_view();
|
|
}
|
|
}
|
|
|
|
//- Decrease the put area by 1 or more elements
|
|
void pop_back(int n = 1)
|
|
{
|
|
for (; (n > 0 && (pbase() < pptr())); --n)
|
|
{
|
|
pbump(-1);
|
|
}
|
|
}
|
|
|
|
//- Overwrite a sub-slice with character content
|
|
void overwrite
|
|
(
|
|
std::streampos pos,
|
|
const char* data,
|
|
std::streamsize count
|
|
)
|
|
{
|
|
if (data && (count > 0) && in_range(pos))
|
|
{
|
|
// Restrict to intersection with current content
|
|
if (span_tellp() <= pos+std::streampos(count))
|
|
{
|
|
count = (span_tellp() - pos);
|
|
}
|
|
std::copy(data, (data+count), (data_bytes()+pos));
|
|
}
|
|
}
|
|
|
|
//- Overwrite a single character
|
|
void overwrite(std::streampos pos, char c)
|
|
{
|
|
if (c && in_range(pos))
|
|
{
|
|
*(data_bytes()+pos) = c;
|
|
}
|
|
}
|
|
|
|
//- Some information about the output buffer position/capacity
|
|
void info(Ostream& os) const
|
|
{
|
|
os << "put=" << span_tellp() << '/' << span_capacity();
|
|
}
|
|
};
|
|
|
|
|
|
/*---------------------------------------------------------------------------*\
|
|
Class memorybuf::out_dynamic Declaration
|
|
\*---------------------------------------------------------------------------*/
|
|
|
|
//- An output streambuf for memory access
|
|
class memorybuf::out_dynamic
|
|
:
|
|
public memorybuf::out_base
|
|
{
|
|
private:
|
|
|
|
//- Character storage.
|
|
// Internally manage like a DynamicList, with its capacity known
|
|
// from the list size and the addressable size known through the
|
|
// stream pointers.
|
|
List<char> storage_;
|
|
|
|
protected:
|
|
|
|
//- Handle overflow
|
|
virtual int overflow(int_type c = traits_type::eof())
|
|
{
|
|
if (c != traits_type::eof())
|
|
{
|
|
// Needed more space?
|
|
extend(1);
|
|
|
|
*(pptr()) = c;
|
|
pbump(1);
|
|
}
|
|
return c;
|
|
}
|
|
|
|
//- Put sequence of characters
|
|
virtual std::streamsize xsputn(const char* s, std::streamsize n)
|
|
{
|
|
// Enough space so that appends work without problem
|
|
extend(n);
|
|
|
|
std::streamsize count = 0;
|
|
while (count < n && pptr() < epptr())
|
|
{
|
|
*(pptr()) = *(s + count++);
|
|
pbump(1);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
public:
|
|
|
|
// Constructors
|
|
|
|
//- Default construct - no initial reserved number of bytes.
|
|
out_dynamic()
|
|
{
|
|
sync_pbuffer();
|
|
}
|
|
|
|
//- Default construct with initial reserved number of bytes.
|
|
out_dynamic(size_t nbytes)
|
|
:
|
|
storage_(label(nbytes))
|
|
{
|
|
sync_pbuffer();
|
|
}
|
|
|
|
//- Move construct from List
|
|
out_dynamic(::Foam::List<char>&& buffer)
|
|
:
|
|
storage_(std::move(buffer))
|
|
{
|
|
sync_pbuffer();
|
|
}
|
|
|
|
//- Move construct from DynamicList (uses entire capacity)
|
|
template<int SizeMin>
|
|
out_dynamic(::Foam::DynamicList<char,SizeMin>&& buffer)
|
|
{
|
|
buffer.resize(buffer.capacity()); // Use entire space
|
|
storage_.transfer(buffer);
|
|
sync_pbuffer();
|
|
}
|
|
|
|
|
|
// Member Functions
|
|
|
|
//- Normal lower capacity limit.
|
|
// 512 bytes is a bit arbitrary but consistent with std::stringstream
|
|
static constexpr label min_size() noexcept { return 512; }
|
|
|
|
//- The largest storage size
|
|
static constexpr label max_size() noexcept
|
|
{
|
|
return UList<char>::max_size();
|
|
}
|
|
|
|
//- The 1/2 of max_size() - rounded to power-of-two
|
|
static constexpr label max_size_2() noexcept
|
|
{
|
|
return (1+max_size()/2);
|
|
}
|
|
|
|
//- The 1/4 of max_size() - rounded to power-of-two
|
|
static constexpr label max_size_4() noexcept
|
|
{
|
|
return (max_size_2()/2);
|
|
}
|
|
|
|
//- Sync put buffer pointers to agree with list dimensions.
|
|
// Sets put pointer to the begin (rewind).
|
|
void sync_pbuffer()
|
|
{
|
|
resetp(storage_.data(), storage_.size());
|
|
}
|
|
|
|
//- Increase capacity (if needed) and adjust buffer pointers.
|
|
//- Applies a min-size and capacity doubling.
|
|
void reserve(const std::streamsize len)
|
|
{
|
|
if (storage_.size() < len)
|
|
{
|
|
const auto cur = span_tellp(); // Output position
|
|
|
|
label size = min_size();
|
|
|
|
if (size < len)
|
|
{
|
|
// Increase capacity, but grow more slowly at the largest
|
|
// sizes. Expect a buffer of char to approach max_size()
|
|
// more commonly than buffers of other data types.
|
|
|
|
if (storage_.size() <= max_size_4())
|
|
{
|
|
// (0 < capacity <= 0.25) : fast growth (2)
|
|
size = label(2*storage_.size());
|
|
}
|
|
else if (storage_.size() <= max_size_2())
|
|
{
|
|
// (0.25 < capacity <= 0.5) : slower growth (1.5)
|
|
size = label((storage_.size()/2)*3);
|
|
}
|
|
else if (storage_.size() <= (max_size_2() + max_size_4()))
|
|
{
|
|
// (0.5 < capacity <= 0.75) : very slow growth (1.25)
|
|
size = label((storage_.size()/4)*5);
|
|
}
|
|
else
|
|
{
|
|
// Already very large - use max (or just len?)
|
|
size = max_size();
|
|
}
|
|
|
|
// max(size, len)
|
|
if (size < len)
|
|
{
|
|
size = len;
|
|
}
|
|
}
|
|
|
|
// std::cerr
|
|
// <<"request:" << len
|
|
// << " old cap:" << storage_.size()
|
|
// << " new cap:" << size
|
|
// << " pos:" << cur << endl;
|
|
|
|
storage_.resize_copy(cur, size);
|
|
sync_pbuffer();
|
|
pbump(cur);
|
|
}
|
|
}
|
|
|
|
//- Increase capacity for at least this size.
|
|
//- Does not apply min-size or capacity doubling etc.
|
|
void reserve_exact(const std::streamsize len)
|
|
{
|
|
if (storage_.size() < len)
|
|
{
|
|
const auto cur = span_tellp(); // Output position
|
|
|
|
storage_.resize_copy(cur, len);
|
|
sync_pbuffer();
|
|
pbump(cur);
|
|
}
|
|
}
|
|
|
|
//- Increase (reserve) space for another \c count entries
|
|
void extend(std::streamsize count)
|
|
{
|
|
reserve(span_tellp() + count);
|
|
}
|
|
|
|
//- Increase (reserve) space for another \c count entries
|
|
void extend_exact(std::streamsize count)
|
|
{
|
|
reserve_exact(span_tellp() + count);
|
|
}
|
|
|
|
//- Clear storage
|
|
void clearStorage()
|
|
{
|
|
storage_.clear();
|
|
sync_pbuffer();
|
|
}
|
|
|
|
//- Shrink storage to addressed storage
|
|
void shrink_to_fit()
|
|
{
|
|
const auto cur = span_tellp(); // Addressed length
|
|
|
|
storage_.resize(cur);
|
|
sync_pbuffer();
|
|
pbump(cur);
|
|
}
|
|
|
|
//- Exchange buffer content and parameter contents, reset positions
|
|
void swap(List<char>& other)
|
|
{
|
|
const auto cur = span_tellp(); // Addressed length
|
|
other.swap(storage_);
|
|
other.resize(cur); // Truncate to output length
|
|
sync_pbuffer();
|
|
}
|
|
|
|
//- Exchange buffer content and parameter contents, reset positions
|
|
template<int SizeMin>
|
|
void swap(DynamicList<char,SizeMin>& other)
|
|
{
|
|
const auto cur = span_tellp(); // Addressed length
|
|
|
|
other.resize(other.capacity()); // Use entire space
|
|
other.swap(storage_); // NB: not storage_.swap(other)
|
|
other.resize(cur); // Restrict to output length
|
|
sync_pbuffer();
|
|
}
|
|
|
|
//- Reset buffer and return contents as a DynamicList.
|
|
//- The list size corresponds to the region of output.
|
|
DynamicList<char> release()
|
|
{
|
|
const auto cur = span_tellp(); // Addressed length
|
|
DynamicList<char> chars(std::move(storage_));
|
|
chars.resize(cur); // Restrict to output length
|
|
|
|
if (chars.empty()) chars.clearStorage(); // Can destroy now
|
|
sync_pbuffer();
|
|
return chars;
|
|
}
|
|
|
|
|
|
// Housekeeping
|
|
|
|
//- Same as shrink_to_fit()
|
|
void shrink() { shrink_to_fit(); }
|
|
};
|
|
|
|
|
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
|
|
|
} // End namespace Foam
|
|
|
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
|
|
|
#endif
|
|
|
|
// ************************************************************************* //
|