Files
openfoam/src/OpenFOAM/db/IOstreams/memory/memoryStreamBuffer.H
Mark Olesen e9fcd75ec4 ENH: add default "constant" name for fileOperations::findTimes()
- 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
2025-10-12 15:53:26 +02:00

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
// ************************************************************************* //