/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2017 OpenFOAM Foundation Copyright (C) 2015-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 . \*---------------------------------------------------------------------------*/ #include "CompactIOList.H" // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // template bool Foam::CompactIOList::readIOcontents() { typedef IOList plain_type; if (isReadRequired() || (isReadOptional() && headerOk())) { Istream& is = readStream(word::null); if (isHeaderClass(typeName)) { // Compact form is >> *this; // or: this->readCompact(is); } else if (isHeaderClass()) { // Non-compact form is >> static_cast(*this); } else { FatalIOErrorInFunction(is) << "Unexpected class name " << headerClassName() << " expected " << typeName << " or " << plain_type::typeName << endl << " while reading object " << name() << exit(FatalIOError); } close(); return true; } return false; } template Foam::label Foam::CompactIOList::readIOsize() { typedef IOList plain_type; label count(-1); if (isReadRequired() || (isReadOptional() && headerOk())) { Istream& is = readStream(word::null); token tok(is); if (tok.isLabel()) { // The majority of files will have lists with sizing prefix count = tok.labelToken(); if (isHeaderClass(typeName)) { // Compact form: read offsets, not content if (--count < 0) { count = 0; } } } else { is.putBack(tok); if (isHeaderClass(typeName)) { // Compact form: can just read the offsets labelList offsets(is); count = Foam::max(0, (offsets.size()-1)); } else if (isHeaderClass()) { // Non-compact form: need to read everything List list(is); count = list.size(); } else { FatalIOErrorInFunction(is) << "Unexpected class name " << headerClassName() << " expected " << typeName << " or " << plain_type::typeName << endl << " while reading object " << name() << exit(FatalIOError); } } close(); } return count; } template bool Foam::CompactIOList::overflows() const { // Can safely assume that int64 will not overflow if constexpr (sizeof(label) < sizeof(int64_t)) { const UList& lists = *this; label total = 0; for (const auto& list : lists) { const label prev = total; total += list.size(); if (total < prev) { return true; } } } return false; } // * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * * // template Foam::CompactIOList::CompactIOList(const IOobject& io) : regIOobject(io) { readIOcontents(); } template Foam::CompactIOList::CompactIOList ( const IOobject& io, Foam::zero ) : regIOobject(io) { readIOcontents(); } template Foam::CompactIOList::CompactIOList ( const IOobject& io, const label len ) : regIOobject(io) { if (!readIOcontents()) { List::resize(len); } } template Foam::CompactIOList::CompactIOList ( const IOobject& io, const UList& content ) : regIOobject(io) { if (!readIOcontents()) { List::operator=(content); } } template Foam::CompactIOList::CompactIOList ( const IOobject& io, List&& content ) : regIOobject(io) { List::transfer(content); readIOcontents(); } // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // template Foam::label Foam::CompactIOList::readContentsSize(const IOobject& io) { IOobject rio(io, IOobjectOption::NO_REGISTER); if (rio.readOpt() == IOobjectOption::READ_MODIFIED) { rio.readOpt(IOobjectOption::MUST_READ); } rio.resetHeader(); // Construct NO_READ, changing after construction const auto rOpt = rio.readOpt(IOobjectOption::NO_READ); CompactIOList reader(rio); reader.readOpt(rOpt); return reader.readIOsize(); } template Foam::List Foam::CompactIOList::readContents(const IOobject& io) { IOobject rio(io, IOobjectOption::NO_REGISTER); if (rio.readOpt() == IOobjectOption::READ_MODIFIED) { rio.readOpt(IOobjectOption::MUST_READ); } rio.resetHeader(); CompactIOList reader(rio); return List(std::move(static_cast&>(reader))); } // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // template bool Foam::CompactIOList::writeObject ( IOstreamOption streamOpt, const bool writeOnProc ) const { if ( streamOpt.format() == IOstreamOption::BINARY && overflows() ) { streamOpt.format(IOstreamOption::ASCII); WarningInFunction << "Overall number of elements of CompactIOList (size:" << this->size() << ") overflows a label (int" << (8*sizeof(label)) << ')' << nl << " Switching to ascii writing" << endl; } if (streamOpt.format() != IOstreamOption::BINARY) { // Change type to be non-compact format type const word oldTypeName(typeName); const_cast(typeName) = IOList::typeName; bool good = regIOobject::writeObject(streamOpt, writeOnProc); // Change type back const_cast(typeName) = oldTypeName; return good; } return regIOobject::writeObject(streamOpt, writeOnProc); } template bool Foam::CompactIOList::writeData(Ostream& os) const { return (os << *this).good(); } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // template Foam::Istream& Foam::CompactIOList::readCompact(Istream& is) { List& lists = *this; // The base type for packed values typedef typename T::value_type base_type; // Read compact: offsets + packed values const labelList offsets(is); List values(is); // Transcribe const label len = Foam::max(0, (offsets.size()-1)); lists.resize_nocopy(len); auto iter = values.begin(); for (label i = 0; i < len; ++i) { auto& list = lists[i]; const label count = (offsets[i+1] - offsets[i]); list.resize_nocopy(count); std::move(iter, iter + count, list.begin()); iter += count; } return is; } template Foam::Ostream& Foam::CompactIOList::writeCompact(Ostream& os) const { const List& lists = *this; // The base type for packed values typedef typename T::value_type base_type; // Convert to compact format label total = 0; const label len = lists.size(); // offsets { labelList offsets(len+1); for (label i = 0; i < len; ++i) { offsets[i] = total; total += lists[i].size(); if (total < offsets[i]) { FatalIOErrorInFunction(os) << "Overall number of elements of CompactIOList (size:" << len << ") overflows the representation of a label" << nl << "Please recompile with a larger representation" << " for label" << exit(FatalIOError); } } offsets[len] = total; os << offsets; } // packed values: make deepCopy for writing { List values(total); auto iter = values.begin(); for (const auto& list : lists) { iter = std::copy_n(list.begin(), list.size(), iter); // With IndirectList? [unlikely] // const label count = list.size(); // for (label i = 0; i < count; (void)++i, (void)++iter) // { // *iter = list[i]; // } } os << values; } return os; } // * * * * * * * * * * * * * * * Friend Operators * * * * * * * * * * * * * // template Foam::Istream& Foam::operator>> ( Foam::Istream& is, Foam::CompactIOList& lists ) { return lists.readCompact(is); } template Foam::Ostream& Foam::operator<< ( Foam::Ostream& os, const Foam::CompactIOList& lists ) { // Keep ASCII writing same if (os.format() != IOstreamOption::BINARY) { os << static_cast&>(lists); } else { lists.writeCompact(os); } return os; } // ************************************************************************* //