ENH: add CircularBuffer container

This commit is contained in:
Mark Olesen 2022-03-14 13:12:05 +01:00
parent 079d5f2771
commit bf46b589cf
7 changed files with 1616 additions and 0 deletions

View File

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

View File

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

View File

@ -0,0 +1,136 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022 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-CircularBuffer
Description
Basic tests for CircularBuffer behaviour and characteristics
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "ListOps.H"
#include "CircularBuffer.H"
#include "StringStream.H"
#include "FlatOutput.H"
using namespace Foam;
template<class T>
inline Ostream& report
(
const CircularBuffer<T>& buf,
bool debugOutput = true
)
{
buf.writeList(Info, 0);
if (debugOutput)
{
Info<< " : ";
buf.info(Info);
}
else
{
Info<< nl;
}
return Info;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
CircularBuffer<label> buf1(1); report(buf1);
buf1.append(10); report(buf1);
Info<< buf1.range_one() << nl;
buf1.append(20); report(buf1);
buf1.append(30); report(buf1);
buf1.push_back(40); report(buf1);
buf1.push_front(-50); report(buf1);
buf1.append(60); report(buf1);
buf1.append(labelList({70,80,90})); report(buf1);
Info<< nl << "access: " << buf1 << nl;
Info<< buf1[-12] << nl;
Info<< "found: " << buf1.found(40) << nl;
buf1.appendUniq(100); report(buf1);
buf1 = Zero; report(buf1);
buf1 = 500; report(buf1);
while (buf1.size() > 2)
{
(void) buf1.pop_front();
}
report(buf1);
buf1.append(identity(5)); report(buf1);
buf1.info(Info);
Info<< buf1 << nl;
CircularBuffer<label> buf2(15);
report(buf2);
buf2 = std::move(buf1);
Info<< "buf1: "; report(buf1);
Info<< "buf2: "; report(buf2);
Info<< "for-range:";
for (const label val : buf2)
{
Info<< ' ' << val;
}
Info<< endl;
{
auto iter = buf2.cbegin();
auto endIter = buf2.cend();
Info<< "iterated:";
while (iter != endIter)
{
Info<< ' ' << *(++iter);
}
Info<< endl;
}
Info<< "normal: " << flatOutput(buf2) << nl;
buf2.reverse();
Info<< "reverse: " << flatOutput(buf2) << nl;
Info<< nl << "End\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,143 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022 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/>.
\*---------------------------------------------------------------------------*/
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
template<class T>
void Foam::CircularBuffer<T>::doReserve
(
const bool nocopy,
const label len
)
{
if (storage_.size() < len)
{
// Increase capacity (doubling)
const label newCapacity =
max(min_size(), max(len+1, label(2*storage_.size())));
if (nocopy || empty())
{
// Simple - no content to preserve
clear(); // Reset begin/end
storage_.resize_nocopy(newCapacity);
}
else
{
// Preserve content
const labelRange range1 = range_one();
const labelRange range2 = range_two();
List<T> old(newCapacity);
storage_.swap(old);
begin_ = 0;
end_ = 0;
for (const label i : range1)
{
storage_[end_++] = std::move(old[i]);
}
for (const label i : range2)
{
storage_[end_++] = std::move(old[i]);
}
}
}
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template<class T>
Foam::SubList<T> Foam::CircularBuffer<T>::array_one()
{
const label len = size_one();
return (len ? storage_.slice(begin_, len) : SubList<T>());
}
template<class T>
Foam::SubList<T> Foam::CircularBuffer<T>::array_two()
{
const label len = size_two();
return (len ? storage_.slice(0, len) : SubList<T>());
}
template<class T>
const Foam::SubList<T> Foam::CircularBuffer<T>::array_one() const
{
const label len = size_one();
return (len ? storage_.slice(begin_, len) : SubList<T>());
}
template<class T>
const Foam::SubList<T> Foam::CircularBuffer<T>::array_two() const
{
const label len = size_two();
return (len ? storage_.slice(0, len) : SubList<T>());
}
template<class T>
Foam::label Foam::CircularBuffer<T>::find(const T& val, label pos) const
{
label i = -1;
const auto list1 = this->array_one();
if (pos < list1.size())
{
i = list1.find(val, pos);
}
if (i < 0)
{
// Not found - search the second list
return this->array_two().find(val, 0);
}
return i;
}
template<class T>
void Foam::CircularBuffer<T>::reverse()
{
const label n = this->size();
const label nBy2 = n/2;
for (label i = 0; i < nBy2; ++i)
{
Foam::Swap(operator[](i), operator[](n-1-i));
}
}
// ************************************************************************* //

View File

@ -0,0 +1,518 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022 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::CircularBuffer
Description
A simple list of objects of type \<T\> that is intended to be used
as a circular buffer (eg, a FIFO) when the alloc/free overhead
associated with a linked-list approach is to be avoided.
The internal storage is addressed by independent begin/end markers.
- The %begin marker points to the \em front.
- The %end marker is a one-past the \em back.
.
This results in a variety ofr different possible buffer states:
-# \em empty (\em begin == \em end)
-# \em simple/linear (\em begin \< \em end) has no wrapping:
\verbatim
|.|.|.|a|b|c|d|.|.|.|
beg ___^
end ___________^
\endverbatim
-# \em split (\em begin \> \em end):
\verbatim
|f|g|h|i|.|.|.|a|b|c|d|e|
end _____^
beg ___________^
\endverbatim
.
The methods range_one(), range_two() return the internal indexing and
the methods array_one(), array_two() provide direct access to the
internal contents.
When filling the buffer, the internal storage will be resized
(doubling strategy) as required. When this occurs, the new list
will be linearized with \em begin = 0.
Simultaneously when filling, the storage buffer will be over-allocated
to avoid ambiguity when (\em begin == \em end), which represents an
\em %empty buffer and not a \em %full buffer. Eg,
\verbatim
|c|d|.|a|b|
end _^
beg ___^
\endverbatim
after appending one more, it would be incorrect to simply fill
the available space:
\verbatim
|c|d|e|a|b|
end ___^ WRONG : would represent empty!
beg ___^
\endverbatim
the storage is instead increased (doubled) and rebalanced before
the append occurs (old capacity 5, new capacity 10):
\verbatim
|a|b|c|d|e|.|.|.|.|.|
_^_ beg
end _______^
\endverbatim
SourceFiles
CircularBuffer.C
CircularBufferI.H
CircularBufferIO.C
\*---------------------------------------------------------------------------*/
#ifndef Foam_CircularBuffer_H
#define Foam_CircularBuffer_H
#include "labelRange.H"
#include "List.H"
#include "SubList.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// Forward Declarations
template<class T> class CircularBuffer;
template<class T>
Istream& operator>>(Istream& is, CircularBuffer<T>& list);
template<class T>
Ostream& operator<<(Ostream& os, const CircularBuffer<T>& list);
/*---------------------------------------------------------------------------*\
Class CircularBuffer Declaration
\*---------------------------------------------------------------------------*/
template<class T>
class CircularBuffer
{
// Private Data
//- The allocated buffer storage
List<T> storage_;
//- The first readable element
label begin_;
//- One past last writable element
label end_;
// Private Member Functions
//- Map the logical location to the buffer location
inline label toGlobal(const label i) const;
//- Length of array one
inline label size_one() const noexcept;
//- Length of array two
inline label size_two() const noexcept;
//- 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
void doReserve(const bool nocopy, const label len);
//- Copy all list contents
template<class OtherListType>
inline void copyList(const OtherListType& rhs);
public:
// STL type definitions
//- The value type the list contains
typedef T value_type;
//- The pointer type for non-const access to value_type items
typedef T* pointer;
//- The pointer type for const access to value_type items
typedef const T* const_pointer;
//- The type used for storing into value_type objects
typedef T& reference;
//- The type used for reading from constant value_type objects
typedef const T& const_reference;
//- The type to represent the size of a buffer
typedef label size_type;
//- The difference between iterator objects
typedef label difference_type;
//- Forward iterator with const access
class const_iterator;
// Constructors
//- Default construct, empty buffer without allocation
inline constexpr CircularBuffer() noexcept;
//- Construct an empty buffer with given reserve size
inline explicit CircularBuffer(const label len);
//- Copy construct
inline CircularBuffer(const CircularBuffer<T>& list);
//- Move construct
inline CircularBuffer(CircularBuffer<T>&& list);
//- Construct from Istream - uses readList
explicit CircularBuffer(Istream& is);
// Member Functions
// Characteristics
//- Lower capacity limit
static constexpr label min_size() noexcept { return 16; }
//- Size of the underlying storage.
inline label capacity() const noexcept;
//- Empty or exhausted buffer
inline bool empty() const noexcept;
//- The current number of buffer items
inline label size() const noexcept;
// Internal Access
//- The nominal space available to fill.
//- Subtract 1 for the number to append before re-balancing is needed.
inline label space() const noexcept;
//- The addressing range covered by array_one()
inline labelRange range_one() const noexcept;
//- The addressing range covered by array_two()
inline labelRange range_two() const noexcept;
//- The contents of the first internal array
SubList<T> array_one();
//- The contents of the first internal array
SubList<T> array_two();
//- The contents of the second internal array
const SubList<T> array_one() const;
//- The contents of the second internal array
const SubList<T> array_two() const;
// Access
//- Access the first element (front). Requires !empty().
T& first();
//- Access the last element (back). Requires !empty().
T& last();
//- Const access to the first element (front). Requires !empty().
const T& first() const;
//- Const access to the last element (back). Requires !empty().
const T& last() const;
// Sizing
//- Reserve allocation space for at least this size, allocating new
//- space if required and \em retaining old content.
// Never shrinks.
inline void reserve(const label len);
//- Reserve allocation space for at least this size, allocating new
//- space if required \em without retaining old content.
// Never shrinks.
inline void reserve_nocopy(const label len);
//- Clear the addressed buffer, does not change allocation
inline void clear() noexcept;
//- Clear the buffer and delete storage.
inline void clearStorage();
//- Swap content, independent of sizing parameter
inline void swap(CircularBuffer<T>& other);
// Search
//- Find index of the first occurrence of the value.
// Any occurrences before the start pos are ignored.
// Linear search.
// \return position in list or -1 if not found.
label find(const T& val, label pos = 0) const;
//- True if the value if found in the list.
// Any occurrences before the start pos are ignored.
// Linear search.
// \return true if found.
inline bool found(const T& val, label pos = 0) const;
// Stack-like Operations
//- Copy prepend an element to the front of the buffer
inline void push_front(const T& val);
//- Move prepend an element to the front of the buffer
inline void push_front(T&& val);
//- 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
inline void push_back(T&& val);
//- Shrink by moving the front of the buffer 1 or more times
inline void pop_front(label n = 1);
//- Shrink by moving the end of the buffer 1 or more times
inline void pop_back(label n = 1);
//- Copy append an element to the end of the buffer
void append(const T& val) { this->push_back(val); }
//- Move append an element to the end of the buffer
void append(T&& val) { this->push_back(std::move(val)); }
//- Copy append multiple elements the end of the buffer
inline void append(const UList<T>& list);
//- Copy append IndirectList elements the end of the buffer
template<class Addr>
inline void append(const IndirectListBase<T, Addr>& list);
//- Append an element if not already in the buffer.
// \return the change in the buffer length
inline label appendUniq(const T& val);
// Other Operations
//- Reverse the buffer order, swapping elements
void reverse();
// Member Operators
//- Non-const access to an element in the list.
// The index is allowed to wrap in both directions
inline T& operator[](const label i);
//- Const access to an element in the list
// The index is allowed to wrap in both directions
inline const T& operator[](const label i) const;
//- Copy construct
inline void operator=(const CircularBuffer<T>& list);
//- Move construct
inline void operator=(CircularBuffer<T>&& list);
//- Assign all addressed elements to the given value
inline void operator=(const T& val);
//- Assignment of all entries to zero
inline void operator=(const Foam::zero);
//- Deep copy values from a list of the addressed elements
inline void operator=(const UList<T>& rhs);
//- Deep copy values from a list of the addressed elements
template<class AnyAddr>
inline void operator=(const IndirectListBase<T, AnyAddr>& rhs);
// IOstream Operators
//- Print information
Ostream& info(Ostream& os) const;
//- Write List, with line-breaks in ASCII when length exceeds shortLen.
// Using '0' suppresses line-breaks entirely.
Istream& readList(Istream& is);
//- Write List, with line-breaks in ASCII when length exceeds shortLen.
// Using '0' suppresses line-breaks entirely.
Ostream& writeList(Ostream& os, const label shortLen=0) const;
//- Use the readList() method to read contents from Istream.
friend Istream& operator>> <T>
(
Istream& is,
CircularBuffer<T>& list
);
//- Write to Ostream
friend Ostream& operator<< <T>
(
Ostream& os,
const CircularBuffer<T>& list
);
// Iterators
//- A simple forward const iterator for a circular buffer
class const_iterator
{
const CircularBuffer<T>* container_;
label iter_;
public:
using difference_type = label;
using value_type = const T;
using pointer = const T*;
using reference = const T&;
using iterator_category = std::forward_iterator_tag;
const_iterator(const const_iterator&) = default;
const_iterator& operator=(const const_iterator&) = default;
const_iterator
(
const CircularBuffer<T>* buffer,
label i
)
:
container_(buffer),
iter_(i)
{}
reference operator*() const
{
return (*container_)[iter_];
}
const_iterator& operator++()
{
++iter_;
return *this;
}
const_iterator operator++(int)
{
auto old(*this);
++iter_;
return old;
}
bool operator==(const const_iterator& rhs) const
{
return iter_ == rhs.iter_;
}
bool operator!=(const const_iterator& rhs) const
{
return iter_ != rhs.iter_;
}
};
// Iterator (const)
//- Return a const_iterator at begin of buffer
inline const_iterator cbegin() const
{
return const_iterator(this, 0);
}
//- Return a const_iterator at end of buffer
inline const_iterator cend() const
{
return const_iterator(this, this->size());
}
//- Return a const_iterator at begin of buffer
inline const_iterator begin() const { return cbegin(); }
//- Return a const_iterator at end of buffer
inline const_iterator end() const { return cend(); }
};
// * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
//- Read List contents from Istream
template<class T>
Istream& operator>>(Istream& is, CircularBuffer<T>& list)
{
return list.readList(is);
}
//- Write List to Ostream, as per UList::writeList() with default length.
template<class T>
Ostream& operator<<(Ostream& os, const CircularBuffer<T>& list)
{
return list.writeList(os);
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#include "CircularBufferI.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#ifdef NoRepository
#include "CircularBuffer.C"
#include "CircularBufferIO.C"
#endif
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,556 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022 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/>.
\*---------------------------------------------------------------------------*/
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
template<class T>
inline Foam::label Foam::CircularBuffer<T>::toGlobal(label i) const
{
const label len = this->size();
if (!len)
{
// Bounds error
return -1;
}
else if (i < 0)
{
// Wrap any number of times
while (i < 0) i += len;
}
else
{
// Wrap any number of times
while (i >= len) i -= len;
}
i += begin_;
if (i >= storage_.size())
{
i -= storage_.size();
}
return i;
}
template<class T>
inline Foam::label Foam::CircularBuffer<T>::size_one() const noexcept
{
return
(
(end_ >= begin_)
? (end_ - begin_)
: (storage_.size() - begin_)
);
}
template<class T>
inline Foam::label Foam::CircularBuffer<T>::size_two() const noexcept
{
return
(
(end_ && end_ < begin_)
? end_
: static_cast<label>(0)
);
}
template<class T>
template<class OtherListType>
inline void Foam::CircularBuffer<T>::copyList(const OtherListType& rhs)
{
this->clear();
const label len = rhs.size();
if (len)
{
reserve(len + 1);
// Never overfilled, simply write at end_ (one-past position)
// - after clear(), begin_ and end_ are both 0
for (label i = 0; i < len; ++i)
{
storage_[end_] = rhs[i]; // copy element
++end_;
}
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template<class T>
inline constexpr Foam::CircularBuffer<T>::CircularBuffer() noexcept
:
storage_(),
begin_(0),
end_(0)
{}
template<class T>
inline Foam::CircularBuffer<T>::CircularBuffer(const label len)
:
storage_(max(min_size(), len + 1)),
begin_(0),
end_(0)
{}
template<class T>
inline Foam::CircularBuffer<T>::CircularBuffer
(
const CircularBuffer<T>& list
)
:
storage_(list.storage_),
begin_(list.begin_),
end_(list.end_)
{}
template<class T>
inline Foam::CircularBuffer<T>::CircularBuffer
(
CircularBuffer<T>&& list
)
:
storage_(std::move(list.storage_)),
begin_(list.begin_),
end_(list.end_)
{
list.begin_ = 0;
list.end_ = 0;
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template<class T>
inline Foam::label Foam::CircularBuffer<T>::capacity() const noexcept
{
return storage_.size();
}
template<class T>
inline bool Foam::CircularBuffer<T>::empty() const noexcept
{
return storage_.empty() || (begin_ == end_);
}
template<class T>
inline Foam::label Foam::CircularBuffer<T>::size() const noexcept
{
const label diff(end_ - begin_);
if (diff < 0)
{
return (storage_.size() + diff);
}
return diff;
}
template<class T>
inline Foam::label Foam::CircularBuffer<T>::space() const noexcept
{
return (storage_.size() - size());
}
template<class T>
inline Foam::labelRange Foam::CircularBuffer<T>::range_one() const noexcept
{
return
(
(begin_ == end_)
? labelRange()
: labelRange(begin_, this->size_one())
);
}
template<class T>
inline Foam::labelRange Foam::CircularBuffer<T>::range_two() const noexcept
{
return labelRange(0, this->size_two());
}
template<class T>
inline void Foam::CircularBuffer<T>::clear() noexcept
{
begin_ = end_ = 0;
}
template<class T>
inline void Foam::CircularBuffer<T>::clearStorage()
{
storage_.clear();
begin_ = end_ = 0;
}
template<class T>
inline void Foam::CircularBuffer<T>::swap(CircularBuffer<T>& other)
{
if (this == &other)
{
return; // Self-swap is a no-op
}
// Swap storage and addressing
storage_.swap(other.storage_);
std::swap(begin_, other.begin_);
std::swap(end_, other.end_);
}
template<class T>
inline void Foam::CircularBuffer<T>::reserve(const label len)
{
this->doReserve(false, len);
}
template<class T>
inline void Foam::CircularBuffer<T>::reserve_nocopy(const label len)
{
this->doReserve(true, len);
}
template<class T>
inline bool Foam::CircularBuffer<T>::found(const T& val, label pos) const
{
return (this->find(val, pos) >= 0);
}
template<class T>
inline T& Foam::CircularBuffer<T>::first()
{
if (empty())
{
FatalErrorInFunction << "Buffer is empty" << abort(FatalError);
}
return storage_[begin_];
}
template<class T>
inline const T& Foam::CircularBuffer<T>::first() const
{
if (empty())
{
FatalErrorInFunction << "Buffer is empty" << abort(FatalError);
}
return storage_[begin_];
}
template<class T>
inline T& Foam::CircularBuffer<T>::last()
{
if (empty())
{
FatalErrorInFunction << "Buffer is empty" << abort(FatalError);
}
return storage_.rcValue(end_);
}
template<class T>
inline const T& Foam::CircularBuffer<T>::last() const
{
if (empty())
{
FatalErrorInFunction << "Buffer is empty" << abort(FatalError);
}
return storage_.rcValue(end_);
}
template<class T>
inline void Foam::CircularBuffer<T>::push_front(const T& val)
{
reserve(size() + 2);
// Never overfilled. Move begin and write
begin_ = storage_.rcIndex(begin_);
storage_[begin_] = val; // copy assign element
}
template<class T>
inline void Foam::CircularBuffer<T>::push_front(T&& val)
{
reserve(size() + 2);
// Never overfilled. Move begin and write
begin_ = storage_.rcIndex(begin_);
storage_[begin_] = std::move(val); // move assign element
}
template<class T>
inline void Foam::CircularBuffer<T>::push_back(const T& val)
{
reserve(size() + 2);
// Never overfilled, simply write at end_ (one-past position)
storage_[end_] = val; // copy assign element
end_ = storage_.fcIndex(end_);
}
template<class T>
inline void Foam::CircularBuffer<T>::push_back(T&& val)
{
reserve(size() + 2);
// Never overfilled, simply write at end_ (one-past position)
storage_[end_] = std::move(val); // move assign element
end_ = storage_.fcIndex(end_);
}
template<class T>
inline void Foam::CircularBuffer<T>::pop_front(label n)
{
if (n >= size())
{
begin_ = end_;
}
else
{
while (n-- > 0)
{
begin_ = storage_.fcIndex(begin_);
}
}
}
template<class T>
inline void Foam::CircularBuffer<T>::pop_back(label n)
{
if (n >= size())
{
end_ = begin_;
}
else
{
while (n-- > 0)
{
end_ = storage_.rcIndex(end_);
}
}
}
template<class T>
inline Foam::label Foam::CircularBuffer<T>::appendUniq(const T& val)
{
if (this->found(val))
{
return 0;
}
else
{
this->push_back(val);
return 1; // Increased list length by one
}
}
template<class T>
inline void Foam::CircularBuffer<T>::append(const UList<T>& rhs)
{
const label len = rhs.size();
if (len)
{
reserve(size() + len + 1);
// Never overfilled, simply write at end_ (one-past position)
for (label i = 0; i < len; ++i)
{
storage_[end_] = rhs[i]; // copy element
end_ = storage_.fcIndex(end_);
}
}
}
template<class T>
template<class Addr>
inline void Foam::CircularBuffer<T>::append
(
const IndirectListBase<T, Addr>& rhs
)
{
const label len = rhs.size();
if (len)
{
reserve(size() + len + 1);
// Never overfilled, simply write at end_ (one-past position)
for (label i = 0; i < len; ++i)
{
storage_[end_] = rhs[i]; // copy element
end_ = storage_.fcIndex(end_);
}
}
}
// * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
template<class T>
inline T& Foam::CircularBuffer<T>::operator[](label i)
{
const label idx = this->toGlobal(i);
return storage_[idx];
}
template<class T>
inline const T& Foam::CircularBuffer<T>::operator[](label i) const
{
const label idx = this->toGlobal(i);
return storage_[idx];
}
template<class T>
inline void Foam::CircularBuffer<T>::operator=(const CircularBuffer<T>& rhs)
{
if (this == &rhs)
{
return; // Self-assignment is a no-op
}
this->clear();
const auto list1 = rhs.array_one();
const auto list2 = rhs.array_two();
const label len = (list1.size() + list2.size());
if (len)
{
reserve(len + 1);
// Never overfilled, simply write at end_ (one-past position)
// - after clear(), begin_ and end_ are both 0
for (const T& val : list1)
{
storage_[end_] = val;
++end_;
}
for (const T& val : list2)
{
storage_[end_] = val;
++end_;
}
}
}
template<class T>
inline void Foam::CircularBuffer<T>::operator=(CircularBuffer<T>&& rhs)
{
if (this == &rhs)
{
return; // Self-assignment is a no-op
}
this->clearStorage();
this->swap(rhs);
}
template<class T>
inline void Foam::CircularBuffer<T>::operator=(const T& val)
{
this->array_one() = val;
this->array_two() = val;
}
template<class T>
inline void Foam::CircularBuffer<T>::operator=(const Foam::zero)
{
this->array_one() = Zero;
this->array_two() = Zero;
}
template<class T>
inline void Foam::CircularBuffer<T>::operator=(const UList<T>& rhs)
{
this->copyList(rhs);
}
template<class T>
template<class AnyAddr>
inline void Foam::CircularBuffer<T>::operator=
(
const IndirectListBase<T, AnyAddr>& rhs
)
{
this->copyList(rhs);
}
// ************************************************************************* //

View File

@ -0,0 +1,258 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022 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 "List.H"
#include "Istream.H"
#include "contiguous.H"
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template<class T>
Foam::CircularBuffer<T>::CircularBuffer(Istream& is)
{
this->readList(is);
}
// * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
template<class T>
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()
<< nl;
return os;
}
template<class T>
Foam::Istream& Foam::CircularBuffer<T>::readList(Istream& is)
{
// Clear list
storage_.clear();
begin_ = 0;
end_ = 0;
// More work than it should be. We avoid completely filled buffers!
is.fatalCheck(FUNCTION_NAME);
token tok(is);
is.fatalCheck
(
"CircularBuffer<T>::readList(Istream&) : "
"reading first token"
);
if (tok.isCompound())
{
// 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());
}
}
else if (tok.isLabel())
{
// Label: could be int(..), int{...} or just a plain '0'
const label len = tok.labelToken();
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);
}
return is;
}
template<class T>
Foam::Ostream& Foam::CircularBuffer<T>::writeList
(
Ostream& os,
const label shortLen
) const
{
const label len = this->size();
const auto list1 = this->array_one();
const auto list2 = this->array_two();
#ifdef FULLDEBUG
if (len != (list1.size() + list2.size()))
{
FatalErrorInFunction
<< "Size check failed"
<< abort(FatalError);
}
#endif
if (os.format() == IOstream::BINARY && is_contiguous<T>::value)
{
// Binary and contiguous
os << nl << len << nl;
if (len)
{
// The TOTAL number of bytes to be written.
// - possibly add start delimiter
// This is much like IndirectListBase output
os.beginRawWrite(len*sizeof(T));
if (!list1.empty())
{
os.writeRaw(list1.cdata_bytes(), list1.size_bytes());
}
if (!list2.empty())
{
os.writeRaw(list2.cdata_bytes(), list2.size_bytes());
}
// End delimiter and/or cleanup.
os.endRawWrite();
}
}
else if
(
(len <= 1 || !shortLen)
||
(
(len <= shortLen)
&&
(
is_contiguous<T>::value
|| Detail::ListPolicy::no_linebreak<T>::value
)
)
)
{
// Single-line output
// Size and start delimiter
os << len << token::BEGIN_LIST;
// Contents
label i = 0;
for (const T& val : list1)
{
if (i++) os << token::SPACE;
os << val;
}
for (const T& val : list2)
{
if (i++) os << token::SPACE;
os << val;
}
// End delimiter
os << token::END_LIST;
}
else
{
// Multi-line output
// Size and start delimiter
os << nl << len << nl << token::BEGIN_LIST << nl;
// Contents
for (const T& val : list1)
{
os << val << nl;
}
for (const T& val : list2)
{
os << val << nl;
}
// End delimiter
os << token::END_LIST << nl;
}
os.check(FUNCTION_NAME);
return os;
}
// ************************************************************************* //