1 /** 2 Copyright: Copyright (c) 2015-2016 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 7 module voxelman.utils.queue; 8 9 import std.range; 10 11 // Any reference type items are set to Item.init when reused. 12 // Traversing with foreach does not modify range. You can remove elements during foreach using NodeAccessor. 13 // To use Queue as consumable range use valueRange prroperty. 14 // To use as reference range use slice operator -> queue[] 15 struct Queue(Item) 16 { 17 static struct NodeAccessor 18 { 19 ref Item value() @property 20 { 21 return node.data; 22 } 23 void remove() 24 { 25 queue.removeNode(prev, node); 26 } 27 28 private Node* prev; 29 private Node* node; 30 private Queue!(Item)* queue; 31 } 32 33 int opApply(int delegate(NodeAccessor) dg) 34 { 35 int result = 0; 36 Node* prev; 37 Node* pointer = first; 38 39 while(pointer) 40 { 41 auto accessor = NodeAccessor(prev, pointer, &this); 42 43 // advance pointers since accessor can remove current one 44 prev = pointer; 45 pointer = pointer.next; 46 47 result = dg(accessor); 48 if (result) 49 break; 50 } 51 52 return result; 53 } 54 55 void put(Item item) 56 { 57 Node* node; 58 if (firstFree !is null) 59 { 60 node = firstFree; 61 firstFree = firstFree.next; 62 node.next = null; 63 node.data = item; 64 } 65 else 66 { 67 node = new Node(item, null); 68 } 69 // first will be also null 70 if (last is null) 71 { 72 first = last = node; 73 } 74 else 75 { 76 last.next = node; 77 last = node; 78 } 79 80 ++length; 81 } 82 83 ValueRange valueRange() @property 84 { 85 return ValueRange(&this); 86 } 87 88 private static struct ValueRange 89 { 90 private Queue!(Item)* queue; 91 92 bool empty() @property 93 { 94 return queue.first is null; 95 } 96 97 Item front() @property 98 { 99 assert(!queue.empty); 100 return queue.first.data; 101 } 102 103 void popFront() 104 { 105 queue.popFrontImpl(); 106 } 107 } 108 109 RefRange opSlice() 110 { 111 return RefRange(first); 112 } 113 114 private static struct RefRange 115 { 116 private Node* pointer; 117 private Node* prev; 118 119 bool empty() @property 120 { 121 return pointer is null; 122 } 123 124 Item front() @property 125 { 126 assert(!empty); 127 return pointer.data; 128 } 129 130 void popFront() 131 { 132 assert(!empty); 133 prev = pointer; 134 pointer = pointer.next; 135 } 136 } 137 138 bool empty() @property 139 { 140 return first is null; 141 } 142 143 bool remove(Item item) 144 { 145 Node* prev; 146 Node* pointer = first; 147 148 while(pointer) 149 { 150 if (pointer.data == item) 151 { 152 removeNode(prev, pointer); 153 return true; 154 } 155 156 prev = pointer; 157 pointer = pointer.next; 158 } 159 160 return false; 161 } 162 163 private void popFrontImpl() 164 { 165 assert(!empty); 166 167 Node* node = first; 168 first = first.next; 169 170 freeNode(node); 171 172 if (first is null) 173 { 174 last = null; 175 } 176 --length; 177 } 178 179 private void removeNode(Node* prev, Node* pointer) 180 { 181 if (pointer == first) 182 popFrontImpl(); 183 else 184 { 185 Node* node = pointer; 186 187 if (pointer == last) 188 { 189 last = prev; 190 } 191 192 prev.next = node.next; 193 194 freeNode(node); 195 --length; 196 } 197 } 198 199 private void freeNode(Node* node) 200 { 201 import std.traits : hasIndirections; 202 static if (hasIndirections!Item) 203 node.data = Item.init; 204 205 node.next = firstFree; 206 firstFree = node; 207 } 208 209 Node* first; 210 Node* last; 211 Node* firstFree; 212 size_t length; 213 214 struct Node 215 { 216 Item data; 217 Node* next; 218 } 219 } 220 221 unittest 222 { 223 import std.algorithm : equal; 224 import std.experimental.logger; 225 Queue!int q; 226 q.put(1); 227 q.put(2); 228 q.put(3); 229 q.put(4); 230 assert(q.length == 4); 231 assert(equal(q[], [1,2,3,4])); 232 233 // test remove 234 q = typeof(q).init; 235 q.put(1); 236 q.put(2); 237 q.put(3); 238 q.put(4); 239 240 assert(q.remove(5) == false); 241 assert(q.remove(2)); 242 assert(q.length == 3); 243 q.remove(3); 244 assert(q.length == 2); 245 q.remove(1); 246 q.remove(4); 247 assert(q.empty); 248 249 // test remove from foreach 250 q = typeof(q).init; 251 q.put(1); 252 q.put(2); 253 q.put(3); 254 q.put(4); 255 256 uint i; 257 foreach(nodeAccess; q) 258 { 259 if (i == 0) 260 nodeAccess.remove(); 261 ++i; 262 } 263 264 assert(equal(q[], [2,3,4])); 265 266 i = 0; 267 foreach(nodeAccess; q) 268 { 269 if (i == 2) 270 nodeAccess.remove(); 271 ++i; 272 } 273 274 assert(equal(q[], [2,3])); 275 }