1 /*******************************************************************************
2 
3     Copyright:
4         Copyright (c) 2008 Kris Bell.
5         Some parts copyright (c) 2009-2017 dunnhumby Germany GmbH.
6         All rights reserved.
7 
8     License:
9         Tango Dual License: 3-Clause BSD License / Academic Free License v3.0.
10         See LICENSE_TANGO.txt for details.
11 
12 *******************************************************************************/
13 
14 module ocean.util.container.more.Stack;
15 
16 import ocean.core.Enforce;
17 
18 version (UnitTest)
19 {
20     import ocean.core.Test;
21 }
22 
23 /*******************************************************************************
24 
25     A stack of the given value-type V, with maximum depth Size. Note
26     that this does no memory allocation of its own when Size != 0, and
27     does heap allocation when Size == 0. Thus you can have a fixed-size
28     low-overhead instance, or a heap oriented instance.
29 
30 *******************************************************************************/
31 
32 public struct Stack ( V, int Size = 0 )
33 {
34     public alias nth         opIndex;
35     public alias slice       opSlice;
36     public alias rotateRight opShrAssign;
37     public alias rotateLeft  opShlAssign;
38     public alias push        opCatAssign;
39 
40     static if (Size == 0)
41     {
42         private uint depth;
43         private V[]  stack;
44     }
45     else
46     {
47         private uint     depth;
48         private V[Size]  stack;
49     }
50 
51     /***************************************************************************
52 
53         Clear the stack
54 
55         Returns: pointer to itself for chaining calls
56 
57     ***************************************************************************/
58 
59     Stack* clear ( )
60     {
61         depth = 0;
62         return (&this);
63     }
64 
65     /***************************************************************************
66 
67         Returns: depth of the stack
68 
69     ***************************************************************************/
70 
71     size_t size ( )
72     {
73         return depth;
74     }
75 
76     /***************************************************************************
77 
78         Returns: remaining unused slots
79 
80     ***************************************************************************/
81 
82     size_t unused  ( )
83     {
84         enforce(.e_bounds, stack.length >= depth);
85         return stack.length - depth;
86     }
87 
88     /***************************************************************************
89 
90         Returns: a (shallow) clone of this stack, on the stack
91 
92     ***************************************************************************/
93 
94     Stack clone ( )
95     {
96         Stack s;
97         static if (Size == 0)
98             s.stack.length = stack.length;
99         s.stack[] = stack;
100         s.depth = depth;
101         return s;
102     }
103 
104     /***************************************************************************
105 
106         Pushes shallow copy of topmost element
107 
108         Returns: pushed copy
109 
110     ***************************************************************************/
111 
112     V dup ( )
113     {
114         auto v = top;
115         push (v);
116         return v;
117     }
118 
119     /***************************************************************************
120 
121         Params:
122             value = valush to push on top of the stack
123 
124         Returns: pointer to itself for call chaining
125 
126         Throws: StackBoundsException when the stack is full
127 
128     ***************************************************************************/
129 
130     Stack* push ( V value )
131     {
132         static if (Size == 0)
133         {
134             if (depth >= stack.length)
135                 stack.length = stack.length + 64;
136             stack[depth++] = value;
137         }
138         else
139         {
140             enforce(.e_bounds, depth < stack.length);
141             stack[depth++] = value;
142         }
143         return (&this);
144     }
145 
146     /***************************************************************************
147 
148         Params:
149             value = array of values to push onto the stack
150 
151         Returns: pointer to itself for call chaining
152 
153         Throws: StackBoundsException when the stack is full
154 
155     ***************************************************************************/
156 
157     Stack* append ( V[] value... )
158     {
159         foreach (v; value)
160             push (v);
161         return (&this);
162     }
163 
164     /***************************************************************************
165 
166         Removes most recent stack element
167 
168         Return: most recent stack element before popping
169 
170         Throws: StackBoundsException when the stack is full
171 
172     ***************************************************************************/
173 
174     V pop ( )
175     {
176         enforce(.e_bounds, depth > 0);
177         return stack[--depth];
178     }
179 
180     /***************************************************************************
181 
182         Returns: most recent stack element
183 
184         Throws: StackBoundsException when the stack is full
185 
186     ***************************************************************************/
187 
188     V top ( )
189     {
190         enforce(.e_bounds, depth > 0);
191         return stack[depth-1];
192     }
193 
194     /***************************************************************************
195 
196         Swaps the top two entries
197 
198         Returns: the top element after swapping
199 
200         Throws: StackBoundsException when the stack has insufficient entries
201 
202     ***************************************************************************/
203 
204     V swap ( )
205     {
206         auto p = stack.ptr + depth;
207         enforce(.e_bounds, p - 2 >= stack.ptr);
208 
209         p -= 2;
210         auto v = p[0];
211         p[0] = p[1];
212         return p[1] = v;
213     }
214 
215     /***************************************************************************
216 
217         Params:
218             i = entry index
219 
220         Returns:
221             stack entry with index `i`, where a zero index represents the
222             newest stack entry (the top).
223 
224         Throws: StackBoundsException when the given index is out of range
225 
226     ***************************************************************************/
227 
228     V nth ( uint i )
229     {
230         enforce(.e_bounds, i < depth);
231         return stack [depth-i-1];
232     }
233 
234     /***************************************************************************
235 
236         Rotate the given number of stack entries
237 
238         Params:
239             d = number of entries
240 
241         Returns: pointer to itself for call chaining
242 
243         Throws: StackBoundsException when the number is out of range
244 
245     ***************************************************************************/
246 
247     Stack* rotateLeft ( uint d )
248     {
249         enforce(.e_bounds, d <= depth);
250         auto p = &stack[depth-d];
251         auto t = *p;
252         while (--d)
253         {
254             *p = *(p+1);
255             p++;
256         }
257         *p = t;
258         return (&this);
259     }
260 
261     /***************************************************************************
262 
263         Rotate the given number of stack entries
264 
265         Params:
266             d = number of entries
267 
268         Returns: pointer to itself for call chaining
269 
270         Throws: StackBoundsException when the number is out of range
271 
272     ***************************************************************************/
273 
274     Stack* rotateRight ( uint d )
275     {
276         enforce(.e_bounds, d <= depth);
277         auto p = &stack[depth-1];
278         auto t = *p;
279         while (--d)
280         {
281             *p = *(p-1);
282             p--;
283         }
284         *p = t;
285         return (&this);
286     }
287 
288     /***************************************************************************
289 
290         Returns:
291             The stack as an array of values, where the first
292             array entry represents the oldest value.
293 
294             Doing a foreach() on the returned array will traverse in
295             the opposite direction of foreach() upon a stack.
296 
297 
298     ***************************************************************************/
299 
300     V[] slice ( )
301     {
302         return stack[0 .. depth];
303     }
304 
305     /***************************************************************************
306 
307         Iterate from the most recent to the oldest stack entries
308 
309     ***************************************************************************/
310 
311     int opApply ( scope int delegate(ref V value) dg )
312     {
313         int result;
314 
315         for (int i=depth; i-- && result is 0;)
316             result = dg (stack[i]);
317 
318         return result;
319     }
320 }
321 
322 ///
323 unittest
324 {
325     Stack!(int) stack;
326     stack.push(42);
327     test!("==")(stack.pop(), 42);
328     testThrown!(StackBoundsException)(stack.pop());
329 }
330 
331 version(UnitTest)
332 {
333     static void runTests ( T ) ( NamedTest t, T stack )
334     {
335         t.test!("==")(stack.size(), 0);
336         testThrown!(StackBoundsException)(stack.pop());
337         stack.push(42);
338         t.test!("==")(stack.size(), 1);
339         stack.clear();
340         t.test!("==")(stack.size(), 0);
341 
342         stack.push(100);
343         t.test!("==")(stack.dup(), 100);
344         t.test!("==")(stack[], [ 100, 100 ]);
345 
346         auto clone = stack.clone();
347         foreach (idx, ref field; clone.tupleof)
348             t.test!("==")(field, stack.tupleof[idx]);
349 
350         stack.clear();
351         stack.append(1, 2, 3, 4);
352         t.test!("==")(stack[], [ 1, 2, 3, 4 ]);
353 
354         t.test!("==")(stack.top(), 4);
355         t.test!("==")(stack.pop(), 4);
356         t.test!("==")(stack.top(), 3);
357         t.test!("==")(stack[0], 3);
358         t.test!("==")(stack[2], 1);
359         testThrown!(StackBoundsException)(stack[10]);
360 
361         stack.swap();
362         t.test!("==")(stack[], [ 1, 3, 2 ]);
363         stack.clear();
364         testThrown!(StackBoundsException)(stack.swap());
365 
366         stack.append(1, 2, 3);
367         stack.rotateLeft(2);
368         t.test!("==")(stack[], [ 1, 3, 2 ]);
369         stack.rotateRight(2);
370         t.test!("==")(stack[], [ 1, 2, 3 ]);
371         testThrown!(StackBoundsException)(stack.rotateLeft(40));
372         testThrown!(StackBoundsException)(stack.rotateRight(40));
373     }
374 }
375 
376 unittest
377 {
378     // common tests
379     runTests(new NamedTest("Dynamic size"), Stack!(int).init);
380     runTests(new NamedTest("Static size"),  Stack!(int, 10).init);
381 }
382 
383 unittest
384 {
385     // fixed size specific tests
386     Stack!(int, 3) stack;
387     test!("==")(stack.unused(), 3);
388 
389     stack.push(1);
390     stack.push(1);
391     stack.push(1);
392     testThrown!(StackBoundsException)(stack.push(1));
393 }
394 
395 unittest
396 {
397     // dynamic size specific tests
398     Stack!(int) stack;
399     test!("==")(stack.unused(), 0);
400 }
401 
402 /*******************************************************************************
403 
404     Exception that indicates any kind of out of bound access in stack, for
405     example, trying to pop from empty one.
406 
407 *******************************************************************************/
408 
409 public class StackBoundsException : Exception
410 {
411     this ( )
412     {
413         super("Out of bounds access attempt to stack struct");
414     }
415 }
416 
417 private StackBoundsException e_bounds;
418 
419 static this ( )
420 {
421     e_bounds = new StackBoundsException;
422 }