-
Notifications
You must be signed in to change notification settings - Fork 56
/
ConcatBuffer.d
429 lines (275 loc) · 11.5 KB
/
ConcatBuffer.d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
/*******************************************************************************
Class templates for reusable buffers with minimal memory allocation.
Each class has its own detailed description and usage example below.
The difference between this and AppendBuffer is that AppendBuffer basically
wraps a dynamic array and keeps track of the length, while this class
provides a way to store multiple arrays by appending them into a single
buffer. The basic ConcatBuffer class returns the slices to the appended
arrays from its 'add' method. The extended SliceBuffer class internally
keeps track of the appended slices, and offers opIndex and opApply methods
over them.
Copyright:
Copyright (c) 2009-2016 dunnhumby Germany GmbH.
All rights reserved.
License:
Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
Alternatively, this file may be distributed under the terms of the Tango
3-Clause BSD License (see LICENSE_BSD.txt for details).
*******************************************************************************/
module ocean.util.container.ConcatBuffer;
import ocean.meta.types.Qualifiers;
import ocean.core.Array : removeShift;
/*******************************************************************************
Concat buffer class template.
Params:
T = element type of buffer
This template is useful for situations where you want to be able to
repeatedly fill and empty a buffer of type T[] without recurring memory
allocation. The buffer will grow over time to the required maximum size, and
then will no longer allocate memory.
ConcatBuffer classes also avoid modifying the .length property of the
internal buffer, which has been observed to be costly, even when extending
an array's length to <= its previous length.
Internally the class stores a single buffer. If an item is added which does
not fit in the currently allocated buffer, then a new expanded buffer is
newed and replaces the old buffer. This means that the old buffer still
exists in memory, and will not be garbage collected until there are no more
references to it. As a result of this behaviour, any slices remaining to the
previous buffer may still safely be used. Only at the point where all these
slices no longer reference the old buffer will it be garbage collected.
Usage example:
---
import ocean.util.container.ConcatBuffer;
// Create a concat buffer
auto buff = new ConcatBuffer!(char);
// Repeatedly...
while ( application_running )
{
// Empty the buffer
buff.clear();
// Add stuff to the buffer
buff.add("hello");
buff.add("world");
}
---
*******************************************************************************/
public class ConcatBuffer ( T )
{
/***************************************************************************
Data buffer.
***************************************************************************/
private T[] buffer;
/***************************************************************************
Current write position in the buffer.
***************************************************************************/
private size_t write_pos;
/***************************************************************************
Constructor.
Params:
len = initial buffer length
***************************************************************************/
public this ( size_t len = 0 )
{
this.buffer.length = len;
}
/***************************************************************************
Appends a new piece of data to the end of the buffer.
Params:
data = data to append to buffer
Returns:
in-place slice to the location in the buffer where the new item was
appended
***************************************************************************/
public T[] add ( const(T)[] data )
{
return this.add(data.length)[] = data[];
}
/***************************************************************************
Reserves a new piece of data at the end of the buffer.
Params:
length = amount of bytes to reserve
Returns:
in-place slice to the reserved data in the buffer
***************************************************************************/
public T[] add ( size_t length )
{
if ( this.write_pos + length > this.buffer.length )
{
this.buffer = new T[this.buffer.length + length];
this.write_pos = 0;
}
auto start = this.write_pos;
auto end = start + length;
this.write_pos = end;
return this.buffer[start .. end];
}
/***************************************************************************
Returns:
the number of elements which the currently allocated buffer can
contain
***************************************************************************/
public size_t dimension ( )
{
return this.buffer.length;
}
/***************************************************************************
Empties the buffer.
***************************************************************************/
public void clear ( )
{
this.write_pos = 0;
}
}
/*******************************************************************************
Slice buffer class template. Extends ConcatBuffer, encapsulating a buffer
with a list of slices to the concatenated items.
Params:
T = element type of buffer
This template is useful for situations where you need to build up a list of
arrays of type T[], and be able to repeatedly fill and empty the list
without recurring memory allocation. Note that once an item is added to the
buffer, it is *not* possible to modify its length, as each item is only
stored as a slice (though it is possible to modify the contents of a slice).
(For situations where you want to be able to modify the lengths of the
individual arrays after adding them to the collection, a Pool of structs
containing arrays would be a suitable solution -- see
ocean.core.ObjectPool.)
Usage example:
---
import ocean.util.container.ConcatBuffer;
// Create a slice buffer
auto buff = new SliceBuffer!(char);
// Repeatedly...
while ( application_running )
{
// Empty the buffer
buff.clear();
// Add stuff to the buffer
buff.add("hello");
buff.add("world");
// Iterate over the items in the buffer
foreach ( index, item; buff )
{
}
}
---
*******************************************************************************/
public class SliceBuffer ( T ) : ConcatBuffer!(T)
{
/***************************************************************************
List of slices into the buffer content. A slice is added to the list
each time an item is added to the buffer.
***************************************************************************/
private T[][] slices;
/***************************************************************************
Constructor.
Params:
len = initial buffer length
***************************************************************************/
public this ( size_t len = 0 )
{
super(len);
}
/***************************************************************************
Appends a new piece of data to the end of the buffer. The item is also
added to the slices list.
Params:
data = data to append to buffer
Returns:
in-place slice to the location in the buffer where the new item was
appended
***************************************************************************/
override public T[] add ( const(T)[] data )
{
auto slice = super.add(data);
this.slices ~= slice;
return slice;
}
/***************************************************************************
Removes an indexed item in the items list, maintaining the order of the
list.
Note that the item's content in the buffer is *not* removed, the item is
simply removed from the list of slices.
Beware that this removal involves a call to memmove.
Params:
index = index of item to remove
Returns:
indexed item
Throws:
out of bounds exception if index is > the number of items added to
the buffer
***************************************************************************/
public T[] removeSlice ( size_t index )
{
auto slice = this.slices[index];
this.slices.removeShift(index);
return slice;
}
/***************************************************************************
Empties the buffer.
***************************************************************************/
override public void clear ( )
{
super.clear;
this.slices.length = 0;
assumeSafeAppend(this.slices);
}
/***************************************************************************
Returns:
the number of items added to the buffer
***************************************************************************/
public size_t length ( )
{
return this.slices.length;
}
/***************************************************************************
Gets an indexed item in the items list.
Params:
index = index of item to get
Returns:
indexed item
Throws:
out of bounds exception if index is > the number of items added to
the buffer
***************************************************************************/
public T[] opIndex ( size_t index )
{
return this.slices[index];
}
/***************************************************************************
Get the stored slices.
Returns:
The currently stored slices.
***************************************************************************/
public T[][] opSlice ( )
{
return this.slices;
}
/***************************************************************************
foreach iterator over the items which have been added to the buffer.
***************************************************************************/
public int opApply ( scope int delegate ( ref T[] ) dg )
{
int res;
foreach ( slice; this.slices )
{
res = dg(slice);
if ( res ) break;
}
return res;
}
/***************************************************************************
foreach iterator over the items which have been added to the buffer and
their indices.
***************************************************************************/
public int opApply ( scope int delegate ( ref size_t, ref T[] ) dg )
{
int res;
foreach ( i, slice; this.slices )
{
res = dg(i, slice);
if ( res ) break;
}
return res;
}
}