Commit graph

29 commits

Author SHA1 Message Date
ead494e465 Invocation: Argument-spec evaluation sufficiently complete for now
Some additional tests to challenge the parser, which seems to work well.
Without extended analysis into the usage of those node specifications,
it is pointless to expand further on its capabilities. For now, it is
sufficient to have a foundation for hash-computation in place.

__Note__: found a nifty way to give lib::Several an easy toString rendering,
without cranking up the header inclusion load.
2025-02-02 17:22:16 +01:00
806db414dd Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
 * there is no entity "Lumiera.org" which holds any copyrights
 * Lumiera source code is provided under the GPL Version 2+

== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''

The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!

The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
7ed8486774 Library: rework detection of ''same object''
We use the memory address to detect reference to ''the same language object.''
While primarily a testing tool, this predicate is also used in the
core application at places, especially to prevent self-assignment
and to handle custom allocations.

It turns out that actually we need two flavours for convenient usage
 - `isSameObject` uses strict comparison of address and accepts only references
 - `isSameAdr` can also accept pointers and even void*, but will dereference pointers
This leads to some further improvements of helper utilities related to memory addresses...
2024-11-15 00:11:14 +01:00
0b9e184fa3 Library: replace usages of rand() in the whole code base
* most usages are drop-in replacements
 * occasionally the other convenience functions can be used
 * verify call-paths from core code to identify usages
 * ensure reseeding for all tests involving some kind of randomness...

__Note__: some tests were not yet converted,
since their usage of randomness is actually not thread-safe.
This problem existed previously, since also `rand()` is not thread safe,
albeit in most cases it is possible to ignore this problem, as
''garbled internal state'' is also somehow „random“
2024-11-13 04:23:46 +01:00
b01fc6e350 Invocation: adjustments to lib::Several to prepare for allocator use
* conduct analysis regarding allocator handling in the Builder
 * turns out we'll have to keep around two different allocators while building
 * ⟹ establish the goal to confine usage of the Node allocator to the lower Levels
 * consequently must open up the `lib::SeveralBuilder` to be usable
   as an intermediary data structure, while building up the target data
 * in the initial design, the `SeveralBuilder` was kept opaque, since
   contents can be expected to be re-located frequently and thus exposing
   elements and taking references could be dangerous — yet this is also
   true for `std::vector` however, so people are assumed to know
   when they want to shoot themselves into their own foot
2024-07-07 16:12:22 +02:00
f632701f48 Library: lib::Several complete and tested (see #473)
As a replacement for the `RefArray` a new generic container
has been implemented and tested, in interplay with `AllocationCluster`
 * the front-end container `lib::Several<I>` exposes only a reference
   to the ''interface type'' `I`, while hiding any storage details
 * data can only be populated through the `lib::SeveralBuilder`
 * a lot of flexibility is allowed for the actual element data types
 * element storage is maintained in a storage extent, managed through
   a custom allocator (defaulting to `std::allocator` ⟹ heap storage)
2024-06-19 19:40:03 +02:00
cf6abf6a3b Library: observe allocator limits on exponential expansion
The `SeveralBuilder` employs the same tactic as `std::vector`,
by over-allocating a reserve buffer, which grows in exponential
increments, to amortise better the costs of re-allocation.

This tactic does not play well with space limited allocators
like `AllocationCluster` however; it is thus necessary to provide
an extension point where the actuall allocator's limitation can be
queried, allowing to use what is available as reserve, but not more.

With these adaptations, a full usage cycle backed by `AllocationCluster`
can be demonstrated, including variations of dynamic allocation adjustment.
2024-06-19 17:35:46 +02:00
39e9ecd90e Library: AllocationCluster and SeveralBuilder logic tweaks
...identified as part of bug investigation

 * make clear that reserve() prepares for an absolute capacity
 * clarify that, to the contrary, ensureStorageCapaciy() means the delta

Moreover, it turns out that the assertion regarding storage limits
triggers frequently while writing the test code; so we can conclude
that the `AllocationCluster` interface lures into allocating without
previous check. Consequently, this check now throws a runtime exception.

As an aside, the size limitation should be accessible on the interface,
similar to `std::vector::max_size()`
2024-06-19 15:45:12 +02:00
7d066a85ee Library: now use AllocationCluster as custom allocator
* this validates usage of the extension point
 * however, there is no special treatment yet,
   and thus a re-alloc leves the previoius block as waste
2024-06-19 01:29:46 +02:00
aacea3c10a Library: lib::Several container now passes test with TrackingAllocator
- decided to allow creating empty lib::Several;
  no need to be overly rigid in this point,
  since it is move-assignable anyway...

- populate with enough elements to provoke several reallocations
  with copying over the existing elements
- precisely calculate and verify the expected allocation size
- verify the use-count due to dedicated allocator instances
  being embedded into both the builder and hidden in the deleter
- move-assign data
- all checksums go to zero at end
2024-06-18 19:09:21 +02:00
50306db164 Library: more stringent deleter logic
The setup for `ArrayBucket` is special, insofar it shell de-allocate itself,
which creates the danger of re-entrant calls, or to the contrary, the danger
to invoke this clean-up function without actually invoking the destructor.

These problems become relevant once the destructor function itself is statefull,
as is the case when embedding a non-trivial, instance bound allocator
to be used for the clean-up work. Using the new `lib::TrackingAllocator`
highlighted this potential problem, since the allocator maintains a use-count.

Thus I decided to move the »destruction mechanics« one level down into
a dedicated and well encapsulated base class; invoking ArrayBucket's destructor
thereby becomes the only way to trigger the clean-up, and even ElementFactory::destroy()
can now safely check if the destructor was already invoked, and otherwise
re-invoke itself through this embedded destructor function. Moreover,
as an additional safety measure, the actual destructor function is now
moved into the local stack frame of the object's destructor call, removing
any possibility for the de-allocation to interfere with the destructor
invokation itself
2024-06-18 18:15:58 +02:00
31c24e0017 Library: investigate discrepancy in allocator
part of the observed deviation stems form bugs in logging and checksum calculation;
but there seems to be a real problem hidden in the allocator usage of the
new component, since the use-cnt of the handle does not drop to zero
2024-06-18 17:20:23 +02:00
09c8c2a29f Library: better handle the alignment issues explicitly
While there might be the possibility to use the magic of the standard library,
it seems prudent rather to handle this insidious problem explicitly,
to make clear what is going on here.

To allow for such explicit alignment handling, I have now changed the
scheme of the storage definition; the actual buffer now starts ''behind''
the `ArrayBucket<I>` object, which thereby becomes a metadata managing header.

__To summarise the problem__: since we are maintaining a dynamically sized buffer,
and since we do not want to expose the actual element type through the
front-end object, we're necessarily bound to perform a raw-memory allocation.
This is denoted in bytes, and thus the allocator can no longer manage
the proper alignment automatically. Rather, we get a storage buffer with
just ''some accidental'' alignment, and we must care to request a sufficient
overhead to be able to shift the actual storage area forward to the next
proper alignment boundary. Obviously this also implies that we must
store this individual padding adjustment somewhere in the metadata,
in order to be able to report the correct size of the block later
on de-allocation.
2024-06-18 03:16:26 +02:00
dc6c8e0858 Library: investigate alignment issues
The solution implemented thus far turns out to be not sufficient
for ''over-aligned-data'', as the raw-allocator can not perform the
''magic work'' because we're exposing only `std::byte` data.
2024-06-17 16:58:07 +02:00
e82dd86b39 Library: reorganise test helpers and cover logging tracker object
...these features are now used quite regularly,
and so a dedicated documentation test seems indicated.

Actually my intention is to add a tracking allocator to these test helpers
(and then to use that to verify the custom allocator usage of `lib::Several`)
2024-06-16 04:22:29 +02:00
3bbdf40c32 Library: verify element placement into storage
...use some pointer arithmetic for this test to verify
some important cases of object placement empirically.

Note: there is possibly a very special problematic case
when ''over aligned objects'' are not placed in accordance
to their alignment requirements. Fixing this problem would
be non-trivial, and thus I have only left a note in #1204
2024-06-16 04:22:28 +02:00
fd1ed7e78f Library: finish coverage of element handling limits and failures
...including the interesting cases where objects are relocated
and the element spread is changed. With the help of the checksum
feature built into the test-dummy objects, the properly balanced
invocation of constructors can be demonstrated


PS: for historical context...
Last week the "Big F**cking Rocket" successfully performed the
test flight 4; both booster and Starship made it back to the
water surface and performed a soft splash-down after decelerating
to speed zero. The Starship was even able to maintain control
in spite of quite some heat damage on the steering flaps.
Yes ... all techies around the world are thrilled...
2024-06-16 04:22:28 +02:00
00287360be 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`)
2024-06-16 04:22:28 +02:00
89dd35e70d Library: cover handling limits for virtual baseclass scenario
building on the preceding analysis, we can now demonstrate that
the container is initially able to grow, but looses this capability
after accepting one element of unknown subclass type...
2024-06-16 04:22:28 +02:00
85e3780a34 Library: reassess logic to reject some types for existing container
`lib::Several` is designed to be highly adaptable, allowing for
several quite distinct usage styles. On the downside, this requires
to perform some checks at runtime only, since the ability to handle
some element depends on specific circumstances.

This is a notable difference to `std::vector`, which is simply not capable
of handling ''non-copyable'' types, even if given an up-front memory reservation.

The last test case provided with the previous changeset did not trigger
an exception, but closer investigation revealed that this is correct,
since in this specific situation the container can accept this object type,
thereby just loosing the ability to move-relocate further objects.

A slightly re-arranged test scenario can be used to demonstrate this fine point.
2024-06-16 04:22:28 +02:00
d9f86ad891 Library: investigate case with known element type
- the test-dummy objects need a `noexcept` move ctor
- **bug** here: need an explicit check to prevent other types
  than the known element type from ''sneaking in''
2024-06-16 04:22:28 +02:00
006809712e Library: some coverage for rejected type placements
The `SeveralBuilder` is very flexible with respect to added elements,
but it will investigate the provided type information and reject any
further build operation that can not be carried out safely.
2024-06-16 04:22:28 +02:00
1169b6272e Library: test coverage for some ''special'' builder usages 2024-06-16 04:22:28 +02:00
601a555e6c Library: builder to add heterogeneous elements
...turns out that we must ensure to pass a plain "object" type
to the standard allocator framework (no const, no references).
Here, ''object in C++ terminology'' means a scalar or record type,
but no functor, no references and no void,
2024-06-16 04:22:28 +02:00
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
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
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