Commit graph

26 commits

Author SHA1 Message Date
1a76fb46f3 Library: elaborate SeveralBuilder operations
Consider what (not) to support.
Notably I decided ''not to support'' moving out of an iterator,
since doing so would contradict the fundamental assumptions of
the »Lumiera Forward Iterator« Concept.

Start verifying some variations of element placement,
still focussing on the simple cases
2024-06-16 04:22:28 +02:00
773325f1bc Library: rearrange strategy code
Parts of the decision logic for element handling was packaged
as separate »strategy« class — but this turned out to be neither
a real abstraction, nor configurable in any way. Thus it is better
to simplify the structure and turn these type predicates into simple
private member functions of the SeveralBuilder itself
2024-06-16 04:22:28 +02:00
6f3bfb5ff3 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.
2024-06-16 04:22:28 +02:00
a3e8579e4a Library: basic functionality of the Several-container working
...passes the simplest unit test
 * create a Several<int>
 * populate from `std::initializer_list`
 * random-access to elements

''next step would be to implement iteration''
2024-06-16 04:22:27 +02:00
8534914c71 Library: work out a solution how to store a Deleter functor
After some fruitless attempts, I settled for using std::function directly,
in order to establish a working baseline of this (tremendously complicated)
allocation logic. Storing a std::function in the ArrayBucket is certainly
wasteful (it costs 4 »slots« of memory), but has the upside that
it handles all those tricky corner cases magically; notably
the functor can be stored completely inline in the most relevant
case where the allocator is a monostate; moreover we bind a lambda,
which can be optimised very effectively, so that in the simplest case
there will be only the single indirection through the ''invoker''.

This **completes the code path for a simple usage cycle**

🠲 ''and hooray ... the test crashes with a double-free''
2024-06-09 23:45:24 +02:00
954a399b1c Library: logic to select a suitable deleter
- ensure the ''deleter function'' is invoked
- care for proper ''deleter'' setup in case of exception while copying
- need to »lock-in« on one specific kind of ''destructor invocation scheme,''
  since we do not keep track of individual concrete element types
2024-06-09 17:19:40 +02:00
54bd568714 Library: integrate storage-management logic
Parts of this logic were first coded down in the `realloc` template method,
where it did not really belong; thus reintegrate similar logic one level above,
in the SeveralBuilder::adjustStorage(). Moreover, for performance reasons,
always start with an initial chunk, similar to what `std::vector` does...
2024-06-09 03:00:12 +02:00
e99f4d531b Library: simplified and generic realloc
since this is meant as a policy implementation, reduce it to the bare operation;
the actual container storage handling logic shall be implemented in the container
and based on those primitive and configurable base operations
2024-06-09 02:06:41 +02:00
3a263c8c63 Library: rearrange logic for trivial move detection
...still fighting to get the design of the `AllocationPolicy`
settled to work well with `AllocationCluster` while also allowing
to handle data types which are (not) trivially copyable.

This changeset attempts to turn the logic round: now we capture
an ''move exclusion flag'' and otherwise allow the Policy to
decide on its own, based on the ''element type''
2024-06-09 00:58:42 +02:00
446f133c09 Library: logic to accept further elements
- verifies if new element can just fit in
- otherwise ensure the storage adjustments are basically possible
- throw exception in case the new element can not be accommodated
- else request possible storage adjustments
- and finally let the allocator place the new element
2024-06-08 17:35:14 +02:00
0a788570a9 Library: integrate strategy for acceptable element types
Draft skeleton of the logic for element creation.
This turns out to be a rather challenging piece of code,
since we have to rely on logical reasoning about properties
of the element types in order to decide if and how these
elements can be emplaced, including the possibility to
re-allocate and move existing data to a new location.

- if we know the exact element type, we can handle any
  copyable or movable object
- however, if the container is filled with a mixture of types,
  we can not re-allocate or grow dynamically, unless all data
  is trivially copyable (and can thus be handled through memmove)
- moreover we must ensure the ability to invoke the proper destructor
2024-06-08 03:52:05 +02:00
bbec35ce65 Library: switch rest of implementation
...and remove now obsolete metadata fields in the collection and builder classes
2024-06-08 02:03:07 +02:00
deaabcda6e Library: adapt allocation and realloc to new layout
significantly simplifies both API and calculations,
since all necessary data is now within the ArrayBucket
2024-06-08 01:50:50 +02:00
130a021020 Library: rearrange storage layout
In-depth analysis of storage management revealed a misconception
with respect to possible storage optimisations, requiring more
metadata fields to handle all corner cases correctly.

It seems prudent to avoid any but the most obvious optimisations
and wait for real-world usage for a better understanding of the
prevalent access patterns. However, in preparation for any future
optimisations, all access coordination and storage metadata is
now relocated into the `ArrayBucket`, and thus resides within the
managed allocation, allowing for localised layout optimisations.

To place this into context: the expected prevalent use case is
for the »Render Nodes Network«, which relies on `AllocationCluster`
for storage management; most nodes will have only a single predecessor
or successor, leading to a large number of lib::Several intsances
populated with a single data element. In such a scenario, it is
indeed rather wasteful to allocate four »slot« of metadata for
each container instance; even more so since most of this
metadata is not even required in such a scenario.
2024-06-08 00:23:42 +02:00
154a7018be 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
2024-06-07 19:04:06 +02:00
24b3e5ceba Library: work out adaptor solution for custom allocator
...turns out to be rather challenging, due to the far reaching requirements
 * the default case (heap allocation) ''must work out-of-the box''
 * optionally a C++ standard conformant `Allocator` can be adapted
 * which works correct even in case this allocator is ''not a monostate''
 * **essential requirement** is to pass an `AllocationCluster` reference directly
 * need a ''generic extension point'' to adapt to similar elaborate custom schemes

__Note__: especially we want to create a direct collaboration between the allocation policy and the underlying allocator to allow support for a dedicate ''realloc operation''
2024-06-07 03:36:25 +02:00
c5d1a7d0df Library: rearrange into standard allocation factory
- code spelled out as intended, according to generic scheme
 - can now encode the »unmanaged« case directly as `null`-deleter,
   because in all other cases a deleter function is mandatory now
 - add default constructor to `ArrayBucket`, detailing the default spread
2024-06-07 01:53:38 +02:00
0e2ca6ee1c Library: consider to align this with the »Factory« concept
even while at first sight only a ''deleter instance'' is required,
it seems prudent to rearrange the code in accordance to the prospective
Allocator / Object Factory concept, and rather try to incorporate
the specifics of the memory layout into this generic view, thereby
abstracting the actual allocator away.

This can be achieved by using a standard-allocator for `std::byte`
as the base allocator and treat each individual element allocator
as a specialised cross-allocator (assuming that this cross adaptation
is actually trivial in almost all cases)
2024-06-07 01:14:55 +02:00
bf74ba6292 Library: sketch for a deleter trampoline
for simple allocators this can be static
2024-06-06 18:41:07 +02:00
802fef9b7c Library: work out Skeleton for memory-handling strategy
- the basic decision is to implement ''realloc'' similar to `std::vector`
- however the situation is complicated by the desire to allow arbitrary element types
- ⟹ must build a strategy based on the properties of the target type
- the completely dynamic growth is only possibly for trivially-movable types
- can introduce a dedicated ''element type'' though, and store a trampolin handler
2024-06-05 02:24:39 +02:00
2abbae77d7 Library: draft memory rearrangements
not clear yet how to handle the classical "realloc" situation
2024-05-29 01:01:16 +02:00
feeee4096d Library: draft skeleton of builder operations
- create by forwarding allocator arguments to policy
- builder-Op to append from iterator
- decide to collapse the ArrayBucket class, since
  access is going through unsafe pointer arithmetic anyway
2024-05-28 18:52:01 +02:00
27b36f0679 Library: implement access to storage and subscript
...not sure if this approach works out OK,
since we can not make a safe downcast to a size known at runtime
2024-05-28 18:07:08 +02:00
f6e4358259 Library: data layout for the new Several container
- favour dynamic polymorphism
- use additional memory for management data alongside the element allocation
- encode a flag and a deleter pointer to enable ownership of the allocation
- inherit base container privately into builder, so the build ends with a slice
2024-05-28 17:20:34 +02:00
73dd24ecef Library: start design draft to replace RefArray
Some decisions
 - use a single template with policy base
 - population via separate builder class
 - implemented similar to vector (start/end)
 - but able to hold larger (subclass) objects
2024-05-28 04:03:51 +02:00
db30da90ce Invocation: consider storage and allocation of fan-in/fan-out
At the time of the initial design attempts, I naively created a
classic interface to describe an fixed container allocated ''elsewhere.''

Meanwhile the C++ language has evolved and this whole idea looks
much more as if it could be a ''Concept'' (C++20). Moreover, having
several implementations of such a container interface is deemed inadequate,
since it would necessitate ''at least two indirections'' — while
going the Concept + Template route would allow to work without any
indirection, given our current understanding that the `ProcNode` itself
is ''not an interface'' — rather a building block.
2024-05-13 18:34:42 +02:00