1 /*******************************************************************************
2 
3     Template for a struct implementing a single bucket in a set (see
4     ocean.util.container.map.model.BucketSet). A bucket contains a set of
5     elements which can be added to, removed from and searched.
6 
7     Each element in a bucket has a unique key with which it can be identified.
8     The elements' key type is templated, but defaults to hash_t. A bucket can
9     only contain one element with a given key - if a duplicate is added it will
10     replace the original. The elements in the bucket are stored as a linked
11     list, for easy removal and insertion.
12 
13     Note that the bucket does not own its elements, these must be managed from
14     the outside in a pool. The bucket itself simply keeps a pointer to the first
15     element which it contains.
16 
17     Two element structs exist in this module, one for a basic bucket element,
18     and one for a bucket element which contains a value in addition to a key.
19 
20     Usage:
21         See ocean.util.container.map.model.BucketSet,
22         ocean.util.container.map.HashMap & ocean.util.container.map.HashSet
23 
24     Copyright:
25         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
26         All rights reserved.
27 
28     License:
29         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
30         Alternatively, this file may be distributed under the terms of the Tango
31         3-Clause BSD License (see LICENSE_BSD.txt for details).
32 
33 *******************************************************************************/
34 
35 module ocean.util.container.map.model.Bucket;
36 
37 
38 import ocean.meta.traits.Basic;
39 
40 /*******************************************************************************
41 
42     Template to be mixed in to a bucket element. Contains the shared core
43     members of a bucket element.
44 
45     This template mixin is used so that we can use structs for the bucket
46     elements rather than classes, thus avoiding the memory overhead of class
47     instances. In the case of bucket elements, which could exist in quantities
48     of many thousands, this is significant.
49 
50     Using structs instead of classes means that we can't use an interface or
51     base class, and the Bucket struct (below) has to simply assume that the
52     Element struct has certain members. As it's purely internal, we can live
53     with this.
54 
55     Params:
56         V = value size (.sizeof of the value type), may be 0 to store no value
57         K = key type
58 
59 *******************************************************************************/
60 
61 public struct Bucket ( size_t V, K = hash_t )
62 {
63     /**************************************************************************
64 
65         Bucket element type
66 
67      **************************************************************************/
68 
69     struct Element
70     {
71         /**********************************************************************
72 
73             Element value, may be a dummy of zero size if no value is stored.
74 
75             !!! WARNING !!!
76 
77             Is extremely important that this "val" is at the top of the struct,
78             otherwise the struct can get into alignment issues that can cause
79             the GC to miss the pointers in the data.
80 
81          **********************************************************************/
82 
83         public alias ubyte[V] Val;
84 
85         union
86         {
87             public Val val;
88 
89             /**********************************************************************
90 
91             Mark this memory as potentially containing pointers and align size of data
92             to next multiple of (void*).sizeof.
93 
94             **********************************************************************/
95             private void*[(V + (void*).sizeof - 1) / (void*).sizeof] _void;
96         }
97 
98         /**********************************************************************
99 
100             Make sure val is properly aligned.
101 
102             See http://www.digitalmars.com/d/1.0/attribute.html#align
103 
104          **********************************************************************/
105 
106         static assert(val.offsetof == 0);
107 
108         /**********************************************************************
109 
110             Key = bucket element key type
111 
112          **********************************************************************/
113 
114         public alias K Key;
115 
116         /**********************************************************************
117 
118             Element key
119 
120          **********************************************************************/
121 
122         public Key key;
123 
124         /**********************************************************************
125 
126             Make sure key is properly aligned.
127 
128          **********************************************************************/
129 
130         static assert(key.offsetof % size_t.sizeof == 0);
131 
132         /**********************************************************************
133 
134             Next and previous element. For the first/last bucket element
135             next/prev is null, respectively.
136 
137          **********************************************************************/
138 
139         package Element* next = null;
140 
141         debug (HostingArrayMapBucket) private Bucket!(V, K)* bucket;
142     }
143 
144     /**************************************************************************
145 
146         First bucket element
147 
148      **************************************************************************/
149 
150     package Element* first = null;
151 
152     /**************************************************************************
153 
154         Tells whether there is at least one element in this bucket.
155 
156         Returns:
157             false if the bucket is empty or true otherwise.
158 
159      **************************************************************************/
160 
161     public bool has_element ( )
162     {
163         return this.first !is null;
164     }
165 
166     /**************************************************************************
167 
168         Looks up the element whose key equals key.
169 
170         Params:
171             key = element key
172 
173         Returns:
174             the element whose key equals key or null if not found.
175 
176      **************************************************************************/
177 
178     public Element* find ( in Element.Key key )
179     out (element)
180     {
181         debug (HostingArrayMapBucket) if (element)
182         {
183             assert (element.bucket, "bucket not set in found element");
184             assert (element.bucket is &this,
185                     "element found is not from this bucket");
186         }
187     }
188     do
189     {
190         for (Element* element = this.first; element; element = element.next)
191         {
192             if (element.key == key)
193             {
194                 return element;
195             }
196         }
197 
198         return null;
199     }
200 
201 
202     /**************************************************************************
203 
204         Adds a bucket element with key as key.
205 
206         The element is inserted as the first bucket element.
207 
208         Params:
209             key = key for the new element
210             new_element = expression returning a new element, evaluated exactly
211                 once, if the key to be added does not already exist in the
212                 bucket
213 
214         Returns:
215             pointer to inserted element
216 
217         Out:
218             The returned pointer is never null.
219 
220      **************************************************************************/
221 
222     public Element* add ( Element.Key key, lazy Element* new_element )
223     out (element)
224     {
225         assert (element !is null);
226     }
227     do
228     {
229         Element* element = this.find(key);
230 
231         if (!element)
232         {
233             element = this.add(new_element);
234 
235             static if (isArrayType!(K) == ArrayKind.Static)
236             {
237                 element.key[] = key;
238             }
239             else
240             {
241                 element.key = key;
242             }
243         }
244 
245         return element;
246     }
247 
248 
249     /**************************************************************************
250 
251         Adds an element to the bucket.
252 
253         The element is inserted as the first bucket element.
254 
255         Params:
256             element = element to add
257 
258         Returns:
259             element
260 
261      **************************************************************************/
262 
263     public Element* add ( Element* element )
264     in
265     {
266         debug (HostingArrayMapBucket) element.bucket = &this;
267     }
268     out
269     {
270         debug (HostingArrayMapBucket)
271         {
272             // Check for cyclic links using 2 pointers, one which traverse
273             // twice as fast as the first one
274             auto ptr1 = this.first;
275             auto ptr2 = ptr1;
276 
277             // Find meeting point
278             while(ptr2 !is null)
279             {
280                 ptr1 = ptr1.next;
281                 if (ptr2.next == null)
282                     break; // We reached end of the list, no loop
283                 else
284                     ptr2 = ptr2.next.next;
285 
286                 assert(ptr1 !is ptr2, "Cyclic linked-list found");
287             }
288         }
289     }
290     do
291     {
292         element.next = this.first;
293         this.first   = element;
294 
295         return element;
296     }
297 
298     /**************************************************************************
299 
300         Looks up the element corresponding to key in this bucket and removes it,
301         if found.
302 
303         The removed element must be recycled by the owner of the bucket.
304 
305         Params:
306             key = key of the element to remove
307 
308         Returns:
309             removed element or null if not found.
310 
311      **************************************************************************/
312 
313     public Element* remove ( K key )
314     out (removed)
315     {
316         if (removed !is null)
317         {
318             assert (removed.next is null, "remove: forgot to clear removed.next");
319 
320             debug (HostingArrayMapBucket) if (removed)
321             {
322                 assert (removed.bucket is &this,
323                         "element to remove is not from this bucket");
324 
325                 removed.bucket = null;
326             }
327         }
328     }
329     do
330     {
331         if (this.first !is null)
332         {
333             if (this.first.key == key)
334             {
335                 Element* removed = this.first;
336 
337                 this.first   = this.first.next;
338                 removed.next = null;
339 
340                 return removed;
341             }
342             else
343             {
344                 Element* element = this.first.next;
345 
346                 for (Element* prev = this.first; element;)
347                 {
348                     if (element.key == key)
349                     {
350                         Element* removed = element;
351 
352                         prev.next    = element.next;
353                         removed.next = null;
354 
355                         return removed;
356                     }
357                     else
358                     {
359                         prev    = element;
360                         element = element.next;
361                     }
362                 }
363             }
364         }
365 
366         return null;
367     }
368 }
369 
370 version (none):
371 
372 /**
373 Order the provided members to minimize size while preserving alignment.
374 Returns a declaration to be mixed in.
375 
376 Example:
377 ---
378 struct Banner {
379 mixin(alignForSize!(byte[6], double)(["name", "height"]));
380 }
381 ---
382 
383 Alignment is not always optimal for 80-bit reals, nor for structs declared
384 as align(1).
385 */
386 char[] alignForSize(E...)(string[] names...)
387 {
388   // Sort all of the members by .alignof.
389   // BUG: Alignment is not always optimal for align(1) structs
390   // or 80-bit reals or 64-bit primitives on x86.
391   // TRICK: Use the fact that .alignof is always a power of 2,
392   // and maximum 16 on extant systems. Thus, we can perform
393   // a very limited radix sort.
394   // Contains the members with .alignof = 64,32,16,8,4,2,1
395 
396   assert(E.length == names.length,
397       "alignForSize: There should be as many member names as the types");
398 
399   char[][7] declaration = ["", "", "", "", "", "", ""];
400 
401   foreach (i, T; E) {
402       auto a = T.alignof;
403       auto k = a>=64? 0 : a>=32? 1 : a>=16? 2 : a>=8? 3 : a>=4? 4 : a>=2? 5 : 6;
404       declaration[k] ~= T.stringof ~ " " ~ names[i] ~ ";\n";
405   }
406 
407   auto s = "";
408   foreach (decl; declaration)
409       s ~= decl;
410   return s;
411 }
412 
413 unittest {
414   static immutable x = alignForSize!(int[], char[3], short, double[5])("x", "y","z", "w");
415   struct Foo{ int x; }
416   static immutable y = alignForSize!(ubyte, Foo, cdouble)("x", "y","z");
417 
418   static if(size_t.sizeof == uint.sizeof)
419   {
420       static immutable passNormalX = x == "double[5u] w;\nint[] x;\nshort z;\nchar[3u] y;\n";
421       static immutable passNormalY = y == "cdouble z;\nFoo y;\nubyte x;\n";
422 
423       static immutable passAbnormalX = x == "int[] x;\ndouble[5u] w;\nshort z;\nchar[3u] y;\n";
424       static immutable passAbnormalY = y == "Foo y;\ncdouble z;\nubyte x;\n";
425       // ^ blame http://d.puremagic.com/issues/show_bug.cgi?id=231
426 
427       static assert(passNormalX || double.alignof <= (int[]).alignof && passAbnormalX);
428       static assert(passNormalY || double.alignof <= int.alignof && passAbnormalY);
429   }
430   else
431   {
432       static assert(x == "int[] x;\ndouble[5LU] w;\nshort z;\nchar[3LU] y;\n");
433       static assert(y == "cdouble z;\nFoo y;\nubyte x;\n");
434   }
435 }