1 /** 2 Stacks 3 4 Copyright: 5 Copyright © 2023-2025, Kitsunebi Games 6 Copyright © 2023-2025, Inochi2D Project 7 8 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 9 Authors: Luna Nielsen 10 */ 11 module nulib.collections.stack; 12 import nulib.collections.internal.marray; 13 import numem.core.exception : enforce; 14 import numem.core.hooks; 15 import numem.core.traits; 16 import numem; 17 18 /** 19 A stack which owns the memory it contains. 20 */ 21 alias stack(T) = StackImpl!(T, true); 22 23 /** 24 A stack which does not own the memory it contains. 25 */ 26 alias weak_stack(T) = StackImpl!(T, false); 27 28 /** 29 A @nogc dynamic stack 30 */ 31 struct StackImpl(T, bool ownsMemory = true) { 32 @nogc: 33 private: 34 alias SelfType = typeof(this); 35 ManagedArray!(T, ownsMemory) memory; 36 37 public: 38 39 /** 40 Gets the internal memory slice. 41 */ 42 @property T[] values() @trusted nothrow pure { return memory; } 43 44 /** 45 The length of the vector 46 */ 47 @property size_t length() const @safe nothrow { return memory.length; } 48 49 /** 50 The capacity of the vector. 51 */ 52 @property size_t capacity() const @safe nothrow { return memory.capacity; } 53 54 /** 55 Whether the vector is empty. 56 */ 57 @property bool empty() const @safe nothrow { return memory.length == 0; } 58 59 ~this() { 60 61 // This essentially frees the memory. 62 memory.reserve(0); 63 memory.capacity = 0; 64 } 65 66 /** 67 Clears the vector, removing all elements from it. 68 */ 69 void clear() @safe { 70 memory.reserve(0); 71 } 72 73 /** 74 Pushes an element onto the stack. 75 76 Params: 77 value = The value to push. 78 */ 79 void push(T value) { 80 memory.resize(length+1); 81 82 static if (hasElaborateMove!T) { 83 this.memory[$-1] = value.move(); 84 } else { 85 this.memory[$-1] = value; 86 } 87 } 88 89 /// ditto 90 void opOpAssign(string op = "~")(auto ref T value) @trusted { 91 memory.resize(length+1); 92 93 static if (hasElaborateMove!T) { 94 this.memory[$-1] = value.move(); 95 } else { 96 this.memory[$-1] = value; 97 } 98 } 99 100 /** 101 Append a range to the stack. 102 103 Params: 104 other = the other range to append. 105 */ 106 void opOpAssign(string op = "~")(T[] other) @trusted { 107 size_t start = memory.length; 108 109 // Self-intersecting move. 110 if (memory.isMovingIntoSelf(other)) { 111 other = rangeCopy(other); 112 memory.resize(memory.length+other.length); 113 memory.moveRange(memory[start..$], other[0..$]); 114 other = other.nu_resize(0); 115 } 116 117 // Basic move. 118 memory.resize(memory.length+other.length); 119 memory.moveRange(memory[start..$], other[0..$]); 120 } 121 122 /** 123 Pops a value from the stack. 124 125 Returns: 126 The value popped. 127 128 Throws: 129 An exception if the stack is empty. 130 */ 131 T pop() { 132 enforce(memory.length > 0, "Stack is empty."); 133 134 T tmp = memory[$-1].move(); 135 memory.resize(length-1); 136 return tmp; 137 } 138 139 /** 140 Attempts to pop a value from the stack. 141 142 Params: 143 dst = The variable to store the popped value in. 144 145 Returns: 146 Whether the operation succeeded. 147 */ 148 bool tryPop(ref T dst) { 149 if (memory.length == 0) 150 return false; 151 152 dst = memory[$-1].move(); 153 memory.resize(length-1); 154 return true; 155 } 156 157 /** 158 Peeks a value in the stack. 159 160 Params: 161 offset = The offset from the top of the stack to peek. 162 163 Returns: 164 The value peeked. 165 166 Throws: 167 An exception if an out-of-range access is attempted. 168 */ 169 T peek(size_t offset) { 170 enforce(memory.length-offset < memory.length, "Out of range access."); 171 return memory[$-1]; 172 } 173 174 /** 175 Attempts to peek a value from the stack. 176 177 Params: 178 offset = The offset from the top of the stack to peek. 179 dst = The variable to store the peeked value in. 180 181 Returns: 182 Whether the operation succeeded. 183 */ 184 bool tryPeek(size_t offset, ref T dst) { 185 if (memory.length-offset > memory.length) 186 return false; 187 188 dst = memory[$-1]; 189 return true; 190 } 191 } 192 193 @(".push(value)") 194 unittest { 195 stack!int values; 196 values.push(1); 197 values.push(2); 198 values.push(3); 199 200 assert(values.values == [1, 2, 3]); 201 } 202 203 @(".pop()") 204 unittest { 205 stack!int values; 206 values.push(1); 207 values.push(2); 208 values.push(3); 209 210 assert(values.values == [1, 2, 3]); 211 assert(values.pop() == 3); 212 assert(values.values == [1, 2]); 213 } 214 215 @(".peek(offset)") 216 unittest { 217 stack!int values; 218 values.push(1); 219 values.push(2); 220 values.push(3); 221 222 assert(values.values == [1, 2, 3]); 223 assert(values.peek(1) == 3); 224 assert(values.values == [1, 2, 3]); 225 }