Library: better alignment handling

Elements maintained within the storage should be placed such
as to comply with their alignment requirements; the element spacing
thus must be increased to be a multiple of the given type's alignment.

This solution works in most common cases, where the alignement is
not larger as the platform's bus width (typically 64bit); but for
''over-aligned types'' this scheme may still generate wrong object
start positions (a completely correct solution would require to
add a fixed offset to the beginning of the storage array and also
to capture the alignment requirements during population and to
re-check for each new type.
This commit is contained in:
Fischlurch 2024-06-11 00:57:24 +02:00
parent 66a1f6f8ab
commit 6f3bfb5ff3
3 changed files with 176 additions and 24 deletions

View file

@ -21,19 +21,31 @@
*/
/** @file several-builder.hpp
** Some (library-) implementations of the RefArray interface.
** Builder to create and populate instances of the lib::Several container.
** For mere usage, inclusion of several.hpp should be sufficient, since the
** container front-end is generic and intends to hide most details of allocation
** and element placement. It is an array-like container, but may hold subclass
** elements, while exposing only a reference to the interface type.
**
** Being an array-like object exposing just a const ref, it is typically used
** on interfaces, and the type of the array "elements" usually is a ABC or interface.
** The actual implementation typically holds a subclass, and is either based on a vector,
** or a fixed storage contained within the implementation. The only price to pay is
** a virtual call on element access.
** # Implementation data layout
**
** For advanced uses it would be possible to have a pointer-array or even an embedded
** storage of variant-records, able to hold a mixture of subclasses. (the latter cases
** will be implemented when needed).
** The front-end container lib::Several<I> is actually just a smart-ptr referring
** to the actual data storage, which resides within an _array bucket._ Typically
** the latter is placed into memory managed by a custom allocator, most notably
** lib::AllocationCluster. However, by default, the ArrayBucket<I> will be placed
** into heap memory. All further meta information is also maintained alongside
** this data allocation, including a _deleter function_ to invoke all element
** destructors and de-allocate the bucket itself. Neither the type of the
** actual elements, nor the type of the allocator is revealed.
**
** @warning WIP and in rework 5/2025 -- not clear yet where this design leads to...
** Since the actual data elements can (optionally) be of a different type than
** the exposed interface type \a I, additional storage and spacing is required
** in the element array. The field ArrayBucket<I>::spread defines this spacing
** and thus the offset used for subscript access.
**
** @todo this is a first implementation solution from 6/2025 and was deemed
** _roughly adequate_ at that time, yet should be revalidated once more
** observations pertaining real-world usage are available...
** @see several-builder-test.cpp
**
*/
@ -79,6 +91,26 @@ namespace lib {
using util::_Fmt;
using std::is_trivially_destructible_v;
/**
* Helper to determine the »spread« required to hold
* elements of type \a TY in memory _with proper alignment._
* @warning assumes that the start of the buffer is also suitably aligned,
* which _may not be the case_ for **over-aligned objects** with
* `alignof(TY) > alignof(void*)`
*/
template<typename TY>
size_t inline constexpr
reqSiz()
{
size_t quant = alignof(TY);
size_t siz = max (sizeof(TY), quant);
size_t req = (siz/quant) * quant;
if (req < siz)
req += quant;
return req;
}
template<class I, template<typename> class ALO>
class ElementFactory
: private ALO<std::byte>
@ -371,7 +403,7 @@ namespace lib {
SeveralBuilder&&
reserve (size_t cntElm)
{
adjustStorage (cntElm, sizeof(E));
adjustStorage (cntElm, reqSiz<E>());
return move(*this);
}
@ -406,19 +438,19 @@ namespace lib {
handling_.template probeMoveCapability<TY>();
// ensure sufficient element capacity or the ability to adapt element spread
if (Coll::spread() < sizeof(TY) and not (Coll::empty() or handling_.canWildMove()))
if (Coll::spread() < reqSiz<TY>() and not (Coll::empty() or handling_.canWildMove()))
throw err::Invalid{_Fmt{"Unable to place element of type %s (size=%d)"
"into container for element size %d."}
% util::typeStr<TY>() % sizeof(TY) % Coll::spread()};
% util::typeStr<TY>() % reqSiz<TY>() % Coll::spread()};
// ensure sufficient storage or verify the ability to re-allocate
if (not (Coll::hasReserve(sizeof(TY))
or POL::canExpand(sizeof(TY))
if (not (Coll::hasReserve(reqSiz<TY>())
or POL::canExpand(reqSiz<TY>())
or not handling_.lock_move))
throw err::Invalid{_Fmt{"Unable to accommodate further element of type %s "}
% util::typeStr<TY>()};
size_t elmSiz = sizeof(TY);
size_t elmSiz = reqSiz<TY>();
size_t newPos = Coll::size();
size_t newCnt = Coll::empty()? INITIAL_ELM_CNT : newPos+1;
adjustStorage (newCnt, max (elmSiz, Coll::spread()));

View file

@ -91,7 +91,7 @@ namespace lib {
Deleter deleter;
/** mark start of the storage area */
alignas(I)
alignas(void*)
std::byte storage[sizeof(I)];

View file

@ -81733,6 +81733,110 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node CREATED="1718035817292" ID="ID_908227037" MODIFIED="1718035826853" TEXT="Verwendungen von sizeof(TY)">
<icon BUILTIN="idea"/>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1718063961925" ID="ID_277616136" MODIFIED="1718064002838" TEXT="naiv ist nicht korrekt &#xd83e;&#xdc32; Vorsicht Falle">
<icon BUILTIN="clanbomber"/>
<node CREATED="1718064017691" ID="ID_725413485" MODIFIED="1718064045946" TEXT="Alignment : Objekt mu&#xdf; an Einheiten dieser Gr&#xf6;&#xdf;e ausgerichtet sein">
<icon BUILTIN="info"/>
</node>
<node CREATED="1718064049623" ID="ID_442869624" MODIFIED="1718064442102">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
unser Storage-Puffer ist alignas(I) &#8212; das kann <b>zu locker</b>&#160;sein f&#252;r das tats&#228;chliche Objekt
</p>
</body>
</html>
</richcontent>
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
<u>Beispiel</u>: <b><font face="Monospaced" color="#560799">I</font></b>&#160;ist eine leere Klasse, hat also sizeof(<b><font face="Monospaced" color="#560799">I</font></b>) &#8801; alignof(<b><font face="Monospaced" color="#560799">I</font></b>) &#8801; 1
</p>
<p>
Wenn's dumm l&#228;uft, kann der Puffer mit jedem beliebigen Byte-Offset beginnen z.B. 3
</p>
<p>
Nun platzieren wir aber eine Subklasse mit alignof(<b><font face="Monospaced" color="#990761">E</font></b>) &#8801; 8 und sizeof(<b><font face="Monospaced" color="#990761">E</font></b>) &#8801; 12
</p>
<p>
&#10233; die naive L&#246;sung beginnt bei offset &#8788; 3 und f&#252;gt f&#252;r jeden Index +12 Bytes hinzu; damit sind <b>alle Objekte grob falsch ausgerichtet</b>
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1718064447034" ID="ID_627454107" MODIFIED="1718064453581" TEXT="korrekt w&#xe4;re...">
<node CREATED="1718064454689" ID="ID_901534814" MODIFIED="1718064478465" TEXT="Anfang des Puffers am Anlignment der Elemente ausrichten"/>
<node CREATED="1718064513825" ID="ID_1012833549" MODIFIED="1718064534602" TEXT="Spread auf das n&#xe4;chst gr&#xf6;&#xdf;ere Vielfache des Element-Alignments aufrunden"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1718064594684" ID="ID_1754699530" MODIFIED="1718064617404" TEXT="Subskript und Platzierung korrekt machen">
<icon BUILTIN="flag-yellow"/>
<node CREATED="1718064618803" ID="ID_371062072" MODIFIED="1718064688256">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
man k&#246;nnte das nun&#160;<i>ungenau </i>oder <i>punktgenau</i>&#160;oder <i>grob korrekt </i>handhaben&#160;
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1718064742835" ID="ID_871080765" MODIFIED="1718064754941" TEXT="&#xbb;grob korrekt&#xab;">
<node CREATED="1718064756929" ID="ID_1870004223" MODIFIED="1718064798319" TEXT="w&#xe4;hle f&#xfc;r den Puffer das Alignment eines void* (&#xbb;slot&#xab;)"/>
<node CREATED="1718064809954" ID="ID_79404905" MODIFIED="1718064835981" TEXT="runde den Spread korrekt auf gem&#xe4;&#xdf; Element-Alignment"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1718064840726" ID="ID_1507935794" MODIFIED="1718065973474" TEXT="Problem: over-aligned objects werden nicht korrekt gehandhabt">
<icon BUILTIN="messagebox_warning"/>
</node>
</node>
<node CREATED="1718064941728" ID="ID_710097250" MODIFIED="1718064947808" TEXT="&#xbb;punktgenau&#xab;">
<node CREATED="1718064950567" ID="ID_1541540344" MODIFIED="1718065006412" TEXT="speichere das Alignment zus&#xe4;tzlich im ArrayBucket"/>
<node CREATED="1718065008351" ID="ID_1578479705" MODIFIED="1718065125011" TEXT="erzeuge einen Offset um das Alignment des Puffer-Start zu korrigieren"/>
<node CREATED="1718065126264" ID="ID_1984046754" MODIFIED="1718065139274" TEXT="runde den Spread korrekt auf gem&#xe4;&#xdf; Element-Alignment"/>
<node CREATED="1718065140862" ID="ID_1650291817" MODIFIED="1718065183602" TEXT="pr&#xfc;fe neben der Gr&#xf6;&#xdf;e auch das Alignment jeden neuen Elements"/>
<node CREATED="1718065924757" ID="ID_488968875" MODIFIED="1718065956907" TEXT="Alignement-&#xc4;nderung ablehnen wenn bereits nicht-verschiebbare Objekte im Container liegen"/>
<node CREATED="1718066029399" ID="ID_1788091685" MODIFIED="1718066036442" TEXT="Schwierigkeiten">
<node CREATED="1718066039054" ID="ID_1592410821" MODIFIED="1718066057551" TEXT="das Schema ist komplex zu implementieren"/>
<node CREATED="1718066058875" ID="ID_524666316" MODIFIED="1718066089035" TEXT="das Alignment mu&#xdf; zwingend gespeichert werden (um &#xc4;nderungen zu erkennen)"/>
<node CREATED="1718066107397" ID="ID_539782163" MODIFIED="1718066130765" TEXT="den Offset sollte man ebenfalls speicheren um den Subscript zu beschleunigen"/>
<node CREATED="1718066219574" ID="ID_712639460" MODIFIED="1718066247894" TEXT="die Rolle des Datenfelds storage wird schwer verst&#xe4;ndlich"/>
</node>
</node>
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1718066280938" ID="ID_1639960105" MODIFIED="1718066299686" TEXT="Beschlu&#xdf;: &#xbb;punktgenau&#xab; wird auf sp&#xe4;ter verschoben">
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
<icon BUILTIN="yes"/>
<node CREATED="1718066307946" ID="ID_1884364301" MODIFIED="1718066372700" TEXT="die m&#xf6;gliche Falle mit overaligned objects wird in Kauf genommen">
<icon BUILTIN="yes"/>
</node>
<node CREATED="1718066332967" ID="ID_1686673435" MODIFIED="1718066368819" TEXT="im Rahmen einer sp&#xe4;teren &#xdc;berarbeitung der Bucket-Storage dar&#xfc;ber nochmal nachdenken">
<icon BUILTIN="yes"/>
</node>
</node>
<node COLOR="#338800" CREATED="1718066378097" ID="ID_1766969093" MODIFIED="1718067200842" TEXT="Anpassungen am Code (&#xbb;grob korrekt&#xab;)">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1718066394480" ID="ID_326301567" MODIFIED="1718067158583" TEXT="Puffer-Alignment &#x27f6; void*">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1718066404826" ID="ID_239748067" MODIFIED="1718067196230" TEXT="Hilfsfunktion reqSiz&lt;TY&gt;()">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1718067163803" ID="ID_1053644006" MODIFIED="1718067198266" TEXT="&quot;sizeof(TY)&quot; &#x27fc; reqSiz&lt;TY&gt;()">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1718067214231" ID="ID_1276439764" LINK="#ID_1830672111" MODIFIED="1718067239581" TEXT="testen">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716901426183" ID="ID_1212967398" MODIFIED="1716901442101" TEXT="Element-Zugriff und Iteration">
@ -81854,8 +81958,8 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1717797674913" ID="ID_1290234834" MODIFIED="1718038739053" TEXT="Iteration">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1717797674913" ID="ID_1290234834" MODIFIED="1718062292947" TEXT="Iteration">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1717797702893" ID="ID_1535587090" MODIFIED="1718040020040" TEXT="Layout">
<icon BUILTIN="button_ok"/>
<node CREATED="1718038819461" ID="ID_834949267" MODIFIED="1718038892881" TEXT="wird wohl eine State-Core-Impl">
@ -81884,11 +81988,24 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="ksmiletris"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1718040049417" ID="ID_360730872" MODIFIED="1718040052952" TEXT="einbauen">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1718040049417" ID="ID_360730872" MODIFIED="1718062160943" TEXT="einbauen">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1717797709076" ID="ID_1453437458" MODIFIED="1718062162753" TEXT="const-Iterator">
<icon BUILTIN="button_ok"/>
<node CREATED="1718062164811" ID="ID_1792709896" MODIFIED="1718062223389">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
einfach ein <font face="Monospaced" color="#68360d">lib::IterIndex&lt;</font><font face="Monospaced" color="#9b1905">const</font><font face="Monospaced" color="#68360d">&#160;</font><font face="Monospaced" color="#7e1040">Several&lt;X&gt;</font><font face="Monospaced" color="#68360d">&gt;</font>
</p>
</body>
</html>
</richcontent>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1717797709076" ID="ID_1453437458" MODIFIED="1717797713403" TEXT="const-Iterator">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
</node>
@ -81928,7 +82045,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node CREATED="1716907312241" ID="ID_835337350" MODIFIED="1716912483271" TEXT="umspannt den Anfang vom Storage-Array"/>
<node COLOR="#338800" CREATED="1716907356091" ID="ID_1876919425" MODIFIED="1716912200686" TEXT="enth&#xe4;lt den subscript-Code">
<icon BUILTIN="button_ok"/>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1716912163674" ID="ID_581123494" MODIFIED="1716912528793" TEXT="mu&#xdf; hier einen offenen Zugriff hinter das Objektende machen">
<node BACKGROUND_COLOR="#dbb34e" COLOR="#7d1d3e" CREATED="1716912163674" ID="ID_581123494" MODIFIED="1718062360235" TEXT="mu&#xdf; hier einen offenen Zugriff hinter das Objektende machen">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
@ -83171,6 +83288,9 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716857382441" ID="ID_1397767362" MODIFIED="1716857402102" TEXT="check_ElementAccess">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1718067224015" ID="ID_1830672111" MODIFIED="1718067235928" TEXT="Alignment-Behandlung verifizieren">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1716857382442" ID="ID_28139752" MODIFIED="1716857402101" TEXT="check_CustomAllocator">
<icon BUILTIN="flag-yellow"/>