1 module anansi.container.list; 2 3 import std.algorithm, std.conv, std.range, std.stdio, std.traits, std.typecons; 4 5 /** 6 * A doubley-linked list with a deliberately leaky abstraction. This container 7 * is explicitly for use with the anansi graph classes. 8 */ 9 public struct List(T) { 10 private: 11 this(size_t s, Node* f, Node* b) { 12 _size = s; 13 _front = f; 14 _back = b; 15 } 16 17 public: 18 this(this) { 19 auto chain = copyChain(_front, _back); 20 _front = chain[0]; 21 _back = chain[1]; 22 } 23 24 public: 25 /** 26 * The internal data storage object 27 */ 28 static struct Node { 29 protected this(T v, Node* p, Node* n) { 30 value = v; 31 p = prev; 32 n = next; 33 } 34 35 public @property inout(Node*) nextNode() inout { 36 return next; 37 } 38 39 protected Node* prev; 40 protected Node* next; 41 public T value; 42 43 public @property ref inout(T) valueRef() inout { 44 return value; 45 } 46 } 47 48 static struct ConstRange { 49 public this(const(Node)* front, const(Node)* back) { 50 _front = front; 51 _back = back; 52 } 53 54 @property bool empty() const { 55 return (_front is null); 56 } 57 58 @property ref const(T) front() const 59 in { 60 assert (_front !is null); 61 } 62 body { 63 return (_front.value); 64 } 65 66 void popFront() 67 in { 68 assert (_front !is null); 69 } 70 body { 71 if (_front is _back) 72 _front = _back = null; 73 else 74 _front = _front.next; 75 } 76 77 @property ConstRange save() { return this; } 78 79 public @property const(Node)* frontNode() { 80 return _front; 81 } 82 83 private: 84 const(Node)* _front; 85 const(Node)* _back; 86 } 87 88 static struct Range { 89 public this(Node* front, Node* back) { 90 _front = front; 91 _back = back; 92 } 93 94 @property bool empty() const { 95 return (_front is null); 96 } 97 98 @property ref T front() 99 in { 100 assert (_front !is null); 101 } 102 body { 103 return _front.value; 104 } 105 106 void popFront() 107 in { 108 assert (_front !is null); 109 } 110 body { 111 if (_front is _back) 112 _front = _back = null; 113 else 114 _front = _front.next; 115 } 116 117 @property Range save() { return this; } 118 119 public @property Node* frontNode() { return _front; } 120 121 private: 122 Node* _front; 123 Node* _back; 124 } 125 126 invariant() { 127 assert (_size >= 0, "Size must remain positive."); 128 } 129 130 this(Stuff)(Stuff stuff) if(isInputRange!Stuff && 131 isImplicitlyConvertible!(ElementType!Range, T)) { 132 foreach(s; stuff) 133 insertBack(s); 134 } 135 136 Range opSlice() { return Range(_front, _back); } 137 ConstRange opSlice() const { return ConstRange(_front, _back); } 138 139 Node* insertBack(T value) 140 out(result) { 141 assert (result !is null); 142 } 143 body { 144 auto newNode = new Node(value, null, null); 145 if (empty) { 146 _front = _back = newNode; 147 } 148 else { 149 newNode.prev = _back; 150 _back.next = newNode; 151 _back = newNode; 152 } 153 154 _size += 1; 155 return newNode; 156 } 157 158 void remove(Node* node) 159 in { 160 assert (node !is null); 161 assert (_size > 0); 162 } 163 out { 164 assert (_size >= 0); 165 } 166 body { 167 if (node is _front && node is _back) { 168 _front = _back = null; 169 } 170 else if (node is _front) { 171 _front = _front.next; 172 _front.prev = null; 173 } 174 else if (node is _back) { 175 _back = _back.prev; 176 _back.next = null; 177 } 178 else { 179 node.next.prev = node.prev; 180 node.prev.next = node.next; 181 } 182 --_size; 183 } 184 185 ref inout(T) front() inout 186 in { 187 assert (_front !is null); 188 } 189 body { 190 return _front.value; 191 } 192 193 @property inout(Node*) frontNode() inout { return _front; } 194 195 ref inout(T) back() inout 196 in { 197 assert (_back !is null); 198 } 199 body { 200 return _back.value; 201 } 202 203 @property inout(Node*) backNode() inout { return _back; } 204 205 @property size_t length() const { 206 return _size; 207 } 208 209 @property bool empty() const { 210 return _back is null; 211 } 212 213 @property List dup() { 214 auto newChain = copyChain(_front, _back); 215 216 return List(_size, newChain[0], newChain[1]); 217 } 218 219 private: 220 static auto copyChain(Node* front, Node* back) { 221 Node* newFront = null; 222 Node* newBack = null; 223 224 if (front !is null) { 225 newFront = newBack = new Node(front.value, null, null); 226 for(auto n = front.next; n != null; n = n.next) { 227 auto newNode = new Node(n.value, newBack, null); 228 newBack.next = newNode; 229 newBack = newNode; 230 } 231 } 232 233 return tuple(newFront, newBack); 234 } 235 236 private: 237 Node* _front; 238 Node* _back; 239 size_t _size; 240 } 241 242 unittest { 243 writeln("List: default construction."); 244 List!int l; 245 assert (l.length == 0); 246 } 247 248 unittest { 249 writeln("List: construct from a Range."); 250 auto data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 251 auto list = List!int(data); 252 assert (list.length == data.length, 253 "Bad length value, expected: " ~ to!string(data.length) ~ 254 ", got: " ~ to!string(list.length)); 255 256 foreach(x; zip(data, list[])) { 257 assert(x[0] == x[1], 258 "Bad list value, expected: " ~ to!string(x[0]) ~ 259 ", got: " ~ to!string(x[1])); 260 } 261 } 262 263 unittest { 264 writeln("List: empty range correctly marked as empty."); 265 List!int l; 266 assert(l[].empty, "An empty range should return empty == true"); 267 } 268 269 unittest { 270 writeln("List: remove only element in list."); 271 List!char l; 272 auto pn = l.insertBack('a'); 273 l.remove(pn); 274 275 assert (l.length == 0, 276 "Length must be 0 after removing the only element."); 277 278 size_t count; 279 foreach(c; l[]) 280 ++count; 281 282 assert(count == 0, 283 "Iterting over an empty list should never invoke loop body."); 284 } 285 286 unittest { 287 writeln("List: remove first element in list."); 288 List!int l; 289 auto n0 = l.insertBack(0); 290 auto n1 = l.insertBack(1); 291 auto n2 = l.insertBack(2); 292 293 l.remove(n0); 294 295 assert (l.length == 2); 296 297 foreach(x; zip([1, 2], l[])) { 298 assert(x[0] == x[1], 299 "Bad list value, expected: " ~ to!string(x[0]) ~ 300 ", got: " ~ to!string(x[1])); 301 } 302 } 303 304 unittest { 305 writeln("List: remove last element in list."); 306 List!int l; 307 auto n0 = l.insertBack(0); 308 auto n1 = l.insertBack(1); 309 auto n2 = l.insertBack(2); 310 311 l.remove(n2); 312 313 assert (l.length == 2); 314 315 foreach(x; zip([0, 1], l[])) { 316 assert(x[0] == x[1], 317 "Bad list value, expected: " ~ to!string(x[0]) ~ 318 ", got: " ~ to!string(x[1])); 319 } 320 } 321 322 unittest { 323 writeln("List: remove middle element in list."); 324 List!int l; 325 auto n0 = l.insertBack(0); 326 auto n1 = l.insertBack(1); 327 auto n2 = l.insertBack(2); 328 329 l.remove(n1); 330 331 assert (l.length == 2); 332 333 foreach(x; zip([0, 2], l[])) { 334 assert(x[0] == x[1], 335 "Bad list value, expected: " ~ to!string(x[0]) ~ 336 ", got: " ~ to!string(x[1])); 337 } 338 339 assert (l.front() == 0); 340 assert (l.back() == 2); 341 } 342 343 unittest { 344 writeln("List: remove all-but-one element in a list."); 345 List!int l; 346 auto n0 = l.insertBack(0); 347 auto n1 = l.insertBack(1); 348 auto n2 = l.insertBack(2); 349 350 l.remove(n1); 351 l.remove(n2); 352 353 assert (l.length == 1); 354 355 foreach(x; zip([0], l[])) { 356 assert(x[0] == x[1], 357 "Bad list value, expected: " ~ to!string(x[0]) ~ 358 ", got: " ~ to!string(x[1])); 359 } 360 } 361 362 unittest { 363 writeln("List: remove all elements in a list."); 364 List!int l; 365 auto n0 = l.insertBack(0); 366 auto n1 = l.insertBack(1); 367 auto n2 = l.insertBack(2); 368 369 l.remove(n1); 370 l.remove(n2); 371 l.remove(n0); 372 373 assert (l.length == 0); 374 size_t count = 0; 375 foreach(_; l[]) { 376 ++count; 377 } 378 379 assert (count == 0, 380 "iterating over an empty range should never hit the loop body."); 381 }