ENH: additional 'nocopy' methods for List resize/reserve methods

- the size of a List often requires adjustment prior to an operation,
  but old values (if any) are not of interest and will be overwritten.

  In these cases can use the _nocopy versions to avoid additional memory
  overhead of the intermediate list and the copy/move overhead of
  retaining the old values (that we will subsequently discard anyhow).

  No equivalent for PtrList/UPtrList - this would be too fragile.

- add swap DynamicField with DynamicList

BUG: fixed Dynamic{Field,List} setCapacity corner case

- for the case when the newly requested capacity coincides with the
  current addressable size, the resize of the underlying list would have
  been bypassed - ie, the real capacity was not actually changed.

- remove (unused) PtrDynList setCapacity method as too fragile
This commit is contained in:
Mark Olesen
2021-10-25 10:38:23 +02:00
parent e2861cc200
commit b8a4b7e80d
20 changed files with 820 additions and 249 deletions

View File

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

View File

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

View File

@ -0,0 +1,155 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2021 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
Test allocation patterns when reading into an existing list.
\*---------------------------------------------------------------------------*/
#include "DynamicList.H"
#include "DynamicField.H"
#include "IOstreams.H"
#include "ITstream.H"
#include "OTstream.H"
#include "StringStream.H"
#include "FlatOutput.H"
#include "ListOps.H"
#include "labelRange.H"
#include "labelIndList.H"
using namespace Foam;
template<class T, int SizeMin>
void printInfo
(
const word& tag,
const DynamicList<T, SizeMin>& list,
const bool showSize = true
)
{
Info<< '<' << tag;
if (showSize)
{
Info<< " size=\"" << list.size()
<< "\" capacity=\"" << list.capacity() << "\"";
if (list.cdata())
{
Info<< " ptr=\"" << name(list.cdata()) << "\"";
}
else
{
Info<< " ptr=\"nullptr\"";
}
}
Info<< '>' << nl << flatOutput(list) << nl
<< "</" << tag << ">\n" << endl;
}
template<class T, int SizeMin>
void printInfo
(
const word& tag,
const DynamicField<T, SizeMin>& list,
const bool showSize = true
)
{
Info<< '<' << tag;
if (showSize)
{
Info<< " size=\"" << list.size()
<< "\" capacity=\"" << list.capacity() << "\"";
if (list.cdata())
{
Info<< " ptr=\"" << name(list.cdata()) << "\"";
}
else
{
Info<< " ptr=\"nullptr\"";
}
}
Info<< '>' << nl << flatOutput(list) << nl
<< "</" << tag << ">\n" << endl;
}
template<class T, int SizeMin>
void readList
(
DynamicList<T, SizeMin>& output,
const UList<T>& input
)
{
OTstream os;
os << input;
ITstream is("input", os.tokens());
is >> output;
}
template<class T, int SizeMin>
void readList
(
DynamicField<T, SizeMin>& output,
const UList<T>& input
)
{
OTstream os;
os << input;
ITstream is("input", os.tokens());
is >> output;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Main program:
int main(int argc, char *argv[])
{
//
{
DynamicList<label, 64> list1;
list1.resize(4);
ListOps::identity(list1);
list1.resize(3);
printInfo("", list1);
// list1.clear();
// printInfo("", list1);
list1.setCapacity(3);
printInfo("", list1);
}
Info<< "\nEnd\n";
return 0;
}
// ************************************************************************* //

View File

@ -290,7 +290,7 @@ public:
inline bool empty() const noexcept;
//- The number of elements that can be stored with reallocating
inline label capacity() const;
inline label capacity() const noexcept;
//- True if all entries have identical values, and list is non-empty
bool uniform() const;
@ -353,17 +353,20 @@ public:
//- Alter the size of the underlying storage.
// The addressed size will be truncated if needed to fit, but will
// remain otherwise untouched.
inline void setCapacity(const label nElem);
inline void setCapacity(const label numElem);
//- Reset addressable list size, does not shrink the allocated size.
// Optionally specify a value for new elements.
inline void resize(const label nElem, const unsigned int val = 0u);
inline void resize(const label numElem, const unsigned int val = 0u);
//- Currently identical to resize. Subject to future change (Oct-2021)
inline void resize_nocopy(const label numElem);
//- Reserve allocation space for at least this size.
// Never shrinks the allocated size.
// The list size is adjusted as per DynamicList with
// SizeInc=0, SizeMult=2, SizeDiv=1
inline void reserve(const label nElem);
inline void reserve(const label numElem);
//- Clear the list, i.e. set addressable size to zero.
// Does not adjust the underlying storage

View File

@ -388,12 +388,22 @@ inline bool Foam::PackedList<Width>::empty() const noexcept
template<unsigned Width>
inline Foam::label Foam::PackedList<Width>::capacity() const
inline Foam::label Foam::PackedList<Width>::capacity() const noexcept
{
return elem_per_block * blocks_.size();
}
template<unsigned Width>
inline void Foam::PackedList<Width>::resize_nocopy
(
const label numElem
)
{
this->resize(numElem);
}
template<unsigned Width>
inline void Foam::PackedList<Width>::resize
(
@ -406,7 +416,7 @@ inline void Foam::PackedList<Width>::resize
const label oldSize = size();
size_ = newSize;
if (oldSize < size())
if (oldSize < newSize)
{
// Fill new elements or newly exposed elements
if (val)
@ -438,7 +448,7 @@ inline void Foam::PackedList<Width>::resize
clear_trailing_bits();
}
}
else if (size() < oldSize)
else if (newSize < oldSize)
{
// The list is now shorter than before, so we zero assign the unused
// blocks and any trailing junk. This costs slightly here, but make

View File

@ -46,7 +46,6 @@ SourceFiles
#define DynamicList_H
#include "List.H"
#include <type_traits>
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -92,6 +91,21 @@ class DynamicList
template<class ListType>
inline void assignDynList(const ListType& list);
//- Alter the size of the underlying storage
// The 'nocopy' option will not attempt to recover old content
inline void doCapacity(const bool nocopy, const label len);
//- 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
inline void doReserve(const bool nocopy, const label len);
//- 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
inline void doResize(const bool nocopy, const label len);
public:
// Constructors
@ -121,7 +135,7 @@ public:
//- Construct from a FixedList
template<unsigned N>
inline DynamicList(const FixedList<T, N>& lst);
inline explicit DynamicList(const FixedList<T, N>& lst);
//- Construct from an initializer list. Size set to list size.
inline explicit DynamicList(std::initializer_list<T> lst);
@ -168,23 +182,49 @@ public:
// The addressed size will be truncated if needed to fit, but will
// remain otherwise untouched.
// Use this or reserve() in combination with append().
inline void setCapacity(const label newCapacity);
inline void setCapacity(const label len);
//- Reserve allocation space for at least this size.
//- Alter the size of the underlying storage,
//- \em without retaining old content.
// The addressed size will be truncated if needed to fit, but will
// remain otherwise untouched.
inline void setCapacity_nocopy(const label len);
//- Change the value for the list capacity directly (ADVANCED, UNSAFE)
//- Does not perform any memory management or resizing.
inline void setCapacity_unsafe(const label len) noexcept;
//- Reserve allocation space for at least this size, allocating new
//- space if required and \em retaining old content.
// Never shrinks the allocated size, use setCapacity() for that.
inline void reserve(const label len);
//- Alter addressable size.
// New space will be allocated if required.
inline void resize(const label newLen);
//- Reserve allocation space for at least this size, allocating new
//- space if required \em without retaining old content.
// Never shrinks the allocated size, use setCapacity() for that.
inline void reserve_nocopy(const label len);
//- Alter addressable size and fill new space with constant value
inline void resize(const label newLen, const T& val);
//- Alter addressable list size, allocating new space if required
//- while \em recovering old content.
// If no reallocation is required, the contents remain untouched.
// Otherwise new entries will be uninitialized.
// Use this to resize the list prior to using the operator[] for
// setting values (as per List usage).
inline void resize(const label len);
//- Alias for resize()
//- Alter addressable size and fill new entries with constant value
inline void resize(const label len, const T& val);
//- Alter addressable list size, allocating new space if required
//- \em without necessarily recovering old content.
// If no reallocation is required, the contents remain untouched.
// Otherwise all entries will be uninitialized.
inline void resize_nocopy(const label len);
//- Same as resize()
void setSize(const label n) { this->resize(n); }
//- Alias for resize()
//- Same as resize()
void setSize(const label n, const T& val) { this->resize(n, val); }
//- Clear the addressed list, i.e. set the size to zero.
@ -198,6 +238,9 @@ public:
// Returns the previous addressable size.
inline label expandStorage() noexcept;
//- Shrink the allocated space to the number of elements used.
inline void shrinkStorage();
//- Shrink the allocated space to the number of elements used.
// Returns a reference to the DynamicList.
inline DynamicList<T, SizeMin>& shrink();

View File

@ -37,22 +37,98 @@ inline void Foam::DynamicList<T, SizeMin>::assignDynList
const ListType& list
)
{
const label newLen = list.size();
const label len = list.size();
if (newLen <= capacity_)
if (capacity_ < len)
{
// Can copy w/o reallocating - adjust addressable size accordingly.
List<T>::setAddressableSize(newLen);
List<T>::operator=(list);
// Needs more space for the copy operation
List<T>::setAddressableSize(capacity_); // Use entire space
List<T>::resize_nocopy(len);
capacity_ = List<T>::size();
}
// Perform copy into addressable portion
List<T>::setAddressableSize(len);
List<T>::operator=(list);
}
template<class T, int SizeMin>
inline void Foam::DynamicList<T, SizeMin>::doCapacity
(
const bool nocopy,
const label newCapacity
)
{
if (newCapacity == capacity_)
{
return;
}
// Addressable length, possibly truncated by new capacity
const label currLen = min(List<T>::size(), newCapacity);
// Corner case...
if (List<T>::size() == newCapacity)
{
// Adjust addressable size to trigger proper resizing.
// Using (old size+1) is safe since it does not affect the 'overlap'
// of old and new addressable regions, but incurs fewew copy
// operations than extending to use the current capacity would.
List<T>::setAddressableSize(currLen+1);
}
if (nocopy)
{
List<T>::resize_nocopy(newCapacity);
}
else
{
// Ensure list size consistency prior to copying.
List<T>::setAddressableSize(capacity_);
List<T>::operator=(list);
capacity_ = List<T>::size();
List<T>::resize(newCapacity);
}
capacity_ = List<T>::size();
List<T>::setAddressableSize(currLen);
}
template<class T, int SizeMin>
inline void Foam::DynamicList<T, SizeMin>::doReserve
(
const bool nocopy,
const label len
)
{
if (capacity_ < len)
{
// Preserve addressed size
const label currLen = List<T>::size();
// Increase capacity (doubling)
capacity_ = max(SizeMin, max(len, label(2*capacity_)));
if (nocopy)
{
List<T>::resize_nocopy(capacity_);
}
else
{
List<T>::resize(capacity_);
}
List<T>::setAddressableSize(currLen);
}
}
template<class T, int SizeMin>
inline void Foam::DynamicList<T, SizeMin>::doResize
(
const bool nocopy,
const label len
)
{
this->doReserve(nocopy, len);
List<T>::setAddressableSize(len);
}
@ -72,7 +148,7 @@ inline Foam::DynamicList<T, SizeMin>::DynamicList(const label len)
List<T>(),
capacity_(0)
{
reserve(len);
reserve_nocopy(len);
}
@ -141,10 +217,9 @@ inline Foam::DynamicList<T, SizeMin>::DynamicList
const FixedList<T, N>& list
)
:
capacity_(0)
{
this->operator=(list);
}
List<T>(list),
capacity_(List<T>::size())
{}
template<class T, int SizeMin>
@ -201,10 +276,9 @@ inline Foam::DynamicList<T, SizeMin>::DynamicList
List<T>&& lst
)
:
capacity_(0)
{
transfer(lst);
}
List<T>(std::move(lst)),
capacity_(List<T>::size())
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
@ -227,20 +301,30 @@ Foam::DynamicList<T, SizeMin>::capacity_bytes() const noexcept
template<class T, int SizeMin>
inline void Foam::DynamicList<T, SizeMin>::setCapacity
(
const label newCapacity
const label len
)
{
label currLen = List<T>::size();
capacity_ = newCapacity;
this->doCapacity(false, len); // nocopy = false
}
if (currLen > capacity_)
{
// Truncate addressed sizes too
currLen = capacity_;
}
List<T>::resize(capacity_);
List<T>::setAddressableSize(currLen);
template<class T, int SizeMin>
inline void Foam::DynamicList<T, SizeMin>::setCapacity_nocopy
(
const label len
)
{
this->doCapacity(true, len); // nocopy = true
}
template<class T, int SizeMin>
inline void Foam::DynamicList<T, SizeMin>::setCapacity_unsafe
(
const label len
) noexcept
{
capacity_ = len;
}
@ -250,52 +334,55 @@ inline void Foam::DynamicList<T, SizeMin>::reserve
const label len
)
{
if (capacity_ < len)
{
// Increase capacity (doubling)
capacity_ = max(SizeMin, max(len, label(2 * capacity_)));
// Adjust allocated size, leave addressed size untouched
const label currLen = List<T>::size();
List<T>::resize(capacity_);
List<T>::setAddressableSize(currLen);
}
this->doReserve(false, len); // nocopy = false
}
template<class T, int SizeMin>
inline void Foam::DynamicList<T, SizeMin>::resize
inline void Foam::DynamicList<T, SizeMin>::reserve_nocopy
(
const label newLen
const label len
)
{
if (capacity_ < newLen)
{
// Increase capacity (doubling)
capacity_ = max(SizeMin, max(newLen, label(2 * capacity_)));
List<T>::resize(capacity_);
}
// Adjust addressed size
List<T>::setAddressableSize(newLen);
this->doReserve(true, len); // nocopy = true
}
template<class T, int SizeMin>
inline void Foam::DynamicList<T, SizeMin>::resize
(
const label newLen,
const label len
)
{
this->doResize(false, len); // nocopy = false
}
template<class T, int SizeMin>
inline void Foam::DynamicList<T, SizeMin>::resize_nocopy
(
const label len
)
{
this->doResize(true, len); // nocopy = true
}
template<class T, int SizeMin>
inline void Foam::DynamicList<T, SizeMin>::resize
(
const label len,
const T& val
)
{
label currLen = List<T>::size();
resize(newLen);
label idx = List<T>::size();
resize(len);
// Fill newly exposed with constant value
while (currLen < newLen)
while (idx < len)
{
this->operator[](currLen++) = val;
this->operator[](idx) = val;
++idx;
}
}
@ -320,7 +407,7 @@ inline Foam::label Foam::DynamicList<T, SizeMin>::expandStorage() noexcept
{
const label currLen = List<T>::size();
// Allow addressing into the entire list
// Address into the entire list
List<T>::setAddressableSize(capacity_);
return currLen;
@ -328,20 +415,25 @@ inline Foam::label Foam::DynamicList<T, SizeMin>::expandStorage() noexcept
template<class T, int SizeMin>
inline Foam::DynamicList<T, SizeMin>&
Foam::DynamicList<T, SizeMin>::shrink()
inline void Foam::DynamicList<T, SizeMin>::shrinkStorage()
{
const label currLen = List<T>::size();
if (currLen < capacity_)
{
// Use the full list when resizing
List<T>::setAddressableSize(capacity_);
// Adjust addressable size to trigger proper resizing
List<T>::setAddressableSize(currLen+1);
// Capacity and size are identical
capacity_ = currLen;
List<T>::resize(currLen);
// Redundant: List<T>::setAddressableSize(currLen);
capacity_ = List<T>::size();
}
}
template<class T, int SizeMin>
inline Foam::DynamicList<T, SizeMin>&
Foam::DynamicList<T, SizeMin>::shrink()
{
this->shrinkStorage();
return *this;
}
@ -745,14 +837,7 @@ inline void Foam::DynamicList<T, SizeMin>::operator=
const FixedList<T, N>& lst
)
{
const label n = lst.size();
resize(n);
for (label i=0; i<n; ++i)
{
this->operator[](i) = lst[i]; // copy element
}
assignDynList(lst);
}

View File

@ -298,6 +298,9 @@ public:
//- Dummy function, to make FixedList consistent with List
inline void resize(const label n);
//- Dummy function, to make FixedList consistent with List
inline void resize_nocopy(const label n);
//- Dummy function, to make FixedList consistent with List
void setSize(const label n) { this->resize(n); }

View File

@ -346,6 +346,16 @@ inline void Foam::FixedList<T, N>::resize(const label n)
#endif
}
template<class T, unsigned N>
inline void Foam::FixedList<T, N>::resize_nocopy(const label n)
{
#ifdef FULLDEBUG
checkSize(n);
#endif
}
template<class T, unsigned N>
inline void Foam::FixedList<T, N>::fill(const T& val)
{

View File

@ -37,54 +37,57 @@ License
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
template<class T>
void Foam::List<T>::doResize(const label newSize)
void Foam::List<T>::doResize(const label len)
{
if (newSize < 0)
if (len == this->size_)
{
FatalErrorInFunction
<< "bad size " << newSize
<< abort(FatalError);
return;
}
if (newSize != this->size_)
if (len > 0)
{
if (newSize > 0)
// With sign-check to avoid spurious -Walloc-size-larger-than
T* nv = new T[len];
const label overlap = min(this->size_, len);
if (overlap)
{
// With sign-check to avoid spurious -Walloc-size-larger-than
T* nv = new T[newSize];
const label overlap = min(this->size_, newSize);
if (overlap)
#ifdef USEMEMCPY
if (is_contiguous<T>::value)
{
#ifdef USEMEMCPY
if (is_contiguous<T>::value)
std::memcpy
(
static_cast<void*>(nv), this->v_, overlap*sizeof(T)
);
}
else
#endif
{
List_ACCESS(T, *this, vp);
for (label i = 0; i < overlap; ++i)
{
std::memcpy
(
static_cast<void*>(nv), this->v_, overlap*sizeof(T)
);
}
else
#endif
{
// No speedup observed for copy assignment on simple types
List_ACCESS(T, *this, vp);
for (label i = 0; i < overlap; ++i)
{
nv[i] = std::move(vp[i]);
}
nv[i] = std::move(vp[i]);
}
}
}
clear();
this->size_ = newSize;
this->v_ = nv;
}
else
clear();
this->size_ = len;
this->v_ = nv;
}
else
{
// Or only #ifdef FULLDEBUG
if (len < 0)
{
clear();
FatalErrorInFunction
<< "bad size " << len
<< abort(FatalError);
}
// #endif
clear();
}
}
@ -257,7 +260,7 @@ Foam::List<T>::List(List<T>& a, bool reuse)
{
if (reuse)
{
// swap content
// Steal content
this->v_ = a.v_;
a.v_ = nullptr;
a.size_ = 0;
@ -435,15 +438,16 @@ Foam::List<T>::~List()
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template<class T>
void Foam::List<T>::resize(const label newSize, const T& val)
void Foam::List<T>::resize(const label len, const T& val)
{
const label oldSize = this->size_;
this->doResize(newSize);
label idx = this->size_;
this->doResize(len);
List_ACCESS(T, *this, vp);
for (label i = oldSize; i < newSize; ++i)
while (idx < len)
{
vp[i] = val;
vp[idx] = val;
++idx;
}
}
@ -562,7 +566,7 @@ template<class T>
template<unsigned N>
void Foam::List<T>::operator=(const FixedList<T, N>& list)
{
reAlloc(label(N));
reAlloc(static_cast<label>(N));
T* iter = this->begin();

View File

@ -43,8 +43,8 @@ SourceFiles
#ifndef List_H
#define List_H
#include "UList.H"
#include "autoPtr.H"
#include "UList.H"
#include "SLListFwd.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -83,14 +83,16 @@ class List
inline void doAlloc();
//- Reallocate list storage to the given size
// Discards old storage (if any). Does not copy old contents
inline void reAlloc(const label len);
//- Copy list of given type.
//- Copy all list contents
template<class List2>
inline void copyList(const List2& list);
//- Change allocation size - backend for resize.
void doResize(const label newSize);
//- Change allocation size of List, retaining old contents.
// Backend for resize
void doResize(const label len);
//- Construct given begin/end iterators and number of elements
// Since the size is provided, the end iterator is actually ignored.
@ -206,10 +208,16 @@ public:
//- Adjust allocated size of list.
// The boolList version fills new memory with false.
inline void resize(const label newLen);
inline void resize(const label len);
//- Adjust allocated size of list and set val for new elements
void resize(const label newLen, const T& val);
void resize(const label len, const T& val);
//- Adjust allocated size of list \b without necessarily
// retaining old content.
// If no reallocation is required, the contents remain untouched.
// Otherwise the contents will be uninitialized.
inline void resize_nocopy(const label len);
//- Alias for resize()
void setSize(const label n) { this->resize(n); }
@ -221,15 +229,19 @@ public:
// Edit
//- Append an element at the end of the list
// If this is frequently required, consider a DynamicList
inline void append(const T& val);
//- Move append an element at the end of the list
// If this is frequently required, consider a DynamicList
inline void append(T&& val);
//- Append a List to the end of this list
// If this is frequently required, consider a DynamicList
inline void append(const UList<T>& list);
//- Append IndirectList contents at the end of this list
// If this is frequently required, consider a DynamicList
template<class Addr>
inline void append(const IndirectListBase<T, Addr>& list);

View File

@ -120,14 +120,13 @@ inline void Foam::List<T>::clear()
delete[] this->v_;
this->v_ = nullptr;
}
this->size_ = 0;
}
namespace Foam
{
// Template specialization for bool. Fills with false
// Template specialization for bool. Fills new entries with false
template<>
inline void List<bool>::resize(const label newLen)
{
@ -137,9 +136,16 @@ namespace Foam
template<class T>
inline void Foam::List<T>::resize(const label newLen)
inline void Foam::List<T>::resize(const label len)
{
this->doResize(newLen);
this->doResize(len);
}
template<class T>
inline void Foam::List<T>::resize_nocopy(const label len)
{
this->reAlloc(len);
}
@ -168,7 +174,10 @@ inline T& Foam::List<T>::newElmt(const label i)
template<class T>
inline void Foam::List<T>::append(const T& val)
{
resize(this->size() + 1, val); // copy element
const label idx = this->size();
resize(idx + 1);
this->operator[](idx) = val; // copy element
}

View File

@ -114,11 +114,7 @@ public:
// Sizing
//- Alter the size of the underlying storage.
inline void setCapacity(const label newCapacity);
//- Reserve allocation space for at least this size.
// Never shrinks the allocated size, use setCapacity() for that.
inline void reserve(const label len);
//- Alter the addressed list size.

View File

@ -97,33 +97,17 @@ inline const T* Foam::PtrDynList<T, SizeMin>::get(const label i) const
}
template<class T, int SizeMin>
inline void Foam::PtrDynList<T, SizeMin>::setCapacity(const label newCapacity)
{
label currLen = PtrList<T>::size();
capacity_ = newCapacity;
if (currLen > capacity_)
{
// Truncate addressed sizes too
currLen = capacity_;
}
PtrList<T>::resize(capacity_);
PtrList<T>::setAddressableSize(currLen);
}
template<class T, int SizeMin>
inline void Foam::PtrDynList<T, SizeMin>::reserve(const label len)
{
if (capacity_ < len)
{
// Increase capacity (doubling)
capacity_ = max(SizeMin, max(len, label(2 * capacity_)));
// Adjust allocated size, leave addressed size untouched
// Preserve addressed size
const label currLen = PtrList<T>::size();
// Increase capacity (doubling)
capacity_ = max(SizeMin, max(len, label(2*capacity_)));
PtrList<T>::resize(capacity_);
PtrList<T>::setAddressableSize(currLen);
}
@ -140,7 +124,7 @@ inline void Foam::PtrDynList<T, SizeMin>::resize(const label newLen)
if (capacity_ < newLen)
{
// Increase capacity (doubling)
capacity_ = max(SizeMin, max(newLen, label(2 * capacity_)));
capacity_ = max(SizeMin, max(newLen, label(2*capacity_)));
PtrList<T>::resize(capacity_);
}
@ -193,13 +177,11 @@ inline void Foam::PtrDynList<T, SizeMin>::shrink()
const label currLen = PtrList<T>::size();
if (currLen < capacity_)
{
// Use the full list when resizing
PtrList<T>::setAddressableSize(capacity_);
// Adjust addressable size to trigger proper resizing
PtrList<T>::setAddressableSize(currLen+1);
// Capacity and size are identical
capacity_ = currLen;
PtrList<T>::resize(currLen);
// Redundant: PtrList<T>::setAddressableSize(currLen);
capacity_ = PtrList<T>::size();
}
}

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2018-2019 OpenCFD Ltd.
Copyright (C) 2018-2021 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -66,6 +66,19 @@ Foam::label Foam::Detail::PtrListDetail<T>::findNull() const
}
template<class T>
void Foam::Detail::PtrListDetail<T>::setNull()
{
List<T*>& ptrs = *this;
const label len = ptrs.size();
for (label i=0; i<len; ++i)
{
ptrs[i] = nullptr;
}
}
template<class T>
void Foam::Detail::PtrListDetail<T>::free()
{

View File

@ -95,6 +95,9 @@ public:
//- Locate the first null entry, -1 if there are not any
label findNull() const;
//- Assign all pointers to nullptr, without deleting.
void setNull();
//- Delete the allocated entries, but retain the list size.
void free();
@ -129,6 +132,9 @@ public:
void setSize(const label) = delete;
void setSize(const label, const T&) = delete;
void setSize(const label, const T*) = delete;
// Too fragile or dangerous
void resize_nocopy(const label) = delete;
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

View File

@ -39,7 +39,7 @@ SourceFiles
#define DynamicField_H
#include "Field.H"
#include <type_traits>
#include "DynamicList.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -78,6 +78,21 @@ class DynamicField
template<class ListType>
inline void assignDynList(const ListType& list);
//- Alter the size of the underlying storage
// The 'nocopy' option will not attempt to recover old content
inline void doCapacity(const bool nocopy, const label len);
//- 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
inline void doReserve(const bool nocopy, const label len);
//- 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
inline void doResize(const bool nocopy, const label len);
public:
// Static Member Functions
@ -128,6 +143,10 @@ public:
template<int AnySizeMin>
inline DynamicField(DynamicField<T, AnySizeMin>&& content);
//- Move construct from DynamicList
template<int AnySizeMin>
inline DynamicField(DynamicList<T, AnySizeMin>&& list);
//- Construct by 1 to 1 mapping from the given field
inline DynamicField
(
@ -178,18 +197,44 @@ public:
// The addressed size will be truncated if needed to fit, but will
// remain otherwise untouched.
// Use this or reserve() in combination with append().
inline void setCapacity(const label newCapacity);
inline void setCapacity(const label len);
//- Reserve allocation space for at least this size.
//- Alter the size of the underlying storage,
//- \em without retaining old content.
// The addressed size will be truncated if needed to fit, but will
// remain otherwise untouched.
inline void setCapacity_nocopy(const label len);
//- Change the value for the list capacity directly (ADVANCED, UNSAFE)
//- Does not perform any memory management or resizing.
inline void setCapacity_unsafe(const label len) noexcept;
//- Reserve allocation space for at least this size, allocating new
//- space if required and \em retaining old content.
// Never shrinks the allocated size, use setCapacity() for that.
inline void reserve(const label len);
//- Alter addressable size.
// New space will be allocated if required.
inline void resize(const label newLen);
//- Reserve allocation space for at least this size, allocating new
//- space if required \em without retaining old content.
// Never shrinks the allocated size, use setCapacity() for that.
inline void reserve_nocopy(const label len);
//- Alter addressable list size, allocating new space if required
//- while \em recovering old content.
// If no reallocation is required, the contents remain untouched.
// Otherwise new entries will be uninitialized.
// Use this to resize the list prior to using the operator[] for
// setting values (as per List usage).
inline void resize(const label len);
//- Alter addressable size and fill new space with constant value
inline void resize(const label newLen, const T& val);
inline void resize(const label len, const T& val);
//- Alter addressable list size, allocating new space if required
//- \em without necessarily recovering old content.
// If no reallocation is required, the contents remain untouched.
// Otherwise all entries will be uninitialized.
inline void resize_nocopy(const label len);
//- Alias for resize()
void setSize(const label n) { this->resize(n); }
@ -208,6 +253,9 @@ public:
// Returns the previous addressable size.
inline label expandStorage() noexcept;
//- Shrink the allocated space to the number of elements used.
inline void shrinkStorage();
//- Shrink the allocated space to the number of elements used.
// Returns a reference to the DynamicField.
inline DynamicField<T, SizeMin>& shrink();
@ -219,6 +267,10 @@ public:
template<int AnySizeMin>
inline void swap(DynamicField<T, AnySizeMin>& other);
//- Swap content with DynamicList, independent of sizing parameter
template<int AnySizeMin>
inline void swap(DynamicList<T, AnySizeMin>& other);
//- Transfer the parameter contents into this
inline void transfer(List<T>& list);
@ -271,6 +323,10 @@ public:
//- Move assignment
inline void operator=(List<T>&& list);
//- Move assignment
template<int AnySizeMin>
inline void operator=(DynamicList<T, AnySizeMin>&& list);
//- Move assignment
inline void operator=(DynamicField<T, SizeMin>&& list);

View File

@ -35,22 +35,94 @@ inline void Foam::DynamicField<T, SizeMin>::assignDynList
const ListType& list
)
{
const label newLen = list.size();
const label len = list.size();
if (newLen <= capacity_)
if (capacity_ < len)
{
// Can copy w/o reallocating - adjust addressable size accordingly.
List<T>::setAddressableSize(newLen);
List<T>::operator=(list);
// Needs more space for the copy operation
List<T>::setAddressableSize(capacity_); // Use entire space
List<T>::resize_nocopy(len);
capacity_ = List<T>::size();
}
// Perform copy into addressable portion
List<T>::setAddressableSize(len);
List<T>::operator=(list);
}
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::doCapacity
(
const bool nocopy,
const label newCapacity
)
{
if (newCapacity == capacity_)
{
return;
}
// Addressable length, possibly truncated by new capacity
const label currLen = min(List<T>::size(), newCapacity);
// Corner case - see comments in DynamicList doCapacity
if (List<T>::size() == newCapacity)
{
List<T>::setAddressableSize(currLen+1);
}
if (nocopy)
{
List<T>::resize_nocopy(newCapacity);
}
else
{
// Ensure list size consistency prior to copying.
List<T>::setAddressableSize(capacity_);
List<T>::operator=(list);
capacity_ = List<T>::size();
List<T>::resize(newCapacity);
}
capacity_ = List<T>::size();
List<T>::setAddressableSize(currLen);
}
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::doReserve
(
const bool nocopy,
const label len
)
{
if (capacity_ < len)
{
// Preserve addressed size
const label currLen = List<T>::size();
// Increase capacity (doubling)
capacity_ = max(SizeMin, max(len, label(2*capacity_)));
if (nocopy)
{
List<T>::resize_nocopy(capacity_);
}
else
{
List<T>::resize(capacity_);
}
List<T>::setAddressableSize(currLen);
}
}
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::doResize
(
const bool nocopy,
const label len
)
{
this->doReserve(nocopy, len);
List<T>::setAddressableSize(len);
}
@ -70,7 +142,7 @@ inline Foam::DynamicField<T, SizeMin>::DynamicField(const label len)
Field<T>(),
capacity_(0)
{
reserve(len);
reserve_nocopy(len);
}
@ -155,6 +227,20 @@ inline Foam::DynamicField<T, SizeMin>::DynamicField
{}
template<class T, int SizeMin>
template<int AnySizeMin>
inline Foam::DynamicField<T, SizeMin>::DynamicField
(
DynamicList<T, AnySizeMin>&& list
)
:
Field<T>(),
capacity_(0)
{
transfer(list);
}
template<class T, int SizeMin>
inline Foam::DynamicField<T, SizeMin>::DynamicField
(
@ -255,20 +341,30 @@ Foam::DynamicField<T, SizeMin>::capacity_bytes() const noexcept
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::setCapacity
(
const label newCapacity
const label len
)
{
label currLen = List<T>::size();
capacity_ = newCapacity;
this->doCapacity(false, len); // nocopy = false
}
if (currLen > capacity_)
{
// Truncate addressed sizes too
currLen = capacity_;
}
List<T>::resize(capacity_);
List<T>::setAddressableSize(currLen);
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::setCapacity_nocopy
(
const label len
)
{
this->doCapacity(true, len); // nocopy = true
}
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::setCapacity_unsafe
(
const label len
) noexcept
{
capacity_ = len;
}
@ -278,52 +374,55 @@ inline void Foam::DynamicField<T, SizeMin>::reserve
const label len
)
{
if (capacity_ < len)
{
// Increase capacity (doubling)
capacity_ = max(SizeMin, max(len, label(2 * capacity_)));
// Adjust allocated size, leave addressed size untouched
const label currLen = List<T>::size();
List<T>::resize(capacity_);
List<T>::setAddressableSize(currLen);
}
this->doReserve(false, len); // nocopy = false
}
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::resize
inline void Foam::DynamicField<T, SizeMin>::reserve_nocopy
(
const label newLen
const label len
)
{
if (capacity_ < newLen)
{
// Increase capacity (doubling)
capacity_ = max(SizeMin, max(newLen, label(2 * capacity_)));
List<T>::resize(capacity_);
}
// Adjust addressed size
List<T>::setAddressableSize(newLen);
this->doReserve(true, len); // nocopy = true
}
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::resize
(
const label newLen,
const label len
)
{
this->doResize(false, len); // nocopy = false
}
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::resize_nocopy
(
const label len
)
{
this->doResize(true, len); // nocopy = true
}
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::resize
(
const label len,
const T& val
)
{
label currLen = List<T>::size();
resize(newLen);
label idx = List<T>::size();
resize(len);
// Fill newly exposed with constant value
while (currLen < newLen)
while (idx < len)
{
this->operator[](currLen++) = val;
this->operator[](idx) = val;
++idx;
}
}
@ -355,21 +454,27 @@ inline Foam::label Foam::DynamicField<T, SizeMin>::expandStorage() noexcept
}
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::shrinkStorage()
{
const label currLen = List<T>::size();
if (currLen < capacity_)
{
// Adjust addressable size to trigger proper resizing
List<T>::setAddressableSize(currLen+1);
List<T>::resize(currLen);
capacity_ = List<T>::size();
}
}
template<class T, int SizeMin>
inline Foam::DynamicField<T, SizeMin>&
Foam::DynamicField<T, SizeMin>::shrink()
{
const label currLen = Field<T>::size();
if (currLen < capacity_)
{
// Use the full list when resizing
List<T>::setAddressableSize(capacity_);
// Capacity and size are identical
capacity_ = currLen;
List<T>::resize(currLen);
// Redundant: List<T>::setAddressableSize(currLen);
}
this->shrinkStorage();
return *this;
}
@ -395,6 +500,34 @@ inline void Foam::DynamicField<T, SizeMin>::swap
}
template<class T, int SizeMin>
template<int AnySizeMin>
inline void Foam::DynamicField<T, SizeMin>::swap
(
DynamicList<T, AnySizeMin>& other
)
{
auto& self = (*this);
// ... not yet needed:
// Cannot compare 'this' for different types, so use cdata()
if (self.cdata() == other.cdata())
{
return; // Self-swap is a no-op
}
// Swap storage and addressable size
UList<T>::swap(other);
// Swap capacity
const label oldCap = self.capacity();
const label newCap = other.capacity();
self.setCapacity_unsafe(newCap);
other.setCapacity_unsafe(oldCap);
}
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::transfer(List<T>& list)
{
@ -421,7 +554,7 @@ inline void Foam::DynamicField<T, SizeMin>::transfer
// clear addressing and storage for old list.
capacity_ = list.capacity();
Field<T>::transfer(static_cast<Field<T>&>(list));
Field<T>::transfer(static_cast<List<T>&>(list));
list.clearStorage(); // Ensure capacity=0
}
@ -443,7 +576,7 @@ inline void Foam::DynamicField<T, SizeMin>::transfer
// clear addressing and storage for old list.
capacity_ = list.capacity();
Field<T>::transfer(static_cast<Field<T>&>(list));
Field<T>::transfer(static_cast<List<T>&>(list));
list.clearStorage(); // Ensure capacity=0
}
@ -531,7 +664,7 @@ inline T& Foam::DynamicField<T, SizeMin>::operator()
const label i
)
{
if (i >= Field<T>::size())
if (i >= List<T>::size())
{
resize(i + 1);
}
@ -616,6 +749,17 @@ inline void Foam::DynamicField<T, SizeMin>::operator=
}
template<class T, int SizeMin>
template<int AnySizeMin>
inline void Foam::DynamicField<T, SizeMin>::operator=
(
DynamicList<T, AnySizeMin>&& list
)
{
transfer(list);
}
// * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
template<class T, int SizeMin>
@ -624,13 +768,14 @@ inline Foam::Istream& Foam::DynamicField<T, SizeMin>::readList
Istream& is
)
{
DynamicField<T, SizeMin>& rhs = *this;
// Use DynamicList::readList for reading DynamicField.
// The logic should be the same and this avoids duplicate code
// Use entire storage - ie, resize(capacity())
(void) rhs.expandStorage();
DynamicList<T, SizeMin> list;
(*this).swap(list);
is >> static_cast<Field<T>&>(rhs);
rhs.capacity_ = rhs.Field<T>::size();
list.readList(is);
(*this).swap(list);
return is;
}

View File

@ -345,6 +345,36 @@ void Foam::Matrix<Form, Type>::resize(const label m, const label n)
}
template<class Form, class Type>
void Foam::Matrix<Form, Type>::resize_nocopy(const label mrow, const label ncol)
{
if (mrow == mRows_ && ncol == nCols_)
{
return;
}
const label oldLen = (mRows_ * nCols_);
const label newLen = (mrow * ncol);
if (oldLen == newLen)
{
// Shallow resize is enough
mRows_ = mrow;
nCols_ = ncol;
}
else
{
this->clear();
mRows_ = mrow;
nCols_ = ncol;
this->doAlloc();
}
}
template<class Form, class Type>
void Foam::Matrix<Form, Type>::round(const scalar tol)
{

View File

@ -357,6 +357,9 @@ public:
//- Change Matrix dimensions, preserving the elements
void resize(const label m, const label n);
//- Change Matrix dimensions \em without preserving existing content
void resize_nocopy(const label mrow, const label ncol);
//- Change Matrix dimensions, preserving the elements
inline void setSize(const label m, const label n);
@ -366,6 +369,7 @@ public:
//- Round elements with magnitude smaller than tol (SMALL) to zero
void round(const scalar tol = SMALL);
// Operations
//- Return (conjugate) transpose of Matrix