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