Library: rework handling of resize and spread changes

- spread change now retains the nominal element reserve
- `capacity()` and `capReserve()` now exposed on the builder API
- factor out the handling check safety functions
- rewrite the `resize()` builder function to be more generic

__Test now covers__ example with trivial data type, which can
indeed be resized and allows to grow buffer on-the fly without
requiring any knowledge of the actual type (due to using `memmove`)
This commit is contained in:
Fischlurch 2024-06-12 20:35:26 +02:00
parent 89dd35e70d
commit 00287360be
4 changed files with 159 additions and 34 deletions

View file

@ -299,10 +299,15 @@ namespace lib {
/* ===== Builder API ===== */
template<typename TY =E>
SeveralBuilder&&
reserve (size_t cntElm)
reserve (size_t cntElm =1
,size_t elmSiz =reqSiz<TY>())
{
adjustStorage (cntElm, reqSiz<E>());
ensureElementCapacity<TY> (elmSiz);
ensureStorageCapacity<TY> (elmSiz,cntElm);
elmSiz = max (elmSiz, Coll::spread());
adjustStorage (cntElm, elmSiz);
return move(*this);
}
@ -365,8 +370,10 @@ namespace lib {
return move (*this);
}
size_t size() const { return Coll::size(); }
bool empty() const { return Coll::empty();}
size_t size() const { return Coll::size(); }
bool empty() const { return Coll::empty();}
size_t capacity() const { return Coll::storageBuffSiz() / Coll::spread(); }
size_t capReserve() const { return capacity() - size(); }
private:
@ -384,23 +391,9 @@ namespace lib {
{
static_assert (is_object_v<TY> and not (is_const_v<TY> or is_volatile_v<TY>));
// mark when target type is not trivially movable
probeMoveCapability<TY>();
// ensure sufficient element capacity or the ability to adapt element spread
if (Coll::spread() < reqSiz<TY>() and not (Coll::empty() or canWildMove()))
throw err::Invalid{_Fmt{"Unable to place element of type %s (size=%d)"
"into Several-container for element size %d."}
% util::typeStr<TY>() % reqSiz<TY>() % Coll::spread()};
// ensure sufficient storage or verify the ability to re-allocate
if (not (Coll::empty() or Coll::hasReserve(reqSiz<TY>())
or POL::canExpand(reqSiz<TY>())
or canDynGrow()))
throw err::Invalid{_Fmt{"Several-container is unable to accommodate further element of type %s; "
"storage reserve (%s bytes) exhausted and unable to move elements "
"of mixed unknown detail type, which are not trivially movable." }
% util::typeStr<TY>() % Coll::storageBuffSiz()};
probeMoveCapability<TY>(); // mark when target type is not (trivially) movable
ensureElementCapacity<TY>(); // sufficient or able to adapt spread
ensureStorageCapacity<TY>(); // sufficient or able to grow buffer
size_t elmSiz = reqSiz<TY>();
size_t newPos = Coll::size();
@ -424,6 +417,34 @@ namespace lib {
Coll::data_->deleter = deleterFunctor;
}
/** ensure sufficient element capacity or the ability to adapt element spread */
template<class TY>
void
ensureElementCapacity (size_t requiredSiz =reqSiz<TY>())
{
if (Coll::spread() < requiredSiz and not (Coll::empty() or canWildMove()))
throw err::Invalid{_Fmt{"Unable to place element of type %s (size=%d)"
"into Several-container for element size %d."}
% util::typeStr<TY>() % requiredSiz % Coll::spread()};
}
/** ensure sufficient storage reserve or verify the ability to re-allocate */
template<class TY>
void
ensureStorageCapacity (size_t requiredSiz =reqSiz<TY>(), size_t newElms =1)
{
if (not (Coll::empty()
or Coll::hasReserve (requiredSiz, newElms)
or POL::canExpand (requiredSiz*newElms)
or canDynGrow()))
throw err::Invalid{_Fmt{"Several-container is unable to accommodate further element of type %s; "
"storage reserve (%d bytes ≙ %d elms) exhausted and unable to move "
"elements of mixed unknown detail type, which are not trivially movable." }
% util::typeStr<TY>() % Coll::storageBuffSiz() % capacity()};
}
/** possibly grow storage and re-arrange elements to accommodate desired capacity */
void
adjustStorage (size_t cnt, size_t spread)
{
@ -433,9 +454,11 @@ namespace lib {
return;
if (demand > buffSiz)
{// grow into exponentially expanded new allocation
if (spread > Coll::spread())
cnt = max (cnt, buffSiz / Coll::spread()); // retain reserve
size_t safetyLim = LUMIERA_MAX_ORDINAL_NUMBER * Coll::spread();
size_t expandAlloc = min (safetyLim
,max (2*buffSiz, demand));
,max (2*buffSiz, cnt*spread));
if (expandAlloc < demand)
throw err::State{_Fmt{"Storage expansion for Several-collection "
"exceeds safety limit of %d bytes"} % safetyLim

View file

@ -217,10 +217,13 @@ namespace lib {
}
bool
hasReserve (size_t extraSize) const
hasReserve (size_t requiredSize, size_t newElms =1) const
{
if (extraSize > spread())
extraSize += (extraSize - spread())*size();
if (requiredSize < spread())
requiredSize = spread();
size_t extraSize{requiredSize * newElms};
if (requiredSize > spread())
extraSize += (requiredSize - spread())*size();
return data_
and data_->buffSiz >= size()*spread() + extraSize;
}

View file

@ -321,6 +321,8 @@ namespace test{
// but we aren't able to move elements safely any more, since we don't capture the type.
builder.emplace<Num<1>>();
CHECK (20 == builder.size());
CHECK (20 == builder.capacity());
CHECK ( 0 == builder.capReserve());
// But here comes the catch: since we choose to accept arbitrary sub-types not identified in detail,
// the container has lost its ability of move-reallocation; with 20 elements the current reserve
@ -336,6 +338,23 @@ namespace test{
CHECK (elms[i].calc(i) == 5 + i + (5+5+5+5+5));
CHECK (elms.back().calc(0) == 1 + 0 + (1));
}
{ // Scenario-3 : arbitrary elements of trivial type
SeveralBuilder<uint8_t> builder;
string BFR{"starship is cool"};
builder.appendAll (BFR);
CHECK (16 == builder.size());
CHECK ( 4 == builder.capReserve());
builder.append(int64_t(33));
CHECK (17 == builder.size());
CHECK ( 3 == builder.capReserve());
auto elms = builder.build();
SHOW_EXPR(elms.size())
SHOW_EXPR(join(elms, "·"))
}
}

View file

@ -82828,6 +82828,59 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node COLOR="#338800" CREATED="1717893927188" ID="ID_407717086" MODIFIED="1717893962751" TEXT="leeren Container gleich mit einer gr&#xf6;&#xdf;eren Allokation starten">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1718212710737" ID="ID_119846820" MODIFIED="1718212744211" TEXT="bei spread-Vergr&#xf6;&#xdf;erung: Puffer gem&#xe4;&#xdf; bestehender Reserve &#xfc;berdimensionieren">
<icon BUILTIN="button_ok"/>
<node CREATED="1718212749620" ID="ID_1659701829" MODIFIED="1718213228515">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
sonst w&#252;rde eine Spread-Vergr&#246;&#223;erung
</p>
<p>
tats&#228;chlich die Reserve <i>verkleinern...</i>
</p>
</body>
</html>
</richcontent>
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<ul>
<li>
Beispiel:
</li>
<li>
Buffer hat Kapazit&#228;t f&#252;r 10 Elemente bei Spread &#8801; 1 und 3 Elemente sind belegt
</li>
<li>
es wird ein 4.Element der Gr&#246;&#223;e 8 bytes verlangt
</li>
<li>
4*8 = 32 &gt; 10 &#10233; <font face="Monospaced">realloc()</font>
</li>
<li>
naiver weise w&#252;rde man jetzt auf 32 Bytes vergr&#246;&#223;ern, aber danach w&#228;re der Buffer bei Spread &#8801; 8 sofort wieder ganz voll
</li>
<li>
daher ist es sinnvoll, die bisherige Reserve von 7 freien Slots zu beachten; d.h. man vergr&#246;&#223;ert auf 10*8 = 80 bytes
</li>
<li>
danach pa&#223;t das 4. Element rein und es ist nach-wie-vor Platz f&#252;r 6 weitere Elemente
</li>
</ul>
<p>
<u>Begr&#252;ndung</u>: das ganze Thema &#187;spread&#171; ist extrem technisch und f&#252;r den Nutzer normalerweise nicht nachvollziehbar, aber die Kapazit&#228;t in Anzahl der freien Slots ist sehr wohl verst&#228;ndlich f&#252;r den User; es w&#228;re also <i>ziemlich &#252;berraschend</i>&#160;wenn &#8212; scheinbar ohne ersichtlichen Grund &#8212; pl&#246;tzlich die Reserve-Kapazit&#228;t verschwunden w&#228;re.
</p>
</body>
</html>
</richcontent>
</node>
</node>
<node COLOR="#338800" CREATED="1717893967870" ID="ID_1707818601" MODIFIED="1717894043447" TEXT="Spread-Anpassung zweistufig">
<icon BUILTIN="button_ok"/>
<node CREATED="1717893983028" ID="ID_571700268" MODIFIED="1717893999382" TEXT="wenn realloc &#x27f9; passiert implizit"/>
@ -83111,16 +83164,37 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node COLOR="#338800" CREATED="1717634297656" ID="ID_330412196" MODIFIED="1717688579115" TEXT="Move-builder und Container per Slice ausgeben">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716914869721" ID="ID_756769385" MODIFIED="1716914902541" TEXT="basis-Allokation">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1718213506519" ID="ID_1529967473" MODIFIED="1718213512271" TEXT="Informationsfunktionen">
<icon BUILTIN="button_ok"/>
<node CREATED="1718213513318" ID="ID_652722114" MODIFIED="1718213515426" TEXT="size()"/>
<node CREATED="1718213516285" ID="ID_325529445" MODIFIED="1718213517889" TEXT="empty()"/>
<node CREATED="1718213518597" ID="ID_1291451885" MODIFIED="1718213521849" TEXT="capacity()">
<node CREATED="1718213537147" HGAP="44" ID="ID_923597162" MODIFIED="1718213590857" TEXT="Anzahl m&#xf6;gliche Elemente bei aktuellem Spread" VSHIFT="3">
<font NAME="SansSerif" SIZE="11"/>
</node>
</node>
<node CREATED="1718213522557" ID="ID_1863830248" MODIFIED="1718213648344" TEXT="capReserve()">
<node CREATED="1718213537147" HGAP="25" ID="ID_744290406" MODIFIED="1718213620124" TEXT="noch unbelegte Reserve m&#xf6;glicher Elemente" VSHIFT="3">
<font NAME="SansSerif" SIZE="11"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1716914877848" ID="ID_1535626389" MODIFIED="1718217001870" TEXT="Gr&#xf6;&#xdf;en&#xe4;nderung Allokation">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1718216970318" ID="ID_653307930" MODIFIED="1718216999025" TEXT="Vergr&#xf6;&#xdf;erung: reserve&lt;T&gt;(cnt, size)">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1718216875365" ID="ID_808859315" MODIFIED="1718216924186" TEXT="Handling-Checks vorher">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1716936092764" ID="ID_618217173" MODIFIED="1718216922790" TEXT="Objekte einzeln verschieben">
<icon BUILTIN="button_ok"/>
<node CREATED="1718216902576" ID="ID_1819814974" MODIFIED="1718216911891" TEXT="entweder per bekanntem Element-Typ"/>
<node CREATED="1716936101233" ID="ID_1155209653" MODIFIED="1718216920176" TEXT="oder via std::memmove"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716914877848" ID="ID_1535626389" MODIFIED="1716914902541" TEXT="Gr&#xf6;&#xdf;en&#xe4;nderung Allokation">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716914886169" ID="ID_505410227" MODIFIED="1716914902542" TEXT="&#xc4;nderung Spread">
<icon BUILTIN="flag-yellow"/>
<node CREATED="1716936092764" ID="ID_618217173" MODIFIED="1716936100479" TEXT="Objekte einzeln verschieben"/>
<node CREATED="1716936101233" ID="ID_1155209653" MODIFIED="1716936109052" TEXT="per std::memmove"/>
</node>
<node COLOR="#338800" CREATED="1716914892972" ID="ID_1080699305" MODIFIED="1718147544739" TEXT="Platzieren neuer Daten">
<icon BUILTIN="button_ok"/>
@ -83553,9 +83627,15 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
<node COLOR="#435e98" CREATED="1718201899221" ID="ID_1719848161" MODIFIED="1718207782278" TEXT="wenn man eine Subklasse platziert, wird die M&#xf6;glichkeit f&#xfc;r Wachstum beschnitten"/>
</node>
<node CREATED="1718122252659" ID="ID_616405505" MODIFIED="1718122291873" TEXT="Triviale Typen">
<node CREATED="1718122293022" ID="ID_697520417" MODIFIED="1718122302248" TEXT="kann wachsen"/>
<node CREATED="1718122302932" ID="ID_293207594" MODIFIED="1718122311738" TEXT="kann spread &#xe4;ndern"/>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1718122252659" ID="ID_616405505" MODIFIED="1718208089182" TEXT="Triviale Typen">
<icon BUILTIN="pencil"/>
<node COLOR="#435e98" CREATED="1718122293022" ID="ID_697520417" MODIFIED="1718209949804" TEXT="kann wachsen"/>
<node COLOR="#435e98" CREATED="1718122302932" ID="ID_293207594" MODIFIED="1718209952087" TEXT="kann spread &#xe4;ndern">
<node COLOR="#435e98" CREATED="1718211785014" ID="ID_185961323" MODIFIED="1718211802523" TEXT="explizit im Debugger beobachtet">
<font NAME="SansSerif" SIZE="10"/>
<icon BUILTIN="idea"/>
</node>
</node>
<node CREATED="1718153752253" ID="ID_1509145683" MODIFIED="1718153763392" TEXT="kann aber keinen nicht-trivialen Typ platzieren"/>
</node>
<node CREATED="1718202490965" ID="ID_247674037" MODIFIED="1718206085439" TEXT="Handhabung von non-copyable-Objekten">