2010-12-18 00:58:19 +01:00
|
|
|
|
/*
|
|
|
|
|
|
AllocationCluster(Test) - verify bulk (de)allocating a family of objects
|
|
|
|
|
|
|
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
|
|
|
|
Copyright (C)
|
|
|
|
|
|
2008, Hermann Vosseler <Ichthyostega@web.de>
|
2010-12-18 00:58:19 +01:00
|
|
|
|
|
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
|
|
|
|
**Lumiera** is free software; you can redistribute it and/or modify it
|
|
|
|
|
|
under the terms of the GNU General Public License as published by the
|
|
|
|
|
|
Free Software Foundation; either version 2 of the License, or (at your
|
|
|
|
|
|
option) any later version. See the file COPYING for further details.
|
2010-12-18 00:58:19 +01:00
|
|
|
|
|
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
|
|
|
|
* *****************************************************************/
|
2010-12-18 00:58:19 +01:00
|
|
|
|
|
2017-02-22 01:54:20 +01:00
|
|
|
|
/** @file allocation-cluster-test.cpp
|
2017-02-22 03:17:18 +01:00
|
|
|
|
** unit test \ref AllocationCluster_test
|
2016-11-03 18:20:10 +01:00
|
|
|
|
*/
|
|
|
|
|
|
|
2010-12-18 00:58:19 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "lib/test/run.hpp"
|
|
|
|
|
|
#include "lib/test/test-helper.hpp"
|
|
|
|
|
|
#include "lib/allocation-cluster.hpp"
|
2024-05-15 19:59:05 +02:00
|
|
|
|
#include "lib/test/diagnostic-output.hpp"/////////////////TODO
|
2024-05-27 23:35:59 +02:00
|
|
|
|
#include "lib/format-util.hpp"/////////////TODO
|
2024-05-15 19:59:05 +02:00
|
|
|
|
#include "lib/iter-explorer.hpp"
|
|
|
|
|
|
#include "lib/util.hpp"
|
2010-12-18 00:58:19 +01:00
|
|
|
|
|
2024-05-15 19:59:05 +02:00
|
|
|
|
#include <functional>
|
2024-05-27 23:35:59 +02:00
|
|
|
|
#include <limits>
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
#include <array>
|
|
|
|
|
|
#include <set>
|
2010-12-18 00:58:19 +01:00
|
|
|
|
|
2024-05-25 20:01:23 +02:00
|
|
|
|
using ::Test;
|
2024-05-15 19:59:05 +02:00
|
|
|
|
using lib::explore;
|
2010-12-18 00:58:19 +01:00
|
|
|
|
using lib::test::showSizeof;
|
2024-11-14 22:10:43 +01:00
|
|
|
|
using util::getAdr;
|
2010-12-18 00:58:19 +01:00
|
|
|
|
using util::isnil;
|
|
|
|
|
|
|
|
|
|
|
|
using std::numeric_limits;
|
2024-05-15 19:59:05 +02:00
|
|
|
|
using std::function;
|
2010-12-18 00:58:19 +01:00
|
|
|
|
using std::vector;
|
2024-05-15 19:59:05 +02:00
|
|
|
|
using std::array;
|
2024-05-19 17:53:51 +02:00
|
|
|
|
using std::byte;
|
2010-12-18 00:58:19 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace lib {
|
|
|
|
|
|
namespace test {
|
|
|
|
|
|
|
|
|
|
|
|
namespace { // a family of test dummy classes
|
|
|
|
|
|
|
2024-05-15 19:59:05 +02:00
|
|
|
|
const uint NUM_CLUSTERS = 5;
|
|
|
|
|
|
const uint NUM_TYPES = 20;
|
|
|
|
|
|
const uint NUM_OBJECTS = 500;
|
2010-12-18 00:58:19 +01:00
|
|
|
|
|
2024-06-19 15:22:26 +02:00
|
|
|
|
const size_t EXTSIZ = AllocationCluster::EXTENT_SIZ;
|
2024-05-19 17:53:51 +02:00
|
|
|
|
|
2024-05-25 20:01:23 +02:00
|
|
|
|
int64_t checksum = 0; // validate proper pairing of ctor/dtor calls
|
2010-12-18 00:58:19 +01:00
|
|
|
|
|
|
|
|
|
|
template<uint i>
|
|
|
|
|
|
class Dummy
|
|
|
|
|
|
{
|
2024-05-15 19:59:05 +02:00
|
|
|
|
static_assert (0 < i);
|
|
|
|
|
|
array<uchar,i> content_;
|
2010-12-18 00:58:19 +01:00
|
|
|
|
|
|
|
|
|
|
public:
|
2024-05-15 19:59:05 +02:00
|
|
|
|
Dummy (uchar id=1)
|
2010-12-18 00:58:19 +01:00
|
|
|
|
{
|
2024-05-15 19:59:05 +02:00
|
|
|
|
content_.fill(id);
|
|
|
|
|
|
checksum += explore(content_).resultSum();
|
2010-12-18 00:58:19 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-05-15 19:59:05 +02:00
|
|
|
|
~Dummy()
|
2010-12-18 00:58:19 +01:00
|
|
|
|
{
|
2024-05-15 19:59:05 +02:00
|
|
|
|
checksum -= explore(content_).resultSum();
|
2010-12-18 00:58:19 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-05-25 20:01:23 +02:00
|
|
|
|
uint getID() { return content_[0]; }
|
2010-12-18 00:58:19 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<uint i>
|
|
|
|
|
|
void
|
2024-05-15 19:59:05 +02:00
|
|
|
|
place_object (AllocationCluster& clu, uchar id)
|
|
|
|
|
|
{
|
|
|
|
|
|
clu.create<Dummy<i>> (id);
|
|
|
|
|
|
}
|
2010-12-18 00:58:19 +01:00
|
|
|
|
|
|
|
|
|
|
|
2024-05-15 19:59:05 +02:00
|
|
|
|
inline array<function<void(AllocationCluster&, uchar)>, NUM_TYPES>
|
|
|
|
|
|
buildTrampoline()
|
|
|
|
|
|
{
|
|
|
|
|
|
return { place_object<1>
|
|
|
|
|
|
, place_object<2>
|
|
|
|
|
|
, place_object<3>
|
|
|
|
|
|
, place_object<5>
|
|
|
|
|
|
, place_object<10>
|
|
|
|
|
|
, place_object<13>
|
|
|
|
|
|
, place_object<14>
|
|
|
|
|
|
, place_object<15>
|
|
|
|
|
|
, place_object<16>
|
|
|
|
|
|
, place_object<17>
|
|
|
|
|
|
, place_object<18>
|
|
|
|
|
|
, place_object<19>
|
|
|
|
|
|
, place_object<20>
|
|
|
|
|
|
, place_object<25>
|
|
|
|
|
|
, place_object<30>
|
|
|
|
|
|
, place_object<35>
|
|
|
|
|
|
, place_object<40>
|
|
|
|
|
|
, place_object<50>
|
|
|
|
|
|
, place_object<100>
|
|
|
|
|
|
, place_object<200>
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
2010-12-18 00:58:19 +01:00
|
|
|
|
|
|
|
|
|
|
void
|
2024-05-15 19:59:05 +02:00
|
|
|
|
fill (AllocationCluster& clu)
|
2010-12-18 00:58:19 +01:00
|
|
|
|
{
|
2024-05-15 19:59:05 +02:00
|
|
|
|
auto invoker = buildTrampoline();
|
2010-12-18 00:58:19 +01:00
|
|
|
|
for (uint i=0; i<NUM_OBJECTS; ++i)
|
2024-11-13 02:23:23 +01:00
|
|
|
|
invoker[rani (NUM_TYPES)] (clu, uchar(i));
|
2010-12-18 00:58:19 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-05-27 23:35:59 +02:00
|
|
|
|
inline uint
|
|
|
|
|
|
sum (uint n) ///< sum of integers 1...N
|
|
|
|
|
|
{
|
|
|
|
|
|
return n*(n+1) / 2;
|
|
|
|
|
|
}
|
2010-12-18 00:58:19 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-10-24 23:06:36 +02:00
|
|
|
|
/*********************************************************************//**
|
2010-12-18 00:58:19 +01:00
|
|
|
|
* @test verify the proper workings of our custom allocation scheme
|
|
|
|
|
|
* managing families of interconnected objects for the segments
|
|
|
|
|
|
* of the low-level model.
|
|
|
|
|
|
*/
|
|
|
|
|
|
class AllocationCluster_test : public Test
|
|
|
|
|
|
{
|
2024-05-15 19:59:05 +02:00
|
|
|
|
virtual void
|
|
|
|
|
|
run (Arg)
|
2010-12-18 00:58:19 +01:00
|
|
|
|
{
|
2024-11-13 02:23:23 +01:00
|
|
|
|
seedRand();
|
|
|
|
|
|
|
2024-05-27 21:21:03 +02:00
|
|
|
|
simpleUsage();
|
|
|
|
|
|
checkLifecycle();
|
2024-05-19 17:53:51 +02:00
|
|
|
|
verifyInternals();
|
|
|
|
|
|
use_as_Allocator();
|
2024-06-13 23:46:17 +02:00
|
|
|
|
dynamicAdjustment();
|
2010-12-18 00:58:19 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
simpleUsage()
|
|
|
|
|
|
{
|
|
|
|
|
|
AllocationCluster clu;
|
2024-05-27 21:21:03 +02:00
|
|
|
|
CHECK (0 == clu.numExtents());
|
2010-12-18 00:58:19 +01:00
|
|
|
|
|
2024-05-15 19:59:05 +02:00
|
|
|
|
char c1(123), c2(45);
|
|
|
|
|
|
Dummy<66>& ref1 = clu.create<Dummy<66>> ();
|
|
|
|
|
|
Dummy<77>& ref2 = clu.create<Dummy<77>> (c1);
|
|
|
|
|
|
Dummy<77>& ref3 = clu.create<Dummy<77>> (c2);
|
2010-12-18 00:58:19 +01:00
|
|
|
|
|
2024-05-15 19:59:05 +02:00
|
|
|
|
//returned references actually point at the objects we created
|
|
|
|
|
|
CHECK (1 ==ref1.getID());
|
2010-12-18 00:58:19 +01:00
|
|
|
|
CHECK (123==ref2.getID());
|
2024-05-15 19:59:05 +02:00
|
|
|
|
CHECK (45 ==ref3.getID());
|
|
|
|
|
|
|
2024-05-27 21:21:03 +02:00
|
|
|
|
CHECK (0 < clu.numExtents());
|
2012-04-30 04:28:16 +02:00
|
|
|
|
|
2024-05-15 19:59:05 +02:00
|
|
|
|
// now use objects and just let them go;
|
2010-12-18 00:58:19 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-05-27 21:21:03 +02:00
|
|
|
|
/** @test Allocation cluster grows when adding objects,
|
|
|
|
|
|
* but discards all objects at once when going out of scope,
|
|
|
|
|
|
* optionally also invoking (or not invoking) destructors.
|
|
|
|
|
|
* @remark no destructors are invoked for any objects allocated
|
|
|
|
|
|
* through the `createDisposable<TY>(args...)` interface,
|
|
|
|
|
|
* or for allocations though the standard allocator adapter.
|
|
|
|
|
|
*/
|
2010-12-18 00:58:19 +01:00
|
|
|
|
void
|
2024-05-15 19:59:05 +02:00
|
|
|
|
checkLifecycle()
|
2010-12-18 00:58:19 +01:00
|
|
|
|
{
|
|
|
|
|
|
CHECK (0==checksum);
|
|
|
|
|
|
{
|
2024-05-15 19:59:05 +02:00
|
|
|
|
vector<AllocationCluster> clusters (NUM_CLUSTERS);
|
|
|
|
|
|
for (auto& clu : clusters)
|
|
|
|
|
|
fill(clu);
|
2010-12-18 00:58:19 +01:00
|
|
|
|
CHECK (0!=checksum);
|
|
|
|
|
|
}
|
|
|
|
|
|
CHECK (0==checksum);
|
2024-05-27 21:21:03 +02:00
|
|
|
|
|
|
|
|
|
|
int64_t allSum;
|
|
|
|
|
|
{// can also be used without invoking any destructors
|
|
|
|
|
|
AllocationCluster clu;
|
|
|
|
|
|
for (uint i=0; i<NUM_OBJECTS; ++i)
|
|
|
|
|
|
clu.createDisposable<Dummy<223>>();
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (clu.numExtents() == NUM_OBJECTS);
|
|
|
|
|
|
CHECK (checksum == NUM_OBJECTS * 223);
|
|
|
|
|
|
allSum = checksum;
|
|
|
|
|
|
}// Memory discarded here without invoking any destructor....
|
|
|
|
|
|
CHECK (allSum == checksum);
|
|
|
|
|
|
checksum = 0;
|
2010-12-18 00:58:19 +01:00
|
|
|
|
}
|
2024-05-19 17:53:51 +02:00
|
|
|
|
|
|
|
|
|
|
|
2024-05-25 20:01:23 +02:00
|
|
|
|
|
2024-05-19 17:53:51 +02:00
|
|
|
|
/** @test cover some tricky aspects of the low-level allocator
|
|
|
|
|
|
* @remark due to the expected leverage of AllocationCluster,
|
|
|
|
|
|
* an optimised low-level approach was taken on various aspects of storage management;
|
|
|
|
|
|
* the additional metadata overhead is a power of two, exploiting contextual knowledge
|
|
|
|
|
|
* about layout; moreover, a special usage-mode allows to skip invocation of destructors.
|
|
|
|
|
|
* To document these machinations, change to internal data is explicitly verified here.
|
|
|
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
|
|
|
verifyInternals()
|
|
|
|
|
|
{
|
|
|
|
|
|
CHECK (0==checksum);
|
2024-05-25 05:14:36 +02:00
|
|
|
|
long markSum;
|
2024-05-19 17:53:51 +02:00
|
|
|
|
{
|
|
|
|
|
|
AllocationCluster clu;
|
2024-05-24 19:06:33 +02:00
|
|
|
|
// no allocation happened yet
|
2024-05-19 17:53:51 +02:00
|
|
|
|
CHECK (0 == clu.numExtents());
|
|
|
|
|
|
CHECK (0 == clu.numBytes());
|
2024-05-24 18:05:21 +02:00
|
|
|
|
CHECK (nullptr == clu.storage_.pos);
|
|
|
|
|
|
CHECK ( 0 == clu.storage_.rest);
|
2024-05-24 19:06:33 +02:00
|
|
|
|
|
|
|
|
|
|
// build a simple object
|
2024-11-13 02:23:23 +01:00
|
|
|
|
auto& i1 = clu.create<uint16_t> (1 + uint16_t(rani()));
|
2024-05-19 17:53:51 +02:00
|
|
|
|
CHECK (i1 > 0);
|
|
|
|
|
|
CHECK (1 == clu.numExtents());
|
2024-05-24 18:05:21 +02:00
|
|
|
|
CHECK (2 == clu.numBytes());
|
|
|
|
|
|
CHECK (clu.storage_.pos != nullptr);
|
2024-05-24 19:06:33 +02:00
|
|
|
|
CHECK (clu.storage_.pos == (& i1) + 1 ); // points directly behind the allocated integer
|
2024-06-19 15:22:26 +02:00
|
|
|
|
CHECK (clu.storage_.rest == EXTSIZ - (2*sizeof(void*) + sizeof(uint16_t)));
|
2024-05-24 18:05:21 +02:00
|
|
|
|
|
2024-05-24 19:06:33 +02:00
|
|
|
|
// Demonstration: how to reconstruct the start of the current extent
|
2024-05-19 17:53:51 +02:00
|
|
|
|
byte* blk = static_cast<std::byte*>(clu.storage_.pos);
|
2024-06-19 15:22:26 +02:00
|
|
|
|
blk += clu.storage_.rest - EXTSIZ;
|
2024-05-24 18:05:21 +02:00
|
|
|
|
CHECK(size_t(blk) < size_t(clu.storage_.pos));
|
2024-05-24 19:06:33 +02:00
|
|
|
|
|
|
|
|
|
|
// some abbreviations for navigating the raw storage blocks...
|
2024-05-24 18:05:21 +02:00
|
|
|
|
auto currBlock = [&]{
|
|
|
|
|
|
byte* blk = static_cast<std::byte*>(clu.storage_.pos);
|
2024-06-19 15:22:26 +02:00
|
|
|
|
blk += clu.storage_.rest - EXTSIZ;
|
2024-05-24 18:05:21 +02:00
|
|
|
|
return blk;
|
|
|
|
|
|
};
|
2024-05-24 19:06:33 +02:00
|
|
|
|
auto posOffset = [&]{
|
2024-05-24 23:27:42 +02:00
|
|
|
|
return size_t(clu.storage_.pos) - size_t(currBlock());
|
2024-05-24 19:06:33 +02:00
|
|
|
|
};
|
2024-05-24 18:05:21 +02:00
|
|
|
|
auto slot = [&](size_t i)
|
|
|
|
|
|
{
|
|
|
|
|
|
size_t* slot = reinterpret_cast<size_t*> (currBlock());
|
|
|
|
|
|
return slot[i];
|
|
|
|
|
|
};
|
2024-05-24 19:06:33 +02:00
|
|
|
|
|
|
|
|
|
|
CHECK (blk == currBlock());
|
2024-05-24 23:27:42 +02:00
|
|
|
|
// current storage pos: 2 »slots« of admin overhead plus the first allocated element
|
2024-05-24 18:05:21 +02:00
|
|
|
|
CHECK (posOffset() == 2 * sizeof(void*) + sizeof(uint16_t));
|
2024-05-24 19:06:33 +02:00
|
|
|
|
CHECK (slot(0) == 0); // only one extent, thus next-* is NULL
|
|
|
|
|
|
|
|
|
|
|
|
// allocate another one
|
|
|
|
|
|
uint16_t i1pre = i1;
|
|
|
|
|
|
auto& i2 = clu.create<uint16_t> (55555);
|
|
|
|
|
|
CHECK (posOffset() == 2 * sizeof(void*) + 2 * sizeof(uint16_t));
|
2024-06-19 15:22:26 +02:00
|
|
|
|
CHECK (clu.storage_.rest == EXTSIZ - posOffset());
|
2024-05-24 19:06:33 +02:00
|
|
|
|
// existing storage unaffected
|
|
|
|
|
|
CHECK (i1 == i1pre);
|
|
|
|
|
|
CHECK (i2 == 55555);
|
2024-05-25 20:01:23 +02:00
|
|
|
|
CHECK (slot(0) == 0); // no administrative data yet...
|
|
|
|
|
|
CHECK (slot(1) == 0);
|
2024-05-24 19:06:33 +02:00
|
|
|
|
|
|
|
|
|
|
// alignment is handled properly
|
|
|
|
|
|
char& c1 = clu.create<char> ('X');
|
|
|
|
|
|
CHECK (posOffset() == 2 * sizeof(void*) + 2 * sizeof(uint16_t) + sizeof(char));
|
|
|
|
|
|
auto& i3 = clu.create<int32_t> (42);
|
|
|
|
|
|
CHECK (posOffset() == 2 * sizeof(void*) + 2 * sizeof(uint16_t) + sizeof(char) + 3*sizeof(byte) + sizeof(int32_t));
|
2024-05-24 23:27:42 +02:00
|
|
|
|
CHECK (i1 == i1pre); // ^^^^Alignment
|
2024-05-24 19:06:33 +02:00
|
|
|
|
CHECK (i2 == 55555);
|
|
|
|
|
|
CHECK (c1 == 'X');
|
|
|
|
|
|
CHECK (i3 == 42);
|
2024-05-24 18:05:21 +02:00
|
|
|
|
CHECK (slot(0) == 0);
|
2024-05-24 23:27:42 +02:00
|
|
|
|
|
|
|
|
|
|
// deliberately fill up the first extent completely
|
|
|
|
|
|
for (uint i=clu.storage_.rest; i>0; --i)
|
|
|
|
|
|
clu.create<uchar> (i);
|
2024-05-25 05:14:36 +02:00
|
|
|
|
CHECK (clu.storage_.rest == 0); // no space left in current extent
|
2024-06-19 15:22:26 +02:00
|
|
|
|
CHECK (posOffset() == EXTSIZ);
|
|
|
|
|
|
CHECK (clu.numBytes() == EXTSIZ - 2*sizeof(void*)); // now using all the rest behind the admin »slots«
|
2024-05-24 23:27:42 +02:00
|
|
|
|
CHECK (clu.numExtents() == 1);
|
|
|
|
|
|
CHECK (slot(0) == 0);
|
|
|
|
|
|
CHECK (blk == currBlock()); // but still in the initial extent
|
|
|
|
|
|
|
|
|
|
|
|
// trigger overflow and allocation of second extent
|
|
|
|
|
|
char& c2 = clu.create<char> ('U');
|
|
|
|
|
|
CHECK (blk != currBlock()); // allocation moved to a new extent
|
2024-11-14 22:10:43 +01:00
|
|
|
|
CHECK (getAdr(c2) == currBlock() + 2*sizeof(void*)); // c2 resides immediately after the two administrative »slots«
|
2024-06-19 15:22:26 +02:00
|
|
|
|
CHECK (clu.storage_.rest == EXTSIZ - posOffset());
|
|
|
|
|
|
CHECK (clu.numBytes() == EXTSIZ - 2*sizeof(void*) + 1); // accounted allocation for the full first block + one byte
|
2024-05-24 23:27:42 +02:00
|
|
|
|
CHECK (clu.numExtents() == 2); // we have two extents now
|
|
|
|
|
|
CHECK (slot(0) == size_t(blk)); // first »slot« of the current block points back to previous block
|
|
|
|
|
|
CHECK (i1 == i1pre);
|
|
|
|
|
|
CHECK (i2 == 55555);
|
|
|
|
|
|
CHECK (c1 == 'X');
|
|
|
|
|
|
CHECK (c2 == 'U');
|
|
|
|
|
|
CHECK (i3 == 42);
|
2024-05-25 05:14:36 +02:00
|
|
|
|
|
|
|
|
|
|
// allocate a "disposable" object (dtor will not be called)
|
|
|
|
|
|
size_t pp = posOffset();
|
|
|
|
|
|
auto& o1 = clu.createDisposable<Dummy<2>> (4);
|
|
|
|
|
|
CHECK (o1.getID() == 4);
|
|
|
|
|
|
markSum = checksum;
|
|
|
|
|
|
CHECK (checksum == 4+4);
|
|
|
|
|
|
CHECK (alignof(Dummy<2>) == alignof(char));
|
2024-05-25 20:01:23 +02:00
|
|
|
|
CHECK (posOffset() - pp == sizeof(Dummy<2>)); // for disposable objects only the object storage itself plus alignment
|
2024-05-25 05:14:36 +02:00
|
|
|
|
|
|
|
|
|
|
// allocate a similar object,
|
|
|
|
|
|
// but this time also enrolling the destructor
|
|
|
|
|
|
pp = posOffset();
|
|
|
|
|
|
auto& o2 = clu.create<Dummy<2>> (8);
|
|
|
|
|
|
CHECK (o2.getID() == 8);
|
|
|
|
|
|
CHECK (checksum == markSum + 8+8);
|
2024-05-25 19:27:17 +02:00
|
|
|
|
CHECK (posOffset() - pp > sizeof(Dummy<2>) + 2*sizeof(void*));
|
|
|
|
|
|
CHECK (slot(1) > 0);
|
|
|
|
|
|
CHECK (size_t(&o2) - slot(1) == 2*sizeof(void*)); // Object resides in a Destructor frame,
|
|
|
|
|
|
using Dtor = AllocationCluster::Destructor; // ... which has been hooked up into admin-slot-1 of the current extent
|
|
|
|
|
|
auto dtor = (Dtor*)slot(1);
|
|
|
|
|
|
CHECK (dtor->next == nullptr);
|
|
|
|
|
|
|
|
|
|
|
|
// any other object with non-trivial destructor....
|
|
|
|
|
|
string rands = lib::test::randStr(9);
|
|
|
|
|
|
pp = posOffset();
|
2024-05-25 20:01:23 +02:00
|
|
|
|
string& s1 = clu.create<string> (rands); // a string that fits into the small-string optimisation
|
|
|
|
|
|
CHECK (s1 == rands);
|
|
|
|
|
|
|
2024-05-25 19:27:17 +02:00
|
|
|
|
CHECK (posOffset() - pp >= sizeof(string) + 2*sizeof(void*));
|
|
|
|
|
|
CHECK (size_t(&s1) - slot(1) == 2*sizeof(void*)); // again the Destructor frame is placed immediately before the object
|
|
|
|
|
|
auto dtor2 = (Dtor*)slot(1); // and it has been prepended to the destructors-list in current extent
|
|
|
|
|
|
CHECK (dtor2->next == dtor); // with the destructor of o2 hooked up behind
|
|
|
|
|
|
CHECK (dtor->next == nullptr);
|
2024-05-25 20:01:23 +02:00
|
|
|
|
|
|
|
|
|
|
// provoke overflow into a new extent
|
|
|
|
|
|
// by placing an object that does not fit
|
|
|
|
|
|
// into the residual space in current one
|
|
|
|
|
|
auto& o3 = clu.create<Dummy<223>> (3);
|
|
|
|
|
|
CHECK (clu.numExtents() == 3); // a third extent has been opened to accommodate this object
|
|
|
|
|
|
CHECK (checksum == markSum + 8+8 + uchar(223*3));
|
|
|
|
|
|
auto dtor3 = (Dtor*)slot(1);
|
|
|
|
|
|
CHECK (dtor3->next == nullptr); // Destructors are chained for each extent separately
|
|
|
|
|
|
CHECK (dtor3 != dtor2);
|
|
|
|
|
|
CHECK (dtor2->next == dtor); // the destructor chain from previous extent is also still valid
|
|
|
|
|
|
CHECK (dtor->next == nullptr);
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (i1 == i1pre); // all data is intact (no corruption)
|
|
|
|
|
|
CHECK (s1 == rands);
|
|
|
|
|
|
CHECK (i2 == 55555);
|
|
|
|
|
|
CHECK (c1 == 'X');
|
|
|
|
|
|
CHECK (c2 == 'U');
|
|
|
|
|
|
CHECK (i3 == 42);
|
|
|
|
|
|
CHECK (o1.getID() == 4);
|
|
|
|
|
|
CHECK (o2.getID() == 8);
|
|
|
|
|
|
CHECK (o3.getID() == 3);
|
2024-05-19 17:53:51 +02:00
|
|
|
|
}
|
2024-05-25 20:01:23 +02:00
|
|
|
|
CHECK (checksum == markSum); // only the destructor of the "disposable" object o1 was not invoked
|
2024-05-19 17:53:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-05-25 20:01:23 +02:00
|
|
|
|
|
2024-05-27 23:35:59 +02:00
|
|
|
|
template<typename X>
|
|
|
|
|
|
using Allo = AllocationCluster::Allocator<X>;
|
|
|
|
|
|
|
|
|
|
|
|
/** @test demonstrate use as Standard-Allocator
|
|
|
|
|
|
* - define a vector, string and set to use the AllocationCluster as backend
|
|
|
|
|
|
* - fill the vector with numbers and the set with random strings
|
|
|
|
|
|
* @note the extent size (hard coded as of 5/24) imposes a serious limitation
|
|
|
|
|
|
* regarding usable data structures; e.g. the std::deque immediately attempts
|
|
|
|
|
|
* to allocate a node buffer with >500 bytes, which is not supported by
|
|
|
|
|
|
* the current (rather simplistic) storage manager in AllocationCluster.
|
2024-05-19 17:53:51 +02:00
|
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
|
|
|
use_as_Allocator()
|
|
|
|
|
|
{
|
2024-05-27 23:35:59 +02:00
|
|
|
|
using VecI = std::vector<uint16_t, Allo<uint16_t>>;
|
|
|
|
|
|
using Strg = std::basic_string<char, std::char_traits<char>, Allo<char>>;
|
|
|
|
|
|
using SetS = std::set<Strg, std::less<Strg>, Allo<Strg>>;
|
|
|
|
|
|
|
|
|
|
|
|
AllocationCluster clu;
|
|
|
|
|
|
CHECK (clu.numExtents() == 0);
|
|
|
|
|
|
|
2024-06-13 23:46:17 +02:00
|
|
|
|
VecI vecI{clu.getAllocator<uint16_t>()};
|
2024-05-27 23:35:59 +02:00
|
|
|
|
|
|
|
|
|
|
// Since vector needs a contiguous allocation,
|
|
|
|
|
|
// the maximum number of elements is limited by the Extent size (256 bytes - 2*sizeof(void*))
|
|
|
|
|
|
// Moreover, the vector grows its capacity; AllocationCluster does not support re-allocation,
|
|
|
|
|
|
// and thus the initial smaller memory chunks will just be abandoned.
|
|
|
|
|
|
const uint MAX = 64;
|
|
|
|
|
|
|
|
|
|
|
|
for (uint i=1; i<=MAX; ++i)
|
2024-06-13 23:46:17 +02:00
|
|
|
|
vecI.push_back(i);
|
2024-05-27 23:35:59 +02:00
|
|
|
|
CHECK (clu.numExtents() == 2);
|
2024-06-13 23:46:17 +02:00
|
|
|
|
CHECK (vecI.capacity() == 64);
|
2024-05-27 23:35:59 +02:00
|
|
|
|
|
|
|
|
|
|
// fill a set with random strings...
|
2024-06-13 23:46:17 +02:00
|
|
|
|
SetS setS{clu.getAllocator<Strg>()};
|
2024-05-27 23:35:59 +02:00
|
|
|
|
|
|
|
|
|
|
for (uint i=0; i<NUM_OBJECTS; ++i)
|
2024-06-13 23:46:17 +02:00
|
|
|
|
setS.emplace (test::randStr(32), clu.getAllocator<char>());
|
|
|
|
|
|
CHECK (setS.size() > 0.9 * NUM_OBJECTS);
|
2024-05-27 23:35:59 +02:00
|
|
|
|
CHECK (clu.numExtents() > 200);
|
|
|
|
|
|
|
|
|
|
|
|
// verify the data in the first allocation is intact
|
2024-06-13 23:46:17 +02:00
|
|
|
|
CHECK (explore(vecI).resultSum() == sum(64));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @test verify the ability to adjust the latest allocation dynamically.
|
|
|
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
|
|
|
dynamicAdjustment()
|
|
|
|
|
|
{
|
|
|
|
|
|
AllocationCluster clu;
|
|
|
|
|
|
auto& l1 = clu.create<array<uchar,12>>();
|
|
|
|
|
|
CHECK (clu.numExtents() == 1);
|
|
|
|
|
|
CHECK (clu.numBytes() == 12);
|
|
|
|
|
|
|
|
|
|
|
|
auto& l2 = clu.create<array<uchar,5>>();
|
|
|
|
|
|
CHECK (clu.numExtents() == 1);
|
|
|
|
|
|
CHECK (clu.numBytes() == 17);
|
|
|
|
|
|
|
|
|
|
|
|
CHECK ( clu.canAdjust (&l2, 5, 8)); // possible since l2 is verifiable as last allocation
|
|
|
|
|
|
CHECK ( clu.canAdjust (&l2, 5, 5)); // arbitrary adjustments are then possible
|
|
|
|
|
|
CHECK ( clu.canAdjust (&l2, 5, 2));
|
|
|
|
|
|
CHECK ( clu.canAdjust (&l2, 5, 0)); // even shrinking to zero
|
|
|
|
|
|
CHECK (not clu.canAdjust (&l1, 12,24)); // but the preceding allocation can not be changed anymore
|
|
|
|
|
|
CHECK (not clu.canAdjust (&l2, 6, 8)); // similarly, reject requests when passing wrong original size
|
|
|
|
|
|
CHECK (not clu.canAdjust (&l2, 4, 8));
|
|
|
|
|
|
CHECK (not clu.canAdjust (&l2, 5, 1000)); // also requests exceeding the remaining extent space are rejected
|
|
|
|
|
|
CHECK ( clu.canAdjust (&l1, 17,24)); // however, can not detect if a passed wrong size accidentally matches
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (clu.numExtents() == 1);
|
|
|
|
|
|
CHECK (clu.numBytes() == 17);
|
|
|
|
|
|
l1[11] = 11; // put some marker values into the storage
|
|
|
|
|
|
l2[0] = 5;
|
|
|
|
|
|
l2[1] = 4;
|
|
|
|
|
|
l2[2] = 3;
|
|
|
|
|
|
l2[3] = 2;
|
|
|
|
|
|
l2[4] = 1;
|
|
|
|
|
|
l2[5] = 55; // yes, even behind the valid range (subscript is unchecked)
|
|
|
|
|
|
l2[6] = 66;
|
|
|
|
|
|
|
|
|
|
|
|
using LERR_(INVALID);
|
|
|
|
|
|
|
|
|
|
|
|
VERIFY_ERROR (INVALID, clu.doAdjust(&l1, 12,24) );
|
|
|
|
|
|
CHECK (clu.numExtents() == 1);
|
|
|
|
|
|
CHECK (clu.numBytes() == 17);
|
|
|
|
|
|
|
|
|
|
|
|
// perform a size adjustment on the latest allocation
|
|
|
|
|
|
clu.doAdjust (&l2, 5,12);
|
|
|
|
|
|
CHECK (clu.numExtents() == 1);
|
|
|
|
|
|
CHECK (clu.numBytes() == 24);
|
|
|
|
|
|
// no memory corruption
|
|
|
|
|
|
CHECK (l1[11] == 11);
|
|
|
|
|
|
CHECK (l2[0] == 5);
|
|
|
|
|
|
CHECK (l2[1] == 4);
|
|
|
|
|
|
CHECK (l2[2] == 3);
|
|
|
|
|
|
CHECK (l2[3] == 2);
|
|
|
|
|
|
CHECK (l2[4] == 1);
|
|
|
|
|
|
CHECK (l2[5] == 55);
|
|
|
|
|
|
CHECK (l2[6] == 66);
|
|
|
|
|
|
|
|
|
|
|
|
// scale down the latest allocation completely
|
|
|
|
|
|
clu.doAdjust (&l2, 12,0);
|
|
|
|
|
|
CHECK (clu.numExtents() == 1);
|
|
|
|
|
|
CHECK (clu.numBytes() == 12);
|
|
|
|
|
|
// no memory corruption
|
|
|
|
|
|
CHECK (l1[11] == 11);
|
|
|
|
|
|
CHECK (l2[0] == 5);
|
|
|
|
|
|
CHECK (l2[1] == 4);
|
|
|
|
|
|
CHECK (l2[2] == 3);
|
|
|
|
|
|
CHECK (l2[3] == 2);
|
|
|
|
|
|
CHECK (l2[4] == 1);
|
|
|
|
|
|
CHECK (l2[5] == 55);
|
|
|
|
|
|
CHECK (l2[6] == 66);
|
2024-05-19 17:53:51 +02:00
|
|
|
|
}
|
2010-12-18 00:58:19 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
LAUNCHER (AllocationCluster_test, "unit common");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}} // namespace lib::test
|