Library: attempt to build a re-alloc on top of the new adapter

...which basically ''seems doable'' now, yet turns up several unsolved problems
- need a way to handle excess storage for the raw allocation
- generally should relocate all metadata into the ArrayBucket
- mismatch at various APIs; must re-think where to pass size explicitly
- unclear yet how and where to pass the actual element type to create
This commit is contained in:
Fischlurch 2024-06-07 19:04:06 +02:00
parent 24b3e5ceba
commit 154a7018be
3 changed files with 296 additions and 34 deletions

View file

@ -43,8 +43,11 @@
#define LIB_SEVERAL_BUILDER_H
#include "lib/error.hpp"
#include "lib/several.hpp"
#include "include/limits.hpp"
#include "lib/iter-explorer.hpp"
#include "lib/format-string.hpp"
#include "lib/util.hpp"
#include <type_traits>
@ -55,6 +58,8 @@
namespace lib {
namespace err = lumiera::error;
using std::vector;
using std::forward;
using std::move;
@ -62,7 +67,9 @@ namespace lib {
namespace {// Allocation management policies
using util::max;
using util::min;
using util::_Fmt;
template<class I, template<typename> class ALO>
class ElementFactory
@ -105,13 +112,13 @@ namespace lib {
using BucketAlloT = typename AlloT::template rebind_traits<Bucket>;
auto bucketAllo = adaptAllocator<Bucket>();
try { BucketAlloT::construct (bucketAllo, loc, spread); }
try { BucketAlloT::construct (bucketAllo, reinterpret_cast<Bucket*> (loc), spread); }
catch(...)
{
AlloT::deallocate (baseAllocator(), loc, storageBytes);
throw;
}
return static_cast<Bucket*> (loc);
return reinterpret_cast<Bucket*> (loc);
};
template<class E, typename...ARGS>
@ -137,8 +144,8 @@ namespace lib {
for (size_t i=0; i<size; ++i)
ElmAlloT::destroy (elmAllo, & bucket->subscript(i));
size_t storageBytes = calcSize (size, bucket->spread);
AlloT::deallocate (baseAllocator(), bucket, storageBytes);
size_t storageBytes = calcSize (size, bucket->spread); ////////////////////////////////////OOO das ist naiv ... es kann mehr Storage sein
AlloT::deallocate (baseAllocator(), reinterpret_cast<std::byte*> (bucket), storageBytes);
};
};
@ -148,12 +155,56 @@ namespace lib {
: ElementFactory<I, ALO>
{
using Fac = ElementFactory<I, ALO>;
using Fac::Fac;
using Bucket = ArrayBucket<I>;
void*
realloc (void* data, size_t oldSiz, size_t newSiz)
using Fac::Fac; // pass-through ctor
Bucket*
realloc (Bucket* data, size_t cnt, size_t& storage, size_t demand)
{
UNIMPLEMENTED ("adjust memory allocation"); ///////////////////////////OOO Problem Objekte verschieben
if (demand == storage)
return data;
if (demand > storage)
{// grow into exponentially expanded new allocation
size_t spread = data? data->spread : sizeof(I);
size_t safetyLim = LUMIERA_MAX_ORDINAL_NUMBER * spread;
size_t expandAlloc = min (safetyLim
,max (2*storage, demand));
if (expandAlloc < demand)
throw err::State{_Fmt{"Storage expansion for Several-collection "
"exceeds safety limit of %d bytes"} % safetyLim
,LERR_(SAFETY_LIMIT)};
// allocate new storage block...
size_t newCnt = demand / spread;
if (newCnt * spread < demand) ++newCnt;
Bucket* newBucket = Fac::create (newCnt, spread);
// move (or copy) existing data...
ENSURE (data or cnt==0);
for (size_t i=0; i<cnt; ++i)
Fac::template createAt<I> (newBucket, i
,std::move_if_noexcept (data->subscript(i)));
////////////////////////////////////////////////////////OOO schee... aba mia brauchn E, ned I !!!!!
// discard old storage
if (data)
Fac::template destroy<I> (data, cnt); /////////////////////////////////////////////OOO Problem mit der überschüssigen Storage
storage = newCnt*spread;
return newBucket;
}
else
{// shrink into precisely fitting new allocation
Bucket* newBucket{nullptr};
if (cnt > 0)
{
REQUIRE (data);
newBucket = Fac::create (cnt, data->spread);
for (size_t i=0; i<cnt; ++i)
Fac::template createAt<I> (newBucket, i
,std::move_if_noexcept (data->subscript(i))); ////////////OOO selbes Problem: E hier
Fac::template destroy<I> (data, cnt); /////////////////////////////////////////////OOO nicht passende Storage!!
storage = cnt * data->spread;
}
return newBucket;
}
}
};
@ -209,8 +260,8 @@ namespace lib {
* Wrap a vector holding objects of a subtype and
* provide array-like access using the interface type.
*/
template<class I ///< Interface or base type visible on resulting Several<I>
,class E =I ///< a subclass element element type (relevant when not trivially movable and destructible)
template<class I ///< Interface or base type visible on resulting Several<I>
,class E =I ///< a subclass element element type (relevant when not trivially movable and destructible)
,class POL =HeapOwn<I> ///< Allocator policy
>
class SeveralBuilder
@ -218,7 +269,7 @@ namespace lib {
, util::MoveOnly
, POL
{
using Col = Several<I>;
using Coll = Several<I>;
size_t storageSiz_{0};
@ -236,7 +287,7 @@ namespace lib {
SeveralBuilder&&
reserve (size_t cntElm)
{
adjustStorage (cntElm, sizeof(I));
adjustStorage (cntElm, sizeof(E));
return move(*this);
}
@ -258,19 +309,24 @@ namespace lib {
void
adjustStorage (size_t cnt, size_t spread)
{
if (cnt*spread > storageSiz_)
size_t demand{cnt*spread};
if (demand > storageSiz_)
{ // need more storage...
Col::data_ = static_cast<typename Col::Bucket> (POL::realloc (Col::data_, storageSiz_, cnt*spread));
storageSiz_ = cnt*spread;
Coll::data_ = static_cast<typename Coll::Bucket> (POL::realloc (Coll::data_, Coll::size_, storageSiz_, demand));
}
ENSURE (Col::data_);
if (spread != Col::data_->spread)
ENSURE (Coll::data_);
if (spread != Coll::data_->spread)
adjustSpread (spread);
if (cnt*spread < storageSiz_)
}
void
fitStorage()
{
if (not Coll::data_) return;
size_t demand{Coll::size_ * Coll::data_->spread};
if (demand < storageSiz_)
{ // attempt to shrink storage
Col::data_ = static_cast<typename Col::Bucket> (POL::realloc (Col::data_, storageSiz_, cnt*spread));
storageSiz_ = cnt*spread;
Coll::data_ = static_cast<typename Coll::Bucket> (POL::realloc (Coll::data_, Coll::size_, storageSiz_, demand));
}
}
@ -278,20 +334,20 @@ namespace lib {
void
adjustSpread (size_t newSpread)
{
REQUIRE (Col::size_);
REQUIRE (Col::data_);
REQUIRE (newSpread * Col::size_ <= storageSiz_);
size_t oldSpread = Col::data_->spread;
REQUIRE (Coll::size_);
REQUIRE (Coll::data_);
REQUIRE (newSpread * Coll::size_ <= storageSiz_);
size_t oldSpread = Coll::data_->spread;
if (newSpread > oldSpread)
// need to spread out
for (size_t i=Col::size_-1; 0<i; --i)
for (size_t i=Coll::size_-1; 0<i; --i)
shiftStorage (i, oldSpread, newSpread);
else
// attempt to condense spread
for (size_t i=1; i<Col::size_; ++i)
for (size_t i=1; i<Coll::size_; ++i)
shiftStorage (i, oldSpread, newSpread);
// data elements now spaced by new spread
Col::data_->spread = newSpread;
Coll::data_->spread = newSpread;
}
void
@ -300,8 +356,8 @@ namespace lib {
REQUIRE (idx);
REQUIRE (oldSpread);
REQUIRE (newSpread);
REQUIRE (Col::data_);
byte* oldPos = Col::data_->storage;
REQUIRE (Coll::data_);
byte* oldPos = Coll::data_->storage;
byte* newPos = oldPos;
oldPos += idx * oldSpread;
newPos += idx * newSpread;
@ -314,14 +370,14 @@ namespace lib {
{
using Val = typename IT::value_type;
size_t elmSiz = sizeof(Val);
adjustStorage (Col::size_+1, requiredSpread(elmSiz));
adjustStorage (Coll::size_+1, requiredSpread(elmSiz));
UNIMPLEMENTED ("emplace data");
}
size_t
requiredSpread (size_t elmSiz)
{
size_t currSpread = Col::empty()? 0 : Col::data_->spread;
size_t currSpread = Coll::empty()? 0 : Coll::data_->spread;
return util::max (currSpread, elmSiz);
}
};

View file

@ -23,7 +23,36 @@
/** @file several.hpp
** Abstraction interface: array-like access by subscript
** Abstraction interface: array-like random access by subscript.
**
** # Design
**
** This is a data structure abstraction suitable for performance critical code.
** It is used pervasively in the backbone of the Lumiera »Render Node Network«.
** - usage is clear and concise, allowing to hide implementation details
** - adaption and optimisation for various usage patterns is possible
** - suitably fast read access with a limited amount of indirections
** \par why not `std::vector`?
** The most prevalent STL container _almost_ fulfils the above mentioned criteria,
** and thus served as a blueprint for design and implementations. Some drawbacks
** however prevent its direct use for this purpose. Notably, `std::vector` leaks
** implementation details of the contained data and generally exposes way too much
** operations; it is not possible to abstract away the concrete element type.
** Moreover, using `vector` with a custom allocator is surprisingly complicated,
** requires to embody the concrete allocator type into the container type and
** requires to store an additional back-link whenever the allocator is not
** a _monostate._ The intended use case calls for a large number of small
** collection elements, which are repeatedly bulk- allocated and deallocated.
**
** The lib::Several container is a smart front-end and exposes array-style
** random access through references to a interface type. It can only be created
** and populated through a builder, and is immutable during lifetime, while it
** can hold non-const element data. The actual implementation data types and the
** allocator framework used are _not exposed in the front-end's type signature._
** The container is single-ownership (move-asignable); some additional metadata
** and the data storage reside in an `ArrayBucket<I>`, managed by the allocator.
** In its simplest form, this storage is heap allocated and automatically deleted.
**
** @todo as of 2016, this concept seems very questionable: do we _really_ want
** to abstract over random access, or do we _actually_ want for-iteration??
** @warning WIP-WIP-WIP in rework 5/2025

View file

@ -82293,6 +82293,37 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node CREATED="1717543083078" ID="ID_530261806" MODIFIED="1717543114805" TEXT="getMover()"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1717777101317" ID="ID_826816346" MODIFIED="1717777105434" TEXT="konkret einbinden">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1717777109755" ID="ID_1395035594" MODIFIED="1717777115683" TEXT="neue Elemente erstellen">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1717777153193" ID="ID_626770308" MODIFIED="1717777157073" TEXT="Element-Typ">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1717777157905" ID="ID_1190162917" MODIFIED="1717777164089" TEXT="beliebiger Typ">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1717777167167" ID="ID_893092417" MODIFIED="1717777178595" TEXT="hier die Einschr&#xe4;nkungen korrekt einarbeiten">
<icon BUILTIN="flag-yellow"/>
</node>
<node CREATED="1717777223888" ID="ID_1213625055" MODIFIED="1717777230003" TEXT="die wild-move-Flag"/>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1717777186269" ID="ID_473170944" MODIFIED="1717777213678" TEXT="can Destroy?">
<icon BUILTIN="flag-pink"/>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1717777198480" ID="ID_158380654" MODIFIED="1717777204412" TEXT="canDynGrow?">
<icon BUILTIN="flag-pink"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1717777116726" ID="ID_1692491444" MODIFIED="1717777127002" TEXT="resize mit Element-Typ">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1717777127619" ID="ID_42350747" MODIFIED="1717777133490" TEXT="resize auf gegebenen Spread">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1717777134500" ID="ID_1083429601" MODIFIED="1717777151106" TEXT="shrink-to-fit">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1717723412216" ID="ID_1483275702" MODIFIED="1717723419487" TEXT="Allokator flexibel einbinden">
@ -82326,6 +82357,39 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="yes"/>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1717776954529" ID="ID_103031227" MODIFIED="1717779551508" TEXT="Basis-Implementierung">
<icon BUILTIN="pencil"/>
<node CREATED="1717776962179" ID="ID_1142886481" MODIFIED="1717776966238" TEXT="wachsen">
<node COLOR="#338800" CREATED="1717776973613" ID="ID_1749910669" MODIFIED="1717776980785" TEXT="exponentielle Erweiterung">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1717776981387" ID="ID_631542593" MODIFIED="1717776987939" TEXT="safety-Limit einbauen">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1717779506814" ID="ID_584325941" MODIFIED="1717779525853" TEXT="Objekte umkopieren">
<linktarget COLOR="#ec1c03" DESTINATION="ID_584325941" ENDARROW="Default" ENDINCLINATION="167;18;" ID="Arrow_ID_1090340504" SOURCE="ID_184907732" STARTARROW="None" STARTINCLINATION="178;0;"/>
<icon BUILTIN="pencil"/>
</node>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1717776994933" ID="ID_916393989" MODIFIED="1717777035492" TEXT="Problem: nicht genau passende Storage">
<arrowlink COLOR="#d84591" DESTINATION="ID_1878698297" ENDARROW="Default" ENDINCLINATION="13;-122;" ID="Arrow_ID_605494253" STARTARROW="None" STARTINCLINATION="-436;31;"/>
<icon BUILTIN="messagebox_warning"/>
</node>
</node>
<node CREATED="1717776966802" ID="ID_1301179349" MODIFIED="1717776969877" TEXT="schrumpfen">
<node COLOR="#338800" CREATED="1717779536089" ID="ID_1997959648" MODIFIED="1717779542019" TEXT="leer-Fall ausschlie&#xdf;en">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1717779506814" ID="ID_1523639170" MODIFIED="1717779525853" TEXT="Objekte umkopieren">
<linktarget COLOR="#ec1c03" DESTINATION="ID_1523639170" ENDARROW="Default" ENDINCLINATION="167;18;" ID="Arrow_ID_15683997" SOURCE="ID_184907732" STARTARROW="None" STARTINCLINATION="178;0;"/>
<icon BUILTIN="pencil"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1717777049607" ID="ID_184907732" MODIFIED="1717779530415" TEXT="brauche den konkreten Zieltyp hier">
<arrowlink COLOR="#ec1c03" DESTINATION="ID_584325941" ENDARROW="Default" ENDINCLINATION="167;18;" ID="Arrow_ID_1090340504" STARTARROW="None" STARTINCLINATION="178;0;"/>
<arrowlink COLOR="#ec1c03" DESTINATION="ID_1523639170" ENDARROW="Default" ENDINCLINATION="167;18;" ID="Arrow_ID_15683997" STARTARROW="None" STARTINCLINATION="178;0;"/>
<icon BUILTIN="flag-pink"/>
</node>
</node>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1717723649872" ID="ID_1412900866" MODIFIED="1717723682406" TEXT="Deleter einbinden?">
<linktarget COLOR="#e1073d" DESTINATION="ID_1412900866" ENDARROW="Default" ENDINCLINATION="344;0;" ID="Arrow_ID_453255140" SOURCE="ID_1791379026" STARTARROW="Default" STARTINCLINATION="344;0;"/>
@ -82336,6 +82400,119 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1717770993835" ID="ID_1878698297" MODIFIED="1717777028572" TEXT="Problem: Gr&#xf6;&#xdf;e der Basis-Allokation">
<linktarget COLOR="#d84591" DESTINATION="ID_1878698297" ENDARROW="Default" ENDINCLINATION="13;-122;" ID="Arrow_ID_605494253" SOURCE="ID_916393989" STARTARROW="None" STARTINCLINATION="-436;31;"/>
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1717771028662" ID="ID_1266328566" MODIFIED="1717771040728" TEXT="diese k&#xf6;nnte....">
<node CREATED="1717771041580" ID="ID_710954788" MODIFIED="1717771046512" TEXT="&#xfc;berdimensioniert sein"/>
<node CREATED="1717771047298" ID="ID_1301246479" MODIFIED="1717771060366" TEXT="nicht genau auf den Element-Spread aufgehen"/>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1717771086306" ID="ID_382189850" MODIFIED="1717771413927" TEXT="mu&#xdf; diesen Wert zur de-Allokation kennen">
<icon BUILTIN="clanbomber"/>
<node CREATED="1717771137791" ID="ID_1321354068" MODIFIED="1717771166027">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
das ist jetzt <b>noch ein weiteres</b>&#160;extra Datenfeld
</p>
</body>
</html>
</richcontent>
<icon BUILTIN="smily_bad"/>
<node CREATED="1717771172737" ID="ID_339804394" MODIFIED="1717771190676" TEXT="das l&#xe4;ge zwar im Bucket in der Storage"/>
<node CREATED="1717771200623" ID="ID_79846525" MODIFIED="1717771208658" TEXT="trotzdem &#xe4;rgerlich">
<node CREATED="1717771319363" ID="ID_165772412" MODIFIED="1717771389855" TEXT="weil es f&#xfc;r den Haupt-Anwendungsfall gar nicht gebraucht wird">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
denn der Allocation-Cluster wei&#223; selber die Zahl seiner belegten Roh-Bl&#246;cke; zur de-Allokation mu&#223; ansonsten gar nichts gemacht werden
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1717771640460" ID="ID_1646507168" MODIFIED="1717771661118" TEXT="weil die Datenstruktur doch so sch&#xf6;n elegant sein sollte....">
<icon BUILTIN="smiley-oh"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1717771668227" ID="ID_218024173" MODIFIED="1717771674057" TEXT="was f&#xfc;r Auswege gibt es?">
<icon BUILTIN="help"/>
<node CREATED="1717771677991" ID="ID_1848656527" MODIFIED="1717771708671" TEXT="das zus&#xe4;tzliche Datenfeld einfach akzeptieren">
<node CREATED="1717771763428" ID="ID_823499935" MODIFIED="1717771767903" TEXT="leicht zu implementieren"/>
<node CREATED="1717771768371" ID="ID_370564159" MODIFIED="1717771775678" TEXT="sicher und klar"/>
<node CREATED="1717771790928" ID="ID_1239859891" MODIFIED="1717772007103" TEXT="Irre verschwenderisch">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Der vermutlich h&#228;ufigste Fall ist ein Several&lt;ProcNode*&gt; &#8212; mit typischerweise genau einem Element mit einem &#187;Slot&#171;Gr&#246;&#223;e. Daf&#252;r haben wir jetzt schon 4 &#187;Slot&#171; Overhead, und jetzt sollen das 5 &#187;Slot&#171; werden....
</p>
</body>
</html>
</richcontent>
</node>
</node>
<node CREATED="1717771727481" ID="ID_1872517693" MODIFIED="1717771751937" TEXT="am Ende des Build-Vorganges stets ein shrink-to-fit machen">
<node CREATED="1717772013611" ID="ID_789464480" MODIFIED="1717772040435" TEXT="im Fall Allocation-Cluster ist das im Regelfall transparent in-place m&#xf6;glich"/>
<node CREATED="1717772041335" ID="ID_871652702" MODIFIED="1717772085852" TEXT="wenn nicht, dann verschwenden wir im AllocationCluster den ganzen bisherigen Platz"/>
<node CREATED="1717772086536" ID="ID_1322940252" MODIFIED="1717772099619" TEXT="und f&#xfc;r Heap-Allokation m&#xfc;ssen wir zumindest noch einmal alles umkopieren"/>
</node>
<node CREATED="1717773260668" ID="ID_1403509932" MODIFIED="1717773690731" TEXT="das ArrayBucket dynamisch flexibel machen">
<icon BUILTIN="forward"/>
<node CREATED="1717773280353" ID="ID_1332303474" MODIFIED="1717773295509" TEXT="alle weiteren Metadaten liegen im ArrayBucket"/>
<node CREATED="1717773296351" ID="ID_129834213" MODIFIED="1717773606110" TEXT="dabei wird ein spezielles optimiertes Placement verwendet">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...das hei&#223;t, wir verwenden im ersten &#187;Slot&#171; ein Bit-Feld, da die Zahl der Elemente ohnehin praktisch begrenzt ist (auf ein paar Hundert); in den freien oberen Bits kann daher das konkrete weitere Layout encodiert werden. Das kostet nur einen minimalen Runtime-Overhead (ein paar Bit-Manipulationen vor der Indirektion zum Datenelement, welches ebenfalls im Cache liegt). Der intendierte use-case nutzt diese Collection als Basis einer verzeigerten Datenstruktur, und nicht in einer innersten Loop.
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1717773313924" ID="ID_1133884189" MODIFIED="1717773348408" TEXT="im wichtigsten Standardfall kann damit die Storage auf 1 &#xbb;slot&#xab; reduziert werden">
<icon BUILTIN="idea"/>
</node>
<node CREATED="1717773611076" ID="ID_569133844" MODIFIED="1717773685833">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
<u>besonderes geschickt</u>: <i>man kann das &#8222;sp&#228;ter mal&#8220; machen</i>
</p>
</body>
</html>
</richcontent>
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Denn Lumiera hat im Moment wirklich andere Sorgen, als die Optimierung einer nocht-gar-nicht-wirklich-genutzten Datenstruktur
</p>
</body>
</html>
</richcontent>
</node>
</node>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1716914864769" ID="ID_966642586" MODIFIED="1716914906125" TEXT="Builder-Operationen">
<icon BUILTIN="pencil"/>