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