ENH: add in-place modification for OCharStream

- enables partial overwriting of content
- make default construct zero-sized, add reserve_exact() method

STYLE: minor adjustments for Enum, IOstreamOption
This commit is contained in:
Mark Olesen
2025-09-24 10:36:22 +02:00
parent d39b8a5c94
commit dd136d61ac
21 changed files with 938 additions and 194 deletions

View File

@ -1,3 +1,3 @@
Test-IStringStream.C Test-IStringStream.cxx
EXE = $(FOAM_USER_APPBIN)/Test-IStringStream EXE = $(FOAM_USER_APPBIN)/Test-IStringStream

View File

@ -111,7 +111,10 @@ template<class BufType>
void printInfo(const BufType& buf) void printInfo(const BufType& buf)
{ {
Info<< nl << "=========================" << endl; Info<< nl << "=========================" << endl;
if constexpr (std::is_same_v<OCharStream, BufType>)
{
buf.print(Info); buf.print(Info);
}
Info<< "addr: " << Foam::name(buf.list().cdata()) << nl; Info<< "addr: " << Foam::name(buf.list().cdata()) << nl;
toString(Info, buf.list()); toString(Info, buf.list());
Info<< nl << "=========================" << endl; Info<< nl << "=========================" << endl;
@ -183,12 +186,25 @@ int main(int argc, char *argv[])
printInfo(obuf); printInfo(obuf);
// Overwrite at some position // Overwrite at some position
obuf.stdStream().rdbuf()->pubseekpos(0.60 * obuf.size()); if (auto i = obuf.view().find("item5"); i != std::string::npos)
obuf << "<" << nl << "OVERWRITE" << nl; {
// obuf.seek(0.60 * obuf.size());
obuf.seek(i);
obuf << "<OVERWRITE>" << nl;
}
Info<<"after overwrite" << nl; Info<<"after overwrite" << nl;
printInfo(obuf); printInfo(obuf);
// Truncate
{
constexpr float fraction = 0.90;
Info<<"truncated at " << (100*fraction) << "% ["
<< int(fraction*obuf.size()) << " chars]" << nl;
obuf.seek(fraction*obuf.size());
printInfo(obuf);
}
Info<< "transfer contents to a List or ICharStream" << nl; Info<< "transfer contents to a List or ICharStream" << nl;
// Reclaim data storage from OCharStream -> ICharStream // Reclaim data storage from OCharStream -> ICharStream

View File

@ -0,0 +1,3 @@
Test-OCharStream2.cxx
EXE = $(FOAM_USER_APPBIN)/Test-OCharStream2

View File

@ -0,0 +1,2 @@
/* EXE_INC = */
/* EXE_LIBS = */

View File

@ -0,0 +1,404 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 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/>.
Description
\*---------------------------------------------------------------------------*/
#include "SpanStream.H"
#include "wordList.H"
#include "IOstreams.H"
#include "argList.H"
#include <cctype>
#include <cstdio>
#include <limits>
#include <iomanip>
using namespace Foam;
Ostream& printString(Ostream& os, const char* first, const char* last)
{
os << '"';
for (; first != last; (void)++first)
{
os << *first;
}
os << '"';
return os;
}
Ostream& printView(Ostream& os, const char* first, const char* last)
{
char buf[4];
os << label(last-first) << '(';
for (; first != last; (void)++first)
{
const char c = *first;
if (isprint(c))
{
os << c;
}
else if (c == '\t')
{
os << "\\t";
}
else if (c == '\n')
{
os << "\\n";
}
else
{
::snprintf(buf, 4, "%02X", c);
os << "\\x" << buf;
}
}
os << ')';
return os;
}
Ostream& printView(Ostream& os, std::string_view s)
{
return printView(os, s.begin(), s.end());
}
Ostream& printView(Ostream& os, const UList<char>& list)
{
return printView(os, list.begin(), list.end());
}
Ostream& writeList(Ostream& os, const UList<char>& list)
{
return printView(os, list);
}
Ostream& toString(Ostream& os, const UList<char>& list)
{
return printString(os, list.begin(), list.end());
}
Ostream& toString(Ostream& os, std::string_view s)
{
return printString(os, s.begin(), s.end());
}
template<class BufType>
void printInfo(const BufType& buf)
{
Info<< nl << "=========================" << endl;
if constexpr (std::is_same_v<OCharStream, BufType>)
{
buf.print(Info);
}
Info<< "addr: " << Foam::name(buf.view().data()) << nl;
toString(Info, buf.view());
Info<< nl << "=========================" << endl;
}
template<class IntType>
void leftpad(std::ostream& os, IntType val, char fillch = ' ')
{
// set fill char and width
os.setf(std::ios_base::left, std::ios_base::adjustfield);
fillch = os.fill(fillch);
os.width(std::numeric_limits<IntType>::digits10+1);
os << val;
// restore fill char
os.fill(fillch);
}
template<class IntType>
void rightpad(std::ostream& os, IntType val, char fillch = ' ')
{
// set fill char and width
os.setf(std::ios_base::right, std::ios_base::adjustfield);
fillch = os.fill(fillch);
os.width(std::numeric_limits<IntType>::digits10+1);
os << val;
// restore fill char
os.fill(fillch);
}
// Left-padded value with trailing comment slashes
template<class IntType>
void leftpad(ocharstream& os, IntType val)
{
const auto beg = os.tellp();
os << val;
int count = (std::numeric_limits<IntType>::digits10+1) - (os.tellp() - beg);
// With two spaces before comments
if (count > 0) { os << ' '; --count; }
if (count > 0) { os << ' '; --count; }
for (const char c = (count >= 2 ? '/' : ' '); count > 0; --count)
{
os << c;
}
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Main program:
int main(int argc, char *argv[])
{
argList::noParallel();
argList::addBoolOption("fake-zerosize", "Fake overwriting with zero data");
argList::addBoolOption("dict-format", "Format as dictionary entry");
#include "setRootCase.H"
const bool optFakeZerosize = args.found("fake-zerosize");
const bool isDictFormat = args.found("dict-format");
// const constexpr int width = (std::numeric_limits<label>::digits10+1);
ocharstream labelbuf{charList(32)};
// Some value
labelbuf.rewind();
rightpad(labelbuf, label(10));
printInfo(labelbuf);
OCharStream obuf{charList(32)};
printInfo(obuf);
// Fill with some content
for (int i = 0; i < 26; ++i)
{
obuf<< char('A' + i);
}
// Change letter 'O' to '_'
if (auto i = obuf.view().find('O'); i != std::string::npos)
{
obuf.overwrite(i, '_');
}
printInfo(obuf);
obuf.overwrite(4, labelbuf.view());
printInfo(obuf);
obuf.overwrite(20, "####");
printInfo(obuf);
Info<< "operation Ignored..." << nl;
obuf.overwrite(45, "????");
printInfo(obuf);
// Update with new value
{
labelbuf.rewind();
rightpad(labelbuf, label(200), '.');
obuf.overwrite(4, labelbuf.view());
printInfo(obuf);
}
// With yet another value (non-fixed width)
{
labelbuf.rewind();
labelbuf << label(15);
obuf.overwrite(4, labelbuf.view());
printInfo(obuf);
}
// Slightly harder test
{
std::string chars(26, '?');
for (int i = 0; i < 26; ++i)
{
chars[i] = char('A' + i);
}
auto& os = obuf;
os.rewind();
const word procName("processor0");
// Write as primitiveEntry or commented content
// // constexpr bool isDictFormat = true;
// if constexpr (isDictFormat)
if (isDictFormat)
{
// Like writeKeyword() with compoundToken
os << nl << procName << ' ' << word("List<char>") << nl;
}
else
{
// Human-readable comments
os << nl << "// " << procName << nl;
}
// This is the code we want to have, but assume we don't know
// the size or data beforehand.
//
// if (str && len > 0)
// {
// // Special treatment for char data (binary I/O only)
// const auto oldFmt = os.format(IOstreamOption::BINARY);
//
// os << label(len) << nl;
// os.write(str, len);
// os << nl;
//
// os.format(oldFmt);
// }
// else
// {
// os << label(0) << nl;
// }
// Position before writing the label
const auto labelBegin = os.tellp();
// Replace: os << label(len) << nl;
// with a fixed-length version
{
labelbuf.rewind();
rightpad(labelbuf, 0);
os.append(labelbuf.view());
os << nl;
}
constexpr bool testUnknown = true;
label dataCount = 0;
if constexpr (testUnknown)
{
// Pretend we don't know the number of characters a priori
const auto oldFmt = os.format(IOstreamOption::BINARY);
const auto lineNumber = os.lineNumber();
// count is unknown but irrelevant for serial
os.beginRawWrite(0);
// Position before raw binary data
const auto dataBegin = os.tellp();
// Some type of output, streaming etc
os.writeRaw(chars.data(), chars.size());
// How many chars of binary data written?
dataCount = (os.tellp() - dataBegin);
os.endRawWrite();
os.lineNumber() = lineNumber;
os << nl;
os.format(oldFmt);
}
else
{
// If we had all data collected a priori
dataCount = chars.size();
const auto oldFmt = os.format(IOstreamOption::BINARY);
if (dataCount > 0)
{
os.write(chars.data(), chars.size());
os << nl;
}
os.format(oldFmt);
}
if (optFakeZerosize)
{
dataCount = 0; // fake zero-size
}
printInfo(os);
// Update the data count with the correct value
if (dataCount > 0)
{
labelbuf.rewind();
leftpad(labelbuf, label(dataCount));
os.overwrite(labelBegin, labelbuf.view());
}
else
{
os.seek(int64_t(labelBegin)-1);
// if constexpr (isDictFormat)
if (isDictFormat)
{
os << ' ' << label(0);
}
else
{
os << nl << label(0) << nl;
}
}
// if constexpr (isDictFormat)
if (isDictFormat)
{
os.endEntry();
}
printInfo(os);
Info<< "view: " << os.view(4, 8) << nl;
Info<< "view: " << os.view(32) << nl;
// Ignores out-of-range
Info<< "view: " << os.view(1000) << nl;
}
Info<< "\nEnd\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -1,3 +1,3 @@
Test-OStringStream.C Test-OStringStream.cxx
EXE = $(FOAM_USER_APPBIN)/Test-OStringStream EXE = $(FOAM_USER_APPBIN)/Test-OStringStream

View File

@ -1,3 +1,3 @@
Test-SHA1.C Test-SHA1.cxx
EXE = $(FOAM_USER_APPBIN)/Test-SHA1 EXE = $(FOAM_USER_APPBIN)/Test-SHA1

View File

@ -64,6 +64,10 @@ int main(int argc, char * argv[])
osha<< str; osha<< str;
Info<< osha.digest() << " : output << to empty" << nl; Info<< osha.digest() << " : output << to empty" << nl;
osha.reset();
osha<< std::string_view(str);
Info<< osha.digest() << " : << string_view [ not quite right !]" << nl;
sha.clear(); sha.clear();
sha.append(str); sha.append(str);
shaDig = sha; shaDig = sha;

View File

@ -172,7 +172,7 @@ public:
//- Emit begin marker for low-level raw binary output. //- Emit begin marker for low-level raw binary output.
// The count indicates the number of bytes for subsequent // The count indicates the number of bytes for subsequent
// writeRaw calls. // writeRaw calls [currently only used Pstream].
virtual bool beginRawWrite(std::streamsize count) = 0; virtual bool beginRawWrite(std::streamsize count) = 0;
//- Emit end marker for low-level raw binary output. //- Emit end marker for low-level raw binary output.

View File

@ -166,9 +166,9 @@ public:
//- Add (unquoted) string contents. //- Add (unquoted) string contents.
// Ensures that SHA1 of C-string or C++-string content are identical. // Ensures that SHA1 of C-string or C++-string content are identical.
virtual Ostream& write(const std::string& str) override virtual Ostream& write(const std::string& s) override
{ {
return writeQuoted(str, false); // Unquoted! return writeQuoted(s.data(), s.size(), false); // Unquoted!
} }
@ -176,10 +176,7 @@ public:
//- Deprecated(2017-07) clear the SHA1 calculation //- Deprecated(2017-07) clear the SHA1 calculation
// \deprecated(2017-07) - use reset() method // \deprecated(2017-07) - use reset() method
void rewind() void rewind() { stream_.reset(); }
{
stream_.sha1().clear();
}
// Additional constructors and methods (as per v2012 and earlier) // Additional constructors and methods (as per v2012 and earlier)

View File

@ -142,9 +142,12 @@ public:
} }
//- A string_view of buffer contents //- A string_view of buffer contents
auto view() const auto view() const { return buffer_type::view(); }
//- A sub-slice string view of the buffer contents
auto view(size_t pos, size_t len = std::string::npos) const
{ {
return buffer_type::view(); return buffer_type::view(pos, len);
} }
//- For istringstream compatibility, return the buffer as string copy. //- For istringstream compatibility, return the buffer as string copy.
@ -291,17 +294,17 @@ public:
UList<char> list() const { return stream_.list(); } UList<char> list() const { return stream_.list(); }
//- A string_view of buffer contents //- A string_view of buffer contents
auto view() const auto view() const { return stream_.view(); }
//- A sub-slice string view of the buffer contents
auto view(size_t pos, size_t len = std::string::npos) const
{ {
return stream_.view(); return stream_.view(pos, len);
} }
//- For IStringStream compatibility, return the buffer as string copy. //- For IStringStream compatibility, return the buffer as string copy.
// Use sparingly - it creates a full copy!! // Use sparingly - it creates a full copy!!
auto str() const auto str() const { return stream_.str(); }
{
return stream_.str();
}
//- Reset content (copy) //- Reset content (copy)
void reset(const char* buffer, size_t nbytes) void reset(const char* buffer, size_t nbytes)

View File

@ -170,9 +170,12 @@ public:
} }
//- A string_view of buffer contents //- A string_view of buffer contents
auto view() const auto view() const { return buffer_type::view(); }
//- A sub-slice string view of the buffer contents
auto view(size_t pos, size_t len = std::string::npos) const
{ {
return buffer_type::view(); return buffer_type::view(pos, len);
} }
//- For istringstream compatibility, return the buffer as string copy. //- For istringstream compatibility, return the buffer as string copy.
@ -352,17 +355,17 @@ public:
UList<char> list() const { return stream_.list(); } UList<char> list() const { return stream_.list(); }
//- A string_view of buffer contents //- A string_view of buffer contents
auto view() const auto view() const { return stream_.view(); }
//- A sub-slice string view of the buffer contents
auto view(size_t pos, size_t len = std::string::npos) const
{ {
return stream_.view(); return stream_.view(pos, len);
} }
//- For IStringStream compatibility, return the buffer as string copy. //- For IStringStream compatibility, return the buffer as string copy.
// Use sparingly - it creates a full copy!! // Use sparingly - it creates a full copy!!
auto str() const auto str() const { return stream_.str(); }
{
return stream_.str();
}
//- Reset input area, position to buffer start and clear errors //- Reset input area, position to buffer start and clear errors
void reset(const char* buffer, size_t nbytes) void reset(const char* buffer, size_t nbytes)

View File

@ -31,8 +31,7 @@ Description
Similar to OStringStream but with a List for its storage instead of Similar to OStringStream but with a List for its storage instead of
as string to allow reuse of List contents without copying. as string to allow reuse of List contents without copying.
The default initial size is 512-bytes and uses size doubling. Internally imposes a 512 byte min-size and uses capacity doubling.
After construction can use the reserve() method to adjust this.
See Also See Also
Foam::ICharStream Foam::ICharStream
@ -124,12 +123,20 @@ public:
return buffer_type::span_capacity(); return buffer_type::span_capacity();
} }
//- Reserve output space for at least this amount //- Reserve output space for at least this amount.
void reserve(const std::streamsize n) //- Applies a min-size and capacity doubling.
void reserve(std::streamsize n)
{ {
buffer_type::reserve(n); buffer_type::reserve(n);
} }
//- Reserve output space for at least this amount.
//- Does not apply min-size or capacity doubling etc.
void reserve_exact(std::streamsize n)
{
buffer_type::reserve_exact(n);
}
//- Rewind the stream, clearing any old errors //- Rewind the stream, clearing any old errors
void rewind() void rewind()
{ {
@ -137,6 +144,25 @@ public:
stream_type::clear(); // Clear old errors stream_type::clear(); // Clear old errors
} }
//- Reposition the stream from the start
void seek(std::streampos pos)
{
if (buffer_type::in_range(pos))
{
buffer_type::pubseekpos(pos, std::ios_base::out);
stream_type::clear(); // Clear old errors
}
}
//- A string_view of buffer contents
auto view() const { return buffer_type::view(); }
//- A sub-slice string view of the buffer contents
auto view(size_t pos, size_t len = std::string::npos) const
{
return buffer_type::view(pos, len);
}
//- Span of the current output characters (is modifiable!) //- Span of the current output characters (is modifiable!)
UList<char> list() const UList<char> list() const
{ {
@ -147,12 +173,6 @@ public:
); );
} }
//- A string_view of buffer contents
auto view() const
{
return buffer_type::view();
}
//- For ostringstream compatibility, return the buffer as string copy. //- For ostringstream compatibility, return the buffer as string copy.
// Use sparingly - it creates a full copy!! // Use sparingly - it creates a full copy!!
std::string str() const std::string str() const
@ -192,6 +212,34 @@ public:
{ {
os << "put=" << output_pos() << '/' << capacity(); os << "put=" << output_pos() << '/' << capacity();
} }
// Extra/Convenience Methods
// No append functions - just use write(...)
//- Overwrite a sub-slice with character content
void overwrite
(
std::streampos pos,
const char* data,
std::streamsize count
)
{
buffer_type::overwrite(pos, data, count);
}
//- Overwrite a single character
void overwrite(std::streampos pos, char c)
{
buffer_type::overwrite(pos, c);
}
//- The output data (start of output characters)
const char* cdata_bytes() const { return buffer_type::cdata_bytes(); }
//- The output data (start of output characters)
char* data_bytes() { return buffer_type::data_bytes(); }
}; };
@ -268,23 +316,27 @@ public:
std::streamsize capacity() const { return stream_.capacity(); } std::streamsize capacity() const { return stream_.capacity(); }
//- Reserve output space for at least this amount //- Reserve output space for at least this amount
void reserve(const std::streamsize n) { stream_.reserve(n); } void reserve(std::streamsize n) { stream_.reserve(n); }
//- Reserve output space for at least this amount.
//- Does not apply min-size or capacity doubling etc.
void reserve_exact(std::streamsize n) { stream_.reserve_exact(n); }
//- A string_view of buffer contents
auto view() const { return stream_.view(); }
//- A sub-slice string view of the buffer contents
auto view(size_t pos, size_t len = std::string::npos) const
{
return stream_.view(pos, len);
}
//- Span of the current output characters (is modifiable!) //- Span of the current output characters (is modifiable!)
UList<char> list() const { return stream_.list(); } UList<char> list() const { return stream_.list(); }
//- A string_view of buffer contents //- For OStringStream compatibility, return buffer as string copy.
auto view() const
{
return stream_.view();
}
//- For OStringStream compatibility, return the buffer as string copy.
// Use sparingly - it creates a full copy!! // Use sparingly - it creates a full copy!!
auto str() const auto str() const { return stream_.str(); }
{
return stream_.str();
}
//- Exchange stream content and parameter contents, reset positions //- Exchange stream content and parameter contents, reset positions
void swap(List<char>& other) void swap(List<char>& other)
@ -320,7 +372,55 @@ public:
virtual void print(Ostream& os) const override; virtual void print(Ostream& os) const override;
// Houskeeping // Extra/Convenience Methods
//- Reposition the stream from the start
void seek(std::streampos pos)
{
stream_.seek(pos);
syncState();
}
//- Append character content - same as writeRaw()
void append(const char* data, std::streamsize count)
{
if (data && count > 0)
{
writeRaw(data, count);
}
}
//- Append character content - same as writeRaw()
void append(std::string_view sv)
{
append(sv.data(), sv.size());
}
//- Overwrite a sub-slice with character content
void overwrite
(
std::streampos pos,
const char* data,
std::streamsize count
)
{
stream_.overwrite(pos, data, count);
}
//- Overwrite a sub-slice with character content
void overwrite(std::streampos pos, std::string_view sv)
{
stream_.overwrite(pos, sv.data(), sv.size());
}
//- Overwrite a single character
void overwrite(std::streampos pos, char c)
{
stream_.overwrite(pos, c);
}
// Housekeeping
//- Block size was used in OpenFOAM-v2306 and earlier //- Block size was used in OpenFOAM-v2306 and earlier
void setBlockSize(int n) {} void setBlockSize(int n) {}

View File

@ -27,7 +27,7 @@ Class
Foam::OSpanStream Foam::OSpanStream
Description Description
Similar to OStringStream but using an externally managed buffer for Similar to OCharStream but using an externally managed buffer for
its output. its output.
This allows the output buffer to be reused and can make it easier when This allows the output buffer to be reused and can make it easier when
@ -152,6 +152,32 @@ public:
return buffer_type::span_capacity(); return buffer_type::span_capacity();
} }
//- Rewind the stream, clearing any old errors
void rewind()
{
buffer_type::pubseekpos(0, std::ios_base::out);
stream_type::clear(); // Clear old errors
}
//- Reposition the stream from the start
void seek(std::streampos pos)
{
if (buffer_type::in_range(pos))
{
buffer_type::pubseekpos(pos, std::ios_base::out);
stream_type::clear(); // Clear old errors
}
}
//- A string_view of buffer contents
auto view() const { return buffer_type::view(); }
//- A sub-slice string view of the buffer contents
auto view(size_t pos, size_t len = std::string::npos) const
{
return buffer_type::view(pos, len);
}
//- Span of the current output characters (is modifiable!) //- Span of the current output characters (is modifiable!)
UList<char> list() const UList<char> list() const
{ {
@ -162,12 +188,6 @@ public:
); );
} }
//- A string_view of buffer contents
auto view() const
{
return buffer_type::view();
}
//- For ostringstream compatibility, return the buffer as string copy. //- For ostringstream compatibility, return the buffer as string copy.
// Use sparingly - it creates a full copy!! // Use sparingly - it creates a full copy!!
std::string str() const std::string str() const
@ -179,18 +199,11 @@ public:
); );
} }
//- Rewind the stream, clearing any old errors
void rewind()
{
buffer_type::pubseekpos(0, std::ios_base::out);
stream_type::clear(); // Clear any old errors
}
//- Reset the put buffer area //- Reset the put buffer area
void reset(char* buffer, size_t nbytes) void reset(char* buffer, size_t nbytes)
{ {
buffer_type::resetp(buffer, nbytes); buffer_type::resetp(buffer, nbytes);
stream_type::clear(); // Clear any old errors stream_type::clear(); // Clear old errors
} }
//- Reset the put buffer area to use the data area from a string //- Reset the put buffer area to use the data area from a string
@ -198,7 +211,7 @@ public:
{ {
s.resize(s.capacity()); s.resize(s.capacity());
buffer_type::resetp(s.data(), s.size()); buffer_type::resetp(s.data(), s.size());
stream_type::clear(); // Clear any old errors stream_type::clear(); // Clear old errors
} }
//- Some information about the output buffer position/capacity //- Some information about the output buffer position/capacity
@ -206,6 +219,34 @@ public:
{ {
os << "put=" << output_pos() << '/' << capacity(); os << "put=" << output_pos() << '/' << capacity();
} }
// Extra/Convenience Methods
// No append functions - just use write(...)
//- Overwrite a sub-slice with character content
void overwrite
(
std::streampos pos,
const char* data,
std::streamsize count
)
{
buffer_type::overwrite(pos, data, count);
}
//- Overwrite a single character
void overwrite(std::streampos pos, char c)
{
buffer_type::overwrite(pos, c);
}
//- The output data (start of output characters)
const char* cdata_bytes() const { return buffer_type::cdata_bytes(); }
//- The output data (start of output characters)
char* data_bytes() { return buffer_type::data_bytes(); }
}; };
@ -302,21 +343,21 @@ public:
//- The put buffer capacity //- The put buffer capacity
std::streamsize capacity() const { return stream_.capacity(); } std::streamsize capacity() const { return stream_.capacity(); }
//- A string_view of buffer contents
auto view() const { return stream_.view(); }
//- A sub-slice string view of the buffer contents
auto view(size_t pos, size_t len = std::string::npos) const
{
return stream_.view(pos, len);
}
//- Span of the current output characters (is modifiable!) //- Span of the current output characters (is modifiable!)
UList<char> list() const { return stream_.list(); } UList<char> list() const { return stream_.list(); }
//- A string_view of buffer contents
auto view() const
{
return stream_.view();
}
//- For OStringStream compatibility, return buffer as string copy. //- For OStringStream compatibility, return buffer as string copy.
// Use sparingly - it creates a full copy!! // Use sparingly - it creates a full copy!!
auto str() const auto str() const { return stream_.str(); }
{
return stream_.str();
}
//- Reset the put area //- Reset the put area
void reset(char* buffer, size_t nbytes) void reset(char* buffer, size_t nbytes)
@ -341,6 +382,54 @@ public:
//- Print stream description to Ostream //- Print stream description to Ostream
virtual void print(Ostream& os) const override; virtual void print(Ostream& os) const override;
// Extra/Convenience Methods
//- Reposition the stream from the start
void seek(std::streampos pos)
{
stream_.seek(pos);
syncState();
}
//- Append character content - same as writeRaw()
void append(const char* data, std::streamsize count)
{
if (data && count > 0)
{
writeRaw(data, count);
}
}
//- Append character content - same as writeRaw()
void append(std::string_view sv)
{
append(sv.data(), sv.size());
}
//- Overwrite a sub-slice with character content
void overwrite
(
std::streampos pos,
const char* data,
std::streamsize count
)
{
stream_.overwrite(pos, data, count);
}
//- Overwrite a sub-slice with character content
void overwrite(std::streampos pos, std::string_view sv)
{
stream_.overwrite(pos, sv.data(), sv.size());
}
//- Overwrite a single character
void overwrite(std::streampos pos, char c)
{
stream_.overwrite(pos, c);
}
}; };

View File

@ -35,10 +35,10 @@ Description
#ifndef Foam_memoryStreamBuffer_H #ifndef Foam_memoryStreamBuffer_H
#define Foam_memoryStreamBuffer_H #define Foam_memoryStreamBuffer_H
#include "stdFoam.H" // For span
#include "DynamicList.H" #include "DynamicList.H"
#include <memory> #include <memory>
#include <string>
#include <type_traits> #include <type_traits>
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -213,11 +213,44 @@ public:
//- The span size (number of input characters) //- The span size (number of input characters)
std::streamsize size_bytes() const { return (egptr() - eback()); } std::streamsize size_bytes() const { return (egptr() - eback()); }
std::string_view view() const //- 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()); 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 //- Some information about the input buffer position/capacity
void info(Ostream& os) const void info(Ostream& os) const
{ {
@ -390,17 +423,81 @@ public:
//- The put buffer capacity //- The put buffer capacity
std::streamsize span_capacity() const { return (epptr() - pbase()); } std::streamsize span_capacity() const { return (epptr() - pbase()); }
//- The span data (start of output characters)
const char* cdata_bytes() const { return pbase(); }
//- The span data (start of output characters) //- The span data (start of output characters)
char* data_bytes() const { return pbase(); } char* data_bytes() const { return pbase(); }
//- The span size (size of output buffer) //- The span size (size of output buffer)
std::streamsize size_bytes() const { return (pptr() - pbase()); } std::streamsize size_bytes() const { return (pptr() - pbase()); }
std::string_view view() const //- 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()); 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();
}
}
//- 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() <= std::streampos(pos+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 //- Some information about the output buffer position/capacity
void info(Ostream& os) const void info(Ostream& os) const
{ {
@ -433,8 +530,8 @@ protected:
{ {
if (c != traits_type::eof()) if (c != traits_type::eof())
{ {
// Need more space? // Needed more space?
reserve(1 + span_tellp()); extend(1);
*(pptr()) = c; *(pptr()) = c;
pbump(1); pbump(1);
@ -446,7 +543,7 @@ protected:
virtual std::streamsize xsputn(const char* s, std::streamsize n) virtual std::streamsize xsputn(const char* s, std::streamsize n)
{ {
// Enough space so that appends work without problem // Enough space so that appends work without problem
reserve(n + span_tellp()); extend(n);
std::streamsize count = 0; std::streamsize count = 0;
while (count < n && pptr() < epptr()) while (count < n && pptr() < epptr())
@ -463,10 +560,14 @@ public:
// Constructors // Constructors
//- Default construct - no initial reserved number of bytes.
out_dynamic()
{
sync_pbuffer();
}
//- Default construct with initial reserved number of bytes. //- Default construct with initial reserved number of bytes.
// The value of 512 is a bit arbitrary, but consistent with out_dynamic(size_t nbytes)
// std::stringstream
out_dynamic(size_t nbytes = 512)
: :
storage_(label(nbytes)) storage_(label(nbytes))
{ {
@ -493,40 +594,88 @@ public:
// Member Functions // Member Functions
//- Increment capacity (if needed) and adjust buffer pointers //- 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) void reserve(const std::streamsize len)
{ {
// The min-size : 512 bytes is a bit arbitrary
// but consistent with std::stringstream
constexpr label minSize = 512;
if (storage_.size() < len) if (storage_.size() < len)
{ {
const auto cur = span_tellp(); // Current location const auto cur = span_tellp(); // Output position
label newCapacity = 512; label size = minSize;
if (newCapacity < len) if (size < len)
{ {
// Increase capacity (doubling) // ratio=2 :
newCapacity = size = label(2*storage_.size());
Foam::max(label(len), label(2*storage_.size()));
// ratio=1.5: // ratio=1.5 :
// Foam::max(label(len), label((storage_.size()/2)*3)); // size = label((storage_.size()/2)*3);
// max(size, len)
if (size < len)
{
size = len;
}
} }
// Info<<"request:" << len // Info<<"request:" << len
// << " cur cap:" << storage_.size() // << " cur cap:" << storage_.size()
// << " new cap:" << newCapacity // << " new cap:" << size
// << " pos:" << cur << endl; // << " pos:" << cur << endl;
storage_.resize(newCapacity); storage_.resize(size);
sync_pbuffer(); sync_pbuffer();
pbump(cur); pbump(cur);
} }
} }
//- Sync put buffer pointers to agree with list dimensions //- Increase capacity for at least this size.
// Sets put pointer to the begin (rewind). //- Does not apply min-size or capacity doubling etc.
void sync_pbuffer() void reserve_exact(const std::streamsize len)
{ {
resetp(storage_.data(), storage_.size()); if (storage_.size() < len)
{
const auto cur = span_tellp(); // Output position
storage_.resize(len);
sync_pbuffer();
pbump(cur);
}
}
//- Shrink storage to addressed storage
void shrink_to_fit()
{
const auto cur = span_tellp(); // Addressed length
storage_.resize(cur);
sync_pbuffer();
pbump(cur);
}
//- Increase (reserve) space for another \c count entries
void extend(const std::streamsize count)
{
reserve(span_tellp() + count);
}
//- Increase (reserve) space for another \c count entries
void extend_exact(const std::streamsize count)
{
reserve_exact(span_tellp() + count);
} }
//- Clear storage //- Clear storage
@ -536,20 +685,10 @@ public:
sync_pbuffer(); sync_pbuffer();
} }
//- Shrink storage to addressed storage
void shrink()
{
const auto cur = span_tellp(); // Addressed length
storage_.resize(cur);
sync_pbuffer();
pbump(cur);
}
//- Exchange buffer content and parameter contents, reset positions //- Exchange buffer content and parameter contents, reset positions
void swap(List<char>& other) void swap(List<char>& other)
{ {
const auto cur = span_tellp(); // Output length const auto cur = span_tellp(); // Addressed length
other.swap(storage_); other.swap(storage_);
other.resize(cur); // Truncate to output length other.resize(cur); // Truncate to output length
sync_pbuffer(); sync_pbuffer();
@ -579,6 +718,12 @@ public:
sync_pbuffer(); sync_pbuffer();
return chars; return chars;
} }
// Housekeeping
//- Same as shrink_to_fit()
void shrink() { shrink_to_fit(); }
}; };

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2018-2024 OpenCFD Ltd. Copyright (C) 2018-2025 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -54,6 +54,7 @@ Foam::IOstreamOption::formatNames
({ ({
{ streamFormat::ASCII, "ascii" }, { streamFormat::ASCII, "ascii" },
{ streamFormat::BINARY, "binary" }, { streamFormat::BINARY, "binary" },
// No selection by name: UNKNOWN_FORMAT
}); });
@ -66,24 +67,22 @@ Foam::IOstreamOption::floatFormatEnum
const floatFormat deflt const floatFormat deflt
) )
{ {
// Handle bad input graciously. A no-op for an empty string if (fmtName.empty())
if (!fmtName.empty())
{ {
auto iter = floatFormatNames.cfind(fmtName); // Empty string (no-op)
}
if (iter.good()) else if (auto iter = floatFormatNames.cfind(fmtName); iter.good())
{ {
return iter.val(); return iter.val();
} }
else
// Fall-through to warning {
// Emit warning for bad input
auto& err = WarningInFunction auto& err = WarningInFunction
<< "Unknown float format '" << fmtName << "' using "; << "Unknown float format '" << fmtName << "' using ";
iter = floatFormatNames.cfind(deflt); if (auto iter = floatFormatNames.cfind(deflt); iter.good())
if (iter.good())
{ {
err << '\'' << iter.key() << '\''; err << '\'' << iter.key() << '\'';
} }
@ -117,24 +116,22 @@ Foam::IOstreamOption::formatEnum
const streamFormat deflt const streamFormat deflt
) )
{ {
// Handle bad input graciously. A no-op for an empty string if (fmtName.empty())
if (!fmtName.empty())
{ {
auto iter = formatNames.cfind(fmtName); // Empty string (no-op)
}
if (iter.good()) else if (auto iter = formatNames.cfind(fmtName); iter.good())
{ {
return iter.val(); return iter.val();
} }
else
// Fall-through to warning {
// Emit warning for bad input
auto& err = WarningInFunction auto& err = WarningInFunction
<< "Unknown stream format '" << fmtName << "' using "; << "Unknown stream format '" << fmtName << "' using ";
iter = formatNames.cfind(deflt); if (auto iter = formatNames.cfind(deflt); iter.good())
if (iter.good())
{ {
err << '\'' << iter.key() << '\''; err << '\'' << iter.key() << '\'';
} }
@ -168,13 +165,11 @@ Foam::IOstreamOption::compressionEnum
const compressionType deflt const compressionType deflt
) )
{ {
// Handle bad input graciously. A no-op for an empty string if (compName.empty())
if (!compName.empty())
{ {
const Switch sw = Switch::find(compName); // Empty string (no-op)
}
if (sw.good()) else if (Switch sw = Switch::find(compName); sw.good())
{ {
return return
( (
@ -183,8 +178,9 @@ Foam::IOstreamOption::compressionEnum
: compressionType::UNCOMPRESSED : compressionType::UNCOMPRESSED
); );
} }
else
// Fall-through to warning {
// Emit warning
WarningInFunction WarningInFunction
<< "Unknown compression specifier '" << compName << "Unknown compression specifier '" << compName

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011-2015 OpenFOAM Foundation Copyright (C) 2011-2015 OpenFOAM Foundation
Copyright (C) 2018-2024 OpenCFD Ltd. Copyright (C) 2018-2025 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -67,11 +67,13 @@ public:
// Public Data Types // Public Data Types
//- Data format (ascii | binary) //- Data format (ascii | binary | coherent)
enum streamFormat : char enum streamFormat : char
{ {
ASCII = 0, //!< "ascii" (normal default) ASCII = 0, //!< "ascii" (normal default)
BINARY //!< "binary" BINARY, //!< "binary"
COHERENT, //!< "coherent"
UNKNOWN_FORMAT
}; };
//- Compression treatment (UNCOMPRESSED | COMPRESSED) //- Compression treatment (UNCOMPRESSED | COMPRESSED)

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2017-2024 OpenCFD Ltd. Copyright (C) 2017-2025 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -242,12 +242,12 @@ public:
//- Return the enumeration corresponding to the given name //- Return the enumeration corresponding to the given name
// FatalError if the name is not found. // FatalError if the name is not found.
// Identical to get() // Identical to get()
inline EnumType operator[](const word& enumName) const; EnumType operator[](const word& k) const { return get(k); }
//- Return the first name corresponding to the given enumeration, //- Return the first name corresponding to the given enumeration,
//- or an empty word on failure. //- or an empty word if not found.
// Identical to get() // Identical to get()
inline const word& operator[](const EnumType e) const; const word& operator[](EnumType e) const { return get(e); }
// Iteration // Iteration
@ -256,11 +256,8 @@ public:
// \note The iterator dereference returns the \b key // \note The iterator dereference returns the \b key
class const_iterator class const_iterator
{ {
//- The list being iterated const Enum* ptr_; //!< The list being iterated upon
const Enum* ptr_; label idx_; //!< Index in the list
//- Index in the list
label idx_;
public: public:
@ -280,6 +277,9 @@ public:
//- Enumeration value at the current index //- Enumeration value at the current index
inline EnumType val() const; inline EnumType val() const;
//- True if iterator points to an entry
explicit operator bool() const noexcept { return good(); }
//- De-referencing returns the name (key) //- De-referencing returns the name (key)
// This is similar to HashSet (not HashTable!) and allows // This is similar to HashSet (not HashTable!) and allows
// convenient output and traversing of the names // convenient output and traversing of the names
@ -375,6 +375,8 @@ public:
}; };
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Ostream Operator // Ostream Operator
//- Write enumeration names, without line-breaks (ie, FlatOutput) //- Write enumeration names, without line-breaks (ie, FlatOutput)

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2017-2024 OpenCFD Ltd. Copyright (C) 2017-2025 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -86,9 +86,9 @@ inline void Foam::Enum<EnumType>::clear()
template<class EnumType> template<class EnumType>
inline bool Foam::Enum<EnumType>::contains(const word& enumName) const inline bool Foam::Enum<EnumType>::contains(const word& key) const
{ {
return keys_.contains(enumName); return (!key.empty() && keys_.contains(key));
} }
@ -242,7 +242,7 @@ template<class EnumType>
inline typename Foam::Enum<EnumType>::const_iterator inline typename Foam::Enum<EnumType>::const_iterator
Foam::Enum<EnumType>::cfind(const word& key) const Foam::Enum<EnumType>::cfind(const word& key) const
{ {
const label idx = keys_.find(key); const label idx = (key.empty() ? -1 : keys_.find(key));
return typename Enum<EnumType>::const_iterator return typename Enum<EnumType>::const_iterator
( (
@ -266,28 +266,6 @@ Foam::Enum<EnumType>::cfind(const EnumType e) const
} }
// * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
template<class EnumType>
inline EnumType Foam::Enum<EnumType>::operator[]
(
const word& enumName
) const
{
return get(enumName);
}
template<class EnumType>
inline const Foam::word& Foam::Enum<EnumType>::operator[]
(
const EnumType e
) const
{
return get(e);
}
// * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * // // * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
template<class EnumType> template<class EnumType>