/*---------------------------------------------------------------------------*\
========= |
\\ / 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;
}
// ************************************************************************* //