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     void* getPtr(size_t n)
67     {
68         if (n >= mNbElements)
69             return null;
70         size_t offset = n * mElementSize;
71         return &mData[offset];
72     }
73 
74 private:
75     size_t  mElementSize;
76     size_t  mChunkSize;
77     size_t  mNbChunks;
78     size_t  mMaxElements;
79     size_t  mNbElements;
80     void[]  mData;
81 }
82 
83 class Pool(T, size_t ChunkSize = 8192) : BasePool
84 {
85     this(in size_t n)
86     {
87         super(T.sizeof, ChunkSize);
88         accomodate(n);
89     }
90 
91     ref T opIndex(size_t n)
92     {
93         return *cast(T*)getPtr(n);
94     }
95 
96     static if (!hasConst!T)
97     {
98         T opIndexAssign(T t, size_t n)
99         {
100             *cast(T*)getPtr(n) = t;
101             return t;
102         }
103     }
104 
105     void initN(size_t n)
106     {
107         import std.conv : emplace;
108         emplace(&this[n]);
109     }
110 }
111 
112 
113 //******************************************************************************
114 //***** UNIT-TESTS
115 //******************************************************************************
116 unittest
117 {
118     static struct TestComponent
119     {
120         int    i;
121         string s;
122     }
123 
124     auto pool0 = new Pool!TestComponent(5);
125     auto pool1 = new Pool!ulong(2000);
126 
127     assert(pool0.nbChunks == 1);
128     assert(pool1.nbChunks == (2000 * ulong.sizeof + 8191) / 8192);
129     assert(pool1.getPtr(1) !is null);
130     assert(pool0.getPtr(5) is null);
131 
132     pool0[0].i = 10; pool0[0].s = "hello";
133     pool0[3] = TestComponent(5, "world");
134 
135     assert(pool0[0].i == 10 && pool0[0].s == "hello");
136     assert(pool0[1].i == 0  && pool0[1].s is null);
137     assert(pool0[2].i == 0  && pool0[2].s is null);
138     assert(pool0[3].i == 5  && pool0[3].s == "world");
139     assert(pool0[4].i == 0  && pool0[4].s is null);
140 
141     pool1[1999] = 325;
142     assert(pool1[1999] == 325);
143 }