@@ -57,11 +57,17 @@ unittest
57
57
}
58
58
59
59
/**
60
- * Array based buffer manager. Uses custom allocator to get the data.
60
+ * Array based buffer manager. Uses custom allocator to get the data. Limits
61
+ * growth to doubling.
62
+ *
63
+ * Params:
64
+ * T = The type of the elements the buffer manager will use
65
+ * Allocator = The allocator to use for adding more elements
66
+ * floorSize = The size that can be freely allocated before growth is restricted to 2x.
61
67
*
62
68
* Based on concept by Dmitry Olshansky
63
69
*/
64
- struct BufferManager (T, Allocator = GCNoPointerAllocator)
70
+ struct BufferManager (T, Allocator = GCNoPointerAllocator, size_t floorSize = 8192 )
65
71
{
66
72
import std.experimental.allocator.common : stateSize;
67
73
import std.experimental.allocator : IAllocator, theAllocator;
@@ -156,52 +162,54 @@ struct BufferManager(T, Allocator = GCNoPointerAllocator)
156
162
size_t extend (size_t request)
157
163
{
158
164
import std.algorithm.mutation : copy;
159
- import std.algorithm.comparison : max;
165
+ import std.algorithm.comparison : max, min ;
160
166
import std.traits : hasMember;
167
+
168
+ // check to see if we can "move" the data for free.
169
+ auto validElems = valid - released;
170
+ if (validElems == 0 )
171
+ valid = released = 0 ;
172
+
161
173
if (buffer.length - valid >= request)
162
174
{
175
+ // buffer has enough free space to accomodate.
163
176
valid += request;
164
177
return request;
165
178
}
166
179
167
- auto validElems = valid - released;
168
180
if (validElems + request <= buffer.length)
169
181
{
170
- // can just move the data
182
+ // buffer has enough space if we move the data to the front.
171
183
copy(buffer[released .. valid], buffer[0 .. validElems]);
172
184
released = 0 ;
173
185
valid = validElems + request;
174
186
return request;
175
187
}
176
188
177
189
// otherwise, we must allocate/extend a new buffer
178
-
190
+ // limit growth to 2x.
191
+ immutable maxBufSize = max(buffer.length * 2 , floorSize);
179
192
static if (hasMember! (Allocator, " expand" ))
180
193
{
181
194
// try expanding, no further copying required
182
195
if (buffer.ptr)
183
196
{
184
197
void [] buftmp = cast (void [])buffer;
185
- if (allocator.expand(buftmp, (request - (buffer.length - valid)) * T.sizeof))
198
+ auto reqSize = min(maxBufSize - buffer.length, request - (buffer.length - valid));
199
+ if (allocator.expand(buftmp, reqSize * T.sizeof))
186
200
{
201
+ auto newElems = buffer.length - valid + reqSize;
202
+ valid += newElems;
187
203
buffer = cast (T[])buftmp;
188
- if (validElems == 0 )
189
- {
190
- valid = request;
191
- released = 0 ;
192
- }
193
- else
194
- {
195
- valid += request;
196
- }
197
- return request;
204
+ return newElems;
198
205
}
199
206
}
200
207
}
201
208
202
209
// copy and allocate a new buffer
203
210
auto oldLen = buffer.length;
204
- // grow by at least 1.4
211
+ // grow by at least 1.4, but not more than maxBufSize
212
+ request = min(request, maxBufSize - validElems);
205
213
auto newLen = max(validElems + request, oldLen * 14 / 10 , INITIAL_LENGTH );
206
214
static if (hasMember! (Allocator, " goodAllocSize" ))
207
215
newLen = allocator.goodAllocSize(newLen * T.sizeof) / T.sizeof;
@@ -222,7 +230,7 @@ struct BufferManager(T, Allocator = GCNoPointerAllocator)
222
230
return request;
223
231
}
224
232
private :
225
- enum size_t INITIAL_LENGTH = 128 ;
233
+ enum size_t INITIAL_LENGTH = ( 128 < floorSize ? 128 : floorSize) ;
226
234
T[] buffer;
227
235
size_t valid;
228
236
size_t released;
0 commit comments