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