Commit 9fd696e1 authored by Mark Olesen's avatar Mark Olesen
Browse files

ENH: add ITstream append and seek methods.

- ITstream append() would previously have used the append from the
  underlying tokenList, which leaves the tokenIndex untouched and
  renders the freshly appended tokens effectively invisible if
  interspersed with primitiveEntry::read() that itself uses tokenIndex
  when building the list.

  The new append() method makes this hidden ITstream bi-directionality
  easier to manage. For efficiency, we only append lists
  (not individual tokens) and support a 'lazy' resizing that allows
  the final resizing to occur later when all tokens have been appended.

- The new ITstream seek() method provides a conveniently means to move
  to the end of the list or reposition to the middle.
  Using rewind() and using seek(0) are identical.

ENH: added OTstream to output directly to a list of tokens

---

BUG: List::newElem resized incorrectly

- had a simple doubling of the List size without checking that this
  would indeed be sufficient for the requested index.

  Bug was not triggered since primitiveEntry was the only class using
  this call, and it added the tokens sequentially.
parent 6b5da706
Test-OTstream.C
EXE = $(FOAM_USER_APPBIN)/Test-OTstream
/* EXE_INC = */
/* EXE_LIBS = */
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2019 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 "ITstream.H"
#include "OTstream.H"
#include "primitiveFields.H"
#include "argList.H"
using namespace Foam;
void printTokens(const UList<token>& toks)
{
label count = 0;
for (const token& t : toks)
{
Info<< "token: " << t.info() << nl;
++count;
}
Info<< count << " tokens" << nl << endl;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Main program:
int main(int argc, char *argv[])
{
// Test fields
{
scalarField fld1({1, 2, 3, 4});
OTstream os;
fld1.writeEntry("field", os);
Info<< "Field: " << fld1 << nl
<< "Tokens: " << flatOutput(os) << nl;
printTokens(os);
}
{
scalarField fld1(10, scalar(5));
OTstream os;
fld1.writeEntry("field", os);
Info<< "Field: " << fld1 << nl
<< "Tokens: " << flatOutput(os) << nl;
printTokens(os);
}
{
vector val(1,2, 3);
OTstream os;
os << val;
Info<< "Value: " << val << nl
<< "Tokens: " << flatOutput(os) << nl;
printTokens(os);
}
{
bool val(true);
OTstream os;
os << val;
Info<< "Value: " << val << nl
<< "Tokens: " << flatOutput(os) << nl;
printTokens(os);
}
{
tensorField fld1(1, tensor::I);
OTstream os;
fld1.writeEntry("field", os);
Info<< "Field: " << fld1 << nl
<< "Tokens: " << flatOutput(os) << nl;
printTokens(os);
}
{
labelList fld1(identity(5));
OTstream os;
fld1.writeEntry("field", os);
Info<< "Field: " << fld1 << nl
<< "Tokens: " << flatOutput(os) << nl;
printTokens(os);
}
Info<< "\nEnd\n" << endl;
return 0;
}
// ************************************************************************* //
......@@ -230,6 +230,7 @@ $(Fstreams)/masterOFstream.C
Tstreams = $(Streams)/Tstreams
$(Tstreams)/ITstream.C
$(Tstreams)/OTstream.C
StringStreams = $(Streams)/StringStreams
$(StringStreams)/StringStream.C
......
......@@ -159,11 +159,17 @@ inline void Foam::List<T>::setSize(const label len, const T& val)
template<class T>
inline T& Foam::List<T>::newElmt(const label i)
{
const label n = this->size();
label n = this->size();
if (i >= n)
{
resize(2*n);
do
{
n *= 2;
}
while (i >= n);
resize(n);
}
return UList<T>::operator[](i);
......
......@@ -100,6 +100,39 @@ Foam::tokenList Foam::ITstream::parse
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::ITstream::reserveCapacity
(
const label nElem,
const bool lazy
)
{
if (lazy)
{
// Reserve - leave excess capacity for further appends
label n = tokenList::size();
if (nElem >= n)
{
do
{
n *= 2;
}
while (nElem >= n);
tokenList::resize(n);
}
}
else
{
// Strict capacity
tokenList::resize(nElem);
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::ITstream::ITstream
......@@ -317,16 +350,81 @@ Foam::Istream& Foam::ITstream::read(char*, std::streamsize)
void Foam::ITstream::rewind()
{
tokenIndex_ = 0;
seek(0);
}
void Foam::ITstream::seek(label pos)
{
lineNumber_ = 0;
tokenList& toks = *this;
if (size())
if (!pos)
{
// Seek begin (rewind)
tokenIndex_ = 0;
if (!toks.empty())
{
lineNumber_ = toks.first().lineNumber();
}
setOpened();
setGood();
}
else if (pos < 0 || pos >= toks.size())
{
// Seek end or seek is out of range
tokenIndex_ = toks.size();
if (!toks.empty())
{
lineNumber_ = toks.last().lineNumber();
}
setEof();
}
else
{
// Seek middle (from the beginning)
tokenIndex_ = pos;
if (!toks.empty())
{
lineNumber_ = toks[tokenIndex_].lineNumber();
}
setOpened();
setGood();
}
}
void Foam::ITstream::append(const tokenList& newTokens, const bool lazy)
{
reserveCapacity(tokenIndex_ + newTokens.size(), lazy);
tokenList& toks = *this;
for (const token& t : newTokens)
{
toks[tokenIndex_] = t; // copy append
++tokenIndex_;
}
}
void Foam::ITstream::append(tokenList&& newTokens, const bool lazy)
{
reserveCapacity(tokenIndex_ + newTokens.size(), lazy);
tokenList& toks = *this;
for (token& t : newTokens)
{
lineNumber_ = tokenList::first().lineNumber();
toks[tokenIndex_] = std::move(t); // move append
++tokenIndex_;
}
setOpened();
setGood();
newTokens.clear();
}
......
......@@ -73,6 +73,15 @@ class ITstream
// \return the number of tokens in the resulting list.
static label parseStream(ISstream& input, tokenList& tokens);
//- An ad hoc combination of reserve and setCapacity somewhat
//- similar to DynamicList.
//
// In lazy mode, increase list size if needed, but leave any
// excess capacity - works like reserve.
//
// In non-lazy mode, set exact capacity
void reserveCapacity(const label nElem, const bool lazy);
public:
......@@ -149,7 +158,7 @@ public:
);
//- Construct as copy
//- Copy construct
ITstream(const ITstream& is)
:
Istream(ASCII, currentVersion),
......@@ -209,13 +218,13 @@ public:
return name_;
}
//- Return the current token index
//- The current token index when reading, or the insertion point.
label tokenIndex() const
{
return tokenIndex_;
}
//- Return non-const access to the current token index
//- Non-const access to the current token index
label& tokenIndex()
{
return tokenIndex_;
......@@ -280,9 +289,30 @@ public:
//- Rewind the stream so that it may be read again
virtual void rewind();
//- Move the tokenIndex to the specified position.
// Using seek(0) is identical to rewind.
// Using seek(-1) moves to the end.
void seek(label pos);
// Edit
//- Copy append a tokenList at the current tokenIndex,
//- incrementing the index.
//
// \param newTokens the list of tokens to copy append
// \param lazy leaves any excess capacity for further appends.
// The caller will be responsible for resizing later.
void append(const tokenList& newTokens, const bool lazy);
//- Move append a tokenList at the current tokenIndex,
//- incrementing the index.
//
// \param newTokens the list of tokens to move append
// \param lazy leaves any excess capacity for further appends.
// The caller will be responsible for resizing later.
void append(tokenList&& newTokens, const bool lazy);
//- Set flags of stream
ios_base::fmtflags flags(const ios_base::fmtflags)
{
......
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2019 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/>.
\*---------------------------------------------------------------------------*/
#include "error.H"
#include "OTstream.H"
#include <cctype>
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
bool Foam::OTstream::write(const token& tok)
{
if (tok.good())
{
append(tok);
return true;
}
return false;
}
bool Foam::OTstream::write(token&& tok)
{
if (tok.good())
{
append(std::move(tok));
return true;
}
return false;
}
Foam::Ostream& Foam::OTstream::write(const char c)
{
if (!std::isspace(c) && std::isprint(c))
{
// Should generally work, but need to verify corner cases
append(token(token::punctuationToken(c)));
}
return *this;
}
Foam::Ostream& Foam::OTstream::write(const char* str)
{
const word nonWhiteChars(string::validate<word>(str));
if (nonWhiteChars.size() == 1)
{
// Like punctuation
write(nonWhiteChars[0]);
}
else if (nonWhiteChars.size())
{
// As a word
write(nonWhiteChars);
}
return *this;
}
Foam::Ostream& Foam::OTstream::write(const word& str)
{
append(token(str)); // tokenType::WORD
return *this;
}
Foam::Ostream& Foam::OTstream::write(const string& str)
{
append(token(str)); // tokenType::STRING
return *this;
}
Foam::Ostream& Foam::OTstream::writeQuoted
(
const std::string& str,
const bool quoted
)
{
if (quoted)
{
append(token(string(str))); // tokenType::STRING
}
else if (!str.empty())
{
append(token(word(str, false))); // tokenType::WORD
}
return *this;
}
Foam::Ostream& Foam::OTstream::write(const int32_t val)
{
append(token(label(val))); // tokenType::LABEL
return *this;
}
Foam::Ostream& Foam::OTstream::write(const int64_t val)
{
append(token(label(val))); // tokenType::LABEL
return *this;
}
Foam::Ostream& Foam::OTstream::write(const floatScalar val)
{
append(token(val)); // tokenType::FLOAT
return *this;
}
Foam::Ostream& Foam::OTstream::write(const doubleScalar val)
{
append(token(val)); // tokenType::DOUBLE
return *this;
}
Foam::Ostream& Foam::OTstream::write(const char* data, std::streamsize count)
{
if (format() != BINARY)
{
FatalErrorInFunction
<< "stream format not binary"
<< Foam::abort(FatalError);
}
NotImplemented;
return *this;
}
Foam::Ostream& Foam::OTstream::writeRaw
(
const char* data,
std::streamsize count
)
{
// No check for format() == BINARY since this is either done in the
// beginRawWrite() method, or the caller knows what they are doing.
NotImplemented;
return *this;
}
bool Foam::OTstream::beginRawWrite(std::streamsize count)
{
if (format() != BINARY)
{
FatalErrorInFunction
<< "stream format not binary"
<< Foam::abort(FatalError);
}
NotImplemented;
return true;
}
void Foam::OTstream::print(Ostream& os) const
{
os << "OTstream : " << name().c_str() << ", " << size() << " tokens, ";
IOstream::print(os);
}
// ************************************************************************* //
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2019 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/>.
Class
Foam::OTstream
Description
A simple output token stream
SourceFiles
OTstream.C