1 /**
2 Memory pool for component storage.
3 
4 Copyright: © 2015-2016 Claude Merle
5 Authors: Claude Merle
6 License: This file is part of EntitySysD.
7 
8 EntitySysD is free software: you can redistribute it and/or modify it
9 under the terms of the Lesser GNU General Public License as published
10 by the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12 
13 EntitySysD is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 Lesser GNU General Public License for more details.
17 
18 You should have received a copy of the Lesser GNU General Public License
19 along with EntitySysD. If not, see $(LINK http://www.gnu.org/licenses/).
20 */
21 
22 module entitysysd.pool;
23 
24 
25 template hasConst(C)
26 {
27     import std.meta : anySatisfy;
28     import std.traits : RepresentationTypeTuple;
29 
30     enum bool isConst(F) = is(F == const);
31     enum bool hasConst = anySatisfy!(isConst, RepresentationTypeTuple!C);
32 }
33 
34 class BasePool
35 {
36 public:
37     this(size_t elementSize, size_t chunkSize)
38     {
39         mElementSize = elementSize;
40         mChunkSize   = chunkSize;
41     }
42 
43     void accomodate(in size_t nbElements)
44     {
45         while (nbElements > mMaxElements)
46         {
47             mNbChunks++;
48             mMaxElements = (mNbChunks * mChunkSize) / mElementSize;
49         }
50 
51         if (mData.length != mNbChunks * mChunkSize)
52             mData.length = mNbChunks * mChunkSize;
53         mNbElements  = nbElements;
54     }
55 
56     size_t nbElements() @property
57     {
58         return mNbElements;
59     }
60 
61     size_t nbChunks() @property
62     {
63         return mNbChunks;
64     }
65 
66 private:
67     size_t  mElementSize;
68     size_t  mChunkSize;
69     size_t  mNbChunks;
70     size_t  mMaxElements;
71     size_t  mNbElements;
72     ubyte[] mData;
73 }
74 
75 class Pool(T, size_t ChunkSize = 8192) : BasePool
76 {
77     this(in size_t n)
78     {
79         super(T.sizeof, ChunkSize);
80         accomodate(n);
81     }
82 
83     ref T opIndex(size_t n)
84     {
85         return *getPtr(n);
86     }
87 
88     static if (!hasConst!T)
89     {
90         T opIndexAssign(T t, size_t n)
91         {
92             *getPtr(n) = t;
93             return t;
94         }
95     }
96 
97     void initN(size_t n)
98     {
99         import std.conv : emplace;
100         emplace(getPtr(n));
101     }
102 
103     T* getPtr(in size_t n)
104     {
105         if (n >= mNbElements)
106             return null;
107         size_t offset = n * mElementSize;
108         return cast(T*)&mData[offset];
109     }
110 
111     T* ptr() @property
112     {
113         return cast(T*)mData.ptr;
114     }
115 
116 }
117 
118 
119 //******************************************************************************
120 //***** UNIT-TESTS
121 //******************************************************************************
122 unittest
123 {
124     static struct TestComponent
125     {
126         int    i;
127         string s;
128     }
129 
130     auto pool0 = new Pool!TestComponent(5);
131     auto pool1 = new Pool!ulong(2000);
132 
133     assert(pool0.nbChunks == 1);
134     assert(pool1.nbChunks == (2000 * ulong.sizeof + 8191) / 8192);
135     assert(pool1.getPtr(1) !is null);
136     assert(pool0.getPtr(5) is null);
137 
138     pool0[0].i = 10; pool0[0].s = "hello";
139     pool0[3] = TestComponent(5, "world");
140 
141     assert(pool0[0].i == 10 && pool0[0].s == "hello");
142     assert(pool0[1].i == 0  && pool0[1].s is null);
143     assert(pool0[2].i == 0  && pool0[2].s is null);
144     assert(pool0[3].i == 5  && pool0[3].s == "world");
145     assert(pool0[4].i == 0  && pool0[4].s is null);
146 
147     pool1[1999] = 325;
148     assert(pool1[1999] == 325);
149 
150 }