mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
- enables partial overwriting of content
- make default construct zero-sized, add reserve_exact() method
- improve sizing behaviour of OCharStream.
Since char buffers will approach the INT_MAX size more quickly than
other content, adapt the following strategy:
| Capacity range | Strategy |
|--------------------|------------------------------|
| 0 < N <= 0.25) | fast growth (2) |
| 0.25 < N <= 0.5) | slower growth (1.5) |
| 0.5 < N <= 0.75) | very slow growth (1.25) |
| 0.75 < N | already large - use max |
474 lines
11 KiB
C++
474 lines
11 KiB
C++
/*---------------------------------------------------------------------------*\
|
|
========= |
|
|
\\ / 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 <charconv>
|
|
#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;
|
|
buf.print(Info);
|
|
Info<< "addr: " << Foam::name(buf.view().data()) << nl;
|
|
toString(Info, buf.view());
|
|
Info<< nl << "=========================" << endl;
|
|
}
|
|
|
|
|
|
// Return a left-padded integer as "word"
|
|
template<class IntType>
|
|
std::string leftpadded(IntType val, char fillch = ' ')
|
|
{
|
|
std::string buf;
|
|
buf.resize((std::numeric_limits<IntType>::digits10+1), fillch);
|
|
|
|
auto first = (buf.data());
|
|
auto last = (buf.data() + buf.size());
|
|
|
|
auto result = std::to_chars(first, last, val);
|
|
|
|
if (result.ec == std::errc{})
|
|
{
|
|
auto* iter = result.ptr;
|
|
int count = std::distance(iter, last);
|
|
|
|
std::cout << "did not fill: " << count << " chars\n";
|
|
|
|
// With two spaces before comments
|
|
if (count > 0) { *iter++ = ' '; --count; }
|
|
if (count > 0) { *iter++ = ' '; --count; }
|
|
for (char c = (count >= 2 ? '/' : ' '); count > 0; --count)
|
|
{
|
|
*iter++ = c;
|
|
}
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
|
|
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::noBanner();
|
|
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);
|
|
|
|
// experiment with to_chars instead of streaming
|
|
{
|
|
// Some value
|
|
label val(1234);
|
|
|
|
auto fixed = leftpadded(val);
|
|
Info<< "leftpadded " << val << " : " << fixed << nl;
|
|
}
|
|
|
|
ocharstream labelbuf;
|
|
labelbuf.reserve_exact(32);
|
|
|
|
// Some value
|
|
labelbuf.rewind();
|
|
rightpad(labelbuf, label(10));
|
|
|
|
printInfo(labelbuf);
|
|
|
|
OCharStream obuf;
|
|
obuf.reserve_exact(48);
|
|
|
|
printInfo(obuf);
|
|
|
|
obuf.push_back('>');
|
|
obuf.append(" string_view ");
|
|
obuf.push_back('<');
|
|
printInfo(obuf);
|
|
|
|
obuf.pop_back(8);
|
|
printInfo(obuf);
|
|
|
|
obuf.pop_back(100);
|
|
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, '_');
|
|
}
|
|
|
|
// append and push_back some content
|
|
obuf.append(5, '<');
|
|
obuf.push_back('#');
|
|
obuf.append(5, '>');
|
|
|
|
printInfo(obuf);
|
|
|
|
obuf.pop_back(8);
|
|
printInfo(obuf);
|
|
|
|
// Slightly silly test
|
|
{
|
|
const auto list = obuf.list();
|
|
Info<< "list content:" << list << nl;
|
|
Info<< "view content:" << nl << list.view() << nl;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
// ************************************************************************* //
|