From 1d1e0c5f1372ccbc1d182df3123ca9ba90e1a5a2 Mon Sep 17 00:00:00 2001 From: Mark Olesen Date: Sat, 27 Sep 2025 19:35:06 +0200 Subject: [PATCH 1/4] ENH: make List::resize_copy() a public method - improves usability of List as a general dynamic container as member data without inheritance. Provide the same method for DynamicList to enable more efficient use. - additional string_view support for charList: useful when being used as a character buffer STYLE: improve allocation handling in List - consolidate routines. Only assign size after allocation to ensure that states are always consistent. --- .../Lists/DynamicList/DynamicList.H | 21 ++- .../Lists/DynamicList/DynamicListI.H | 101 +++++++---- src/OpenFOAM/containers/Lists/List/List.C | 137 +++++++-------- src/OpenFOAM/containers/Lists/List/List.H | 49 +++--- src/OpenFOAM/containers/Lists/List/ListI.H | 54 +++--- src/OpenFOAM/containers/Lists/List/UList.H | 9 + .../fields/Fields/DynamicField/DynamicField.H | 21 ++- .../Fields/DynamicField/DynamicFieldI.H | 162 +++++++++++------- 8 files changed, 312 insertions(+), 242 deletions(-) diff --git a/src/OpenFOAM/containers/Lists/DynamicList/DynamicList.H b/src/OpenFOAM/containers/Lists/DynamicList/DynamicList.H index 92932c92ba..2e92c83e3c 100644 --- a/src/OpenFOAM/containers/Lists/DynamicList/DynamicList.H +++ b/src/OpenFOAM/containers/Lists/DynamicList/DynamicList.H @@ -91,19 +91,14 @@ class DynamicList template inline void doAssignDynList(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); + //- Alter the size of the underlying storage, + //- retaining the first count elements. + inline void doCapacity_copy(label count, const label len); - //- Reserve allocation space for at least this size. + //- Reserve allocation space for at least this size, + //- retaining the first count elements. // 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); + inline void doReserve_copy(label count, const label len); //- Read List from Istream between '(' and ')' delimiters. //- The size is not known a priori. @@ -232,6 +227,10 @@ public: //- Alter addressable size and fill \em new entries with constant value inline void resize(const label len, const T& val); + //- Alter addressable size, retaining the first count contents. + // \note Only uses a limited number of internal checks. + void resize_copy(label count, const label len); + //- Alter addressable size and set val for \em all addressed entries inline void resize_fill(const label len, const T& val); diff --git a/src/OpenFOAM/containers/Lists/DynamicList/DynamicListI.H b/src/OpenFOAM/containers/Lists/DynamicList/DynamicListI.H index ed94ae38b4..bd2b6ffe8c 100644 --- a/src/OpenFOAM/containers/Lists/DynamicList/DynamicListI.H +++ b/src/OpenFOAM/containers/Lists/DynamicList/DynamicListI.H @@ -54,9 +54,9 @@ inline void Foam::DynamicList::doAssignDynList template -inline void Foam::DynamicList::doCapacity +inline void Foam::DynamicList::doCapacity_copy ( - const bool nocopy, + label count, const label newCapacity ) { @@ -68,16 +68,22 @@ inline void Foam::DynamicList::doCapacity // Addressable length, possibly truncated by new capacity const label currLen = Foam::min(List::size(), newCapacity); + // The count truncated by the new addressable range + if (count > currLen) + { + count = currLen; + } + // Consistent allocated sizing List::setAddressableSize(capacity_); - if (nocopy) + if (count > 0) { - List::resize_nocopy(newCapacity); + List::resize_copy(count, newCapacity); } else { - List::resize_copy(currLen, newCapacity); + List::resize_nocopy(newCapacity); } capacity_ = List::size(); @@ -86,9 +92,9 @@ inline void Foam::DynamicList::doCapacity template -inline void Foam::DynamicList::doReserve +inline void Foam::DynamicList::doReserve_copy ( - const bool nocopy, + label count, const label len ) { @@ -97,20 +103,27 @@ inline void Foam::DynamicList::doReserve // Preserve addressed size const label currLen = List::size(); + // The count truncated by the addressable range + if (count > currLen) + { + count = currLen; + } + // Consistent allocated sizing List::setAddressableSize(capacity_); // Increase capacity (eg, doubling) + // - this may need better handling for when lists become very large capacity_ = Foam::ListPolicy::reserve_size(len, capacity_); - if (nocopy) + if (count > 0) { - List::resize_nocopy(capacity_); + List::resize_copy(count, capacity_); } else { - List::resize_copy(currLen, capacity_); + List::resize_nocopy(capacity_); } capacity_ = List::size(); @@ -119,18 +132,6 @@ inline void Foam::DynamicList::doReserve } -template -inline void Foam::DynamicList::doResize -( - const bool nocopy, - const label len -) -{ - this->doReserve(nocopy, len); - List::setAddressableSize(len); -} - - // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // template @@ -323,7 +324,8 @@ inline void Foam::DynamicList::setCapacity const label len ) { - this->doCapacity(false, len); // nocopy = false + // Preserve all content + doCapacity_copy(List::size(), len); } @@ -333,7 +335,8 @@ inline void Foam::DynamicList::setCapacity_nocopy const label len ) { - this->doCapacity(true, len); // nocopy = true + // Preserve 0 content + doCapacity_copy(0, len); } @@ -343,7 +346,8 @@ inline void Foam::DynamicList::reserve const label len ) { - this->doReserve(false, len); // nocopy = false + // Preserve all content + doReserve_copy(List::size(), len); } @@ -353,7 +357,8 @@ inline void Foam::DynamicList::reserve_nocopy const label len ) { - this->doReserve(true, len); // nocopy = true + // Preserve 0 content + doReserve_copy(0, len); } @@ -371,9 +376,12 @@ inline void Foam::DynamicList::reserve_exact // Consistent allocated sizing List::setAddressableSize(capacity_); + // Exact length + capacity_ = len; + // if (!nocopy) { - List::resize_copy(currLen, len); + List::resize_copy(currLen, capacity_); } capacity_ = List::size(); @@ -388,7 +396,21 @@ inline void Foam::DynamicList::resize const label len ) { - this->doResize(false, len); // nocopy = false + // Preserve all content + resize_copy(List::size(), len); +} + + +template +inline void Foam::DynamicList::resize_copy +( + label count, + const label len +) +{ + // Preserve count elements + doReserve_copy(count, len); + List::setAddressableSize(len); } @@ -399,7 +421,7 @@ inline void Foam::DynamicList::resize_fill const T& val ) { - this->doResize(true, len); // nocopy = true + resize_nocopy(len); UList::operator=(val); } @@ -410,7 +432,8 @@ inline void Foam::DynamicList::resize_nocopy const label len ) { - this->doResize(true, len); // nocopy = true + // Preserve 0 content + resize_copy(0, len); } @@ -422,6 +445,8 @@ inline void Foam::DynamicList::resize ) { const label oldLen = List::size(); + + // Preserve all content resize(len); // Fill newly exposed with constant value @@ -445,6 +470,9 @@ inline void Foam::DynamicList::clear() noexcept template inline void Foam::DynamicList::clearStorage() { + // Extra safety...? + if (!List::cdata() && List::empty()) capacity_ = 0; + // Consistent allocated sizing List::setAddressableSize(capacity_); List::clear(); @@ -515,7 +543,7 @@ inline void Foam::DynamicList::swap UList::swap(other); // Swap capacity - std::swap(this->capacity_, other.capacity_); + std::swap(capacity_, other.capacity_); } @@ -537,7 +565,7 @@ template inline void Foam::DynamicList::transfer ( - DynamicList& list + DynamicList& other ) { if @@ -545,7 +573,7 @@ Foam::DynamicList::transfer FOAM_UNLIKELY ( static_cast*>(this) - == static_cast*>(&list) + == static_cast*>(&other) ) ) { @@ -554,10 +582,11 @@ Foam::DynamicList::transfer // Consistent allocated sizing List::setAddressableSize(capacity_); - List::transfer(static_cast&>(list)); + List::transfer(static_cast&>(other)); - capacity_ = list.capacity(); - list.setCapacity_unsafe(0); // All contents moved + // Update capacity information + capacity_ = other.capacity(); + other.setCapacity_unsafe(0); // All contents moved } diff --git a/src/OpenFOAM/containers/Lists/List/List.C b/src/OpenFOAM/containers/Lists/List/List.C index fa1e680e9c..bec1e08157 100644 --- a/src/OpenFOAM/containers/Lists/List/List.C +++ b/src/OpenFOAM/containers/Lists/List/List.C @@ -28,27 +28,38 @@ License #include "List.H" #include "FixedList.H" -#include "PtrList.H" +#include "UPtrList.H" -// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * // +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // +// Only a limited number of internal size checks. +// Caller knows what they are doing. template -void Foam::List::resize_copy(const label count, const label len) +void Foam::List::resize_copy(label count, const label len) { - // Only a limited number of internal size checks. - // Caller knows what they are doing. - - if (FOAM_LIKELY(len > 0)) + if (this->size_ == len) + { + // no-op + } + else if (FOAM_LIKELY(len > 0)) { // With sign-check to avoid spurious -Walloc-size-larger-than - const label oldLen = this->size_; - const label overlap = Foam::min(count, len); - // Extra safety, not currently necessary: - // const label overlap = Foam::min(Foam::min(count, oldLen), len); T* old = this->v_; + const label oldLen = this->size_; - if (overlap > 0) + if (count > len) + { + count = len; // The count truncated by the new length + } + + // Extra safety, probably not necessary: + // if (count > oldLen) + // { + // count = oldLen; // The count truncated by the old length + // } + + if (count > 0) { // Recover overlapping content when resizing @@ -58,7 +69,7 @@ void Foam::List::resize_copy(const label count, const label len) // Can dispatch with // - std::execution::par_unseq // - std::execution::unseq - std::move(old, (old + overlap), this->v_); + std::move(old, (old + count), this->v_); ListPolicy::deallocate(old, oldLen); } @@ -91,8 +102,6 @@ void Foam::List::resize_copy(const label count, const label len) template Foam::List::List(const label len) -: - UList(nullptr, len) { if (FOAM_UNLIKELY(len < 0)) { @@ -103,15 +112,14 @@ Foam::List::List(const label len) if (len > 0) { - doAlloc(); + // resize_nocopy() + doAlloc(len); } } template Foam::List::List(const label len, const T& val) -: - UList(nullptr, len) { if (FOAM_UNLIKELY(len < 0)) { @@ -122,7 +130,8 @@ Foam::List::List(const label len, const T& val) if (len > 0) { - doAlloc(); + // resize_fill() + doAlloc(len); UList::operator=(val); } } @@ -130,8 +139,6 @@ Foam::List::List(const label len, const T& val) template Foam::List::List(const label len, Foam::zero) -: - UList(nullptr, len) { if (FOAM_UNLIKELY(len < 0)) { @@ -142,7 +149,8 @@ Foam::List::List(const label len, Foam::zero) if (len > 0) { - doAlloc(); + // resize_fill() + doAlloc(len); UList::operator=(Foam::zero{}); } } @@ -177,12 +185,10 @@ Foam::List::List(Foam::one, Foam::zero) template Foam::List::List(const UList& list) -: - UList(nullptr, list.size_) { - if (this->size_ > 0) + if (!list.empty()) { - doAlloc(); + doAlloc(list.size()); UList::deepCopy(list); } } @@ -190,12 +196,10 @@ Foam::List::List(const UList& list) template Foam::List::List(const List& list) -: - UList(nullptr, list.size_) { - if (this->size_ > 0) + if (!list.empty()) { - doAlloc(); + doAlloc(list.size()); UList::deepCopy(list); } } @@ -203,19 +207,18 @@ Foam::List::List(const List& list) template Foam::List::List(List& list, bool reuse) -: - UList(nullptr, list.size_) { if (reuse) { // Steal content this->v_ = list.v_; + this->size_ = list.size_; list.v_ = nullptr; list.size_ = 0; } - else if (this->size_ > 0) + else if (!list.empty()) { - doAlloc(); + doAlloc(list.size()); UList::deepCopy(list); } } @@ -223,11 +226,12 @@ Foam::List::List(List& list, bool reuse) template Foam::List::List(const UList& list, const labelUList& indices) -: - UList(nullptr, indices.size()) { - doAlloc(); - copyList(list, indices); // <- deepCopy() + if (!indices.empty()) + { + doAlloc(indices.size()); + copyList(list, indices); // <- deepCopy() + } } @@ -238,11 +242,12 @@ Foam::List::List const UList& list, const FixedList& indices ) -: - UList(nullptr, indices.size()) { - doAlloc(); - copyList(list, indices); // <- deepCopy() + // if (!FixedList::empty()) is always true + { + doAlloc(indices.size()); + copyList(list, indices); // <- deepCopy() + } } @@ -255,24 +260,23 @@ Foam::List::List(const FixedList& list) template -Foam::List::List(const PtrList& list) -: - UList(nullptr, list.size()) +Foam::List::List(const UPtrList& list) { - doAlloc(); - copyList(list); + if (!list.empty()) + { + doAlloc(list.size()); + copyList(list); + } } template template Foam::List::List(const IndirectListBase& list) -: - UList(nullptr, list.size()) { - if (this->size_ > 0) + if (!list.empty()) { - doAlloc(); + doAlloc(list.size()); UList::deepCopy(list); } } @@ -285,21 +289,9 @@ Foam::List::List(std::initializer_list list) {} -template -Foam::List::List(List&& list) noexcept -: - UList(list.data(), list.size()) -{ - list.size_ = 0; - list.v_ = nullptr; -} - - template template Foam::List::List(DynamicList&& list) -: - UList() { transfer(list); } @@ -327,7 +319,7 @@ void Foam::List::resize(const label len, const T& val) return; } - this->resize_copy(oldLen, len); + resize_copy(oldLen, len); // Fill trailing part with new values if (oldLen < this->size_) @@ -368,7 +360,7 @@ void Foam::List::transfer(DynamicList& list) // Shrink the allocated space to the number of elements used list.shrink_to_fit(); transfer(static_cast&>(list)); - list.clearStorage(); // Deletion, capacity=0 etc. + list.setCapacity_unsafe(0); // All contents moved } @@ -382,9 +374,9 @@ void Foam::List::operator=(const UList& list) return; // Self-assignment is a no-op } - reAlloc(list.size_); + resize_nocopy(list.size()); - if (this->size_ > 0) + if (!list.empty()) { UList::deepCopy(list); } @@ -399,9 +391,9 @@ void Foam::List::operator=(const List& list) return; // Self-assignment is a no-op } - reAlloc(list.size_); + resize_nocopy(list.size()); - if (this->size_ > 0) + if (!list.empty()) { UList::deepCopy(list); } @@ -412,7 +404,7 @@ template template void Foam::List::operator=(const FixedList& list) { - reAlloc(list.size()); + resize_nocopy(list.size()); std::copy(list.begin(), list.end(), this->v_); } @@ -422,7 +414,7 @@ template template void Foam::List::operator=(const IndirectListBase& list) { - reAlloc(list.size()); + resize_nocopy(list.size()); UList::deepCopy(list); } @@ -430,8 +422,7 @@ void Foam::List::operator=(const IndirectListBase& list) template void Foam::List::operator=(std::initializer_list list) { - reAlloc(list.size()); - + resize_nocopy(list.size()); std::copy(list.begin(), list.end(), this->v_); } diff --git a/src/OpenFOAM/containers/Lists/List/List.H b/src/OpenFOAM/containers/Lists/List/List.H index 1dcfc62ba4..eaaaaea6a9 100644 --- a/src/OpenFOAM/containers/Lists/List/List.H +++ b/src/OpenFOAM/containers/Lists/List/List.H @@ -56,7 +56,7 @@ template class List; template class FixedList; template class DynamicList; -template class PtrList; +template class UPtrList; template Istream& operator>>(Istream& is, List& list); @@ -77,12 +77,8 @@ class List { // Private Member Functions - //- Allocate list storage - 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); + //- Allocate list storage. Assumes there is no existing content + inline void doAlloc(const label len); //- Copy all list contents. Uses operator[] on the input list template @@ -98,8 +94,8 @@ class List template inline List ( - InputIterator firstIter, - InputIterator lastIter, // (unused) + InputIterator input, + InputIterator inputEnd, // (unused) const label len ); @@ -111,17 +107,7 @@ class List // Methods as per DynamicList to simplify code maintenance //- Stub method for internal naming as per DynamicList - void setCapacity_nocopy(const label len) { resize_nocopy(len); } - - -protected: - - // Protected Member Functions - - //- Low-level resizing (backend for resize). - //- Change allocation size of list, retaining the first count contents. - // \note Only uses a limited number of internal checks. - void resize_copy(const label count, const label len); + void setCapacity_nocopy(label len) { resize_nocopy(len); } public: @@ -185,8 +171,8 @@ public: template explicit List(const FixedList& list); - //- Construct as copy of PtrList - explicit List(const PtrList& list); + //- Construct as copy of UPtrList content + explicit List(const UPtrList& list); //- Construct as copy of IndirectList contents template @@ -196,7 +182,7 @@ public: List(std::initializer_list list); //- Move construct from List - List(List&& list) noexcept; + inline List(List&& list) noexcept; //- Move construct from DynamicList template @@ -227,6 +213,10 @@ public: //- Adjust allocated size of list and set val for \em new elements void resize(const label len, const T& val); + //- Change allocated size of list, retaining the first count elements. + // \note Only uses a limited number of internal checks. + void resize_copy(label count, const label len); + //- Adjust allocated size of list and set val for \em all elements inline void resize_fill(const label len, const T& val); @@ -234,14 +224,9 @@ public: // retaining old content. // If no reallocation is required, the contents remain untouched. // Otherwise the contents will be uninitialized. + // Shorthand for \c resize(0, len) inline void resize_nocopy(const label len); - //- Alias for resize() - void setSize(const label n) { this->resize(n); } - - //- Alias for resize() - void setSize(const label n, const T& val) { this->resize(n, val); } - // Edit @@ -404,6 +389,12 @@ public: //- Same as push_uniq() FOAM_DEPRECATED_FOR(2022-10, "push_uniq()") label appendUniq(const T& val) { return this->push_uniq(val); } + + //- Alias for resize() + void setSize(label n) { this->resize(n); } + + //- Alias for resize() + void setSize(label n, const T& val) { this->resize(n, val); } }; diff --git a/src/OpenFOAM/containers/Lists/List/ListI.H b/src/OpenFOAM/containers/Lists/List/ListI.H index e37a2ea271..591e6a2e60 100644 --- a/src/OpenFOAM/containers/Lists/List/ListI.H +++ b/src/OpenFOAM/containers/Lists/List/ListI.H @@ -29,24 +29,13 @@ License // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // template -inline void Foam::List::doAlloc() +inline void Foam::List::doAlloc(const label len) { - if (this->size_ > 0) + if (len > 0) { // With sign-check to avoid spurious -Walloc-size-larger-than - this->v_ = ListPolicy::allocate(this->size_); - } -} - - -template -inline void Foam::List::reAlloc(const label len) -{ - if (this->size_ != len) - { - clear(); + this->v_ = ListPolicy::allocate(len); this->size_ = len; - doAlloc(); } } @@ -95,23 +84,24 @@ template template inline Foam::List::List ( - InputIterator firstIter, - InputIterator lastIter, // (unused) + InputIterator input, + InputIterator inputEnd, // (unused) const label len ) -: - UList(nullptr, len) { if (len > 0) { - doAlloc(); + doAlloc(len); // Like std::copy() or std::copy_n() // but without any requirements on the iterator category - for (label i = 0; i < len; (void)++i, (void)++firstIter) + auto iter = this->v_; + const auto endIter = (iter + len); + + for (; iter != endIter; (void)++iter, (void)++input) { - this->v_[i] = *firstIter; + *iter = *input; } } } @@ -124,6 +114,17 @@ inline constexpr Foam::List::List() noexcept {} +template +inline Foam::List::List(List&& list) noexcept +: + UList(list.data(), list.size()) +{ + // Stole content + list.size_ = 0; + list.v_ = nullptr; +} + + template inline Foam::autoPtr> Foam::List::clone() const { @@ -158,7 +159,7 @@ inline void Foam::List::resize(const label len) { if (this->size_ != len) { - this->resize_copy(this->size_, len); + resize_copy(this->size_, len); } } @@ -166,7 +167,7 @@ inline void Foam::List::resize(const label len) template inline void Foam::List::resize_fill(const label len, const T& val) { - this->reAlloc(len); + resize_nocopy(len); UList::operator=(val); } @@ -174,7 +175,12 @@ inline void Foam::List::resize_fill(const label len, const T& val) template inline void Foam::List::resize_nocopy(const label len) { - this->reAlloc(len); + // Same as resize_copy(0, len); + if (this->size_ != len) + { + clear(); + doAlloc(len); + } } diff --git a/src/OpenFOAM/containers/Lists/List/UList.H b/src/OpenFOAM/containers/Lists/List/UList.H index bdac7f55a3..7614575dc8 100644 --- a/src/OpenFOAM/containers/Lists/List/UList.H +++ b/src/OpenFOAM/containers/Lists/List/UList.H @@ -628,6 +628,15 @@ public: return false; } + //- Return a string_view of the charList. Content is non-modifiable. + template + std::enable_if_t + >, std::string_view> + inline view() const + { + return std::string_view(v_, size_); + } + // Hashing diff --git a/src/OpenFOAM/fields/Fields/DynamicField/DynamicField.H b/src/OpenFOAM/fields/Fields/DynamicField/DynamicField.H index 5679ca5b9c..3b5f3e5321 100644 --- a/src/OpenFOAM/fields/Fields/DynamicField/DynamicField.H +++ b/src/OpenFOAM/fields/Fields/DynamicField/DynamicField.H @@ -79,19 +79,14 @@ class DynamicField template inline void doAssignDynList(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); + //- Alter the size of the underlying storage, + //- retaining the first count elements. + inline void doCapacity_copy(label count, const label len); - //- Reserve allocation space for at least this size. + //- Reserve allocation space for at least this size, + //- retaining the first count elements. // 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); + inline void doReserve_copy(label count, const label len); public: @@ -246,6 +241,10 @@ public: //- Alter addressable size and fill \em new entries with constant value inline void resize(const label len, const T& val); + //- Alter addressable size, retaining the first count contents. + // \note Only uses a limited number of internal checks. + inline void resize_copy(label count, const label len); + //- Alter addressable size and set val for \em all addressed entries inline void resize_fill(const label len, const T& val); diff --git a/src/OpenFOAM/fields/Fields/DynamicField/DynamicFieldI.H b/src/OpenFOAM/fields/Fields/DynamicField/DynamicFieldI.H index 67ced92b34..602d4aae99 100644 --- a/src/OpenFOAM/fields/Fields/DynamicField/DynamicFieldI.H +++ b/src/OpenFOAM/fields/Fields/DynamicField/DynamicFieldI.H @@ -52,9 +52,9 @@ inline void Foam::DynamicField::doAssignDynList template -inline void Foam::DynamicField::doCapacity +inline void Foam::DynamicField::doCapacity_copy ( - const bool nocopy, + label count, const label newCapacity ) { @@ -66,16 +66,22 @@ inline void Foam::DynamicField::doCapacity // Addressable length, possibly truncated by new capacity const label currLen = Foam::min(List::size(), newCapacity); + // The count truncated by the new addressable range + if (count > currLen) + { + count = currLen; + } + // Consistent allocated sizing List::setAddressableSize(capacity_); - if (nocopy) + if (count > 0) { - List::resize_nocopy(newCapacity); + List::resize_copy(count, newCapacity); } else { - List::resize_copy(currLen, newCapacity); + List::resize_nocopy(newCapacity); } capacity_ = List::size(); @@ -84,9 +90,9 @@ inline void Foam::DynamicField::doCapacity template -inline void Foam::DynamicField::doReserve +inline void Foam::DynamicField::doReserve_copy ( - const bool nocopy, + label count, const label len ) { @@ -95,6 +101,12 @@ inline void Foam::DynamicField::doReserve // Preserve addressed size const label currLen = List::size(); + // The count truncated by the addressable range + if (count > currLen) + { + count = currLen; + } + // Consistent allocated sizing List::setAddressableSize(capacity_); @@ -102,13 +114,13 @@ inline void Foam::DynamicField::doReserve capacity_ = Foam::ListPolicy::reserve_size(len, capacity_); - if (nocopy) + if (count > 0) { - List::resize_nocopy(capacity_); + List::resize_copy(count, capacity_); } else { - List::resize_copy(currLen, capacity_); + List::resize_nocopy(capacity_); } capacity_ = List::size(); @@ -117,18 +129,6 @@ inline void Foam::DynamicField::doReserve } -template -inline void Foam::DynamicField::doResize -( - const bool nocopy, - const label len -) -{ - this->doReserve(nocopy, len); - List::setAddressableSize(len); -} - - // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // template @@ -342,7 +342,7 @@ inline Foam::DynamicField::DynamicField ) : Field(), - capacity_(content.size()) + capacity_(0) { if (reuse) { @@ -352,6 +352,7 @@ inline Foam::DynamicField::DynamicField { Field::operator=(content); } + capacity_ = Field::size(); } @@ -424,7 +425,8 @@ inline void Foam::DynamicField::setCapacity const label len ) { - this->doCapacity(false, len); // nocopy = false + // Preserve all content + doCapacity_copy(List::size(), len); } @@ -434,7 +436,8 @@ inline void Foam::DynamicField::setCapacity_nocopy const label len ) { - this->doCapacity(true, len); // nocopy = true + // Preserve 0 content + doCapacity_copy(0, len); } @@ -444,7 +447,8 @@ inline void Foam::DynamicField::reserve const label len ) { - this->doReserve(false, len); // nocopy = false + // Preserve all content + doReserve_copy(List::size(), len); } @@ -454,7 +458,8 @@ inline void Foam::DynamicField::reserve_nocopy const label len ) { - this->doReserve(true, len); // nocopy = true + // Preserve 0 content + doReserve_copy(0, len); } @@ -472,9 +477,12 @@ inline void Foam::DynamicField::reserve_exact // Consistent allocated sizing List::setAddressableSize(capacity_); + // Exact length + capacity_ = len; + // if (!nocopy) { - List::resize_copy(currLen, len); + List::resize_copy(currLen, capacity_); } capacity_ = List::size(); @@ -489,17 +497,21 @@ inline void Foam::DynamicField::resize const label len ) { - this->doResize(false, len); // nocopy = false + // Preserve all content + resize_copy(List::size(), len); } template -inline void Foam::DynamicField::resize_nocopy +inline void Foam::DynamicField::resize_copy ( + label count, const label len ) { - this->doResize(true, len); // nocopy = true + // Preserve count elements + doReserve_copy(count, len); + List::setAddressableSize(len); } @@ -510,11 +522,22 @@ inline void Foam::DynamicField::resize_fill const T& val ) { - this->doResize(true, len); // nocopy = true + resize_nocopy(len); UList::operator=(val); } +template +inline void Foam::DynamicField::resize_nocopy +( + const label len +) +{ + // Preserve 0 content + resize_copy(0, len); +} + + template inline void Foam::DynamicField::resize ( @@ -523,6 +546,8 @@ inline void Foam::DynamicField::resize ) { const label oldLen = List::size(); + + // Preserve all content resize(len); // Fill newly exposed with constant value @@ -546,6 +571,11 @@ inline void Foam::DynamicField::clear() noexcept template inline void Foam::DynamicField::clearStorage() { + // Extra safety...? + if (!List::cdata() && List::empty()) capacity_ = 0; + + // Consistent allocated sizing + List::setAddressableSize(capacity_); List::clear(); capacity_ = 0; } @@ -555,7 +585,6 @@ template inline void Foam::DynamicField::shrink_to_fit() { const label currLen = List::size(); - if (currLen < capacity_) { List::setAddressableSize(capacity_); @@ -571,8 +600,11 @@ Foam::DynamicField::swap(List& other) { if ( - static_cast*>(this) - == static_cast*>(&other) + FOAM_UNLIKELY + ( + static_cast*>(this) + == static_cast*>(&other) + ) ) { return; // Self-swap is a no-op @@ -598,8 +630,11 @@ inline void Foam::DynamicField::swap { if ( - static_cast*>(this) - == static_cast*>(&other) + FOAM_UNLIKELY + ( + static_cast*>(this) + == static_cast*>(&other) + ) ) { return; // Self-swap is a no-op @@ -622,8 +657,11 @@ inline void Foam::DynamicField::swap { if ( - static_cast*>(this) - == static_cast*>(&other) + FOAM_UNLIKELY + ( + static_cast*>(this) + == static_cast*>(&other) + ) ) { return; // Self-swap is a no-op @@ -633,17 +671,19 @@ inline void Foam::DynamicField::swap UList::swap(other); // Swap capacity - const label oldCap = this->capacity(); - const label newCap = other.capacity(); - - this->setCapacity_unsafe(newCap); - other.setCapacity_unsafe(oldCap); + auto old = capacity_; + this->setCapacity_unsafe(other.capacity()); + other.setCapacity_unsafe(old); } template inline void Foam::DynamicField::transfer(List& list) { + // No check for self-assignment (different types) + + // Consistent allocated sizing + List::setAddressableSize(capacity_); Field::transfer(list); capacity_ = Field::size(); } @@ -653,7 +693,7 @@ template template inline void Foam::DynamicField::transfer ( - DynamicList& list + DynamicList& other ) { if @@ -661,19 +701,22 @@ inline void Foam::DynamicField::transfer FOAM_UNLIKELY ( static_cast*>(this) - == static_cast*>(&list) + == static_cast*>(&other) ) ) { return; // Self-assignment is a no-op } - // Consistent allocated sizing - List::setAddressableSize(capacity_); - Field::transfer(static_cast&>(list)); + // Remove storage + this->clearStorage(); - capacity_ = list.capacity(); - list.setCapacity_unsafe(0); // All contents moved + // Swap storage and addressable size + UList::swap(other); + + // Update capacity + capacity_ = other.capacity(); + other.setCapacity_unsafe(0); } @@ -681,7 +724,7 @@ template template inline void Foam::DynamicField::transfer ( - DynamicField& list + DynamicField& other ) { if @@ -689,19 +732,22 @@ inline void Foam::DynamicField::transfer FOAM_UNLIKELY ( static_cast*>(this) - == static_cast*>(&list) + == static_cast*>(&other) ) ) { return; // Self-assignment is a no-op } - // Consistent allocated sizing - List::setAddressableSize(capacity_); - Field::transfer(static_cast&>(list)); + // Remove storage + this->clearStorage(); - capacity_ = list.capacity(); - list.setCapacity_unsafe(0); // All contents moved + // Swap storage and addressable size + UList::swap(other); + + // Update capacity + capacity_ = other.capacity(); + other.setCapacity_unsafe(0); } From c6fc90b629c77ed005e4b61e0c3513e0d693c59e Mon Sep 17 00:00:00 2001 From: Mark Olesen Date: Sun, 28 Sep 2025 11:37:10 +0200 Subject: [PATCH 2/4] ENH: add in-place modification for OCharStream - enables partial overwriting of content - make default construct zero-sized, add reserve_exact() method - improve sizing behaviour of OCharStream. Since char buffers will approach the INT_MAX size more quickly than other content, adapt the following strategy: | Capacity range | Strategy | |--------------------|------------------------------| | 0 < N <= 0.25) | fast growth (2) | | 0.25 < N <= 0.5) | slower growth (1.5) | | 0.5 < N <= 0.75) | very slow growth (1.25) | | 0.75 < N | already large - use max | --- .../test/OCharStream1/Test-OCharStream1.cxx | 17 +- applications/test/OCharStream2/Make/files | 3 + applications/test/OCharStream2/Make/options | 2 + .../test/OCharStream2/Test-OCharStream2.cxx | 473 ++++++++++++++++++ .../db/IOstreams/memory/OCharStream.H | 139 ++++- .../db/IOstreams/memory/OSpanStream.H | 59 ++- .../db/IOstreams/memory/memoryStreamBuffer.H | 162 +++++- 7 files changed, 827 insertions(+), 28 deletions(-) create mode 100644 applications/test/OCharStream2/Make/files create mode 100644 applications/test/OCharStream2/Make/options create mode 100644 applications/test/OCharStream2/Test-OCharStream2.cxx diff --git a/applications/test/OCharStream1/Test-OCharStream1.cxx b/applications/test/OCharStream1/Test-OCharStream1.cxx index b5def1b8fa..2474cfd5df 100644 --- a/applications/test/OCharStream1/Test-OCharStream1.cxx +++ b/applications/test/OCharStream1/Test-OCharStream1.cxx @@ -183,12 +183,25 @@ int main(int argc, char *argv[]) printInfo(obuf); // Overwrite at some position - obuf.stdStream().rdbuf()->pubseekpos(0.60 * obuf.size()); - obuf << "<" << nl << "OVERWRITE" << nl; + if (auto i = obuf.view().find("item5"); i != std::string::npos) + { + // obuf.seek(0.60 * obuf.size()); + obuf.seek(i); + obuf << "" << nl; + } Info<<"after overwrite" << nl; printInfo(obuf); + // Truncate + { + constexpr float fraction = 0.90; + Info<<"truncated at " << (100*fraction) << "% [" + << int(fraction*obuf.size()) << " chars]" << nl; + obuf.seek(fraction*obuf.size()); + printInfo(obuf); + } + Info<< "transfer contents to a List or ICharStream" << nl; // Reclaim data storage from OCharStream -> ICharStream diff --git a/applications/test/OCharStream2/Make/files b/applications/test/OCharStream2/Make/files new file mode 100644 index 0000000000..e2ffcd6203 --- /dev/null +++ b/applications/test/OCharStream2/Make/files @@ -0,0 +1,3 @@ +Test-OCharStream2.cxx + +EXE = $(FOAM_USER_APPBIN)/Test-OCharStream2 diff --git a/applications/test/OCharStream2/Make/options b/applications/test/OCharStream2/Make/options new file mode 100644 index 0000000000..18e6fe47af --- /dev/null +++ b/applications/test/OCharStream2/Make/options @@ -0,0 +1,2 @@ +/* EXE_INC = */ +/* EXE_LIBS = */ diff --git a/applications/test/OCharStream2/Test-OCharStream2.cxx b/applications/test/OCharStream2/Test-OCharStream2.cxx new file mode 100644 index 0000000000..5c66990c41 --- /dev/null +++ b/applications/test/OCharStream2/Test-OCharStream2.cxx @@ -0,0 +1,473 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2025 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 . + +Description + +\*---------------------------------------------------------------------------*/ + +#include "SpanStream.H" +#include "wordList.H" +#include "IOstreams.H" +#include "argList.H" + +#include +#include +#include +#include +#include + +using namespace Foam; + +Ostream& printString(Ostream& os, const char* first, const char* last) +{ + os << '"'; + for (; first != last; (void)++first) + { + os << *first; + } + os << '"'; + + return os; +} + + +Ostream& printView(Ostream& os, const char* first, const char* last) +{ + char buf[4]; + os << label(last-first) << '('; + + for (; first != last; (void)++first) + { + const char c = *first; + + if (isprint(c)) + { + os << c; + } + else if (c == '\t') + { + os << "\\t"; + } + else if (c == '\n') + { + os << "\\n"; + } + else + { + ::snprintf(buf, 4, "%02X", c); + os << "\\x" << buf; + } + } + os << ')'; + + return os; +} + + +Ostream& printView(Ostream& os, std::string_view s) +{ + return printView(os, s.begin(), s.end()); +} + + +Ostream& printView(Ostream& os, const UList& list) +{ + return printView(os, list.begin(), list.end()); +} + + +Ostream& writeList(Ostream& os, const UList& list) +{ + return printView(os, list); +} + + +Ostream& toString(Ostream& os, const UList& list) +{ + return printString(os, list.begin(), list.end()); +} + + +Ostream& toString(Ostream& os, std::string_view s) +{ + return printString(os, s.begin(), s.end()); +} + + +template +void printInfo(const BufType& buf) +{ + Info<< nl << "=========================" << endl; + buf.print(Info); + Info<< "addr: " << Foam::name(buf.view().data()) << nl; + toString(Info, buf.view()); + Info<< nl << "=========================" << endl; +} + + +// Return a left-padded integer as "word" +template +std::string leftpadded(IntType val, char fillch = ' ') +{ + std::string buf; + buf.resize((std::numeric_limits::digits10+1), fillch); + + auto first = (buf.data()); + auto last = (buf.data() + buf.size()); + + auto result = std::to_chars(first, last, val); + + if (result.ec == std::errc{}) + { + auto* iter = result.ptr; + int count = std::distance(iter, last); + + std::cout << "did not fill: " << count << " chars\n"; + + // With two spaces before comments + if (count > 0) { *iter++ = ' '; --count; } + if (count > 0) { *iter++ = ' '; --count; } + for (char c = (count >= 2 ? '/' : ' '); count > 0; --count) + { + *iter++ = c; + } + } + + return buf; +} + + +template +void leftpad(std::ostream& os, IntType val, char fillch = ' ') +{ + // set fill char and width + os.setf(std::ios_base::left, std::ios_base::adjustfield); + fillch = os.fill(fillch); + os.width(std::numeric_limits::digits10+1); + os << val; + // restore fill char + os.fill(fillch); +} + + +template +void rightpad(std::ostream& os, IntType val, char fillch = ' ') +{ + // set fill char and width + os.setf(std::ios_base::right, std::ios_base::adjustfield); + fillch = os.fill(fillch); + os.width(std::numeric_limits::digits10+1); + os << val; + // restore fill char + os.fill(fillch); +} + + +// Left-padded value with trailing comment slashes +template +void leftpad(ocharstream& os, IntType val) +{ + const auto beg = os.tellp(); + os << val; + int count = (std::numeric_limits::digits10+1) - (os.tellp() - beg); + + // With two spaces before comments + if (count > 0) { os << ' '; --count; } + if (count > 0) { os << ' '; --count; } + for (const char c = (count >= 2 ? '/' : ' '); count > 0; --count) + { + os << c; + } +} + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +// Main program: + +int main(int argc, char *argv[]) +{ + argList::noBanner(); + argList::noParallel(); + argList::addBoolOption("fake-zerosize", "Fake overwriting with zero data"); + argList::addBoolOption("dict-format", "Format as dictionary entry"); + + #include "setRootCase.H" + + const bool optFakeZerosize = args.found("fake-zerosize"); + const bool isDictFormat = args.found("dict-format"); + + // const constexpr int width = (std::numeric_limits