BUG: guard against potential wasted memory in DynamicField

- specialize transfer and swap to ensure allocated capacity isn't
  forgotten.
This commit is contained in:
Mark Olesen 2018-03-21 15:30:14 +01:00
parent 4cb763f9d2
commit 35d348c00d
4 changed files with 296 additions and 122 deletions

View File

@ -39,10 +39,7 @@ template<class T, int SizeMin>
Foam::tmp<Foam::DynamicField<T, SizeMin>> Foam::tmp<Foam::DynamicField<T, SizeMin>>
Foam::DynamicField<T, SizeMin>::clone() const Foam::DynamicField<T, SizeMin>::clone() const
{ {
return tmp<DynamicField<T, SizeMin>> return tmp<DynamicField<T, SizeMin>>::New(*this);
(
new DynamicField<T, SizeMin>(*this)
);
} }

View File

@ -51,20 +51,20 @@ template<class T, int SizeMin> class DynamicField;
template<class T, int SizeMin> template<class T, int SizeMin>
Ostream& operator<< Ostream& operator<<
( (
Ostream&, Ostream& os,
const DynamicField<T, SizeMin>& const DynamicField<T, SizeMin>& fld
); );
template<class T, int SizeMin> template<class T, int SizeMin>
Istream& operator>> Istream& operator>>
( (
Istream&, Istream& is,
DynamicField<T, SizeMin>& DynamicField<T, SizeMin>& fld
); );
/*---------------------------------------------------------------------------*\ /*---------------------------------------------------------------------------*\
Class DynamicField Declaration Class DynamicField Declaration
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
template<class T, int SizeMin=64> template<class T, int SizeMin=64>
@ -84,7 +84,7 @@ class DynamicField
//- Copy assignment from another list //- Copy assignment from another list
template<class ListType> template<class ListType>
inline void assignDynField(const ListType& lst); inline void assignDynField(const ListType& list);
public: public:
@ -102,15 +102,25 @@ public:
//- Construct null //- Construct null
inline constexpr DynamicField() noexcept; inline constexpr DynamicField() noexcept;
//- Construct with given capacity. //- Construct empty field with given reserve size.
explicit inline DynamicField(const label len); explicit inline DynamicField(const label len);
//- Construct given size and initial value
inline DynamicField(const label len, const T& val);
//- Construct given size and initial value of zero
inline DynamicField(const label len, const zero);
//- Copy construct //- Copy construct
inline DynamicField(const DynamicField<T, SizeMin>& lst); inline DynamicField(const DynamicField<T, SizeMin>& list);
//- Copy construct with different sizing parameters
template<int AnySizeMin>
inline DynamicField(const DynamicField<T, AnySizeMin>& list);
//- Copy construct from UList. Size set to UList size. //- Copy construct from UList. Size set to UList size.
// Also constructs from DynamicField with different sizing parameters. // Also constructs from DynamicField with different sizing parameters.
explicit inline DynamicField(const UList<T>& lst); explicit inline DynamicField(const UList<T>& list);
//- Copy construct from UIndirectList //- Copy construct from UIndirectList
explicit inline DynamicField(const UIndirectList<T>& list); explicit inline DynamicField(const UIndirectList<T>& list);
@ -118,9 +128,13 @@ public:
//- Move construct from List contents //- Move construct from List contents
explicit inline DynamicField(List<T>&& content); explicit inline DynamicField(List<T>&& content);
//- Move construct from Field contents //- Move construct from dynamic Field contents
inline DynamicField(DynamicField<T, SizeMin>&& content); inline DynamicField(DynamicField<T, SizeMin>&& content);
//- Move construct with different sizing parameters
template<int AnySizeMin>
inline DynamicField(DynamicField<T, AnySizeMin>&& content);
//- Construct by 1 to 1 mapping from the given field //- Construct by 1 to 1 mapping from the given field
inline DynamicField inline DynamicField
( (
@ -152,87 +166,108 @@ public:
// Member Functions // Member Functions
// Access // Access
//- Size of the underlying storage. //- Size of the underlying storage.
inline label capacity() const; inline label capacity() const;
// Edit // Edit
//- Alter the size of the underlying storage. //- Alter the size of the underlying storage.
// The addressed size will be truncated if needed to fit, but will // The addressed size will be truncated if needed to fit, but will
// remain otherwise untouched. // remain otherwise untouched.
// Use this or reserve() in combination with append(). // Use this or reserve() in combination with append().
inline void setCapacity(const label nElem); inline void setCapacity(const label nElem);
//- Alter the addressed list size. //- Alter the addressed list size.
// New space will be allocated if required. // New space will be allocated if required.
// Use this to resize the list prior to using the operator[] for // Use this to resize the list prior to using the operator[] for
// setting values (as per List usage). // setting values (as per List usage).
inline void setSize(const label nElem); inline void setSize(const label nElem);
//- Alter the addressed list size and fill new space with a //- Alter the addressed list size and fill new space with a constant.
// constant. inline void setSize(const label nElem, const T& val);
inline void setSize(const label nElem, const T& val);
//- Alter the addressed list size. //- Alter the addressed list size.
// New space will be allocated if required. // New space will be allocated if required.
// Use this to resize the list prior to using the operator[] for // Use this to resize the list prior to using the operator[] for
// setting values (as per List usage). // setting values (as per List usage).
inline void resize(const label nElem); inline void resize(const label nElem);
//- Alter the addressed list size and fill new space with a //- Alter the addressed list size and fill new space with a
// constant. // constant.
inline void resize(const label nElem, const T& val); inline void resize(const label nElem, const T& val);
//- Reserve allocation space for at least this size. //- Reserve allocation space for at least this size.
// Never shrinks the allocated size, use setCapacity() for that. // Never shrinks the allocated size, use setCapacity() for that.
inline void reserve(const label nElem); inline void reserve(const label nElem);
//- Clear the addressed list, i.e. set the size to zero. //- Clear the addressed list, i.e. set the size to zero.
// Allocated size does not change // Allocated size does not change
inline void clear(); inline void clear();
//- Clear the list and delete storage. //- Clear the list and delete storage.
inline void clearStorage(); inline void clearStorage();
//- Expand the addressable size to fit the allocated capacity. //- Expand the addressable size to fit the allocated capacity.
// Returns the previous addressable size. // Returns the previous addressable size.
inline label expandStorage(); inline label expandStorage();
//- Shrink the allocated space to the number of elements used. //- Shrink the allocated space to the number of elements used.
// Returns a reference to the DynamicField. // Returns a reference to the DynamicField.
inline DynamicField<T, SizeMin>& shrink(); inline DynamicField<T, SizeMin>& shrink();
//- Swap content with any sized DynamicField
template<int AnySizeMin>
inline void swap(DynamicField<T, AnySizeMin>& list);
//- Transfer the parameter contents into this
inline void transfer(List<T>& list);
//- Transfer the parameter contents into this
template<int AnySizeMin>
inline void transfer(DynamicList<T, AnySizeMin>& list);
//- Transfer the parameter contents into this
template<int AnySizeMin>
inline void transfer(DynamicField<T, AnySizeMin>& list);
// Member Operators //- Append an element at the end of the list
inline DynamicField<T, SizeMin>&
append(const T& val);
//- Append an element at the end of the list //- Append a List at the end of this list
inline DynamicField<T, SizeMin>& inline DynamicField<T, SizeMin>&
append(const T& val); append(const UList<T>& list);
//- Append a List at the end of this list //- Remove and return the top element
inline DynamicField<T, SizeMin>& inline T remove();
append(const UList<T>& lst);
//- Remove and return the top element
inline T remove();
//- Return non-const access to an element, resizing list if // Member Operators
// necessary
inline T& operator()(const label i);
//- Assignment of all addressed entries to the given value //- Return non-const access to an element, resizing list if necessary
inline void operator=(const T& val); inline T& operator()(const label i);
//- Assignment to DynamicField //- Assign addressed entries to the given value
inline void operator= inline void operator=(const T& val);
(
const DynamicField<T, SizeMin>& lst
);
//- Assignment to UList //- Copy assignment
inline void operator=(const UList<T>& lst); inline void operator=(const UList<T>& list);
//- Copy assignment
inline void operator=(const DynamicField<T, SizeMin>& list);
//- Move assignment
inline void operator=(List<T>&& list);
//- Move assignment
inline void operator=(DynamicField<T, SizeMin>&& list);
//- Move assignment
template<int AnySizeMin>
inline void operator=(DynamicField<T, AnySizeMin>&& list);
}; };

View File

@ -29,23 +29,23 @@ template<class T, int SizeMin>
template<class ListType> template<class ListType>
inline void Foam::DynamicField<T, SizeMin>::assignDynField inline void Foam::DynamicField<T, SizeMin>::assignDynField
( (
const ListType& lst const ListType& list
) )
{ {
const label newSize = lst.size(); const label newSize = list.size();
if (capacity_ >= newSize) if (capacity_ >= newSize)
{ {
// Can copy w/o reallocating - adjust addressable size accordingly. // Can copy w/o reallocating - adjust addressable size accordingly.
Field<T>::size(lst.size()); Field<T>::size(list.size());
Field<T>::operator=(lst); Field<T>::operator=(list);
} }
else else
{ {
// Ensure list size consistency prior to copying. // Ensure list size consistency prior to copying.
Field<T>::size(capacity_); Field<T>::size(capacity_);
Field<T>::operator=(lst); Field<T>::operator=(list);
capacity_ = Field<T>::size(); capacity_ = Field<T>::size();
} }
} }
@ -62,15 +62,11 @@ inline constexpr Foam::DynamicField<T, SizeMin>::DynamicField() noexcept
template<class T, int SizeMin> template<class T, int SizeMin>
inline Foam::DynamicField<T, SizeMin>::DynamicField inline Foam::DynamicField<T, SizeMin>::DynamicField(const label len)
(
const label len
)
: :
Field<T>(len), Field<T>(len),
capacity_(Field<T>::size()) capacity_(Field<T>::size())
{ {
// We could also enforce sizing granularity
Field<T>::size(0); Field<T>::size(0);
} }
@ -78,21 +74,11 @@ inline Foam::DynamicField<T, SizeMin>::DynamicField
template<class T, int SizeMin> template<class T, int SizeMin>
inline Foam::DynamicField<T, SizeMin>::DynamicField inline Foam::DynamicField<T, SizeMin>::DynamicField
( (
const DynamicField<T, SizeMin>& lst const label len,
const T& val
) )
: :
Field<T>(lst), Field<T>(len, val),
capacity_(lst.capacity())
{}
template<class T, int SizeMin>
inline Foam::DynamicField<T, SizeMin>::DynamicField
(
const UList<T>& lst
)
:
Field<T>(lst),
capacity_(Field<T>::size()) capacity_(Field<T>::size())
{} {}
@ -100,10 +86,56 @@ inline Foam::DynamicField<T, SizeMin>::DynamicField
template<class T, int SizeMin> template<class T, int SizeMin>
inline Foam::DynamicField<T, SizeMin>::DynamicField inline Foam::DynamicField<T, SizeMin>::DynamicField
( (
const UIndirectList<T>& lst const label len,
const zero
) )
: :
Field<T>(lst), Field<T>(len, Zero),
capacity_(Field<T>::size())
{}
template<class T, int SizeMin>
inline Foam::DynamicField<T, SizeMin>::DynamicField
(
const DynamicField<T, SizeMin>& list
)
:
Field<T>(list),
capacity_(Field<T>::size())
{}
template<class T, int SizeMin>
template<int AnySizeMin>
inline Foam::DynamicField<T, SizeMin>::DynamicField
(
const DynamicField<T, AnySizeMin>& list
)
:
Field<T>(list),
capacity_(Field<T>::size())
{}
template<class T, int SizeMin>
inline Foam::DynamicField<T, SizeMin>::DynamicField
(
const UList<T>& list
)
:
Field<T>(list),
capacity_(Field<T>::size())
{}
template<class T, int SizeMin>
inline Foam::DynamicField<T, SizeMin>::DynamicField
(
const UIndirectList<T>& list
)
:
Field<T>(list),
capacity_(Field<T>::size()) capacity_(Field<T>::size())
{} {}
@ -125,10 +157,24 @@ inline Foam::DynamicField<T, SizeMin>::DynamicField
DynamicField<T, SizeMin>&& content DynamicField<T, SizeMin>&& content
) )
: :
Field<T>(std::move(content)), Field<T>(),
capacity_(Field<T>::size()) capacity_(0)
{ {
content.clear(); transfer(content);
}
template<class T, int SizeMin>
template<int AnySizeMin>
inline Foam::DynamicField<T, SizeMin>::DynamicField
(
DynamicField<T, AnySizeMin>&& content
)
:
Field<T>(),
capacity_(0)
{
transfer(content);
} }
@ -173,8 +219,7 @@ inline Foam::DynamicField<T, SizeMin>::DynamicField
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template<class T, int SizeMin> template<class T, int SizeMin>
inline Foam::label Foam::DynamicField<T, SizeMin>::capacity() inline Foam::label Foam::DynamicField<T, SizeMin>::capacity() const
const
{ {
return capacity_; return capacity_;
} }
@ -343,6 +388,73 @@ Foam::DynamicField<T, SizeMin>::shrink()
} }
template<class T, int SizeMin>
template<int AnySizeMin>
inline void Foam::DynamicField<T, SizeMin>::swap
(
DynamicField<T, AnySizeMin>& lst
)
{
DynamicList<T, SizeMin>& cur = *this;
// Make addressable size identical to the allocated capacity
const label oldSize1 = cur.expandStorage();
const label oldSize2 = lst.expandStorage();
// Swap storage
Field<T>::swap(lst);
// Match capacity to the underlying allocated list size
cur.setCapacity(cur.size());
lst.setCapacity(lst.size());
// Set addressable size
cur.setSize(oldSize2);
lst.setSize(oldSize1);
}
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::transfer(List<T>& list)
{
// Take over storage, clear addressing for list.
capacity_ = list.size();
Field<T>::transfer(list);
}
template<class T, int SizeMin>
template<int AnySizeMin>
inline void Foam::DynamicField<T, SizeMin>::transfer
(
DynamicList<T, AnySizeMin>& list
)
{
// Take over storage as-is (without shrink, without using SizeMin)
// clear addressing and storage for old list.
capacity_ = list.capacity();
Field<T>::transfer(static_cast<Field<T>&>(list));
list.clearStorage(); // Ensure capacity=0
}
template<class T, int SizeMin>
template<int AnySizeMin>
inline void Foam::DynamicField<T, SizeMin>::transfer
(
DynamicField<T, AnySizeMin>& list
)
{
// Take over storage as-is (without shrink, without using SizeMin)
// clear addressing and storage for old list.
capacity_ = list.capacity();
Field<T>::transfer(static_cast<Field<T>&>(list));
list.clearStorage(); // Ensure capacity=0
}
template<class T, int SizeMin> template<class T, int SizeMin>
inline Foam::DynamicField<T, SizeMin>& inline Foam::DynamicField<T, SizeMin>&
Foam::DynamicField<T, SizeMin>::append Foam::DynamicField<T, SizeMin>::append
@ -353,7 +465,7 @@ Foam::DynamicField<T, SizeMin>::append
const label idx = List<T>::size(); const label idx = List<T>::size();
setSize(idx + 1); setSize(idx + 1);
this->operator[](idx) = val; this->operator[](idx) = val; // copy element
return *this; return *this;
} }
@ -362,21 +474,21 @@ template<class T, int SizeMin>
inline Foam::DynamicField<T, SizeMin>& inline Foam::DynamicField<T, SizeMin>&
Foam::DynamicField<T, SizeMin>::append Foam::DynamicField<T, SizeMin>::append
( (
const UList<T>& lst const UList<T>& list
) )
{ {
if (this == &lst) if (this == &list)
{ {
FatalErrorInFunction FatalErrorInFunction
<< "attempted appending to self" << abort(FatalError); << "attempted appending to self" << abort(FatalError);
} }
label nextFree = List<T>::size(); label idx = List<T>::size();
setSize(nextFree + lst.size()); setSize(idx + list.size());
forAll(lst, i) for (const T& val : list)
{ {
this->operator[](nextFree++) = lst[i]; this->operator[](idx++) = val; // copy element
} }
return *this; return *this;
} }
@ -432,26 +544,57 @@ inline void Foam::DynamicField<T, SizeMin>::operator=
template<class T, int SizeMin> template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::operator= inline void Foam::DynamicField<T, SizeMin>::operator=
( (
const DynamicField<T, SizeMin>& lst const UList<T>& list
) )
{ {
if (this == &lst) assignDynField(list);
{
FatalErrorInFunction
<< "Attempted assignment to self" << abort(FatalError);
}
assignDynField(lst);
} }
template<class T, int SizeMin> template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::operator= inline void Foam::DynamicField<T, SizeMin>::operator=
( (
const UList<T>& lst const DynamicField<T, SizeMin>& list
) )
{ {
assignDynField(lst); if (this == &list)
{
FatalErrorInFunction
<< "Attempted assignment to self" << abort(FatalError);
}
assignDynField(list);
}
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::operator=
(
List<T>&& list
)
{
transfer(list);
}
template<class T, int SizeMin>
inline void Foam::DynamicField<T, SizeMin>::operator=
(
DynamicField<T, SizeMin>&& list
)
{
transfer(list);
}
template<class T, int SizeMin>
template<int AnySizeMin>
inline void Foam::DynamicField<T, SizeMin>::operator=
(
DynamicField<T, AnySizeMin>&& list
)
{
transfer(list);
} }

View File

@ -186,5 +186,4 @@ inline void Foam::Field<Type>::operator=(const zero)
} }
// ************************************************************************* // // ************************************************************************* //