ENH: eliminate reliance on SLList during reading

- fully implement DynamicList::readList() instead of simply
  redirecting to List::readList(). This also benefits DynamicField.
  Leverage DynamicList reading to simplify and improve CircularBuffer
  reading.

- bracket lists are now read chunk-wise instead of using a
  singly-linked list. For integral and vector-space types
  (eg, scalar, vector, etc) this avoids intermediate allocations
  for each element.

ENH: add CircularBuffer emplace_front/emplace_back

STYLE: isolate to-be-deprecated construct/assign forms

- still have construct/assign FixedList from a C-array.
  This is not really needed, can use std::initializer_list

- still have construct/assign List from SLList.
  Prefer to avoid these in the future.

DEFEATURE: remove construct/assign FixedList from SLList

- never used

DEFEATURE: remove move construct/assign List from SLList

- now unused. Retain copy construct/assign from SLList for transition
  purposes.
This commit is contained in:
Mark Olesen
2023-08-04 12:33:06 +02:00
parent 7828067ef6
commit fabd3f4e0c
20 changed files with 850 additions and 263 deletions

View File

@ -92,7 +92,10 @@ int main(int argc, char *argv[])
}
report(buf1);
buf1.push_back(identity(5)); report(buf1);
buf1.push_back(identity(5));
buf1.emplace_front(-1000);
buf1.emplace_back(1000);
report(buf1);
buf1.info(Info);
Info<< buf1 << nl;

View File

@ -202,14 +202,31 @@ int main(int argc, char *argv[])
Info<< "get<3>: " << list1.get<3>() << nl;
// Will not compile: Info<< "get<4>: " << list1.get<4>() << nl;
label a[4] = {0, 1, 2, 3};
FixedList<label, 4> list2(a);
// Test deprecated form
label array2[4] = {0, 1, 2, 3};
FixedList<label, 4> list2(array2);
Info<< "list2:" << list2
<< " hash:" << FixedList<label, 4>::hasher()(list2) << nl
<< " hash:" << Hash<FixedList<label, 4>>()(list2) << nl;
// Test deprecated form
SLList<label> sllist3;
{
sllist3.push_back(0);
sllist3.push_back(1);
sllist3.push_back(2);
sllist3.push_back(3);
}
FixedList<label, 4> list3(sllist3);
Info<< "list3:" << list3 << nl;
// Test deprecated forms
list3 = array2;
list2 = sllist3;
// Using FixedList for content too
{
List<FixedList<label, 4>> twolists{list1, list2};

View File

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

View File

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

View File

@ -0,0 +1,234 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2023 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/>.
Application
Test-ListRead1
Description
List reading
\*---------------------------------------------------------------------------*/
#include "OSspecific.H"
#include "argList.H"
#include "wordRes.H"
#include "IOstreams.H"
#include "Fstream.H"
#include "StringStream.H"
#include "scalar.H"
#include "vector.H"
#include "labelRange.H"
#include "scalarList.H"
#include "HashOps.H"
#include "ListOps.H"
#include "IndirectList.H"
#include "SubList.H"
#include "SliceList.H"
#include "ListPolicy.H"
#include <list>
#include <numeric>
#include <functional>
using namespace Foam;
label chunkSize = 128;
template<class T>
bool readBracketList(List<T>& list, Istream& is)
{
is.fatalCheck(FUNCTION_NAME);
token tok(is);
is.fatalCheck
(
"List<T>::readBracketList(Istream&) : reading first token"
);
if (!tok.isPunctuation(token::BEGIN_LIST))
{
is.putBack(tok);
return false;
}
{
// "(...)" : read element-wise.
// Uses chunk-wise reading to avoid too many re-allocations
// and avoids relocation of contiguous memory until all of the reading
// is completed. Chunks are wrapped as unique_ptr to ensure proper
// cleanup on failure.
// The choice of chunk-size is somewhat arbitrary...
// constexpr label chunkSize = 128;
typedef std::unique_ptr<List<T>> chunkType;
is >> tok;
is.fatalCheck(FUNCTION_NAME);
if (tok.isPunctuation(token::END_LIST))
{
// Trivial case, an empty list
list.clear();
return true;
}
// Use all storage
//private:// list.resize(list.capacity());
// Start with a few slots, recover current memory where possible
List<chunkType> chunks(16);
if (list.empty())
{
chunks[0] = chunkType(new List<T>(chunkSize));
}
else
{
chunks[0] = chunkType(new List<T>(std::move(list)));
}
label nChunks = 1; // Active number of chunks
label totalCount = 0; // Total number of elements
label localIndex = 0; // Chunk-local index
InfoErr
<< nl << "initial chunk: " << chunks[0]->size() << endl;
while (!tok.isPunctuation(token::END_LIST))
{
is.putBack(tok);
if (chunks[nChunks-1]->size() <= localIndex)
{
// Increase number of slots (doubling)
if (nChunks >= chunks.size())
{
chunks.resize(2*chunks.size());
}
InfoErr<< "new chunk" << endl;
chunks[nChunks] = chunkType(new List<T>(chunkSize));
++nChunks;
localIndex = 0;
}
is >> chunks[nChunks-1]->operator[](localIndex);
++localIndex;
++totalCount;
InfoErr
<< " chunk=" << nChunks
<< " index=" << localIndex
<< " total=" << totalCount << nl;
is.fatalCheck
(
"List<T>::readBracketList(Istream&) : "
"reading entry"
);
is >> tok;
is.fatalCheck(FUNCTION_NAME);
}
// Simple case
if (nChunks == 1)
{
list = std::move(*(chunks[0]));
list.resize(totalCount);
return true;
}
// Destination
//private:// list.setCapacity_nocopy(totalCount);
list.resize_nocopy(totalCount);
auto dest = list.begin();
for (label chunki = 0; chunki < nChunks; ++chunki)
{
List<T> currChunk(std::move(*(chunks[chunki])));
chunks[chunki].reset(nullptr);
const label localLen = min(currChunk.size(), totalCount);
dest = std::move
(
currChunk.begin(),
currChunk.begin(localLen),
dest
);
totalCount -= localLen;
}
}
return true;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Main program:
int main(int argc, char *argv[])
{
argList::noBanner();
argList::noParallel();
argList::noFunctionObjects();
argList::addOption("chunk-size", "value", "change read chunk size");
argList::addArgument("file1 .. fileN");
argList args(argc, argv, false, true);
args.readIfPresent("chunk-size", chunkSize);
Info<< "chunk-size: " << chunkSize << nl;
if (args.size() <= 1)
{
InfoErr<< "Provide a file or files to test" << nl;
}
else
{
for (label argi=1; argi < args.size(); ++argi)
{
const auto input = args.get<fileName>(argi);
IFstream is(input);
while (!is.eof())
{
labelList list;
readBracketList(list, is);
Info<< "read: " << flatOutput(list) << endl;
}
}
}
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,17 @@
(
0 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32 33 34 35 36 37 38 39
40 41 42 43 44 45 46 47 48 49
)
(
0 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32 33 34 35 36 37 38 39
40 41 42 43 44 45 46 47 48 49
)
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

View File

@ -303,12 +303,22 @@ public:
//- Move prepend an element to the front of the buffer
inline void push_front(T&& val);
//- Construct an element at the front of the buffer,
//- return reference to the new element
template<class... Args>
inline T& emplace_front(Args&&... args);
//- Copy append an element to the end of the buffer
inline void push_back(const T& val);
//- Move Append an element to the end of the buffer
//- Move append an element to the end of the buffer
inline void push_back(T&& val);
//- Construct an element at the end of the buffer,
//- return reference to the new element
template<class... Args>
inline T& emplace_back(Args&&... args);
//- Shrink by moving the front of the buffer 1 or more times
inline void pop_front(label n = 1);

View File

@ -334,6 +334,21 @@ inline void Foam::CircularBuffer<T>::push_front(T&& val)
}
template<class T>
template<class... Args>
inline T& Foam::CircularBuffer<T>::emplace_front(Args&&... args)
{
reserve(size() + 2);
// Never overfilled. Move begin and write
begin_ = storage_.rcIndex(begin_);
storage_[begin_] = T(std::forward<Args>(args)...);
return storage_[begin_];
}
template<class T>
inline void Foam::CircularBuffer<T>::push_back(const T& val)
{
@ -358,6 +373,22 @@ inline void Foam::CircularBuffer<T>::push_back(T&& val)
}
template<class T>
template<class... Args>
inline T& Foam::CircularBuffer<T>::emplace_back(Args&&... args)
{
reserve(size() + 2);
// Never overfilled, simply write at end_ (one-past position)
const label backIndex = end_;
storage_[end_] = T(std::forward<Args>(args)...);
end_ = storage_.fcIndex(end_);
return storage_[backIndex];
}
template<class T>
inline void Foam::CircularBuffer<T>::pop_front(label n)
{

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022 OpenCFD Ltd.
Copyright (C) 2022-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -25,7 +25,7 @@ License
\*---------------------------------------------------------------------------*/
#include "List.H"
#include "DynamicList.H"
#include "Istream.H"
#include "contiguous.H"
@ -46,8 +46,8 @@ Foam::Ostream& Foam::CircularBuffer<T>::info(Ostream& os) const
os << "size=" << size() << '/' << capacity()
<< " begin=" << begin_
<< " end=" << end_
/// << " one=" << this->range_one() << this->array_one()
/// << " two=" << this->range_two() << this->array_two()
// << " one=" << this->range_one() << this->array_one()
// << " two=" << this->range_two() << this->array_two()
<< nl;
return os;
@ -57,91 +57,27 @@ Foam::Ostream& Foam::CircularBuffer<T>::info(Ostream& os) const
template<class T>
Foam::Istream& Foam::CircularBuffer<T>::readList(Istream& is)
{
// Clear list
storage_.clear();
// Delegate to DynamicList for reading
DynamicList<T> elements(std::move(storage_));
elements.readList(is);
// Reset the list addressing range
begin_ = 0;
end_ = 0;
end_ = elements.size();
// More work than it should be. We avoid completely filled buffers!
const label minLen = (end_ + min_size());
is.fatalCheck(FUNCTION_NAME);
token tok(is);
is.fatalCheck
(
"CircularBuffer<T>::readList(Istream&) : "
"reading first token"
);
if (tok.isCompound())
if (!elements.empty() && (elements.capacity() < minLen))
{
// Compound: simply transfer contents
storage_.transfer
(
dynamicCast<token::Compound<List<T>>>
(
tok.transferCompoundToken(is)
)
);
end_ = storage_.size();
if (end_)
{
// Resize larger to avoid full buffer
storage_.resize(end_ + min_size());
}
// Avoid full buffer (beg/end ambiguity)
// Use setCapacity instead of resize to avoid additional doubling...
elements.setCapacity(minLen);
}
else if (tok.isLabel())
{
// Label: could be int(..), int{...} or just a plain '0'
const label len = tok.labelToken();
// Use the entire storage
elements.resize(elements.capacity());
end_ = len;
if (end_)
{
// Resize larger to avoid full buffer
storage_.resize(end_ + min_size());
}
// Dispatch to UList reading...
UList<T> list(storage_.data(), end_);
is.putBack(tok);
list.readList(is);
}
else if (tok.isPunctuation(token::BEGIN_LIST))
{
// "(...)" : read as SLList and transfer contents
is.putBack(tok); // Putback the opening bracket
SLList<T> sll(is); // Read as singly-linked list
const label len = sll.size();
end_ = len;
if (end_)
{
// Resize larger to avoid full buffer
storage_.resize(end_ + min_size());
// Move assign each list element
for (label i = 0; i < len; ++i)
{
storage_[i] = std::move(sll.removeHead());
}
}
}
else
{
FatalIOErrorInFunction(is)
<< "incorrect first token, expected <int> or '(', found "
<< tok.info() << nl
<< exit(FatalIOError);
}
storage_ = std::move(elements);
return is;
}

View File

@ -105,6 +105,10 @@ class DynamicList
// 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.
//- The size is not known a priori.
bool readBracketList(Istream& is);
public:

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2021 OpenCFD Ltd.
Copyright (C) 2021-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -28,8 +28,8 @@ License
#include "List.H"
#include "Istream.H"
#include "token.H"
#include "SLList.H"
#include "contiguous.H"
#include <memory>
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
@ -43,6 +43,133 @@ Foam::DynamicList<T, SizeMin>::DynamicList(Istream& is)
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
template<class T, int SizeMin>
bool Foam::DynamicList<T, SizeMin>::readBracketList(Istream& is)
{
DynamicList<T, SizeMin>& list = *this;
is.fatalCheck(FUNCTION_NAME);
token tok(is);
is.fatalCheck
(
"DynamicList<T>::readBracketList(Istream&) : reading first token"
);
if (!tok.isPunctuation(token::BEGIN_LIST))
{
is.putBack(tok);
return false;
}
{
// "(...)" : read element-wise.
// Uses chunk-wise reading to avoid too many re-allocations
// and avoids relocation of contiguous memory until all of the reading
// is completed. Chunks are wrapped as unique_ptr to ensure proper
// cleanup on failure.
// The choice of chunk-size is somewhat arbitrary...
constexpr label chunkSize = 128;
typedef std::unique_ptr<List<T>> chunkType;
is >> tok;
is.fatalCheck(FUNCTION_NAME);
if (tok.isPunctuation(token::END_LIST))
{
// Trivial case, an empty list
list.clear();
return true;
}
// Use all storage
list.resize(list.capacity());
// Start with a few slots, recover current memory where possible
List<chunkType> chunks(16);
if (list.empty())
{
chunks[0] = chunkType(new List<T>(chunkSize));
}
else
{
chunks[0] = chunkType(new List<T>(std::move(list)));
}
label nChunks = 1; // Active number of chunks
label totalCount = 0; // Total number of elements
label localIndex = 0; // Chunk-local index
while (!tok.isPunctuation(token::END_LIST))
{
is.putBack(tok);
if (chunks[nChunks-1]->size() <= localIndex)
{
// Increase number of slots (doubling)
if (nChunks >= chunks.size())
{
chunks.resize(2*chunks.size());
}
chunks[nChunks] = chunkType(new List<T>(chunkSize));
++nChunks;
localIndex = 0;
}
is >> chunks[nChunks-1]->operator[](localIndex);
++localIndex;
++totalCount;
is.fatalCheck
(
"DynamicList<T>::readBracketList(Istream&) : "
"reading entry"
);
is >> tok;
is.fatalCheck(FUNCTION_NAME);
}
// Simple case
if (nChunks == 1)
{
list = std::move(*(chunks[0]));
list.resize(totalCount);
return true;
}
// Destination
list.setCapacity_nocopy(totalCount);
list.resize_nocopy(totalCount);
auto dest = list.begin();
for (label chunki = 0; chunki < nChunks; ++chunki)
{
List<T> currChunk(std::move(*(chunks[chunki])));
chunks[chunki].reset(nullptr);
const label localLen = min(currChunk.size(), totalCount);
dest = std::move
(
currChunk.begin(),
currChunk.begin(localLen),
dest
);
totalCount -= localLen;
}
}
return true;
}
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
template<class T, int SizeMin>
@ -50,12 +177,136 @@ Foam::Istream& Foam::DynamicList<T, SizeMin>::readList(Istream& is)
{
DynamicList<T, SizeMin>& list = *this;
// Needs rewrite (2021-10)
// Use entire storage - ie, resize(capacity())
(void) list.expandStorage();
is.fatalCheck(FUNCTION_NAME);
static_cast<List<T>&>(list).readList(is);
list.capacity_ = list.size();
token tok(is);
is.fatalCheck("DynamicList<T>::readList(Istream&) : reading first token");
if (tok.isCompound())
{
// Compound: simply transfer contents
list.clearStorage(); // Remove old contents
list.transfer
(
dynamicCast<token::Compound<List<T>>>
(
tok.transferCompoundToken(is)
)
);
}
else if (tok.isLabel())
{
// Label: could be int(..), int{...} or just a plain '0'
const label len = tok.labelToken();
// Resize to length required
list.resize_nocopy(len);
if (is.format() == IOstreamOption::BINARY && is_contiguous<T>::value)
{
// Binary and contiguous
if (len)
{
Detail::readContiguous<T>
(
is,
list.data_bytes(),
list.size_bytes()
);
is.fatalCheck
(
"DynamicList<T>::readList(Istream&) : "
"reading binary block"
);
}
}
else
{
// Begin of contents marker
const char delimiter = is.readBeginList("List");
if (len)
{
if (delimiter == token::BEGIN_LIST)
{
for (label i=0; i<len; ++i)
{
is >> list[i];
is.fatalCheck
(
"DynamicList<T>::readList(Istream&) : "
"reading entry"
);
}
}
else
{
// Uniform content (delimiter == token::BEGIN_BLOCK)
T elem;
is >> elem;
is.fatalCheck
(
"DynamicList<T>::readList(Istream&) : "
"reading the single entry"
);
// Fill with the value
this->fill_uniform(elem);
}
}
// End of contents marker
is.readEndList("List");
}
}
else if (tok.isPunctuation(token::BEGIN_LIST))
{
// "(...)" : read read as bracketed list
is.putBack(tok);
this->readBracketList(is);
// Could also simply be done with emplace_back for each element
// but prefer the same mechanism as List::readList to avoid
// intermediate resizing
// // list.clear(); // Clear addressing, leave storage intact
// //
// // is >> tok;
// // is.fatalCheck(FUNCTION_NAME);
// //
// // while (!tok.isPunctuation(token::END_LIST))
// // {
// // is.putBack(tok);
// // is >> list.emplace_back();
// //
// // is.fatalCheck
// // (
// // "DynamicList<T>::readList(Istream&) : "
// // "reading entry"
// // );
// //
// // is >> tok;
// // is.fatalCheck(FUNCTION_NAME);
// // }
}
else
{
list.clear(); // Clear old contents
FatalIOErrorInFunction(is)
<< "incorrect first token, expected <int> or '(', found "
<< tok.info() << nl
<< exit(FatalIOError);
}
return is;
}

View File

@ -48,9 +48,9 @@ SourceFiles
#include "stdFoam.H"
#include "autoPtr.H"
#include "Hash.H"
#include "SLListFwd.H"
#include "ListPolicy.H"
// <algorithm> already included by stdFoam.H
#include <iterator>
#include <limits>
#include <type_traits>
@ -154,9 +154,6 @@ public:
//- Construct and initialize all entries to zero
inline explicit FixedList(const Foam::zero);
//- Copy construct from C-array (deprecated)
inline explicit FixedList(const T list[N]);
//- Copy construct
inline FixedList(const FixedList<T, N>& list);
@ -185,9 +182,6 @@ public:
const FixedList<label, N>& indices
);
//- Construct from SLList. Runtime size check
inline explicit FixedList(const SLList<T>& list);
//- Construct from Istream
explicit FixedList(Istream& is);
@ -342,15 +336,9 @@ public:
//- Return element of constant FixedList
inline const T& operator[](const label i) const;
//- Assignment to array operator. Takes linear time
inline void operator=(const T list[N]);
//- Assignment to UList operator. Takes linear time
inline void operator=(const UList<T>& list);
//- Assignment to SLList operator. Takes linear time
inline void operator=(const SLList<T>& list);
//- Assignment to an initializer list. Takes linear time
inline void operator=(std::initializer_list<T> list);
@ -529,6 +517,14 @@ public:
{
return this->contains(val, pos);
}
//- Deprecated: copy construct from C-array
explicit FixedList(const T list[N]) { std::copy_n(list, N, v_); }
//- Deprecated: assignment from C-array
// \deprecated(2023-08) - use other assignment operators
void operator=(const T list[N]) { std::copy_n(list, N, v_); }
};

View File

@ -27,8 +27,6 @@ License
\*---------------------------------------------------------------------------*/
#include "UList.H"
#include "SLList.H"
// <algorithm> already included by stdFoam.H
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
@ -55,16 +53,6 @@ inline Foam::FixedList<T, N>::FixedList(const Foam::zero)
}
template<class T, unsigned N>
inline Foam::FixedList<T, N>::FixedList(const T list[N])
{
for (unsigned i=0; i<N; ++i)
{
v_[i] = list[i];
}
}
template<class T, unsigned N>
inline Foam::FixedList<T, N>::FixedList(const FixedList<T, N>& list)
{
@ -140,20 +128,6 @@ inline Foam::FixedList<T, N>::FixedList
}
template<class T, unsigned N>
inline Foam::FixedList<T, N>::FixedList(const SLList<T>& list)
{
checkSize(list.size());
auto iter = list.begin();
for (unsigned i=0; i<N; ++i)
{
v_[i] = *iter;
++iter;
}
}
template<class T, unsigned N>
inline Foam::autoPtr<Foam::FixedList<T, N>>
Foam::FixedList<T, N>::clone() const
@ -444,15 +418,6 @@ inline const T& Foam::FixedList<T, N>::operator[](const label i) const
}
template<class T, unsigned N>
inline void Foam::FixedList<T, N>::operator=(const T list[N])
{
for (unsigned i=0; i<N; ++i)
{
v_[i] = list[i];
}
}
template<class T, unsigned N>
inline void Foam::FixedList<T, N>::operator=(const UList<T>& list)
{
@ -464,18 +429,6 @@ inline void Foam::FixedList<T, N>::operator=(const UList<T>& list)
}
}
template<class T, unsigned N>
inline void Foam::FixedList<T, N>::operator=(const SLList<T>& list)
{
checkSize(list.size());
auto iter = list.begin();
for (unsigned i=0; i<N; ++i)
{
v_[i] = *iter;
++iter;
}
}
template<class T, unsigned N>
inline void Foam::FixedList<T, N>::operator=(std::initializer_list<T> list)

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2017-2022 OpenCFD Ltd.
Copyright (C) 2017-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -30,7 +30,6 @@ License
#include "ListLoopM.H"
#include "FixedList.H"
#include "PtrList.H"
#include "SLList.H"
#include "contiguous.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
@ -314,13 +313,6 @@ Foam::List<T>::List(const PtrList<T>& list)
}
template<class T>
Foam::List<T>::List(const SLList<T>& list)
:
List<T>(list.begin(), list.end(), list.size())
{}
template<class T>
template<class Addr>
Foam::List<T>::List(const IndirectListBase<T, Addr>& list)
@ -359,15 +351,6 @@ Foam::List<T>::List(DynamicList<T, SizeMin>&& list)
}
template<class T>
Foam::List<T>::List(SLList<T>&& list)
:
UList<T>()
{
operator=(std::move(list));
}
// * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * * //
template<class T>
@ -463,28 +446,6 @@ void Foam::List<T>::operator=(const List<T>& list)
}
template<class T>
void Foam::List<T>::operator=(const SLList<T>& list)
{
const label len = list.size();
reAlloc(len);
if (len)
{
// std::copy(list.begin(), list.end(), this->v_);
T* iter = this->begin();
for (const T& val : list)
{
*iter = val;
++iter;
}
}
}
template<class T>
template<unsigned N>
void Foam::List<T>::operator=(const FixedList<T, N>& list)
@ -562,22 +523,6 @@ void Foam::List<T>::operator=(DynamicList<T, SizeMin>&& list)
}
template<class T>
void Foam::List<T>::operator=(SLList<T>&& list)
{
label len = list.size();
reAlloc(len);
for (T* iter = this->begin(); len--; ++iter)
{
*iter = std::move(list.removeHead());
}
list.clear();
}
// * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
template<class T>
@ -626,4 +571,36 @@ void Foam::sortedOrder
}
// * * * * * * * * * * * * * * * Housekeeping * * * * * * * * * * * * * * * //
#include "SLList.H"
template<class T>
Foam::List<T>::List(const SLList<T>& list)
:
List<T>(list.begin(), list.end(), list.size())
{}
template<class T>
void Foam::List<T>::operator=(const SLList<T>& list)
{
const label len = list.size();
reAlloc(len);
// Cannot use std::copy algorithm
// - SLList doesn't define iterator category
T* iter = this->begin();
for (const T& val : list)
{
*iter = val;
++iter;
}
}
// ************************************************************************* //

View File

@ -103,6 +103,19 @@ class List
const label len
);
//- Read List from Istream between '(' and ')' delimiters.
//- The size is not known a priori.
bool readBracketList(Istream& is);
// Methods as per DynamicList to simplify code maintenance
//- Stub method for internal naming as per DynamicList
label capacity() const noexcept { return UList<T>::size(); }
//- Stub method for internal naming as per DynamicList
void setCapacity_nocopy(const label len) { resize_nocopy(len); }
public:
@ -164,9 +177,6 @@ public:
//- Construct as copy of PtrList<T>
explicit List(const PtrList<T>& list);
//- Construct as copy of SLList<T>
explicit List(const SLList<T>& list);
//- Construct as copy of IndirectList contents
template<class Addr>
explicit List(const IndirectListBase<T, Addr>& list);
@ -181,9 +191,6 @@ public:
template<int SizeMin>
List(DynamicList<T, SizeMin>&& list);
//- Move construct from SLList
List(SLList<T>&& list);
//- Construct from Istream
List(Istream& is);
@ -279,9 +286,6 @@ public:
//- Assignment operator. Takes linear time
void operator=(const List<T>& list);
//- Assignment to SLList operator. Takes linear time
void operator=(const SLList<T>& list);
//- Assignment from IndirectList. Takes linear time
template<class Addr>
void operator=(const IndirectListBase<T, Addr>& list);
@ -306,9 +310,6 @@ public:
template<int SizeMin>
void operator=(DynamicList<T, SizeMin>&& list);
//- Move assignment. Takes constant time
void operator=(SLList<T>&& list);
// Reading/writing
@ -389,6 +390,13 @@ public:
//- Append an element if not already in the list.
//FOAM_DEPRECATED_FOR(2022-10, "push_uniq()")
label appendUniq(const T& val) { return this->push_uniq(val); }
//- Copy construct from SLList
explicit List(const SLList<T>& list);
//- Copy assign from SLList in linear time
void operator=(const SLList<T>& list);
};

View File

@ -29,21 +29,148 @@ License
#include "List.H"
#include "Istream.H"
#include "token.H"
#include "SLList.H"
#include "contiguous.H"
#include <memory>
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template<class T>
Foam::List<T>::List(Istream& is)
:
UList<T>(nullptr, 0)
UList<T>()
{
this->readList(is);
}
// * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
template<class T>
bool Foam::List<T>::readBracketList(Istream& is)
{
List<T>& list = *this;
is.fatalCheck(FUNCTION_NAME);
token tok(is);
is.fatalCheck
(
"List<T>::readBracketList(Istream&) : reading first token"
);
if (!tok.isPunctuation(token::BEGIN_LIST))
{
is.putBack(tok);
return false;
}
{
// "(...)" : read element-wise.
// Uses chunk-wise reading to avoid too many re-allocations
// and avoids relocation of contiguous memory until all of the reading
// is completed. Chunks are wrapped as unique_ptr to ensure proper
// cleanup on failure.
// The choice of chunk-size is somewhat arbitrary...
constexpr label chunkSize = 128;
typedef std::unique_ptr<List<T>> chunkType;
is >> tok;
is.fatalCheck(FUNCTION_NAME);
if (tok.isPunctuation(token::END_LIST))
{
// Trivial case, an empty list
list.clear();
return true;
}
// Use all storage
list.resize(list.capacity());
// Start with a few slots, recover current memory where possible
List<chunkType> chunks(16);
if (list.empty())
{
chunks[0] = chunkType(new List<T>(chunkSize));
}
else
{
chunks[0] = chunkType(new List<T>(std::move(list)));
}
label nChunks = 1; // Active number of chunks
label totalCount = 0; // Total number of elements
label localIndex = 0; // Chunk-local index
while (!tok.isPunctuation(token::END_LIST))
{
is.putBack(tok);
if (chunks[nChunks-1]->size() <= localIndex)
{
// Increase number of slots (doubling)
if (nChunks >= chunks.size())
{
chunks.resize(2*chunks.size());
}
chunks[nChunks] = chunkType(new List<T>(chunkSize));
++nChunks;
localIndex = 0;
}
is >> chunks[nChunks-1]->operator[](localIndex);
++localIndex;
++totalCount;
is.fatalCheck
(
"List<T>::readBracketList(Istream&) : "
"reading entry"
);
is >> tok;
is.fatalCheck(FUNCTION_NAME);
}
// Simple case
if (nChunks == 1)
{
list = std::move(*(chunks[0]));
list.resize(totalCount);
return true;
}
// Destination
list.setCapacity_nocopy(totalCount);
list.resize_nocopy(totalCount);
auto dest = list.begin();
for (label chunki = 0; chunki < nChunks; ++chunki)
{
List<T> currChunk(std::move(*(chunks[chunki])));
chunks[chunki].reset(nullptr);
const label localLen = min(currChunk.size(), totalCount);
dest = std::move
(
currChunk.begin(),
currChunk.begin(localLen),
dest
);
totalCount -= localLen;
}
}
return true;
}
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
template<class T>
Foam::Istream& Foam::List<T>::readList(Istream& is)
@ -142,15 +269,9 @@ Foam::Istream& Foam::List<T>::readList(Istream& is)
}
else if (tok.isPunctuation(token::BEGIN_LIST))
{
// "(...)" : read as SLList and transfer contents
list.clear(); // Clear old contents
is.putBack(tok); // Putback the opening bracket
SLList<T> sll(is); // Read as singly-linked list
// Reallocate and move assign list elements
list = std::move(sll);
// "(...)" : read as bracketed list
is.putBack(tok);
this->readBracketList(is);
}
else
{

View File

@ -29,7 +29,6 @@ License
#include "UList.H"
#include "Ostream.H"
#include "token.H"
#include "SLList.H"
#include "contiguous.H"
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
@ -275,25 +274,46 @@ Foam::Istream& Foam::UList<T>::readList(Istream& is)
}
else if (tok.isPunctuation(token::BEGIN_LIST))
{
// "(...)" : read as SLList and transfer contents
// "(...)" : read into list, handling size-mismatch after
is.putBack(tok); // Putback the opening bracket
SLList<T> sll(is); // Read as singly-linked list
is >> tok;
is.fatalCheck(FUNCTION_NAME);
label inputLen = 0;
while (!tok.isPunctuation(token::END_LIST))
{
is.putBack(tok);
if (inputLen < len)
{
is >> list[inputLen];
}
else
{
// Read and discard
T dummy;
is >> dummy;
}
++inputLen;
is.fatalCheck
(
"UList<T>::readList(Istream&) : "
"reading entry"
);
is >> tok;
is.fatalCheck(FUNCTION_NAME);
}
// List lengths must match
if (sll.size() != len)
if (inputLen != len)
{
FatalIOErrorInFunction(is)
<< "incorrect length for UList. Read "
<< sll.size() << " expected " << len
<< inputLen << " expected " << len
<< exit(FatalIOError);
}
// Move assign each list element
for (label i = 0; i < len; ++i)
{
list[i] = std::move(sll.removeHead());
}
}
else
{

View File

@ -50,9 +50,6 @@ Istream& List<char>::readList(Istream& is)
{
List<char>& list = *this;
// Anull list
list.clear();
is.fatalCheck(FUNCTION_NAME);
token tok(is);
@ -63,6 +60,7 @@ Istream& List<char>::readList(Istream& is)
{
// Compound: simply transfer contents
list.clear(); // Clear old contents
list.transfer
(
dynamicCast<token::Compound<List<char>>>
@ -77,8 +75,8 @@ Istream& List<char>::readList(Istream& is)
const label len = tok.labelToken();
// Resize to actual length read
list.resize(len);
// Resize to length required
list.resize_nocopy(len);
// Binary, always contiguous
@ -100,6 +98,8 @@ Istream& List<char>::readList(Istream& is)
}
else
{
list.clear(); // Clear old contents
FatalIOErrorInFunction(is)
<< "incorrect first token, expected <int>, found "
<< tok.info() << nl

View File

@ -35,10 +35,11 @@ SourceFiles
\*---------------------------------------------------------------------------*/
#ifndef blockMeshTools_H
#define blockMeshTools_H
#ifndef Foam_blockMeshTools_H
#define Foam_blockMeshTools_H
#include "dictionary.H"
#include "DynamicList.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2016 OpenFOAM Foundation
Copyright (C) 2021 OpenCFD Ltd.
Copyright (C) 2021-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -40,12 +40,14 @@ void Foam::blockMeshTools::read
if (tok.isLabel())
{
// Label: should be int(..)
const label len = tok.labelToken();
// Set list length to that read
list.resize(len);
// Resize to length required
list.resize_nocopy(len);
// Read beginning of contents
// Begin of contents marker
const char delimiter = is.readBeginList("List");
if (len)
@ -59,12 +61,15 @@ void Foam::blockMeshTools::read
}
}
// Read end of contents
// End of contents marker
is.readEndList("List");
}
else if (tok.isPunctuation(token::BEGIN_LIST))
{
SLList<T> sll;
// "(...)" : read with DynamicList and transfer contents
DynamicList<T> elements(std::move(list));
elements.clear(); // Reset addressing only
is >> tok;
is.fatalCheck(FUNCTION_NAME);
@ -73,16 +78,14 @@ void Foam::blockMeshTools::read
{
is.putBack(tok);
T elem;
read(is, elem, dict);
sll.append(elem);
read(is, elements.emplace_back(), dict);
is >> tok;
is.fatalCheck(FUNCTION_NAME);
}
// Convert the singly-linked list to this list
list = std::move(sll);
// Transfer back to regular list
list = std::move(elements);
}
else
{