1 /*******************************************************************************
2 
3     Simple pool class template with the following features:
4         * Get and recycle items. Recycled items will be re-used before creating
5           new items.
6         * Fill the free list with a specific number of idle items.
7         * The number of idle items in the free list can be queried.
8 
9     A free list does not manage busy items, it only stores a reference to any
10     idle / free items, making them available for re-use.
11 
12     TODO:
13         - Free lists could support limiting, simply by counting the number of
14           allocated (i.e. busy) items. A count of allocated items would also allow a
15           length() method (with the same meaning as the method in IPool) to be
16           implemented.
17         - Add idle items iterators (safe & unsafe). These could probably share
18           code with the aggregate pool.
19 
20     Copyright:
21         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
22         All rights reserved.
23 
24     License:
25         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
26         Alternatively, this file may be distributed under the terms of the Tango
27         3-Clause BSD License (see LICENSE_BSD.txt for details).
28 
29 *******************************************************************************/
30 
31 module ocean.util.container.pool.FreeList;
32 
33 
34 
35 import ocean.util.container.pool.model.IFreeList;
36 
37 import ocean.core.Array : pop;
38 
39 import ocean.meta.traits.Basic;
40 import ocean.meta.types.Typedef;
41 
42 import ocean.transition;
43 import ocean.core.Buffer;
44 import ocean.core.TypeConvert;
45 
46 version (UnitTest)
47     import ocean.core.Test;
48 
49 /*******************************************************************************
50 
51     Template to determine the internal item type of a free list.
52 
53     Params:
54         T = item type to be stored in free list
55 
56 *******************************************************************************/
57 
58 private template ItemType_ ( T )
59 {
60     static if (isTypedef!(T))
61     {
62         alias ItemType_!(TypedefBaseType!(T)) ItemType_;
63     }
64     else static if (
65            isReferenceType!(T)
66         || isArrayType!(T) == ArrayKind.Dynamic
67         || isArrayType!(T) == ArrayKind.Associative)
68     {
69         alias T ItemType_;
70     }
71     else
72     {
73         alias T* ItemType_;
74     }
75 }
76 
77 
78 /*******************************************************************************
79 
80     Free list class template.
81 
82     Params:
83         T = item type to be stored in free list
84 
85 *******************************************************************************/
86 
87 public class FreeList ( T ) : IFreeList!(ItemType_!(T))
88 {
89     import ocean.core.Verify;
90 
91     /***************************************************************************
92 
93         Free lists of static arrays are not allowed. Likewise a free list of an
94         atomic type (int, float, etc) is not allowed -- this would be rather
95         pointless.
96 
97     ***************************************************************************/
98 
99     static assert(
100         isArrayType!(T) != ArrayKind.Static,
101         "Cannot use static array type '"
102             ~ T.stringof ~ "' as base type for FreeList"
103     );
104 
105     static assert(
106         !isPrimitiveType!(T),
107         "Cannot use primitive type '" ~ T.stringof ~
108             "' as base type for FreeList"
109     );
110 
111 
112     /***************************************************************************
113 
114         Alias for item type to be stored in free list and accepted by public
115         methods.
116 
117         Reference types (including dynamic and associative arrays) can be stored
118         directly in the free list. Pointers are stored for other types.
119 
120     ***************************************************************************/
121 
122     public alias ItemType_!(T) ItemType;
123 
124 
125     /***************************************************************************
126 
127         List of free items.
128 
129     ***************************************************************************/
130 
131     private ItemType[] free_list;
132 
133 
134     /***************************************************************************
135 
136         Gets an item from the free list.
137 
138         Params:
139             new_item = new item, will only be evaluated in the case when no
140                 items are available in the free list
141 
142         Returns:
143             new item
144 
145     ***************************************************************************/
146 
147     public ItemType get ( lazy ItemType new_item )
148     {
149         if ( this.free_list.length )
150         {
151             ItemType item;
152             auto popped = this.free_list.pop(item);
153             verify(popped, "Item failed to be popped from non-empty free list");
154             return item;
155         }
156         else
157         {
158             return new_item;
159         }
160     }
161 
162 
163     /***************************************************************************
164 
165         Recycles an item into the free list.
166 
167         Params:
168             item = item to be put into the free list
169 
170     ***************************************************************************/
171 
172     public void recycle ( ItemType item )
173     {
174         this.free_list ~= item;
175     }
176 
177 
178     /***************************************************************************
179 
180         Ensures that the free list contains at least the specified number of
181         (idle) items. Useful to pre-allocate a free list of a certain size.
182 
183         Params:
184             num = minimum number of items desired in pool
185             new_item = expression that creates a new instance of T
186 
187         Returns:
188             this
189 
190     ***************************************************************************/
191 
192     public typeof(this) fill ( size_t num, lazy ItemType new_item )
193     out
194     {
195         assert(this.free_list.length >= num);
196     }
197     body
198     {
199         if ( this.free_list.length < num )
200         {
201             auto extra = num - this.free_list.length;
202             for ( size_t i; i < extra; i++ )
203             {
204                 this.free_list ~= new_item;
205             }
206         }
207 
208         return this;
209     }
210 
211 
212     /***************************************************************************
213 
214         Ensures that the free list contains at most the specified number of
215         (idle) items.
216 
217         Params:
218             num = maximum number of idle items desired in free list
219 
220         Returns:
221             this
222 
223     ***************************************************************************/
224 
225     public typeof(this) minimize ( size_t num = 0 )
226     {
227         if ( this.free_list.length > num )
228         {
229             this.free_list.length = num;
230             enableStomping(this.free_list);
231         }
232 
233         return this;
234     }
235 
236 
237     /***************************************************************************
238 
239         Returns:
240             the number of idle (available) items in the free list
241 
242     ***************************************************************************/
243 
244     public size_t num_idle ( )
245     {
246         return this.free_list.length;
247     }
248 }
249 
250 
251 /*******************************************************************************
252 
253     Unit test.
254 
255     Tests:
256         * All methods of IFreeList.
257         * FreeList of strings, structs and classes.
258 
259 *******************************************************************************/
260 
261 version ( UnitTest )
262 {
263     import ocean.core.Test;
264 
265     /***************************************************************************
266 
267         String free list tester.
268 
269     ***************************************************************************/
270 
271     private alias FreeList!(char[]) StringFreeList;
272     private class StringFreeListTester : FreeListTester!(StringFreeList.ItemType)
273     {
274         public this ( ) { super(new StringFreeList); }
275 
276         protected override Item newItem ( )
277         {
278             return new char[10];
279         }
280 
281         protected override void setItem ( ref Item item, size_t i )
282         {
283             item.length = 1;
284             item[0] = cast(char)(i + 32);
285         }
286 
287         protected override void checkItem ( ref Item item, size_t i )
288         {
289             .test!("==")(item.length, 1, "item length wrong");
290             .test!("==")(item[0], cast(char)(i + 32), "item content wrong");
291         }
292     }
293 
294 
295     /***************************************************************************
296 
297         Struct free list tester.
298 
299     ***************************************************************************/
300 
301     private struct Struct
302     {
303         size_t i;
304         char[] s;
305     }
306 
307     private alias FreeList!(Struct) StructFreeList;
308     private class StructFreeListTester : FreeListTester!(StructFreeList.ItemType)
309     {
310         public this ( ) { super(new StructFreeList); }
311 
312         protected override Item newItem ( )
313         {
314             return new Struct;
315         }
316 
317         protected override void setItem ( ref Item item, size_t i )
318         {
319             item.i = i;
320             item.s.length = 1;
321             item.s[0] = cast(char)(i + 32);
322         }
323 
324         protected override void checkItem ( ref Item item, size_t i )
325         {
326             .test!("==")(item.i, i, "item integer wrong");
327             .test!("==")(item.s.length, 1, "item string length wrong");
328             .test!("==")(item.s[0], cast(char)(i + 32), "item string content wrong");
329         }
330     }
331 
332     /***************************************************************************
333 
334         Buffer free list tester.
335 
336     ***************************************************************************/
337 
338     private alias FreeList!(Buffer!(void)) BufferFreeList;
339     private class BufferFreeListTester : FreeListTester!(BufferFreeList.ItemType)
340     {
341         public this ( ) { super(new BufferFreeList); }
342 
343         protected override Item newItem ( )
344         {
345             return new Buffer!(void);
346         }
347 
348         protected override void setItem ( ref Item item, size_t i )
349         {
350             *item ~= 40;
351             *item ~= 41;
352             *item ~= 42;
353         }
354 
355         protected override void checkItem ( ref Item item, size_t i )
356         {
357             void[] a = (*item)[];
358             void[] b = arrayOf!(ubyte)(40, 41, 42);
359             .test!("==")(a, b);
360         }
361     }
362 
363     /***************************************************************************
364 
365         Class free list tester.
366 
367     ***************************************************************************/
368 
369     private class Class
370     {
371         mixin(genOpEquals(`
372         {
373             auto crhs = cast(typeof(this)) rhs;
374             return this.i == crhs.i && this.s == crhs.s;
375         }`));
376 
377         size_t i;
378         char[] s;
379     }
380 
381     private alias FreeList!(Class) ClassFreeList;
382     private class ClassFreeListTester : FreeListTester!(ClassFreeList.ItemType)
383     {
384         public this ( ) { super(new ClassFreeList); }
385 
386         protected override Item newItem ( )
387         {
388             return new Class;
389         }
390 
391         protected override void setItem ( ref Item item, size_t i )
392         {
393             item.i = i;
394             item.s.length = 1;
395             item.s[0] = cast(char)(i + 32);
396         }
397 
398         protected override void checkItem ( ref Item item, size_t i )
399         {
400             .test!("==")(item.i, i, "item integer wrong");
401             .test!("==")(item.s.length, 1, "item string length wrong");
402             .test!("==")(item.s[0], cast(char)(i + 32), "item string content wrong");
403         }
404     }
405 }
406 
407 unittest
408 {
409     // String (arrays) free list test
410     {
411         scope fl = new StringFreeListTester;
412         fl.test();
413     }
414 
415     // Struct free list test
416     {
417         scope fl = new StructFreeListTester;
418         fl.test();
419     }
420 
421     // Buffer free list test
422     {
423         scope fl = new BufferFreeListTester;
424         fl.test();
425     }
426 
427     // Class free list test
428     {
429         scope fl = new ClassFreeListTester;
430         fl.test();
431     }
432 }
433