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); printInfo(obuf);
// Overwrite at some position // Overwrite at some position
obuf.stdStream().rdbuf()->pubseekpos(0.60 * obuf.size()); if (auto i = obuf.view().find("item5"); i != std::string::npos)
obuf << "<" << nl << "OVERWRITE" << nl; {
// obuf.seek(0.60 * obuf.size());
obuf.seek(i);
obuf << "<OVERWRITE>" << nl;
}
Info<<"after overwrite" << nl; Info<<"after overwrite" << nl;
printInfo(obuf); printInfo(obuf);
// Truncate
{
constexpr float fraction = 0.90;
Info<<"truncated at " << (100*fraction) << "% ["
<< int(fraction*obuf.size()) << " chars]" << nl;
obuf.seek(fraction*obuf.size());
printInfo(obuf);
}
Info<< "transfer contents to a List or ICharStream" << nl; Info<< "transfer contents to a List or ICharStream" << nl;
// Reclaim data storage from OCharStream -> ICharStream // Reclaim data storage from OCharStream -> ICharStream

View File

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

View File

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

View File

@ -0,0 +1,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> template<class ListType>
inline void doAssignDynList(const ListType& list); inline void doAssignDynList(const ListType& list);
//- Alter the size of the underlying storage //- Alter the size of the underlying storage,
// The 'nocopy' option will not attempt to recover old content //- retaining the first count elements.
inline void doCapacity(const bool nocopy, const label len); 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. // Never shrinks the allocated size, use setCapacity() for that.
// The 'nocopy' option will not attempt to recover old content inline void doReserve_copy(label count, const label len);
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);
//- Read List from Istream between '(' and ')' delimiters. //- Read List from Istream between '(' and ')' delimiters.
//- The size is not known a priori. //- The size is not known a priori.
@ -232,6 +227,10 @@ public:
//- Alter addressable size and fill \em new entries with constant value //- Alter addressable size and fill \em new entries with constant value
inline void resize(const label len, const T& val); 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 //- Alter addressable size and set val for \em all addressed entries
inline void resize_fill(const label len, const T& val); 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> 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 const label newCapacity
) )
{ {
@ -68,16 +68,22 @@ inline void Foam::DynamicList<T, SizeMin>::doCapacity
// Addressable length, possibly truncated by new capacity // Addressable length, possibly truncated by new capacity
const label currLen = Foam::min(List<T>::size(), newCapacity); 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 // Consistent allocated sizing
List<T>::setAddressableSize(capacity_); List<T>::setAddressableSize(capacity_);
if (nocopy) if (count > 0)
{ {
List<T>::resize_nocopy(newCapacity); List<T>::resize_copy(count, newCapacity);
} }
else else
{ {
List<T>::resize_copy(currLen, newCapacity); List<T>::resize_nocopy(newCapacity);
} }
capacity_ = List<T>::size(); capacity_ = List<T>::size();
@ -86,9 +92,9 @@ inline void Foam::DynamicList<T, SizeMin>::doCapacity
template<class T, int SizeMin> 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 const label len
) )
{ {
@ -97,20 +103,27 @@ inline void Foam::DynamicList<T, SizeMin>::doReserve
// Preserve addressed size // Preserve addressed size
const label currLen = List<T>::size(); const label currLen = List<T>::size();
// The count truncated by the addressable range
if (count > currLen)
{
count = currLen;
}
// Consistent allocated sizing // Consistent allocated sizing
List<T>::setAddressableSize(capacity_); List<T>::setAddressableSize(capacity_);
// Increase capacity (eg, doubling) // Increase capacity (eg, doubling)
// - this may need better handling for when lists become very large
capacity_ = capacity_ =
Foam::ListPolicy::reserve_size<SizeMin, 2>(len, 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 else
{ {
List<T>::resize_copy(currLen, capacity_); List<T>::resize_nocopy(capacity_);
} }
capacity_ = List<T>::size(); 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 * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template<class T, int SizeMin> template<class T, int SizeMin>
@ -323,7 +324,8 @@ inline void Foam::DynamicList<T, SizeMin>::setCapacity
const label len 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 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 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 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 // Consistent allocated sizing
List<T>::setAddressableSize(capacity_); List<T>::setAddressableSize(capacity_);
// Exact length
capacity_ = len;
// if (!nocopy) // if (!nocopy)
{ {
List<T>::resize_copy(currLen, len); List<T>::resize_copy(currLen, capacity_);
} }
capacity_ = List<T>::size(); capacity_ = List<T>::size();
@ -388,7 +396,21 @@ inline void Foam::DynamicList<T, SizeMin>::resize
const label len 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 const T& val
) )
{ {
this->doResize(true, len); // nocopy = true resize_nocopy(len);
UList<T>::operator=(val); UList<T>::operator=(val);
} }
@ -410,7 +432,8 @@ inline void Foam::DynamicList<T, SizeMin>::resize_nocopy
const label len 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(); const label oldLen = List<T>::size();
// Preserve all content
resize(len); resize(len);
// Fill newly exposed with constant value // Fill newly exposed with constant value
@ -445,6 +470,9 @@ inline void Foam::DynamicList<T, SizeMin>::clear() noexcept
template<class T, int SizeMin> template<class T, int SizeMin>
inline void Foam::DynamicList<T, SizeMin>::clearStorage() inline void Foam::DynamicList<T, SizeMin>::clearStorage()
{ {
// Extra safety...?
if (!List<T>::cdata() && List<T>::empty()) capacity_ = 0;
// Consistent allocated sizing // Consistent allocated sizing
List<T>::setAddressableSize(capacity_); List<T>::setAddressableSize(capacity_);
List<T>::clear(); List<T>::clear();
@ -515,7 +543,7 @@ inline void Foam::DynamicList<T, SizeMin>::swap
UList<T>::swap(other); UList<T>::swap(other);
// Swap capacity // Swap capacity
std::swap(this->capacity_, other.capacity_); std::swap(capacity_, other.capacity_);
} }
@ -537,7 +565,7 @@ template<int AnySizeMin>
inline void inline void
Foam::DynamicList<T, SizeMin>::transfer Foam::DynamicList<T, SizeMin>::transfer
( (
DynamicList<T, AnySizeMin>& list DynamicList<T, AnySizeMin>& other
) )
{ {
if if
@ -545,7 +573,7 @@ Foam::DynamicList<T, SizeMin>::transfer
FOAM_UNLIKELY FOAM_UNLIKELY
( (
static_cast<const List<T>*>(this) 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 // Consistent allocated sizing
List<T>::setAddressableSize(capacity_); List<T>::setAddressableSize(capacity_);
List<T>::transfer(static_cast<List<T>&>(list)); List<T>::transfer(static_cast<List<T>&>(other));
capacity_ = list.capacity(); // Update capacity information
list.setCapacity_unsafe(0); // All contents moved capacity_ = other.capacity();
other.setCapacity_unsafe(0); // All contents moved
} }

View File

@ -28,27 +28,38 @@ License
#include "List.H" #include "List.H"
#include "FixedList.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> 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. if (this->size_ == len)
// Caller knows what they are doing. {
// no-op
if (FOAM_LIKELY(len > 0)) }
else if (FOAM_LIKELY(len > 0))
{ {
// With sign-check to avoid spurious -Walloc-size-larger-than // 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_; 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 // Recover overlapping content when resizing
@ -58,7 +69,7 @@ void Foam::List<T>::resize_copy(const label count, const label len)
// Can dispatch with // Can dispatch with
// - std::execution::par_unseq // - std::execution::par_unseq
// - std::execution::unseq // - std::execution::unseq
std::move(old, (old + overlap), this->v_); std::move(old, (old + count), this->v_);
ListPolicy::deallocate(old, oldLen); ListPolicy::deallocate(old, oldLen);
} }
@ -91,8 +102,6 @@ void Foam::List<T>::resize_copy(const label count, const label len)
template<class T> template<class T>
Foam::List<T>::List(const label len) Foam::List<T>::List(const label len)
:
UList<T>(nullptr, len)
{ {
if (FOAM_UNLIKELY(len < 0)) if (FOAM_UNLIKELY(len < 0))
{ {
@ -103,15 +112,14 @@ Foam::List<T>::List(const label len)
if (len > 0) if (len > 0)
{ {
doAlloc(); // resize_nocopy()
doAlloc(len);
} }
} }
template<class T> template<class T>
Foam::List<T>::List(const label len, const T& val) Foam::List<T>::List(const label len, const T& val)
:
UList<T>(nullptr, len)
{ {
if (FOAM_UNLIKELY(len < 0)) if (FOAM_UNLIKELY(len < 0))
{ {
@ -122,7 +130,8 @@ Foam::List<T>::List(const label len, const T& val)
if (len > 0) if (len > 0)
{ {
doAlloc(); // resize_fill()
doAlloc(len);
UList<T>::operator=(val); UList<T>::operator=(val);
} }
} }
@ -130,8 +139,6 @@ Foam::List<T>::List(const label len, const T& val)
template<class T> template<class T>
Foam::List<T>::List(const label len, Foam::zero) Foam::List<T>::List(const label len, Foam::zero)
:
UList<T>(nullptr, len)
{ {
if (FOAM_UNLIKELY(len < 0)) if (FOAM_UNLIKELY(len < 0))
{ {
@ -142,7 +149,8 @@ Foam::List<T>::List(const label len, Foam::zero)
if (len > 0) if (len > 0)
{ {
doAlloc(); // resize_fill()
doAlloc(len);
UList<T>::operator=(Foam::zero{}); UList<T>::operator=(Foam::zero{});
} }
} }
@ -177,12 +185,10 @@ Foam::List<T>::List(Foam::one, Foam::zero)
template<class T> template<class T>
Foam::List<T>::List(const UList<T>& list) 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); UList<T>::deepCopy(list);
} }
} }
@ -190,12 +196,10 @@ Foam::List<T>::List(const UList<T>& list)
template<class T> template<class T>
Foam::List<T>::List(const List<T>& list) 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); UList<T>::deepCopy(list);
} }
} }
@ -203,19 +207,18 @@ Foam::List<T>::List(const List<T>& list)
template<class T> template<class T>
Foam::List<T>::List(List<T>& list, bool reuse) Foam::List<T>::List(List<T>& list, bool reuse)
:
UList<T>(nullptr, list.size_)
{ {
if (reuse) if (reuse)
{ {
// Steal content // Steal content
this->v_ = list.v_; this->v_ = list.v_;
this->size_ = list.size_;
list.v_ = nullptr; list.v_ = nullptr;
list.size_ = 0; list.size_ = 0;
} }
else if (this->size_ > 0) else if (!list.empty())
{ {
doAlloc(); doAlloc(list.size());
UList<T>::deepCopy(list); UList<T>::deepCopy(list);
} }
} }
@ -223,11 +226,12 @@ Foam::List<T>::List(List<T>& list, bool reuse)
template<class T> template<class T>
Foam::List<T>::List(const UList<T>& list, const labelUList& indices) Foam::List<T>::List(const UList<T>& list, const labelUList& indices)
:
UList<T>(nullptr, indices.size())
{ {
doAlloc(); if (!indices.empty())
{
doAlloc(indices.size());
copyList(list, indices); // <- deepCopy() copyList(list, indices); // <- deepCopy()
}
} }
@ -238,11 +242,12 @@ Foam::List<T>::List
const UList<T>& list, const UList<T>& list,
const FixedList<label,N>& indices const FixedList<label,N>& indices
) )
:
UList<T>(nullptr, indices.size())
{ {
doAlloc(); // if (!FixedList::empty()) is always true
{
doAlloc(indices.size());
copyList(list, indices); // <- deepCopy() copyList(list, indices); // <- deepCopy()
}
} }
@ -255,24 +260,23 @@ Foam::List<T>::List(const FixedList<T, N>& list)
template<class T> template<class T>
Foam::List<T>::List(const PtrList<T>& list) Foam::List<T>::List(const UPtrList<T>& list)
:
UList<T>(nullptr, list.size())
{ {
doAlloc(); if (!list.empty())
{
doAlloc(list.size());
copyList(list); copyList(list);
}
} }
template<class T> template<class T>
template<class Addr> template<class Addr>
Foam::List<T>::List(const IndirectListBase<T, Addr>& list) 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); 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<class T>
template<int SizeMin> template<int SizeMin>
Foam::List<T>::List(DynamicList<T, SizeMin>&& list) Foam::List<T>::List(DynamicList<T, SizeMin>&& list)
:
UList<T>()
{ {
transfer(list); transfer(list);
} }
@ -327,7 +319,7 @@ void Foam::List<T>::resize(const label len, const T& val)
return; return;
} }
this->resize_copy(oldLen, len); resize_copy(oldLen, len);
// Fill trailing part with new values // Fill trailing part with new values
if (oldLen < this->size_) 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 // Shrink the allocated space to the number of elements used
list.shrink_to_fit(); list.shrink_to_fit();
transfer(static_cast<List<T>&>(list)); 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 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); UList<T>::deepCopy(list);
} }
@ -399,9 +391,9 @@ void Foam::List<T>::operator=(const List<T>& list)
return; // Self-assignment is a no-op 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); UList<T>::deepCopy(list);
} }
@ -412,7 +404,7 @@ template<class T>
template<unsigned N> template<unsigned N>
void Foam::List<T>::operator=(const FixedList<T, N>& list) 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_); std::copy(list.begin(), list.end(), this->v_);
} }
@ -422,7 +414,7 @@ template<class T>
template<class Addr> template<class Addr>
void Foam::List<T>::operator=(const IndirectListBase<T, Addr>& list) void Foam::List<T>::operator=(const IndirectListBase<T, Addr>& list)
{ {
reAlloc(list.size()); resize_nocopy(list.size());
UList<T>::deepCopy(list); UList<T>::deepCopy(list);
} }
@ -430,8 +422,7 @@ void Foam::List<T>::operator=(const IndirectListBase<T, Addr>& list)
template<class T> template<class T>
void Foam::List<T>::operator=(std::initializer_list<T> list) 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_); 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, unsigned N> class FixedList;
template<class T, int SizeMin> class DynamicList; 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); template<class T> Istream& operator>>(Istream& is, List<T>& list);
@ -77,12 +77,8 @@ class List
{ {
// Private Member Functions // Private Member Functions
//- Allocate list storage //- Allocate list storage. Assumes there is no existing content
inline void doAlloc(); inline void doAlloc(const label len);
//- Reallocate list storage to the given size
// Discards old storage (if any). Does not copy old contents
inline void reAlloc(const label len);
//- Copy all list contents. Uses operator[] on the input list //- Copy all list contents. Uses operator[] on the input list
template<class ListType> template<class ListType>
@ -98,8 +94,8 @@ class List
template<class InputIterator> template<class InputIterator>
inline List inline List
( (
InputIterator firstIter, InputIterator input,
InputIterator lastIter, // (unused) InputIterator inputEnd, // (unused)
const label len const label len
); );
@ -111,17 +107,7 @@ class List
// Methods as per DynamicList to simplify code maintenance // Methods as per DynamicList to simplify code maintenance
//- Stub method for internal naming as per DynamicList //- Stub method for internal naming as per DynamicList
void setCapacity_nocopy(const label len) { resize_nocopy(len); } void setCapacity_nocopy(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);
public: public:
@ -185,8 +171,8 @@ public:
template<unsigned N> template<unsigned N>
explicit List(const FixedList<T, N>& list); explicit List(const FixedList<T, N>& list);
//- Construct as copy of PtrList<T> //- Construct as copy of UPtrList<T> content
explicit List(const PtrList<T>& list); explicit List(const UPtrList<T>& list);
//- Construct as copy of IndirectList contents //- Construct as copy of IndirectList contents
template<class Addr> template<class Addr>
@ -196,7 +182,7 @@ public:
List(std::initializer_list<T> list); List(std::initializer_list<T> list);
//- Move construct from List //- Move construct from List
List(List<T>&& list) noexcept; inline List(List<T>&& list) noexcept;
//- Move construct from DynamicList //- Move construct from DynamicList
template<int SizeMin> template<int SizeMin>
@ -227,6 +213,10 @@ public:
//- Adjust allocated size of list and set val for \em new elements //- Adjust allocated size of list and set val for \em new elements
void resize(const label len, const T& val); 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 //- Adjust allocated size of list and set val for \em all elements
inline void resize_fill(const label len, const T& val); inline void resize_fill(const label len, const T& val);
@ -234,14 +224,9 @@ public:
// retaining old content. // retaining old content.
// If no reallocation is required, the contents remain untouched. // If no reallocation is required, the contents remain untouched.
// Otherwise the contents will be uninitialized. // Otherwise the contents will be uninitialized.
// Shorthand for \c resize(0, len)
inline void resize_nocopy(const label 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 // Edit
@ -404,6 +389,12 @@ public:
//- Same as push_uniq() //- Same as push_uniq()
FOAM_DEPRECATED_FOR(2022-10, "push_uniq()") FOAM_DEPRECATED_FOR(2022-10, "push_uniq()")
label appendUniq(const T& val) { return this->push_uniq(val); } 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 * * * * * * * * * * * // // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
template<class T> 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 // With sign-check to avoid spurious -Walloc-size-larger-than
this->v_ = ListPolicy::allocate<T>(this->size_); this->v_ = ListPolicy::allocate<T>(len);
}
}
template<class T>
inline void Foam::List<T>::reAlloc(const label len)
{
if (this->size_ != len)
{
clear();
this->size_ = len; this->size_ = len;
doAlloc();
} }
} }
@ -95,23 +84,24 @@ template<class T>
template<class InputIterator> template<class InputIterator>
inline Foam::List<T>::List inline Foam::List<T>::List
( (
InputIterator firstIter, InputIterator input,
InputIterator lastIter, // (unused) InputIterator inputEnd, // (unused)
const label len const label len
) )
:
UList<T>(nullptr, len)
{ {
if (len > 0) if (len > 0)
{ {
doAlloc(); doAlloc(len);
// Like std::copy() or std::copy_n() // Like std::copy() or std::copy_n()
// but without any requirements on the iterator category // 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> template<class T>
inline Foam::autoPtr<Foam::List<T>> Foam::List<T>::clone() const 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) 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> template<class T>
inline void Foam::List<T>::resize_fill(const label len, const T& val) inline void Foam::List<T>::resize_fill(const label len, const T& val)
{ {
this->reAlloc(len); resize_nocopy(len);
UList<T>::operator=(val); UList<T>::operator=(val);
} }
@ -174,7 +175,12 @@ inline void Foam::List<T>::resize_fill(const label len, const T& val)
template<class T> template<class T>
inline void Foam::List<T>::resize_nocopy(const label len) 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 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 // Hashing

View File

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

View File

@ -175,7 +175,8 @@ public:
//- operation. //- operation.
virtual Istream& readRaw(char* data, std::streamsize count) = 0; 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; virtual void rewind() = 0;

View File

@ -563,6 +563,8 @@ Foam::label Foam::UIPstreamBase::remaining() const noexcept
void Foam::UIPstreamBase::rewind() void Foam::UIPstreamBase::rewind()
{ {
Istream::rewind(); // Drop any putback
recvBufPos_ = 0; // Assume the entire buffer is for us to read from recvBufPos_ = 0; // Assume the entire buffer is for us to read from
setOpened(); setOpened();
setGood(); 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() void Foam::ISstream::rewind()
{ {
Istream::rewind(); // Drop any putback
lineNumber_ = 1; // Reset line number lineNumber_ = 1; // Reset line number
stdStream().clear(); // Clear the iostate error state flags stdStream().clear(); // Clear the iostate error state flags

View File

@ -531,7 +531,11 @@ public:
virtual bool endRawRead() override { return false; } virtual bool endRawRead() override { return false; }
//- Rewind the stream so that it may be read again. Same as seek(0) //- 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 // Output

View File

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

View File

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

View File

@ -31,8 +31,7 @@ Description
Similar to OStringStream but with a List for its storage instead of Similar to OStringStream but with a List for its storage instead of
as string to allow reuse of List contents without copying. as string to allow reuse of List contents without copying.
The default initial size is 512-bytes and uses size doubling. Internally imposes a 512 byte min-size and uses capacity doubling.
After construction can use the reserve() method to adjust this.
See Also See Also
Foam::ICharStream Foam::ICharStream
@ -131,6 +130,25 @@ public:
buffer_type::reserve(n); buffer_type::reserve(n);
} }
//- Reserve output space for at least this amount.
//- Does not apply min-size or capacity doubling etc.
void reserve_exact(std::streamsize n)
{
buffer_type::reserve_exact(n);
}
//- 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 //- Rewind the stream, clearing any old errors
void rewind() void rewind()
{ {
@ -209,6 +227,64 @@ public:
//- Information about stream //- Information about stream
void print(Ostream& os) const { debug_info(os); os << '\n'; } 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 //- Reserve output space for at least this amount
void reserve(std::streamsize n) { stream_.reserve(n); } void reserve(std::streamsize n) { stream_.reserve(n); }
//- Reserve output space for at least this amount.
//- Does not apply min-size or capacity doubling etc.
void reserve_exact(std::streamsize n) { stream_.reserve_exact(n); }
//- 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 //- A string_view of buffer contents
auto view() const { return stream_.view(); } 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 // Housekeeping
//- Block size was used in OpenFOAM-v2306 and earlier //- Block size was used in OpenFOAM-v2306 and earlier

View File

@ -226,6 +226,23 @@ public:
// Extra/Convenience Methods // 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) //- The output data (start of output characters)
const char* cdata_bytes() const { return buffer_type::data_bytes(); } const char* cdata_bytes() const { return buffer_type::data_bytes(); }
@ -234,7 +251,6 @@ public:
//- The current number of output characters //- The current number of output characters
std::streamsize size_bytes() const { return buffer_type::size_bytes(); } std::streamsize size_bytes() const { return buffer_type::size_bytes(); }
}; };
@ -382,6 +398,47 @@ public:
stream_.debug_info(os); stream_.debug_info(os);
os << '\n'; 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 //- Some information about the output buffer position/capacity
void info(Ostream& os) const void info(Ostream& os) const
{ {
@ -500,8 +537,8 @@ protected:
{ {
if (c != traits_type::eof()) if (c != traits_type::eof())
{ {
// Need more space? // Needed more space?
reserve(1 + span_tellp()); extend(1);
*(pptr()) = c; *(pptr()) = c;
pbump(1); pbump(1);
@ -513,7 +550,7 @@ protected:
virtual std::streamsize xsputn(const char* s, std::streamsize n) virtual std::streamsize xsputn(const char* s, std::streamsize n)
{ {
// Enough space so that appends work without problem // Enough space so that appends work without problem
reserve(n + span_tellp()); extend(n);
std::streamsize count = 0; std::streamsize count = 0;
while (count < n && pptr() < epptr()) while (count < n && pptr() < epptr())
@ -530,10 +567,14 @@ public:
// Constructors // Constructors
//- Default construct - no initial reserved number of bytes.
out_dynamic()
{
sync_pbuffer();
}
//- Default construct with initial reserved number of bytes. //- Default construct with initial reserved number of bytes.
// The value of 512 is a bit arbitrary, but consistent with out_dynamic(size_t nbytes)
// std::stringstream
out_dynamic(size_t nbytes = 512)
: :
storage_(label(nbytes)) storage_(label(nbytes))
{ {
@ -560,40 +601,115 @@ public:
// Member Functions // 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) void reserve(const std::streamsize len)
{ {
if (storage_.size() < len) if (storage_.size() < len)
{ {
const auto cur = span_tellp(); // Current location const auto cur = span_tellp(); // Output position
label newCapacity = 512; label size = min_size();
if (newCapacity < len) if (size < len)
{ {
// Increase capacity (doubling) // Increase capacity, but grow more slowly at the largest
newCapacity = // sizes. Expect a buffer of char to approach max_size()
Foam::max(label(len), label(2*storage_.size())); // more commonly than buffers of other data types.
// ratio=1.5:
// Foam::max(label(len), label((storage_.size()/2)*3)); 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();
} }
// Info<<"request:" << len // max(size, len)
// << " cur cap:" << storage_.size() if (size < len)
// << " new cap:" << newCapacity {
size = len;
}
}
// std::cerr
// <<"request:" << len
// << " old cap:" << storage_.size()
// << " new cap:" << size
// << " pos:" << cur << endl; // << " pos:" << cur << endl;
storage_.resize(newCapacity); storage_.resize_copy(cur, size);
sync_pbuffer(); sync_pbuffer();
pbump(cur); pbump(cur);
} }
} }
//- Sync put buffer pointers to agree with list dimensions //- Increase capacity for at least this size.
// Sets put pointer to the begin (rewind). //- Does not apply min-size or capacity doubling etc.
void sync_pbuffer() void reserve_exact(const std::streamsize len)
{ {
resetp(storage_.data(), storage_.size()); if (storage_.size() < len)
{
const auto cur = span_tellp(); // Output position
storage_.resize_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 //- Clear storage

View File

@ -79,19 +79,14 @@ class DynamicField
template<class ListType> template<class ListType>
inline void doAssignDynList(const ListType& list); inline void doAssignDynList(const ListType& list);
//- Alter the size of the underlying storage //- Alter the size of the underlying storage,
// The 'nocopy' option will not attempt to recover old content //- retaining the first count elements.
inline void doCapacity(const bool nocopy, const label len); 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. // Never shrinks the allocated size, use setCapacity() for that.
// The 'nocopy' option will not attempt to recover old content inline void doReserve_copy(label count, const label len);
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);
public: public:
@ -246,6 +241,10 @@ public:
//- Alter addressable size and fill \em new entries with constant value //- Alter addressable size and fill \em new entries with constant value
inline void resize(const label len, const T& val); 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 //- Alter addressable size and set val for \em all addressed entries
inline void resize_fill(const label len, const T& val); 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> 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 const label newCapacity
) )
{ {
@ -66,16 +66,22 @@ inline void Foam::DynamicField<T, SizeMin>::doCapacity
// Addressable length, possibly truncated by new capacity // Addressable length, possibly truncated by new capacity
const label currLen = Foam::min(List<T>::size(), newCapacity); 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 // Consistent allocated sizing
List<T>::setAddressableSize(capacity_); List<T>::setAddressableSize(capacity_);
if (nocopy) if (count > 0)
{ {
List<T>::resize_nocopy(newCapacity); List<T>::resize_copy(count, newCapacity);
} }
else else
{ {
List<T>::resize_copy(currLen, newCapacity); List<T>::resize_nocopy(newCapacity);
} }
capacity_ = List<T>::size(); capacity_ = List<T>::size();
@ -84,9 +90,9 @@ inline void Foam::DynamicField<T, SizeMin>::doCapacity
template<class T, int SizeMin> 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 const label len
) )
{ {
@ -95,6 +101,12 @@ inline void Foam::DynamicField<T, SizeMin>::doReserve
// Preserve addressed size // Preserve addressed size
const label currLen = List<T>::size(); const label currLen = List<T>::size();
// The count truncated by the addressable range
if (count > currLen)
{
count = currLen;
}
// Consistent allocated sizing // Consistent allocated sizing
List<T>::setAddressableSize(capacity_); List<T>::setAddressableSize(capacity_);
@ -102,13 +114,13 @@ inline void Foam::DynamicField<T, SizeMin>::doReserve
capacity_ = capacity_ =
Foam::ListPolicy::reserve_size<SizeMin, 2>(len, 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 else
{ {
List<T>::resize_copy(currLen, capacity_); List<T>::resize_nocopy(capacity_);
} }
capacity_ = List<T>::size(); 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 * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template<class T, int SizeMin> template<class T, int SizeMin>
@ -342,7 +342,7 @@ inline Foam::DynamicField<T, SizeMin>::DynamicField
) )
: :
Field<T>(), Field<T>(),
capacity_(content.size()) capacity_(0)
{ {
if (reuse) if (reuse)
{ {
@ -352,6 +352,7 @@ inline Foam::DynamicField<T, SizeMin>::DynamicField
{ {
Field<T>::operator=(content); Field<T>::operator=(content);
} }
capacity_ = Field<T>::size();
} }
@ -424,7 +425,8 @@ inline void Foam::DynamicField<T, SizeMin>::setCapacity
const label len 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 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 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 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 // Consistent allocated sizing
List<T>::setAddressableSize(capacity_); List<T>::setAddressableSize(capacity_);
// Exact length
capacity_ = len;
// if (!nocopy) // if (!nocopy)
{ {
List<T>::resize_copy(currLen, len); List<T>::resize_copy(currLen, capacity_);
} }
capacity_ = List<T>::size(); capacity_ = List<T>::size();
@ -489,17 +497,21 @@ inline void Foam::DynamicField<T, SizeMin>::resize
const label len const label len
) )
{ {
this->doResize(false, len); // nocopy = false // Preserve all content
resize_copy(List<T>::size(), len);
} }
template<class T, int SizeMin> 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 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 const T& val
) )
{ {
this->doResize(true, len); // nocopy = true resize_nocopy(len);
UList<T>::operator=(val); 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> template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::resize inline void Foam::DynamicField<T, SizeMin>::resize
( (
@ -523,6 +546,8 @@ inline void Foam::DynamicField<T, SizeMin>::resize
) )
{ {
const label oldLen = List<T>::size(); const label oldLen = List<T>::size();
// Preserve all content
resize(len); resize(len);
// Fill newly exposed with constant value // Fill newly exposed with constant value
@ -546,6 +571,11 @@ inline void Foam::DynamicField<T, SizeMin>::clear() noexcept
template<class T, int SizeMin> template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::clearStorage() 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(); List<T>::clear();
capacity_ = 0; capacity_ = 0;
} }
@ -555,7 +585,6 @@ template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::shrink_to_fit() inline void Foam::DynamicField<T, SizeMin>::shrink_to_fit()
{ {
const label currLen = List<T>::size(); const label currLen = List<T>::size();
if (currLen < capacity_) if (currLen < capacity_)
{ {
List<T>::setAddressableSize(capacity_); List<T>::setAddressableSize(capacity_);
@ -570,10 +599,13 @@ inline void
Foam::DynamicField<T, SizeMin>::swap(List<T>& other) Foam::DynamicField<T, SizeMin>::swap(List<T>& other)
{ {
if if
(
FOAM_UNLIKELY
( (
static_cast<const List<T>*>(this) static_cast<const List<T>*>(this)
== static_cast<const List<T>*>(&other) == static_cast<const List<T>*>(&other)
) )
)
{ {
return; // Self-swap is a no-op return; // Self-swap is a no-op
} }
@ -597,10 +629,13 @@ inline void Foam::DynamicField<T, SizeMin>::swap
) noexcept ) noexcept
{ {
if if
(
FOAM_UNLIKELY
( (
static_cast<const List<T>*>(this) static_cast<const List<T>*>(this)
== static_cast<const List<T>*>(&other) == static_cast<const List<T>*>(&other)
) )
)
{ {
return; // Self-swap is a no-op return; // Self-swap is a no-op
} }
@ -621,10 +656,13 @@ inline void Foam::DynamicField<T, SizeMin>::swap
) noexcept ) noexcept
{ {
if if
(
FOAM_UNLIKELY
( (
static_cast<const List<T>*>(this) static_cast<const List<T>*>(this)
== static_cast<const List<T>*>(&other) == static_cast<const List<T>*>(&other)
) )
)
{ {
return; // Self-swap is a no-op return; // Self-swap is a no-op
} }
@ -633,17 +671,19 @@ inline void Foam::DynamicField<T, SizeMin>::swap
UList<T>::swap(other); UList<T>::swap(other);
// Swap capacity // Swap capacity
const label oldCap = this->capacity(); auto old = capacity_;
const label newCap = other.capacity(); this->setCapacity_unsafe(other.capacity());
other.setCapacity_unsafe(old);
this->setCapacity_unsafe(newCap);
other.setCapacity_unsafe(oldCap);
} }
template<class T, int SizeMin> template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::transfer(List<T>& list) 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); Field<T>::transfer(list);
capacity_ = Field<T>::size(); capacity_ = Field<T>::size();
} }
@ -653,7 +693,7 @@ template<class T, int SizeMin>
template<int AnySizeMin> template<int AnySizeMin>
inline void Foam::DynamicField<T, SizeMin>::transfer inline void Foam::DynamicField<T, SizeMin>::transfer
( (
DynamicList<T, AnySizeMin>& list DynamicList<T, AnySizeMin>& other
) )
{ {
if if
@ -661,19 +701,22 @@ inline void Foam::DynamicField<T, SizeMin>::transfer
FOAM_UNLIKELY FOAM_UNLIKELY
( (
static_cast<const UList<T>*>(this) 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 return; // Self-assignment is a no-op
} }
// Consistent allocated sizing // Remove storage
List<T>::setAddressableSize(capacity_); this->clearStorage();
Field<T>::transfer(static_cast<List<T>&>(list));
capacity_ = list.capacity(); // Swap storage and addressable size
list.setCapacity_unsafe(0); // All contents moved 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> template<int AnySizeMin>
inline void Foam::DynamicField<T, SizeMin>::transfer inline void Foam::DynamicField<T, SizeMin>::transfer
( (
DynamicField<T, AnySizeMin>& list DynamicField<T, AnySizeMin>& other
) )
{ {
if if
@ -689,19 +732,22 @@ inline void Foam::DynamicField<T, SizeMin>::transfer
FOAM_UNLIKELY FOAM_UNLIKELY
( (
static_cast<const UList<T>*>(this) 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 return; // Self-assignment is a no-op
} }
// Consistent allocated sizing // Remove storage
List<T>::setAddressableSize(capacity_); this->clearStorage();
Field<T>::transfer(static_cast<List<T>&>(list));
capacity_ = list.capacity(); // Swap storage and addressable size
list.setCapacity_unsafe(0); // All contents moved UList<T>::swap(other);
// Update capacity
capacity_ = other.capacity();
other.setCapacity_unsafe(0);
} }

View File

@ -5,6 +5,7 @@ UPstreamCommunicator.C
UPstreamGatherScatter.C UPstreamGatherScatter.C
UPstreamReduce.C UPstreamReduce.C
UPstreamRequest.C UPstreamRequest.C
UPstreamFile.C
UPstreamWindow.C UPstreamWindow.C
UIPstreamRead.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 UPstreamGatherScatter.C
UPstreamReduce.C UPstreamReduce.C
UPstreamRequest.C UPstreamRequest.C
UPstreamFile.C
UPstreamWindow.C UPstreamWindow.C
UIPstreamRead.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);
// }
// ************************************************************************* //