Merge branch 'modifiable-memory-streams' into 'develop'

Improve handling and sizing behaviour of memory streams

See merge request Development/openfoam!755
This commit is contained in:
Kutalmış Berçin
2025-10-01 15:43:01 +01:00
32 changed files with 2771 additions and 273 deletions

View File

@ -183,12 +183,25 @@ int main(int argc, char *argv[])
printInfo(obuf);
// Overwrite at some position
obuf.stdStream().rdbuf()->pubseekpos(0.60 * obuf.size());
obuf << "<" << nl << "OVERWRITE" << nl;
if (auto i = obuf.view().find("item5"); i != std::string::npos)
{
// obuf.seek(0.60 * obuf.size());
obuf.seek(i);
obuf << "<OVERWRITE>" << nl;
}
Info<<"after overwrite" << nl;
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;
// 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,473 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / 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;
}
// ************************************************************************* //

View File

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

View File

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

View File

@ -0,0 +1,260 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 Mark Olesen
-------------------------------------------------------------------------------
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/>.
Application
Test-parallel-file-write1
Description
Simple test of writing with MPI/IO
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "Time.H"
#include "Switch.H"
#include "UPstreamFile.H"
#include "SpanStream.H"
using namespace Foam;
template<class IntType>
void zeropadded(std::ostream& os, IntType val, char fillch = '0')
{
// 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);
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::noCheckProcessorDirectories();
argList::addVerboseOption();
argList::addBoolOption("master-footer", "Write footer from master");
#include "setRootCase.H"
if (!UPstream::parRun())
{
Info<< "###############" << nl
<< "Not running in parallel. Stopping now" << nl
<< "###############" << endl;
return 1;
}
const bool optMasterFooter = args.found("master-footer");
Info<< nl << "Write master-footer: " << Switch::name(optMasterFooter)
<< nl << nl;
Info<< "Create time (without controlDict)\n" << endl;
auto runTimePtr = Time::New();
auto& runTime = runTimePtr();
const auto myProc = UPstream::myProcNo();
const auto nProcs = UPstream::nProcs();
// Some content
OCharStream charset;
for (int i = 0; i < 10; ++i)
{
charset<< char('A' + i);
}
// Header/footer buffers - these can be separate or bundled into
// the first/last blocks
OCharStream header;
OCharStream footer;
// Content buffer
OCharStream os(IOstream::BINARY);
{
const auto v = charset.view();
os << nl;
os.beginBlock(word("rank" + Foam::name(myProc)));
for (int repeat = 0; repeat <= myProc; ++repeat)
{
os << indent << word("entry" + Foam::name(repeat))
<< ' ' << word("List<char>");
// os << nl;
os << ' ';
os << label(v.size());
os.write(v.data(), v.size());
// os << nl;
os.endEntry();
}
os.endBlock();
}
// Bundle the footer into the last block
if (!optMasterFooter && (myProc == nProcs-1))
{
IOobject::writeEndDivider(os);
}
// All content now exists - commit to disk
const std::string_view blockData(os.view());
const int64_t blockSize(blockData.size());
// Collect sizes
const List<int64_t> sizes
(
UPstream::allGatherValues(blockSize, UPstream::worldComm)
);
// Format header with size information
if (UPstream::master())
{
header
<< "Simple MPI/IO test with " << nProcs << " ranks" << nl << nl;
ocharstream labelbuf;
labelbuf.reserve_exact(32);
// Position before writing a label
auto labelBegin = header.tellp();
header.beginBlock("meta");
{
header << indent << word("data.start") << ' ';
labelBegin = header.tellp();
// Add the start value (placeholder)
{
labelbuf.rewind();
zeropadded(labelbuf, label(0));
header.append(labelbuf.view());
}
header.endEntry();
header << indent << word("data.sizes") << nl;
sizes.writeList(header); // flatOutput
header.endEntry();
}
header.endBlock();
header << nl;
IOobject::writeDivider(header);
// Now update with the correct size
{
labelbuf.rewind();
zeropadded(labelbuf, label(header.view().size()));
header.overwrite(labelBegin, labelbuf.view());
}
// Bundled the footer into the last block or from master?
if (optMasterFooter)
{
IOobject::writeEndDivider(footer);
}
}
// With additional header/footer
int64_t headerSize(header.view().size());
int64_t footerSize(footer.view().size());
Pstream::broadcast(headerSize);
if (optMasterFooter)
{
Pstream::broadcast(footerSize);
}
int64_t totalSize(headerSize);
for (int i = 0; i < myProc; ++i)
{
totalSize += sizes[i];
}
const int64_t blockOffset(totalSize);
for (int i = myProc; i < nProcs; ++i)
{
totalSize += sizes[i];
}
const int64_t footerOffset(totalSize);
totalSize += footerSize;
Pout<< "write size=" << label(blockSize)
<< " at=" << label(blockOffset) << " total=" << label(totalSize) << nl;
{
UPstream::File file;
bool ok = file.open_write
(
UPstream::worldComm,
runTime.globalPath()/"mpiio-test1.txt",
IOstreamOption::ATOMIC
);
if (ok)
{
Info<< "writing: " << file.name() << nl;
if (UPstream::master())
{
// A no-op for empty buffer
ok = file.write_at(0, header.view());
}
ok = file.write_at_all(blockOffset, blockData);
if (UPstream::master())
{
// A no-op for empty buffer
ok = file.write_at(footerOffset, footer.view());
}
}
file.set_size(totalSize);
file.close();
}
Info<< "\nEnd\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -91,19 +91,14 @@ class DynamicList
template<class ListType>
inline void doAssignDynList(const ListType& list);
//- Alter the size of the underlying storage
// The 'nocopy' option will not attempt to recover old content
inline void doCapacity(const bool nocopy, const label len);
//- Alter the size of the underlying storage,
//- retaining the first count elements.
inline void doCapacity_copy(label count, const label len);
//- Reserve allocation space for at least this size.
//- Reserve allocation space for at least this size,
//- retaining the first count elements.
// Never shrinks the allocated size, use setCapacity() for that.
// The 'nocopy' option will not attempt to recover old content
inline void doReserve(const bool nocopy, const label len);
//- Reserve allocation space for at least this size.
// Never shrinks the allocated size, use setCapacity() for that.
// The 'nocopy' option will not attempt to recover old content
inline void doResize(const bool nocopy, const label len);
inline void doReserve_copy(label count, const label len);
//- Read List from Istream between '(' and ')' delimiters.
//- The size is not known a priori.
@ -232,6 +227,10 @@ public:
//- Alter addressable size and fill \em new entries with constant value
inline void resize(const label len, const T& val);
//- Alter addressable size, retaining the first count contents.
// \note Only uses a limited number of internal checks.
void resize_copy(label count, const label len);
//- Alter addressable size and set val for \em all addressed entries
inline void resize_fill(const label len, const T& val);

View File

@ -54,9 +54,9 @@ inline void Foam::DynamicList<T, SizeMin>::doAssignDynList
template<class T, int SizeMin>
inline void Foam::DynamicList<T, SizeMin>::doCapacity
inline void Foam::DynamicList<T, SizeMin>::doCapacity_copy
(
const bool nocopy,
label count,
const label newCapacity
)
{
@ -68,16 +68,22 @@ inline void Foam::DynamicList<T, SizeMin>::doCapacity
// Addressable length, possibly truncated by new capacity
const label currLen = Foam::min(List<T>::size(), newCapacity);
// The count truncated by the new addressable range
if (count > currLen)
{
count = currLen;
}
// Consistent allocated sizing
List<T>::setAddressableSize(capacity_);
if (nocopy)
if (count > 0)
{
List<T>::resize_nocopy(newCapacity);
List<T>::resize_copy(count, newCapacity);
}
else
{
List<T>::resize_copy(currLen, newCapacity);
List<T>::resize_nocopy(newCapacity);
}
capacity_ = List<T>::size();
@ -86,9 +92,9 @@ inline void Foam::DynamicList<T, SizeMin>::doCapacity
template<class T, int SizeMin>
inline void Foam::DynamicList<T, SizeMin>::doReserve
inline void Foam::DynamicList<T, SizeMin>::doReserve_copy
(
const bool nocopy,
label count,
const label len
)
{
@ -97,20 +103,27 @@ inline void Foam::DynamicList<T, SizeMin>::doReserve
// Preserve addressed size
const label currLen = List<T>::size();
// The count truncated by the addressable range
if (count > currLen)
{
count = currLen;
}
// Consistent allocated sizing
List<T>::setAddressableSize(capacity_);
// Increase capacity (eg, doubling)
// - this may need better handling for when lists become very large
capacity_ =
Foam::ListPolicy::reserve_size<SizeMin, 2>(len, capacity_);
if (nocopy)
if (count > 0)
{
List<T>::resize_nocopy(capacity_);
List<T>::resize_copy(count, capacity_);
}
else
{
List<T>::resize_copy(currLen, capacity_);
List<T>::resize_nocopy(capacity_);
}
capacity_ = List<T>::size();
@ -119,18 +132,6 @@ inline void Foam::DynamicList<T, SizeMin>::doReserve
}
template<class T, int SizeMin>
inline void Foam::DynamicList<T, SizeMin>::doResize
(
const bool nocopy,
const label len
)
{
this->doReserve(nocopy, len);
List<T>::setAddressableSize(len);
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template<class T, int SizeMin>
@ -323,7 +324,8 @@ inline void Foam::DynamicList<T, SizeMin>::setCapacity
const label len
)
{
this->doCapacity(false, len); // nocopy = false
// Preserve all content
doCapacity_copy(List<T>::size(), len);
}
@ -333,7 +335,8 @@ inline void Foam::DynamicList<T, SizeMin>::setCapacity_nocopy
const label len
)
{
this->doCapacity(true, len); // nocopy = true
// Preserve 0 content
doCapacity_copy(0, len);
}
@ -343,7 +346,8 @@ inline void Foam::DynamicList<T, SizeMin>::reserve
const label len
)
{
this->doReserve(false, len); // nocopy = false
// Preserve all content
doReserve_copy(List<T>::size(), len);
}
@ -353,7 +357,8 @@ inline void Foam::DynamicList<T, SizeMin>::reserve_nocopy
const label len
)
{
this->doReserve(true, len); // nocopy = true
// Preserve 0 content
doReserve_copy(0, len);
}
@ -371,9 +376,12 @@ inline void Foam::DynamicList<T, SizeMin>::reserve_exact
// Consistent allocated sizing
List<T>::setAddressableSize(capacity_);
// Exact length
capacity_ = len;
// if (!nocopy)
{
List<T>::resize_copy(currLen, len);
List<T>::resize_copy(currLen, capacity_);
}
capacity_ = List<T>::size();
@ -388,7 +396,21 @@ inline void Foam::DynamicList<T, SizeMin>::resize
const label len
)
{
this->doResize(false, len); // nocopy = false
// Preserve all content
resize_copy(List<T>::size(), len);
}
template<class T, int SizeMin>
inline void Foam::DynamicList<T, SizeMin>::resize_copy
(
label count,
const label len
)
{
// Preserve count elements
doReserve_copy(count, len);
List<T>::setAddressableSize(len);
}
@ -399,7 +421,7 @@ inline void Foam::DynamicList<T, SizeMin>::resize_fill
const T& val
)
{
this->doResize(true, len); // nocopy = true
resize_nocopy(len);
UList<T>::operator=(val);
}
@ -410,7 +432,8 @@ inline void Foam::DynamicList<T, SizeMin>::resize_nocopy
const label len
)
{
this->doResize(true, len); // nocopy = true
// Preserve 0 content
resize_copy(0, len);
}
@ -422,6 +445,8 @@ inline void Foam::DynamicList<T, SizeMin>::resize
)
{
const label oldLen = List<T>::size();
// Preserve all content
resize(len);
// Fill newly exposed with constant value
@ -445,6 +470,9 @@ inline void Foam::DynamicList<T, SizeMin>::clear() noexcept
template<class T, int SizeMin>
inline void Foam::DynamicList<T, SizeMin>::clearStorage()
{
// Extra safety...?
if (!List<T>::cdata() && List<T>::empty()) capacity_ = 0;
// Consistent allocated sizing
List<T>::setAddressableSize(capacity_);
List<T>::clear();
@ -515,7 +543,7 @@ inline void Foam::DynamicList<T, SizeMin>::swap
UList<T>::swap(other);
// Swap capacity
std::swap(this->capacity_, other.capacity_);
std::swap(capacity_, other.capacity_);
}
@ -537,7 +565,7 @@ template<int AnySizeMin>
inline void
Foam::DynamicList<T, SizeMin>::transfer
(
DynamicList<T, AnySizeMin>& list
DynamicList<T, AnySizeMin>& other
)
{
if
@ -545,7 +573,7 @@ Foam::DynamicList<T, SizeMin>::transfer
FOAM_UNLIKELY
(
static_cast<const List<T>*>(this)
== static_cast<const List<T>*>(&list)
== static_cast<const List<T>*>(&other)
)
)
{
@ -554,10 +582,11 @@ Foam::DynamicList<T, SizeMin>::transfer
// Consistent allocated sizing
List<T>::setAddressableSize(capacity_);
List<T>::transfer(static_cast<List<T>&>(list));
List<T>::transfer(static_cast<List<T>&>(other));
capacity_ = list.capacity();
list.setCapacity_unsafe(0); // All contents moved
// Update capacity information
capacity_ = other.capacity();
other.setCapacity_unsafe(0); // All contents moved
}

View File

@ -28,27 +28,38 @@ License
#include "List.H"
#include "FixedList.H"
#include "PtrList.H"
#include "UPtrList.H"
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
// Only a limited number of internal size checks.
// Caller knows what they are doing.
template<class T>
void Foam::List<T>::resize_copy(const label count, const label len)
void Foam::List<T>::resize_copy(label count, const label len)
{
// Only a limited number of internal size checks.
// Caller knows what they are doing.
if (FOAM_LIKELY(len > 0))
if (this->size_ == len)
{
// no-op
}
else if (FOAM_LIKELY(len > 0))
{
// With sign-check to avoid spurious -Walloc-size-larger-than
const label oldLen = this->size_;
const label overlap = Foam::min(count, len);
// Extra safety, not currently necessary:
// const label overlap = Foam::min(Foam::min(count, oldLen), len);
T* old = this->v_;
const label oldLen = this->size_;
if (overlap > 0)
if (count > len)
{
count = len; // The count truncated by the new length
}
// Extra safety, probably not necessary:
// if (count > oldLen)
// {
// count = oldLen; // The count truncated by the old length
// }
if (count > 0)
{
// Recover overlapping content when resizing
@ -58,7 +69,7 @@ void Foam::List<T>::resize_copy(const label count, const label len)
// Can dispatch with
// - std::execution::par_unseq
// - std::execution::unseq
std::move(old, (old + overlap), this->v_);
std::move(old, (old + count), this->v_);
ListPolicy::deallocate(old, oldLen);
}
@ -91,8 +102,6 @@ void Foam::List<T>::resize_copy(const label count, const label len)
template<class T>
Foam::List<T>::List(const label len)
:
UList<T>(nullptr, len)
{
if (FOAM_UNLIKELY(len < 0))
{
@ -103,15 +112,14 @@ Foam::List<T>::List(const label len)
if (len > 0)
{
doAlloc();
// resize_nocopy()
doAlloc(len);
}
}
template<class T>
Foam::List<T>::List(const label len, const T& val)
:
UList<T>(nullptr, len)
{
if (FOAM_UNLIKELY(len < 0))
{
@ -122,7 +130,8 @@ Foam::List<T>::List(const label len, const T& val)
if (len > 0)
{
doAlloc();
// resize_fill()
doAlloc(len);
UList<T>::operator=(val);
}
}
@ -130,8 +139,6 @@ Foam::List<T>::List(const label len, const T& val)
template<class T>
Foam::List<T>::List(const label len, Foam::zero)
:
UList<T>(nullptr, len)
{
if (FOAM_UNLIKELY(len < 0))
{
@ -142,7 +149,8 @@ Foam::List<T>::List(const label len, Foam::zero)
if (len > 0)
{
doAlloc();
// resize_fill()
doAlloc(len);
UList<T>::operator=(Foam::zero{});
}
}
@ -177,12 +185,10 @@ Foam::List<T>::List(Foam::one, Foam::zero)
template<class T>
Foam::List<T>::List(const UList<T>& list)
:
UList<T>(nullptr, list.size_)
{
if (this->size_ > 0)
if (!list.empty())
{
doAlloc();
doAlloc(list.size());
UList<T>::deepCopy(list);
}
}
@ -190,12 +196,10 @@ Foam::List<T>::List(const UList<T>& list)
template<class T>
Foam::List<T>::List(const List<T>& list)
:
UList<T>(nullptr, list.size_)
{
if (this->size_ > 0)
if (!list.empty())
{
doAlloc();
doAlloc(list.size());
UList<T>::deepCopy(list);
}
}
@ -203,19 +207,18 @@ Foam::List<T>::List(const List<T>& list)
template<class T>
Foam::List<T>::List(List<T>& list, bool reuse)
:
UList<T>(nullptr, list.size_)
{
if (reuse)
{
// Steal content
this->v_ = list.v_;
this->size_ = list.size_;
list.v_ = nullptr;
list.size_ = 0;
}
else if (this->size_ > 0)
else if (!list.empty())
{
doAlloc();
doAlloc(list.size());
UList<T>::deepCopy(list);
}
}
@ -223,11 +226,12 @@ Foam::List<T>::List(List<T>& list, bool reuse)
template<class T>
Foam::List<T>::List(const UList<T>& list, const labelUList& indices)
:
UList<T>(nullptr, indices.size())
{
doAlloc();
copyList(list, indices); // <- deepCopy()
if (!indices.empty())
{
doAlloc(indices.size());
copyList(list, indices); // <- deepCopy()
}
}
@ -238,11 +242,12 @@ Foam::List<T>::List
const UList<T>& list,
const FixedList<label,N>& indices
)
:
UList<T>(nullptr, indices.size())
{
doAlloc();
copyList(list, indices); // <- deepCopy()
// if (!FixedList::empty()) is always true
{
doAlloc(indices.size());
copyList(list, indices); // <- deepCopy()
}
}
@ -255,24 +260,23 @@ Foam::List<T>::List(const FixedList<T, N>& list)
template<class T>
Foam::List<T>::List(const PtrList<T>& list)
:
UList<T>(nullptr, list.size())
Foam::List<T>::List(const UPtrList<T>& list)
{
doAlloc();
copyList(list);
if (!list.empty())
{
doAlloc(list.size());
copyList(list);
}
}
template<class T>
template<class Addr>
Foam::List<T>::List(const IndirectListBase<T, Addr>& list)
:
UList<T>(nullptr, list.size())
{
if (this->size_ > 0)
if (!list.empty())
{
doAlloc();
doAlloc(list.size());
UList<T>::deepCopy(list);
}
}
@ -285,21 +289,9 @@ Foam::List<T>::List(std::initializer_list<T> list)
{}
template<class T>
Foam::List<T>::List(List<T>&& list) noexcept
:
UList<T>(list.data(), list.size())
{
list.size_ = 0;
list.v_ = nullptr;
}
template<class T>
template<int SizeMin>
Foam::List<T>::List(DynamicList<T, SizeMin>&& list)
:
UList<T>()
{
transfer(list);
}
@ -327,7 +319,7 @@ void Foam::List<T>::resize(const label len, const T& val)
return;
}
this->resize_copy(oldLen, len);
resize_copy(oldLen, len);
// Fill trailing part with new values
if (oldLen < this->size_)
@ -368,7 +360,7 @@ void Foam::List<T>::transfer(DynamicList<T, SizeMin>& list)
// Shrink the allocated space to the number of elements used
list.shrink_to_fit();
transfer(static_cast<List<T>&>(list));
list.clearStorage(); // Deletion, capacity=0 etc.
list.setCapacity_unsafe(0); // All contents moved
}
@ -382,9 +374,9 @@ void Foam::List<T>::operator=(const UList<T>& list)
return; // Self-assignment is a no-op
}
reAlloc(list.size_);
resize_nocopy(list.size());
if (this->size_ > 0)
if (!list.empty())
{
UList<T>::deepCopy(list);
}
@ -399,9 +391,9 @@ void Foam::List<T>::operator=(const List<T>& list)
return; // Self-assignment is a no-op
}
reAlloc(list.size_);
resize_nocopy(list.size());
if (this->size_ > 0)
if (!list.empty())
{
UList<T>::deepCopy(list);
}
@ -412,7 +404,7 @@ template<class T>
template<unsigned N>
void Foam::List<T>::operator=(const FixedList<T, N>& list)
{
reAlloc(list.size());
resize_nocopy(list.size());
std::copy(list.begin(), list.end(), this->v_);
}
@ -422,7 +414,7 @@ template<class T>
template<class Addr>
void Foam::List<T>::operator=(const IndirectListBase<T, Addr>& list)
{
reAlloc(list.size());
resize_nocopy(list.size());
UList<T>::deepCopy(list);
}
@ -430,8 +422,7 @@ void Foam::List<T>::operator=(const IndirectListBase<T, Addr>& list)
template<class T>
void Foam::List<T>::operator=(std::initializer_list<T> list)
{
reAlloc(list.size());
resize_nocopy(list.size());
std::copy(list.begin(), list.end(), this->v_);
}

View File

@ -56,7 +56,7 @@ template<class T> class List;
template<class T, unsigned N> class FixedList;
template<class T, int SizeMin> class DynamicList;
template<class T> class PtrList;
template<class T> class UPtrList;
template<class T> Istream& operator>>(Istream& is, List<T>& list);
@ -77,12 +77,8 @@ class List
{
// Private Member Functions
//- Allocate list storage
inline void doAlloc();
//- Reallocate list storage to the given size
// Discards old storage (if any). Does not copy old contents
inline void reAlloc(const label len);
//- Allocate list storage. Assumes there is no existing content
inline void doAlloc(const label len);
//- Copy all list contents. Uses operator[] on the input list
template<class ListType>
@ -98,8 +94,8 @@ class List
template<class InputIterator>
inline List
(
InputIterator firstIter,
InputIterator lastIter, // (unused)
InputIterator input,
InputIterator inputEnd, // (unused)
const label len
);
@ -111,17 +107,7 @@ class List
// Methods as per DynamicList to simplify code maintenance
//- Stub method for internal naming as per DynamicList
void setCapacity_nocopy(const label len) { resize_nocopy(len); }
protected:
// Protected Member Functions
//- Low-level resizing (backend for resize).
//- Change allocation size of list, retaining the first count contents.
// \note Only uses a limited number of internal checks.
void resize_copy(const label count, const label len);
void setCapacity_nocopy(label len) { resize_nocopy(len); }
public:
@ -185,8 +171,8 @@ public:
template<unsigned N>
explicit List(const FixedList<T, N>& list);
//- Construct as copy of PtrList<T>
explicit List(const PtrList<T>& list);
//- Construct as copy of UPtrList<T> content
explicit List(const UPtrList<T>& list);
//- Construct as copy of IndirectList contents
template<class Addr>
@ -196,7 +182,7 @@ public:
List(std::initializer_list<T> list);
//- Move construct from List
List(List<T>&& list) noexcept;
inline List(List<T>&& list) noexcept;
//- Move construct from DynamicList
template<int SizeMin>
@ -227,6 +213,10 @@ public:
//- Adjust allocated size of list and set val for \em new elements
void resize(const label len, const T& val);
//- Change allocated size of list, retaining the first count elements.
// \note Only uses a limited number of internal checks.
void resize_copy(label count, const label len);
//- Adjust allocated size of list and set val for \em all elements
inline void resize_fill(const label len, const T& val);
@ -234,14 +224,9 @@ public:
// retaining old content.
// If no reallocation is required, the contents remain untouched.
// Otherwise the contents will be uninitialized.
// Shorthand for \c resize(0, len)
inline void resize_nocopy(const label len);
//- Alias for resize()
void setSize(const label n) { this->resize(n); }
//- Alias for resize()
void setSize(const label n, const T& val) { this->resize(n, val); }
// Edit
@ -404,6 +389,12 @@ public:
//- Same as push_uniq()
FOAM_DEPRECATED_FOR(2022-10, "push_uniq()")
label appendUniq(const T& val) { return this->push_uniq(val); }
//- Alias for resize()
void setSize(label n) { this->resize(n); }
//- Alias for resize()
void setSize(label n, const T& val) { this->resize(n, val); }
};

View File

@ -29,24 +29,13 @@ License
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
template<class T>
inline void Foam::List<T>::doAlloc()
inline void Foam::List<T>::doAlloc(const label len)
{
if (this->size_ > 0)
if (len > 0)
{
// With sign-check to avoid spurious -Walloc-size-larger-than
this->v_ = ListPolicy::allocate<T>(this->size_);
}
}
template<class T>
inline void Foam::List<T>::reAlloc(const label len)
{
if (this->size_ != len)
{
clear();
this->v_ = ListPolicy::allocate<T>(len);
this->size_ = len;
doAlloc();
}
}
@ -95,23 +84,24 @@ template<class T>
template<class InputIterator>
inline Foam::List<T>::List
(
InputIterator firstIter,
InputIterator lastIter, // (unused)
InputIterator input,
InputIterator inputEnd, // (unused)
const label len
)
:
UList<T>(nullptr, len)
{
if (len > 0)
{
doAlloc();
doAlloc(len);
// Like std::copy() or std::copy_n()
// but without any requirements on the iterator category
for (label i = 0; i < len; (void)++i, (void)++firstIter)
auto iter = this->v_;
const auto endIter = (iter + len);
for (; iter != endIter; (void)++iter, (void)++input)
{
this->v_[i] = *firstIter;
*iter = *input;
}
}
}
@ -124,6 +114,17 @@ inline constexpr Foam::List<T>::List() noexcept
{}
template<class T>
inline Foam::List<T>::List(List<T>&& list) noexcept
:
UList<T>(list.data(), list.size())
{
// Stole content
list.size_ = 0;
list.v_ = nullptr;
}
template<class T>
inline Foam::autoPtr<Foam::List<T>> Foam::List<T>::clone() const
{
@ -158,7 +159,7 @@ inline void Foam::List<T>::resize(const label len)
{
if (this->size_ != len)
{
this->resize_copy(this->size_, len);
resize_copy(this->size_, len);
}
}
@ -166,7 +167,7 @@ inline void Foam::List<T>::resize(const label len)
template<class T>
inline void Foam::List<T>::resize_fill(const label len, const T& val)
{
this->reAlloc(len);
resize_nocopy(len);
UList<T>::operator=(val);
}
@ -174,7 +175,12 @@ inline void Foam::List<T>::resize_fill(const label len, const T& val)
template<class T>
inline void Foam::List<T>::resize_nocopy(const label len)
{
this->reAlloc(len);
// Same as resize_copy(0, len);
if (this->size_ != len)
{
clear();
doAlloc(len);
}
}

View File

@ -628,6 +628,15 @@ public:
return false;
}
//- Return a string_view of the charList. Content is non-modifiable.
template<class TypeT = T>
std::enable_if_t
<std::is_same_v<char, std::remove_cv_t<TypeT>>, std::string_view>
inline view() const
{
return std::string_view(v_, size_);
}
// Hashing

View File

@ -275,9 +275,11 @@ const std::istream& Foam::IFstream::stdStream() const
void Foam::IFstream::rewind()
{
Istream::rewind(); // Drop any putback
lineNumber_ = 1; // Reset line number
if (IOstreamOption::COMPRESSED == ifstreamPointer::whichCompression())
{
lineNumber_ = 1; // Reset line number
ifstreamPointer::reopen_gz(this->name());
setState(ifstreamPointer::get()->rdstate());
}

View File

@ -218,6 +218,12 @@ char Foam::Istream::readEndList(const char* funcName)
}
void Foam::Istream::rewind()
{
putBackClear(); // Drop any putback
}
Foam::Istream& Foam::Istream::operator()() const
{
if (!good())

View File

@ -175,7 +175,8 @@ public:
//- operation.
virtual Istream& readRaw(char* data, std::streamsize count) = 0;
//- Rewind the stream so that it may be read again
//- Rewind the stream so that it may be read again.
// The base implementation clears any putback.
virtual void rewind() = 0;

View File

@ -563,6 +563,8 @@ Foam::label Foam::UIPstreamBase::remaining() const noexcept
void Foam::UIPstreamBase::rewind()
{
Istream::rewind(); // Drop any putback
recvBufPos_ = 0; // Assume the entire buffer is for us to read from
setOpened();
setGood();

View File

@ -0,0 +1,267 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 Mark Olesen
-------------------------------------------------------------------------------
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::UPstream::File
Description
An opaque wrapper for MPI_File methods
without any \c <mpi.h> header dependency.
Note
Not included as part of UPstream.H - only include locally as required
SourceFiles
UPstreamFile.txx
\*---------------------------------------------------------------------------*/
#ifndef Foam_UPstreamFile_H
#define Foam_UPstreamFile_H
#include "fileName.H"
#include "UPstream.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
/*---------------------------------------------------------------------------*\
Class UPstream::File Declaration
\*---------------------------------------------------------------------------*/
class UPstream::File
{
// Forward Declaration
class Impl;
// Private Data
//- Implementation wrapper of MPI_File etc
std::unique_ptr<Impl> file_;
protected:
// Protected Method Functions
//- MPI_File_write [non-collective] : write data
bool write_data
(
//! Source buffer
const void* buffer,
//! The data count - number of elements
std::streamsize count,
const UPstream::dataTypes dataTypeId
);
//- MPI_File_write_at [non-collective] : write data at specified offset
bool write_data_at
(
//! The offset - number of 'view' elements (default: byte)
std::streamsize offset,
//! Source buffer
const void* buffer,
//! The data count - number of elements
std::streamsize count,
const UPstream::dataTypes dataTypeId
);
//- MPI_File_write_all [collective] : write data
bool write_data_all
(
//! Source buffer
const void* buffer,
//! The data count - number of elements
std::streamsize count,
const UPstream::dataTypes dataTypeId
);
//- MPI_File_write_at_all [collective] :
//- write data at specified offset
bool write_data_at_all
(
//! The offset - number of 'view' elements (default: byte)
std::streamsize offset,
//! Source buffer
const void* buffer,
//! The data count - number of elements
std::streamsize count,
const UPstream::dataTypes dataTypeId
);
public:
// Generated Methods
//- No copy construct
File(const File&&) = delete;
//- Move construct
File(File&&) noexcept;
//- No copy assignment
File& operator=(const File&) = delete;
//- Move assignment
File& operator=(File&&) noexcept;
// Constructors
//- Default construct
File();
//- Destructor. Non-default in header (incomplete types)
~File();
// Static Member Functions
//- True if MPI/IO appears to be supported
static bool supported();
// Member Functions
// Access
//- The name of the open stream
const fileName& name() const;
//- True if allocated and open has been called
bool is_open() const;
// Basics
//- MPI_File_open [collective] :
//- open file in write-only mode, no-append
bool open_write
(
//! The OpenFOAM communicator index
const int communicator,
//! Full file path (parent directory must exist before calling)
const fileName& pathname,
//! Simulated atomic file handling
IOstreamOption::atomicType = IOstreamOption::NON_ATOMIC
);
//- MPI_File_close [collective]
bool close();
//- Set the (output) file size [collective]
bool set_size(std::streamsize num_bytes);
// Writing
//- MPI_File_write [non-collective] : write data.
// A no-op and return true if content is empty
inline bool write(std::string_view sv);
//- MPI_File_write [non-collective] : write data.
// A no-op and return true if buffer is nullptr or count is zero
template<class Type>
inline bool write
(
//! The content
const Type* buffer,
//! The data count - number of elements
std::streamsize count
);
//- MPI_File_write_at [non-collective] : write data at specified offset.
// A no-op and return true if content is empty
inline bool write_at(std::streamsize offset, std::string_view sv);
//- MPI_File_write_at [non-collective] : write data at specified offset.
// A no-op and return true if buffer is nullptr or count is zero
template<class Type>
inline bool write_at
(
//! The offset within the file - number of 'view' elements
std::streamsize offset,
//! The content
const Type* buffer,
//! The data count - number of elements
std::streamsize count
);
//- MPI_File_write_all [collective] : write data
inline bool write_all(std::string_view sv);
//- MPI_File_write_all [collective] : write data
template<class Type>
inline bool write_all
(
//! The content
const Type* buffer,
//! The data count - number of elements
std::streamsize count
);
//- MPI_File_write_at_all [collective] :
//- write data at specified offset
inline bool write_at_all
(
//! The offset within the file - number of 'view' elements
std::streamsize offset,
//! The content
std::string_view sv
);
//- MPI_File_write_at_all [collective] :
//- write data at specified offset
template<class Type>
inline bool write_at_all
(
//! The offset within the file - number of 'view' elements
std::streamsize offset,
//! The content
const Type* buffer,
//! The data count - number of elements
std::streamsize count
);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#include "UPstreamFile.txx"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,220 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 Mark Olesen
-------------------------------------------------------------------------------
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/>.
\*---------------------------------------------------------------------------*/
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
inline bool Foam::UPstream::File::write
(
std::string_view sv
)
{
if (sv.empty())
{
// no-op for no content
return true;
}
return this->write_data
(
sv.data(), sv.size(),
UPstream::dataTypes::type_byte
);
}
template<class Type>
bool Foam::UPstream::File::write
(
const Type* buffer,
std::streamsize count
)
{
if constexpr (!is_contiguous_v<Type>)
{
FatalErrorInFunction
<< "Only contiguous data can be supported!"
<< Foam::abort(FatalError);
return false;
}
else if (buffer && count > 1)
{
// Use element or component type (or byte-wise) for data type
return this->write_data
(
buffer, // The data or cmpt pointer
UPstream_dataType<Type>::size(count),
UPstream_dataType<Type>::datatype_id
);
}
else
{
// no-op for no content
return true;
}
}
inline bool Foam::UPstream::File::write_at
(
std::streamsize offset,
std::string_view sv
)
{
if (sv.empty())
{
// no-op for no content
return true;
}
return this->write_data_at
(
offset,
sv.data(), sv.size(),
UPstream::dataTypes::type_byte
);
}
template<class Type>
bool Foam::UPstream::File::write_at
(
std::streamsize offset,
const Type* buffer,
std::streamsize count
)
{
if constexpr (!is_contiguous_v<Type>)
{
FatalErrorInFunction
<< "Only contiguous data can be supported!"
<< Foam::abort(FatalError);
return false;
}
else if (buffer && count > 1)
{
// Use element or component type (or byte-wise) for data type
return this->write_data_at
(
offset,
buffer, // The data or cmpt pointer
UPstream_dataType<Type>::size(count),
UPstream_dataType<Type>::datatype_id
);
}
else
{
// no-op for no content
return true;
}
}
inline bool Foam::UPstream::File::write_all
(
std::string_view sv
)
{
return this->write_data_all
(
sv.data(), sv.size(),
UPstream::dataTypes::type_byte
);
}
template<class Type>
bool Foam::UPstream::File::write_all
(
const Type* buffer,
std::streamsize count
)
{
if constexpr (!is_contiguous_v<Type>)
{
FatalErrorInFunction
<< "Only contiguous data can be supported!"
<< Foam::abort(FatalError);
return false;
}
else
{
// Use element or component type (or byte-wise) for data type
return this->write_data_all
(
buffer, // The data or cmpt pointer
UPstream_dataType<Type>::size(count),
UPstream_dataType<Type>::datatype_id
);
}
}
inline bool Foam::UPstream::File::write_at_all
(
std::streamsize offset,
std::string_view sv
)
{
return this->write_data_at_all
(
offset,
sv.data(), sv.size(),
UPstream::dataTypes::type_byte
);
}
template<class Type>
bool Foam::UPstream::File::write_at_all
(
std::streamsize offset,
const Type* buffer,
std::streamsize count
)
{
if constexpr (!is_contiguous_v<Type>)
{
FatalErrorInFunction
<< "Only contiguous data can be supported!"
<< Foam::abort(FatalError);
return false;
}
else
{
// Use element or component type (or byte-wise) for data type
return this->write_data_at_all
(
offset,
buffer, // The data or cmpt pointer
UPstream_dataType<Type>::size(count),
UPstream_dataType<Type>::datatype_id
);
}
}
// ************************************************************************* //

View File

@ -1084,6 +1084,7 @@ bool Foam::ISstream::endRawRead()
void Foam::ISstream::rewind()
{
Istream::rewind(); // Drop any putback
lineNumber_ = 1; // Reset line number
stdStream().clear(); // Clear the iostate error state flags

View File

@ -531,7 +531,11 @@ public:
virtual bool endRawRead() override { return false; }
//- Rewind the stream so that it may be read again. Same as seek(0)
virtual void rewind() override { ITstream::seek(0); }
virtual void rewind() override
{
Istream::rewind();
ITstream::seek(0);
}
// Output

View File

@ -357,6 +357,7 @@ public:
//- Rewind the stream, clearing any old errors
virtual void rewind() override
{
Istream::rewind();
stream_.rewind();
syncState();
}

View File

@ -419,6 +419,7 @@ public:
//- Rewind the stream, clearing any old errors
virtual void rewind() override
{
Istream::rewind();
stream_.rewind();
syncState();
}

View File

@ -31,8 +31,7 @@ Description
Similar to OStringStream but with a List for its storage instead of
as string to allow reuse of List contents without copying.
The default initial size is 512-bytes and uses size doubling.
After construction can use the reserve() method to adjust this.
Internally imposes a 512 byte min-size and uses capacity doubling.
See Also
Foam::ICharStream
@ -131,6 +130,25 @@ public:
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);
}
//- Increase (reserve) space for another \c count entries
void extend(std::streamsize count)
{
buffer_type::extend(count);
}
//- Increase (reserve) space for another \c count entries
void extend_exact(std::streamsize count)
{
buffer_type::extend_exact(count);
}
//- Rewind the stream, clearing any old errors
void rewind()
{
@ -209,6 +227,64 @@ public:
//- Information about stream
void print(Ostream& os) const { debug_info(os); os << '\n'; }
// Extra/Convenience Methods
//- Append a single character to the end
void push_back(char c) { stream_type::put(c); }
//- Rewind the end by 1 or more elements
void pop_back(int n = 1) { buffer_type::pop_back(n); }
//- Append repeated character content
void append(std::streamsize count, char c)
{
if (count > 0)
{
buffer_type::extend(count);
while (count-- > 0)
{
stream_type::put(c);
}
}
}
//- Append character content - like a plain write()
void append(const char* data, std::streamsize count)
{
if (data && count > 0)
{
buffer_type::extend(count);
write(data, count);
}
}
//- Overwrite a single character
void overwrite(std::streampos pos, char c)
{
buffer_type::overwrite(pos, c);
}
//- Overwrite a sub-slice with character content
void overwrite
(
std::streampos pos,
const char* data,
std::streamsize count
)
{
buffer_type::overwrite(pos, data, count);
}
//- The output data (start of output characters)
const char* cdata_bytes() const { return buffer_type::data_bytes(); }
//- The output data (start of output characters)
char* data_bytes() { return buffer_type::data_bytes(); }
//- The current number of output characters
std::streamsize size_bytes() const { return buffer_type::size_bytes(); }
};
@ -287,6 +363,15 @@ public:
//- Reserve output space for at least this amount
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); }
//- Increase (reserve) space for another \c n entries
void extend(std::streamsize n) { stream_.extend(n); }
//- Increase (reserve) space for another \c n entries
void extend_exact(std::streamsize n) { stream_.extend_exact(n); }
//- A string_view of buffer contents
auto view() const { return stream_.view(); }
@ -350,6 +435,56 @@ public:
}
// Extra/Convenience Methods
//- Append a single character to the end
void push_back(char c) { stream_.push_back(c); }
//- Rewind the end by 1 or more elements
void pop_back(int n = 1) { stream_.pop_back(n); }
//- Append repeated character content
void append(std::streamsize count, char c)
{
stream_.append(count, c);
}
//- Append character content
void append(const char* data, std::streamsize count)
{
stream_.append(data, count);
}
//- Append character content
void append(std::string_view sv)
{
stream_.append(sv.data(), sv.size());
}
//- Overwrite a single character
void overwrite(std::streampos pos, char c)
{
stream_.overwrite(pos, c);
}
//- 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());
}
// Housekeeping
//- Block size was used in OpenFOAM-v2306 and earlier

View File

@ -226,6 +226,23 @@ public:
// Extra/Convenience Methods
//- Overwrite a single character
void overwrite(std::streampos pos, char c)
{
buffer_type::overwrite(pos, c);
}
//- Overwrite a sub-slice with character content
void overwrite
(
std::streampos pos,
const char* data,
std::streamsize count
)
{
buffer_type::overwrite(pos, data, count);
}
//- The output data (start of output characters)
const char* cdata_bytes() const { return buffer_type::data_bytes(); }
@ -234,7 +251,6 @@ public:
//- The current number of output characters
std::streamsize size_bytes() const { return buffer_type::size_bytes(); }
};
@ -382,6 +398,47 @@ public:
stream_.debug_info(os);
os << '\n';
}
// Extra/Convenience Methods
//- 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 single character
void overwrite(std::streampos pos, char c)
{
stream_.overwrite(pos, c);
}
//- 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());
}
};

View File

@ -468,6 +468,43 @@ public:
}
}
//- 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() <= 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
void info(Ostream& os) const
{
@ -500,8 +537,8 @@ protected:
{
if (c != traits_type::eof())
{
// Need more space?
reserve(1 + span_tellp());
// Needed more space?
extend(1);
*(pptr()) = c;
pbump(1);
@ -513,7 +550,7 @@ protected:
virtual std::streamsize xsputn(const char* s, std::streamsize n)
{
// Enough space so that appends work without problem
reserve(n + span_tellp());
extend(n);
std::streamsize count = 0;
while (count < n && pptr() < epptr())
@ -530,10 +567,14 @@ public:
// Constructors
//- Default construct - no initial reserved number of bytes.
out_dynamic()
{
sync_pbuffer();
}
//- Default construct with initial reserved number of bytes.
// The value of 512 is a bit arbitrary, but consistent with
// std::stringstream
out_dynamic(size_t nbytes = 512)
out_dynamic(size_t nbytes)
:
storage_(label(nbytes))
{
@ -560,40 +601,115 @@ public:
// Member Functions
//- Increment capacity (if needed) and adjust buffer pointers
//- 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(); // Current location
const auto cur = span_tellp(); // Output position
label newCapacity = 512;
label size = min_size();
if (newCapacity < len)
if (size < len)
{
// Increase capacity (doubling)
newCapacity =
Foam::max(label(len), label(2*storage_.size()));
// ratio=1.5:
// Foam::max(label(len), label((storage_.size()/2)*3));
// 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;
}
}
// Info<<"request:" << len
// << " cur cap:" << storage_.size()
// << " new cap:" << newCapacity
// std::cerr
// <<"request:" << len
// << " old cap:" << storage_.size()
// << " new cap:" << size
// << " pos:" << cur << endl;
storage_.resize(newCapacity);
storage_.resize_copy(cur, size);
sync_pbuffer();
pbump(cur);
}
}
//- Sync put buffer pointers to agree with list dimensions
// Sets put pointer to the begin (rewind).
void sync_pbuffer()
//- Increase capacity for at least this size.
//- Does not apply min-size or capacity doubling etc.
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_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

View File

@ -79,19 +79,14 @@ class DynamicField
template<class ListType>
inline void doAssignDynList(const ListType& list);
//- Alter the size of the underlying storage
// The 'nocopy' option will not attempt to recover old content
inline void doCapacity(const bool nocopy, const label len);
//- Alter the size of the underlying storage,
//- retaining the first count elements.
inline void doCapacity_copy(label count, const label len);
//- Reserve allocation space for at least this size.
//- Reserve allocation space for at least this size,
//- retaining the first count elements.
// Never shrinks the allocated size, use setCapacity() for that.
// The 'nocopy' option will not attempt to recover old content
inline void doReserve(const bool nocopy, const label len);
//- Reserve allocation space for at least this size.
// Never shrinks the allocated size, use setCapacity() for that.
// The 'nocopy' option will not attempt to recover old content
inline void doResize(const bool nocopy, const label len);
inline void doReserve_copy(label count, const label len);
public:
@ -246,6 +241,10 @@ public:
//- Alter addressable size and fill \em new entries with constant value
inline void resize(const label len, const T& val);
//- Alter addressable size, retaining the first count contents.
// \note Only uses a limited number of internal checks.
inline void resize_copy(label count, const label len);
//- Alter addressable size and set val for \em all addressed entries
inline void resize_fill(const label len, const T& val);

View File

@ -52,9 +52,9 @@ inline void Foam::DynamicField<T, SizeMin>::doAssignDynList
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::doCapacity
inline void Foam::DynamicField<T, SizeMin>::doCapacity_copy
(
const bool nocopy,
label count,
const label newCapacity
)
{
@ -66,16 +66,22 @@ inline void Foam::DynamicField<T, SizeMin>::doCapacity
// Addressable length, possibly truncated by new capacity
const label currLen = Foam::min(List<T>::size(), newCapacity);
// The count truncated by the new addressable range
if (count > currLen)
{
count = currLen;
}
// Consistent allocated sizing
List<T>::setAddressableSize(capacity_);
if (nocopy)
if (count > 0)
{
List<T>::resize_nocopy(newCapacity);
List<T>::resize_copy(count, newCapacity);
}
else
{
List<T>::resize_copy(currLen, newCapacity);
List<T>::resize_nocopy(newCapacity);
}
capacity_ = List<T>::size();
@ -84,9 +90,9 @@ inline void Foam::DynamicField<T, SizeMin>::doCapacity
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::doReserve
inline void Foam::DynamicField<T, SizeMin>::doReserve_copy
(
const bool nocopy,
label count,
const label len
)
{
@ -95,6 +101,12 @@ inline void Foam::DynamicField<T, SizeMin>::doReserve
// Preserve addressed size
const label currLen = List<T>::size();
// The count truncated by the addressable range
if (count > currLen)
{
count = currLen;
}
// Consistent allocated sizing
List<T>::setAddressableSize(capacity_);
@ -102,13 +114,13 @@ inline void Foam::DynamicField<T, SizeMin>::doReserve
capacity_ =
Foam::ListPolicy::reserve_size<SizeMin, 2>(len, capacity_);
if (nocopy)
if (count > 0)
{
List<T>::resize_nocopy(capacity_);
List<T>::resize_copy(count, capacity_);
}
else
{
List<T>::resize_copy(currLen, capacity_);
List<T>::resize_nocopy(capacity_);
}
capacity_ = List<T>::size();
@ -117,18 +129,6 @@ inline void Foam::DynamicField<T, SizeMin>::doReserve
}
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::doResize
(
const bool nocopy,
const label len
)
{
this->doReserve(nocopy, len);
List<T>::setAddressableSize(len);
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template<class T, int SizeMin>
@ -342,7 +342,7 @@ inline Foam::DynamicField<T, SizeMin>::DynamicField
)
:
Field<T>(),
capacity_(content.size())
capacity_(0)
{
if (reuse)
{
@ -352,6 +352,7 @@ inline Foam::DynamicField<T, SizeMin>::DynamicField
{
Field<T>::operator=(content);
}
capacity_ = Field<T>::size();
}
@ -424,7 +425,8 @@ inline void Foam::DynamicField<T, SizeMin>::setCapacity
const label len
)
{
this->doCapacity(false, len); // nocopy = false
// Preserve all content
doCapacity_copy(List<T>::size(), len);
}
@ -434,7 +436,8 @@ inline void Foam::DynamicField<T, SizeMin>::setCapacity_nocopy
const label len
)
{
this->doCapacity(true, len); // nocopy = true
// Preserve 0 content
doCapacity_copy(0, len);
}
@ -444,7 +447,8 @@ inline void Foam::DynamicField<T, SizeMin>::reserve
const label len
)
{
this->doReserve(false, len); // nocopy = false
// Preserve all content
doReserve_copy(List<T>::size(), len);
}
@ -454,7 +458,8 @@ inline void Foam::DynamicField<T, SizeMin>::reserve_nocopy
const label len
)
{
this->doReserve(true, len); // nocopy = true
// Preserve 0 content
doReserve_copy(0, len);
}
@ -472,9 +477,12 @@ inline void Foam::DynamicField<T, SizeMin>::reserve_exact
// Consistent allocated sizing
List<T>::setAddressableSize(capacity_);
// Exact length
capacity_ = len;
// if (!nocopy)
{
List<T>::resize_copy(currLen, len);
List<T>::resize_copy(currLen, capacity_);
}
capacity_ = List<T>::size();
@ -489,17 +497,21 @@ inline void Foam::DynamicField<T, SizeMin>::resize
const label len
)
{
this->doResize(false, len); // nocopy = false
// Preserve all content
resize_copy(List<T>::size(), len);
}
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::resize_nocopy
inline void Foam::DynamicField<T, SizeMin>::resize_copy
(
label count,
const label len
)
{
this->doResize(true, len); // nocopy = true
// Preserve count elements
doReserve_copy(count, len);
List<T>::setAddressableSize(len);
}
@ -510,11 +522,22 @@ inline void Foam::DynamicField<T, SizeMin>::resize_fill
const T& val
)
{
this->doResize(true, len); // nocopy = true
resize_nocopy(len);
UList<T>::operator=(val);
}
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::resize_nocopy
(
const label len
)
{
// Preserve 0 content
resize_copy(0, len);
}
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::resize
(
@ -523,6 +546,8 @@ inline void Foam::DynamicField<T, SizeMin>::resize
)
{
const label oldLen = List<T>::size();
// Preserve all content
resize(len);
// Fill newly exposed with constant value
@ -546,6 +571,11 @@ inline void Foam::DynamicField<T, SizeMin>::clear() noexcept
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::clearStorage()
{
// Extra safety...?
if (!List<T>::cdata() && List<T>::empty()) capacity_ = 0;
// Consistent allocated sizing
List<T>::setAddressableSize(capacity_);
List<T>::clear();
capacity_ = 0;
}
@ -555,7 +585,6 @@ template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::shrink_to_fit()
{
const label currLen = List<T>::size();
if (currLen < capacity_)
{
List<T>::setAddressableSize(capacity_);
@ -571,8 +600,11 @@ Foam::DynamicField<T, SizeMin>::swap(List<T>& other)
{
if
(
static_cast<const List<T>*>(this)
== static_cast<const List<T>*>(&other)
FOAM_UNLIKELY
(
static_cast<const List<T>*>(this)
== static_cast<const List<T>*>(&other)
)
)
{
return; // Self-swap is a no-op
@ -598,8 +630,11 @@ inline void Foam::DynamicField<T, SizeMin>::swap
{
if
(
static_cast<const List<T>*>(this)
== static_cast<const List<T>*>(&other)
FOAM_UNLIKELY
(
static_cast<const List<T>*>(this)
== static_cast<const List<T>*>(&other)
)
)
{
return; // Self-swap is a no-op
@ -622,8 +657,11 @@ inline void Foam::DynamicField<T, SizeMin>::swap
{
if
(
static_cast<const List<T>*>(this)
== static_cast<const List<T>*>(&other)
FOAM_UNLIKELY
(
static_cast<const List<T>*>(this)
== static_cast<const List<T>*>(&other)
)
)
{
return; // Self-swap is a no-op
@ -633,17 +671,19 @@ inline void Foam::DynamicField<T, SizeMin>::swap
UList<T>::swap(other);
// Swap capacity
const label oldCap = this->capacity();
const label newCap = other.capacity();
this->setCapacity_unsafe(newCap);
other.setCapacity_unsafe(oldCap);
auto old = capacity_;
this->setCapacity_unsafe(other.capacity());
other.setCapacity_unsafe(old);
}
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::transfer(List<T>& list)
{
// No check for self-assignment (different types)
// Consistent allocated sizing
List<T>::setAddressableSize(capacity_);
Field<T>::transfer(list);
capacity_ = Field<T>::size();
}
@ -653,7 +693,7 @@ template<class T, int SizeMin>
template<int AnySizeMin>
inline void Foam::DynamicField<T, SizeMin>::transfer
(
DynamicList<T, AnySizeMin>& list
DynamicList<T, AnySizeMin>& other
)
{
if
@ -661,19 +701,22 @@ inline void Foam::DynamicField<T, SizeMin>::transfer
FOAM_UNLIKELY
(
static_cast<const UList<T>*>(this)
== static_cast<const UList<T>*>(&list)
== static_cast<const UList<T>*>(&other)
)
)
{
return; // Self-assignment is a no-op
}
// Consistent allocated sizing
List<T>::setAddressableSize(capacity_);
Field<T>::transfer(static_cast<List<T>&>(list));
// Remove storage
this->clearStorage();
capacity_ = list.capacity();
list.setCapacity_unsafe(0); // All contents moved
// Swap storage and addressable size
UList<T>::swap(other);
// Update capacity
capacity_ = other.capacity();
other.setCapacity_unsafe(0);
}
@ -681,7 +724,7 @@ template<class T, int SizeMin>
template<int AnySizeMin>
inline void Foam::DynamicField<T, SizeMin>::transfer
(
DynamicField<T, AnySizeMin>& list
DynamicField<T, AnySizeMin>& other
)
{
if
@ -689,19 +732,22 @@ inline void Foam::DynamicField<T, SizeMin>::transfer
FOAM_UNLIKELY
(
static_cast<const UList<T>*>(this)
== static_cast<const UList<T>*>(&list)
== static_cast<const UList<T>*>(&other)
)
)
{
return; // Self-assignment is a no-op
}
// Consistent allocated sizing
List<T>::setAddressableSize(capacity_);
Field<T>::transfer(static_cast<List<T>&>(list));
// Remove storage
this->clearStorage();
capacity_ = list.capacity();
list.setCapacity_unsafe(0); // All contents moved
// Swap storage and addressable size
UList<T>::swap(other);
// Update capacity
capacity_ = other.capacity();
other.setCapacity_unsafe(0);
}

View File

@ -5,6 +5,7 @@ UPstreamCommunicator.C
UPstreamGatherScatter.C
UPstreamReduce.C
UPstreamRequest.C
UPstreamFile.C
UPstreamWindow.C
UIPstreamRead.C

View File

@ -0,0 +1,170 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 Mark Olesen
-------------------------------------------------------------------------------
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/>.
\*---------------------------------------------------------------------------*/
#include "fileName.H"
#include "UPstreamFile.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
/*---------------------------------------------------------------------------*\
Class UPstream::File::Impl Declaration
\*---------------------------------------------------------------------------*/
class UPstream::File::Impl {};
} // End namespace Foam
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
bool Foam::UPstream::File::supported()
{
return false;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::UPstream::File::File() {}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::UPstream::File::~File()
{} // Non-default in header (incomplete types)
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
const Foam::fileName& Foam::UPstream::File::name() const
{
return fileName::null;
}
bool Foam::UPstream::File::is_open() const
{
return false;
}
bool Foam::UPstream::File::close()
{
return false;
}
// * * * * * * * * * * * * Member Functions (Reading) * * * * * * * * * * * //
#if 0
bool Foam::UPstream::File::open_read
(
const int communicator,
const fileName& pathname
)
{
NotImplemented;
return false;
}
#endif
// * * * * * * * * * * * * Member Functions (Writing) * * * * * * * * * * * //
bool Foam::UPstream::File::open_write
(
const int communicator,
const fileName& pathname,
IOstreamOption::atomicType
)
{
NotImplemented;
return false;
}
bool Foam::UPstream::File::write_data
(
const void* data,
std::streamsize count,
const UPstream::dataTypes dataTypeId
)
{
NotImplemented;
return false;
}
bool Foam::UPstream::File::write_data_at
(
std::streamsize offset,
const void* data,
std::streamsize count,
const UPstream::dataTypes dataTypeId
)
{
NotImplemented;
return false;
}
bool Foam::UPstream::File::write_data_all
(
const void* data,
std::streamsize count,
const UPstream::dataTypes dataTypeId
)
{
NotImplemented;
return false;
}
bool Foam::UPstream::File::write_data_at_all
(
std::streamsize offset,
const void* data,
std::streamsize count,
const UPstream::dataTypes dataTypeId
)
{
NotImplemented;
return false;
}
bool Foam::UPstream::File::set_size(std::streamsize num_bytes)
{
NotImplemented;
return false;
}
// ************************************************************************* //

View File

@ -6,6 +6,7 @@ UPstreamCommunicator.C
UPstreamGatherScatter.C
UPstreamReduce.C
UPstreamRequest.C
UPstreamFile.C
UPstreamWindow.C
UIPstreamRead.C

View File

@ -0,0 +1,687 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 Mark Olesen
-------------------------------------------------------------------------------
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/>.
\*---------------------------------------------------------------------------*/
#include "fileName.H"
#include "UPstreamFile.H"
#include "PstreamGlobals.H"
#include "OSspecific.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Has _c() version?
#undef Foam_UPstream_largeCountFile
#if defined(OMPI_MAJOR_VERSION)
#if (OMPI_MAJOR_VERSION >= 5)
#define Foam_UPstream_largeCountFile
#endif
#endif
// Macros for calling versions with or without '_c'
#ifdef Foam_UPstream_largeCountFile
#define Foam_mpiCall(Function) Function##_c
#else
#define Foam_mpiCall(Function) Function
#endif
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
namespace
{
inline bool checkCount(std::streamsize count, const char* what)
{
#ifndef Foam_UPstream_largeCountFile
if (FOAM_UNLIKELY(count > std::streamsize(INT_MAX)))
{
using namespace Foam;
FatalErrorInFunction
<< "Write size " << label(count)
<< " exceeds INT_MAX bytes for '" << what << "'\n"
<< Foam::abort(Foam::FatalError);
return false;
}
#endif
return true;
}
} // End anonymous namespace
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
/*---------------------------------------------------------------------------*\
Class UPstream::File::Impl Declaration
\*---------------------------------------------------------------------------*/
class UPstream::File::Impl
{
//- The file-handle
MPI_File fh_;
//- Path of the open file
fileName name_;
//- The current state (open|read|write|closed etc)
int state_;
//- The associated rank when openned
int rank_;
public:
//- The file states
enum states : int { CLOSED = 0, READ, WRITE, ATOMIC_WRITE };
// Constructors
//- Default construct
Impl()
:
fh_(MPI_FILE_NULL),
state_(CLOSED),
rank_(0)
{}
// Member Functions
// The file handle
const MPI_File& handle() const noexcept { return fh_; }
MPI_File& handle() noexcept { return fh_; }
// Path to the open file
const fileName& name() const noexcept { return name_; }
fileName& name() noexcept { return name_; }
// Change the file state, return the old value
int state(states val) noexcept
{
int old(state_);
state_ = val;
return old;
}
//- Is rank 0 ? (master rank)
bool master() const noexcept { return (rank_ == 0); }
//- Get the associated rank
int rank() const noexcept { return rank_; }
//- Set the associated rank
void rank(int val) noexcept { rank_ = val; }
// Checks
// The file state
bool is_open() const noexcept { return state_; }
// The file read state
bool is_read() const noexcept
{
return (states::READ == state_);
}
// The file write atomic state
bool is_atomic() const noexcept
{
return (states::ATOMIC_WRITE == state_);
}
// The file write state (atomic or non-atomic)
bool is_write() const noexcept
{
return (states::ATOMIC_WRITE == state_ || states::WRITE == state_);
}
//- Assert is_read() or FatalError
inline bool checkReadable(const char* what) const
{
if (FOAM_UNLIKELY(!is_read()))
{
FatalErrorInFunction
<< "File handler not open for reading '" << what << "'\n"
<< "name: " << name() << nl
<< Foam::exit(Foam::FatalError);
return false;
}
return true;
}
//- Assert is_write() or FatalError
inline bool checkWritable(const char* what) const
{
if (FOAM_UNLIKELY(!is_write()))
{
FatalErrorInFunction
<< "File handler not open for writing'" << what << "'\n"
<< "name: " << name() << nl
<< Foam::exit(Foam::FatalError);
return false;
}
return true;
}
};
} // End namespace Foam
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
bool Foam::UPstream::File::supported()
{
return true;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::UPstream::File::File()
:
file_(new UPstream::File::Impl)
{}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::UPstream::File::~File()
{
if (FOAM_UNLIKELY(file_ && file_->is_open()))
{
WarningInFunction
<< "Exited scope without close()" << nl
<< " FIX YOUR CODE!!" << endl;
// Do not call close() since we don't know where that collective
// should have been called
}
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
const Foam::fileName& Foam::UPstream::File::name() const
{
return (file_ ? file_->name() : fileName::null);
}
bool Foam::UPstream::File::is_open() const
{
return bool(file_ && file_->is_open());
}
bool Foam::UPstream::File::close()
{
if (FOAM_UNLIKELY(!file_->is_open()))
{
WarningInFunction
<< "Called without an open file handler !" << endl;
return false;
}
MPI_File_close(&(file_->handle()));
// Atomic rename of file (master only)
const fileName& pathname = file_->name();
if (file_->master() && file_->is_atomic() && !pathname.empty())
{
std::rename
(
(pathname + "~tmp~").c_str(),
pathname.c_str()
);
}
file_->state(Impl::CLOSED);
file_->name() = "";
file_->rank(0);
return true;
}
// * * * * * * * * * * * * Member Functions (Reading) * * * * * * * * * * * //
#if 0
bool Foam::UPstream::File::open_read
(
const int communicator,
const fileName& pathname
)
{
//Needed? PstreamGlobals::checkCommunicator(communicator, 0);
if (FOAM_UNLIKELY(file_->is_open()))
{
WarningInFunction
<< "Previous use of file handler did not call close()" << nl
<< " FIX YOUR CODE!!" << endl;
// Do not call close() since we don't know where that collective
// should have been called
}
file_->state(Impl::CLOSED);
file_->name() = pathname; // <- set now for external error messages
file_->rank(0);
int returnCode = MPI_File_open
(
PstreamGlobals::MPICommunicators_[communicator],
pathname.c_str(),
(MPI_MODE_RDONLY),
MPI_INFO_NULL,
&(file_->handle())
);
if (FOAM_UNLIKELY(MPI_SUCCESS != returnCode))
{
FatalErrorInFunction
<< "Error encounted in MPI_File_open() : "
<< pathname << nl
<< Foam::exit(Foam::FatalError);
return false;
}
file_->state(Impl::READ);
file_->name() = pathname;
file_->rank(UPstream::myProcNo(communicator));
return true; // ie, is_read()
}
#endif
// * * * * * * * * * * * * Non-Collective Reading * * * * * * * * * * * * * //
#if 0
bool Foam::UPstream::File::get_header(DynamicList<char>& content)
{
std::streamsize headerSize(4096);
// constexpr const char* const func = "MPI_File_read_at";
file_->checkReadable("MPI_File_read_at");
if (off_t fileLen = Foam::fileSize(this->name()); fileLen >= 0)
{
std::streamsize size = std::streamsize(fileLen);
if (headerSize > size)
{
headerSize = size;
}
}
else
{
content.clear();
return false;
}
// Get the first header content:
content.resize_nocopy(headerSize);
int returnCode = Foam_mpiCall(MPI_File_read_at)
(
file_->handle(),
0, // offset
content.data(),
content.size(),
MPI_BYTE,
MPI_STATUS_IGNORE
);
// Wrap as ISpanStream headerStream(content);
if (MPI_SUCCESS == returnCode)
{
ISpanStream is(content);
dictionary headerDict;
// Read the regular "FoamFile" header
bool ok = io.readHeader(headerDict, is);
// Probably collated - extract class from "data.class"
if
(
decomposedBlockData::isCollatedType(io)
&& headerDict.readIfPresent("data.class", io.headerClassName())
)
{
return ok;
}
}
return (MPI_SUCCESS == returnCode);
}
#endif
// * * * * * * * * * * * * Member Functions (Writing) * * * * * * * * * * * //
bool Foam::UPstream::File::open_write
(
const int communicator,
const fileName& pathname,
IOstreamOption::atomicType atomicType
)
{
//Needed? PstreamGlobals::checkCommunicator(communicator, 0);
if (FOAM_UNLIKELY(file_->is_open()))
{
WarningInFunction
<< "Previous use of file handler did not call close()" << nl
<< " FIX YOUR CODE!!" << endl;
// Do not call close() since we don't know where that collective
// should have been called
}
file_->state(Impl::CLOSED);
file_->name() = pathname; // <- set now for external error messages
file_->rank(0);
const bool atomic = (IOstreamOption::atomicType::ATOMIC == atomicType);
// When opening new files, remove file variants out of the way.
// Eg, opening "file1"
// - remove old "file1.gz" (compressed)
// - also remove old "file1" if it is a symlink
const fileName pathname_gz(pathname + ".gz");
const fileName pathname_tmp(pathname + "~tmp~");
// File to open with MPI_File_open
const auto& target = (atomic ? pathname_tmp : pathname);
// Remove old compressed version (if any)
if
(
auto fType = Foam::type(pathname_gz, false);
(fType == fileName::SYMLINK || fType == fileName::FILE)
)
{
Foam::rm(pathname_gz);
}
// Avoid writing into symlinked files (non-append mode)
if
(
auto fType = Foam::type(target, false);
fType == fileName::SYMLINK
)
{
Foam::rm(target);
}
int returnCode = MPI_File_open
(
PstreamGlobals::MPICommunicators_[communicator],
target.c_str(),
(MPI_MODE_CREATE | MPI_MODE_WRONLY),
MPI_INFO_NULL,
&(file_->handle())
);
if (FOAM_UNLIKELY(MPI_SUCCESS != returnCode))
{
FatalErrorInFunction
<< "Error encounted in MPI_File_open() : "
<< target << nl
<< Foam::exit(Foam::FatalError);
return false;
}
file_->state(atomic ? Impl::ATOMIC_WRITE : Impl::WRITE);
file_->name() = pathname;
file_->rank(UPstream::myProcNo(communicator));
return true; // ie, is_write()
}
bool Foam::UPstream::File::set_size(std::streamsize num_bytes)
{
if (FOAM_UNLIKELY(!file_->is_open()))
{
WarningInFunction
<< "Called without an open file handler !" << endl;
return false;
}
int returnCode = MPI_File_set_size(file_->handle(), num_bytes);
return (MPI_SUCCESS == returnCode);
}
// * * * * * * * * * * * * Non-Collective Writing * * * * * * * * * * * * * //
bool Foam::UPstream::File::write_data
(
const void* data,
std::streamsize count,
const UPstream::dataTypes dataTypeId
)
{
MPI_Datatype datatype = PstreamGlobals::getDataType(dataTypeId);
// constexpr const char* const func = "MPI_File_write";
file_->checkWritable("MPI_File_write");
checkCount(count, "MPI_File_write");
int returnCode = Foam_mpiCall(MPI_File_write)
(
file_->handle(),
data,
count,
datatype,
MPI_STATUS_IGNORE
);
return (MPI_SUCCESS == returnCode);
}
bool Foam::UPstream::File::write_data_at
(
std::streamsize offset,
const void* data,
std::streamsize count,
const UPstream::dataTypes dataTypeId
)
{
MPI_Datatype datatype = PstreamGlobals::getDataType(dataTypeId);
// constexpr const char* const func = "MPI_File_write_at";
file_->checkWritable("MPI_File_write_at");
checkCount(count, "MPI_File_write_at");
int returnCode = Foam_mpiCall(MPI_File_write_at)
(
file_->handle(),
offset,
data,
count,
datatype,
MPI_STATUS_IGNORE
);
return (MPI_SUCCESS == returnCode);
}
// * * * * * * * * * * * * * Collective Writing * * * * * * * * * * * * * * //
bool Foam::UPstream::File::write_data_all
(
const void* data,
std::streamsize count,
const UPstream::dataTypes dataTypeId
)
{
MPI_Datatype datatype = PstreamGlobals::getDataType(dataTypeId);
// constexpr const char* const func = "MPI_File_write_all";
file_->checkWritable("MPI_File_write_all");
checkCount(count, "MPI_File_write_all");
int returnCode = Foam_mpiCall(MPI_File_write_all)
(
file_->handle(),
data,
count,
datatype,
MPI_STATUS_IGNORE
);
return (MPI_SUCCESS == returnCode);
}
bool Foam::UPstream::File::write_data_at_all
(
std::streamsize offset,
const void* data,
std::streamsize count,
const UPstream::dataTypes dataTypeId
)
{
MPI_Datatype datatype = PstreamGlobals::getDataType(dataTypeId);
// constexpr const char* const func = "MPI_File_write_at_all";
file_->checkWritable("MPI_File_write_at_all");
checkCount(count, "MPI_File_write_at_all");
int returnCode = Foam_mpiCall(MPI_File_write_at_all)
(
file_->handle(),
offset,
data,
count,
datatype,
MPI_STATUS_IGNORE
);
return (MPI_SUCCESS == returnCode);
}
// bool Foam::UPstream::File::write_data_all_begin
// (
// const void* data,
// std::streamsize count,
// const UPstream::dataTypes dataTypeId
// )
// {
// MPI_Datatype datatype = PstreamGlobals::getDataType(dataTypeId);
//
// // constexpr const char* const func = "MPI_File_write_all_begin";
// file_->checkWritable("MPI_File_write_all_begin");
// checkCount(count, "MPI_File_write_all_begin");
//
// int returnCode = Foam_mpiCall(MPI_File_write_all_begin)
// (
// file_->handle(),
// data,
// count,
// datatype,
// MPI_STATUS_IGNORE
// );
//
// return (MPI_SUCCESS == returnCode);
// }
// bool Foam::UPstream::File::write_data_all_end
// (
// const void* data
// )
// {
// file_->checkWritable("MPI_File_write_all_end");
// int returnCode = Foam_mpiCall(MPI_File_write_all_end)
// (
// file_->handle(),
// data
// MPI_STATUS_IGNORE
// );
//
// return (MPI_SUCCESS == returnCode);
// }
// bool Foam::UPstream::File::write_data_at_all_begin
// (
// std::streamsize offset,
// const void* data,
// std::streamsize count,
// const UPstream::dataTypes dataTypeId
// )
// {
// MPI_Datatype datatype = PstreamGlobals::getDataType(dataTypeId);
//
// // constexpr const char* const func = "MPI_File_write_at_all_begin";
// file_->checkWritable("MPI_File_write_at_all_begin");
// checkCount(count, "MPI_File_write_at_all_begin");
//
// int returnCode = Foam_mpiCall(MPI_File_write_at_all_begin)
// (
// file_->handle(),
// offset,
// data,
// count,
// datatype,
// MPI_STATUS_IGNORE
// );
//
// return (MPI_SUCCESS == returnCode);
// }
// bool Foam::UPstream::File::write_data_at_all_end
// (
// const void* data
// )
// {
// file_->checkWritable("MPI_File_write_at_all_end");
//
// int returnCode = Foam_mpiCall(MPI_File_write_at_all_end)
// (
// file_->handle(),
// data
// MPI_STATUS_IGNORE
// );
//
// return (MPI_SUCCESS == returnCode);
// }
// ************************************************************************* //