1 /*******************************************************************************
2 
3     Utility template to implement Map.opApply()/Set.opApply(), working around
4     the problem that opApply() cannot have static array parameters because 'ref'
5     is forbidden for static arrays. The solution is to use dynamic arrays
6     instead and pass an array slice to to the 'foreach' loop body delegate.
7 
8     Copyright:
9         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
10         All rights reserved.
11 
12     License:
13         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
14         Alternatively, this file may be distributed under the terms of the Tango
15         3-Clause BSD License (see LICENSE_BSD.txt for details).
16 
17 *******************************************************************************/
18 
19 module ocean.util.container.map.model.MapIterator;
20 
21 import ocean.util.container.map.model.Bucket;
22 
23 /*******************************************************************************
24 
25     Mixin that adds an iterator class and a member variable of it.
26     Note that this.iterator still has to be initialized by the constructor.
27 
28     Params:
29         ParentIterator = parent class to inherit from
30         IteratorTemplate = Instance of the MapIterator template
31 
32 *******************************************************************************/
33 
34 public template IteratorClass ( alias ParentIterator, alias IteratorTemplate )
35 {
36     /***************************************************************************
37 
38         Iterator class offering specialized versions of opApply for the given
39         map/set class.
40 
41         The appropriate type for the delegates is taken from the MapIterator
42         template.
43 
44     ***************************************************************************/
45 
46     class Iterator : ParentIterator
47     {
48         /***********************************************************************
49 
50             Ctor
51 
52         ***********************************************************************/
53 
54         public this ( )
55         {
56             super(true);
57         }
58 
59         /***********************************************************************
60 
61             Protected Ctor, used for inheriting classes to ensure a certain
62             behavior
63 
64             Params:
65                 reset_after_foreach = whether to reset iteration counters
66                                       after a foreach (true) or not (false)
67 
68         ***********************************************************************/
69 
70         protected this ( bool reset_after_foreach )
71         {
72             super(reset_after_foreach);
73         }
74 
75         /***********************************************************************
76 
77             Foreach support with counter
78 
79         ***********************************************************************/
80 
81         public int opApply ( IteratorTemplate.Dgi dgi )
82         {
83             return super.opApply((ref size_t i, ref Bucket.Element e )
84                                  {
85                                     return IteratorTemplate.iterate(dgi, i, e);
86                                  });
87         }
88 
89         /***********************************************************************
90 
91             Foreach support
92 
93         ***********************************************************************/
94 
95         public int opApply ( IteratorTemplate.Dg dg )
96         {
97             return super.opApply((ref Bucket.Element e )
98                                  {
99                                     return IteratorTemplate.iterate(dg, e);
100                                  });
101         }
102     }
103 
104     /***************************************************************************
105 
106         Interruptible iterator
107 
108     ***************************************************************************/
109 
110     class InterruptibleIterator  : Iterator
111     {
112         /***********************************************************************
113 
114             Whether the iteration finished, set by resetIterator(), reset by
115             reset()
116 
117         ***********************************************************************/
118 
119         protected bool _finished;
120 
121         /***********************************************************************
122 
123             Constructor
124 
125         ***********************************************************************/
126 
127         public this ( )
128         {
129             super(false);
130         }
131 
132         /***********************************************************************
133 
134             Set the finished flag.
135 
136             Params:
137                 interrupted = if true, the foreach iteration was interrupted
138                               with a break, if false, it finished the iteration
139 
140         ***********************************************************************/
141 
142         protected override void resetIterator ( bool interrupted )
143         {
144             this._finished = !interrupted;
145         }
146 
147         /***********************************************************************
148 
149             Prepare the iterator to restart the iteration from the beginning
150 
151         ***********************************************************************/
152 
153         public override void reset ( )
154         {
155             this._finished = false;
156             super.reset();
157         }
158 
159         /***********************************************************************
160 
161             Whether iteration finished
162 
163             Returns:
164                 true, if iteration finished
165                 false, if not
166 
167         ***********************************************************************/
168 
169         public bool finished ( )
170         {
171             return _finished;
172         }
173     }
174 }
175 
176 /******************************************************************************
177 
178     opApply wrapper to work around the problem that it isn't possible to have a
179     static array opApply() argument because 'ref' is not allowed with a static
180     array. Instead, the wrapper slices the argument and passes the slice to the
181     'foreach' body.
182 
183     If the value type is 'void', the iteration delegate will only have a key
184     argument.
185 
186     Params:
187         V = value type; 'void' indicates that there are no values at all.
188         K = key type
189 
190  ******************************************************************************/
191 
192 template MapIterator ( V, K = hash_t )
193 {
194     import ocean.core.Verify;
195 
196     /**************************************************************************
197 
198         Kref type alias definition: A dynamic array of the base type of K if K
199         is a static array or K itself otherwise.
200 
201      **************************************************************************/
202 
203     static if (is (K Kelement : Kelement[]) && !is (K == Kelement[]))
204     {
205         static immutable k_is_static_array = true;
206 
207         alias Kelement[] Kref;
208     }
209     else
210     {
211         static immutable k_is_static_array = false;
212 
213         alias K Kref;
214     }
215 
216     /**************************************************************************
217 
218         Alias definitions of the Vref, the bucket element and the delegate type.
219 
220         Vref is
221             - a dynamic array of the base type of V if V is a static array,
222             - not defined at all if V is 'void'
223             - V itself otherwise.
224 
225         The delegate complies to the opApply() iteration delegate and iterates
226         over Kref only if V is 'void' or over Kref and Vref otherwise.
227 
228      **************************************************************************/
229 
230     static if (is (V == void))
231     {
232         static immutable v_is_static_array = false;
233 
234         alias int delegate ( ref Kref ) Dg;
235         alias int delegate ( ref size_t i, ref Kref ) Dgi;
236 
237         alias Bucket!(cast (size_t) 0, K).Element Element;
238     }
239     else
240     {
241         static if (is (V Velement : Velement[]) && !is (V == Velement[]))
242         {
243             alias Velement[] Vref;
244 
245             static immutable v_is_static_array = true;
246         }
247         else
248         {
249             alias V Vref;
250 
251             static immutable v_is_static_array = false;
252         }
253 
254         alias int delegate ( ref Kref, ref Vref ) Dg;
255         alias int delegate ( ref size_t i, ref Kref, ref Vref ) Dgi;
256 
257         alias Bucket!(V.sizeof, K).Element Element;
258     }
259 
260     /**************************************************************************
261 
262         Invokes dg with the key and, unless V is 'void', the value of element.
263 
264         Do not modify the key in-place.
265 
266         If K or V (or both) are a static array, a dynamic array slice is passed
267         to dg. Do not do a 'ref' iteration over static array keys or values.
268         To obtain a pointer to the static array key or value currently iterating
269         over, use the .ptr of the iteration variable.
270 
271         Example: Consider a map that stores char[5] values with hash_t keys.
272 
273         ---
274             foreach (ref key, val; map)
275             {
276                 // typeof(key) is hash_t. A pointer to the key can be obtained
277                 // using &key.
278 
279                 // typeof(val) is char[], val is a dynamic array slice
280                 // referencing the value in the map. A pointer to the value can
281                 // be obtained using val.ptr.
282             }
283         ---
284 
285         Params:
286             dg      = iteration delegate
287             element = bucket element
288 
289         Returns:
290             passes through the return type of dg.
291 
292      **************************************************************************/
293 
294     int iterate ( Dg dg, ref Element element )
295     {
296         // temporary replacement for nested function which purpose is to
297         // prevent allocation of closure in D2 (because it tries to access
298         // host function stack). In the long term will be replaced by better
299         // d1to2fix support.
300 
301         static struct Delegate
302         {
303             Dg dg;
304 
305             static if (is (V == void))
306             {
307                 int call ( ref size_t i, ref Kref k )
308                 {
309                     return dg(k);
310                 }
311             }
312             else
313             {
314                 int call ( ref size_t i, ref Kref k, ref Vref v )
315                 {
316                     return dg(k, v);
317                 }
318             }
319         }
320 
321         auto tmpDg = Delegate(dg);
322 
323         return iterate(&tmpDg.call, 0, element);
324     }
325 
326     /***************************************************************************
327 
328         Same method as above, but with counter
329 
330     ***************************************************************************/
331 
332     int iterate ( Dgi dg, size_t i, ref Element element )
333     {
334         static if (k_is_static_array)
335         {
336             Kref key = element.key;
337 
338             Kref* key_ptr = &key;
339         }
340         else
341         {
342             K* key = &element.key;
343 
344             alias key key_ptr;
345         }
346 
347         scope (success)
348         {
349             // cast(bool) to handle Key==Object: opEquals returns int
350             verify (cast(bool)(*key_ptr == element.key),
351                     "attempted to change the key during iteration");
352         }
353 
354         static if (is (V == void))
355         {
356             return dg(i, *key_ptr);
357         }
358         else static if (v_is_static_array)
359         {
360             Vref val = *cast (V*) element.val.ptr;
361 
362             size_t vlen = val.length;
363 
364             scope (success)
365             {
366                 verify (val.length == vlen,
367                         "attempted to change the length of a static array "
368                       ~ "during iteration");
369             }
370 
371             return dg(i, *key_ptr, val);
372         }
373         else
374         {
375             return dg(i, *key_ptr, *cast (V*) element.val.ptr);
376         }
377     }
378 }