1 module cachetools.containers.lists; 2 3 private import std.experimental.allocator; 4 private import std.experimental.allocator.mallocator : Mallocator; 5 private import std.experimental.logger; 6 private import std.format; 7 8 /// 9 /// N-way multilist 10 struct MultiDList(T, int N, Allocator = Mallocator) 11 { 12 alias allocator = Allocator.instance; 13 struct Node { 14 T payload; 15 private: 16 Link[N] links; 17 Node* next(size_t i) @safe @nogc 18 { 19 return links[i].next; 20 } 21 Node* prev(size_t i) @safe @nogc 22 { 23 return links[i].prev; 24 } 25 alias payload this; 26 } 27 private 28 { 29 struct Link 30 { 31 Node* prev; 32 Node* next; 33 } 34 Node*[N] _heads; 35 Node*[N] _tails; 36 size_t _length; 37 38 } 39 size_t length() const pure nothrow @safe @nogc { 40 return _length; 41 } 42 43 Node* insert_last(T v) @safe nothrow 44 out 45 { 46 assert(_length>0); 47 } 48 do 49 { 50 auto n = make!(Node)(allocator, v); 51 static foreach(index;0..N) { 52 if ( _heads[index] is null ) { 53 _heads[index] = n; 54 } 55 n.links[index].prev = _tails[index]; 56 if ( _tails[index] !is null ) 57 { 58 _tails[index].links[index].next = n; 59 } 60 _tails[index] = n; 61 } 62 _length++; 63 return n; 64 } 65 66 void move_to_tail(Node* n, size_t i) @safe @nogc 67 in 68 { 69 assert(i < N); 70 assert(_length>0); 71 } 72 out 73 { 74 assert(_heads[i] !is null && _tails[i] !is null); 75 } 76 do 77 { 78 if ( n == _tails[i] ) { 79 return; 80 } 81 // unlink 82 if ( n.links[i].prev is null ) 83 { 84 _heads[i] = n.links[i].next; 85 } 86 else 87 { 88 n.links[i].prev.links[i].next = n.links[i].next; 89 } 90 if ( n.links[i].next is null ) 91 { 92 _tails[i] = n.links[i].prev; 93 } 94 else 95 { 96 n.links[i].next.links[i].prev = n.links[i].prev; 97 } 98 // insert back 99 if ( _heads[i] is null ) { 100 _heads[i] = n; 101 } 102 n.links[i].prev = _tails[i]; 103 if ( _tails[i] !is null ) 104 { 105 _tails[i].links[i].next = n; 106 } 107 n.links[i].next = null; 108 _tails[i] = n; 109 } 110 111 void remove(Node* n) nothrow @safe @nogc 112 { 113 if ( n is null || _length == 0 ) 114 { 115 return; 116 } 117 static foreach(i;0..N) { 118 if ( n.links[i].prev !is null ) { 119 n.links[i].prev.links[i].next = n.links[i].next; 120 } 121 if ( n.links[i].next !is null ) { 122 n.links[i].next.links[i].prev = n.links[i].prev; 123 } 124 if ( n == _tails[i] ) { 125 _tails[i] = n.links[i].prev; 126 } 127 if ( n == _heads[i] ) { 128 _heads[i] = n.links[i].next; 129 } 130 } 131 (() @trusted {dispose(allocator, n);})(); 132 _length--; 133 } 134 Node* tail(size_t i) pure nothrow @safe @nogc 135 { 136 return _tails[i]; 137 } 138 Node* head(size_t i) pure nothrow @safe @nogc 139 { 140 return _heads[i]; 141 } 142 void clear() nothrow @safe @nogc 143 { 144 while(_length>0) 145 { 146 auto n = _heads[0]; 147 remove(n); 148 } 149 } 150 } 151 152 @safe unittest { 153 import std.algorithm; 154 import std.stdio; 155 import std.range; 156 struct Person 157 { 158 string name; 159 int age; 160 } 161 MultiDList!(Person*, 2) mdlist; 162 Person[3] persons = [{"Alice", 11}, {"Bob", 9}, {"Carl", 10}]; 163 foreach(i; 0..persons.length) 164 { 165 mdlist.insert_last(&persons[i]); 166 } 167 enum NameIndex = 0; 168 enum AgeIndex = 1; 169 assert(mdlist.head(NameIndex).payload.name == "Alice"); 170 assert(mdlist.head(AgeIndex).payload.age == 11); 171 assert(mdlist.tail(NameIndex).payload.name == "Carl"); 172 assert(mdlist.tail(AgeIndex).payload.age == 10); 173 auto alice = mdlist.head(NameIndex); 174 auto bob = alice.next(NameIndex); 175 auto carl = bob.next(NameIndex); 176 mdlist.move_to_tail(alice, AgeIndex); 177 assert(mdlist.tail(AgeIndex).payload.age == 11); 178 mdlist.remove(alice); 179 assert(mdlist.head(NameIndex).payload.name == "Bob"); 180 assert(mdlist.tail(NameIndex).payload.name == "Carl"); 181 assert(mdlist.head(AgeIndex).payload.age == 9); 182 assert(mdlist.tail(AgeIndex).payload.age == 10); 183 } 184 185 struct DList(T, Allocator = Mallocator) { 186 this(this) @disable; 187 struct Node(T) { 188 T payload; 189 private Node!T* prev; 190 private Node!T* next; 191 alias payload this; 192 } 193 private { 194 alias allocator = Allocator.instance; 195 Node!T* _head; 196 Node!T* _tail; 197 ulong _length; 198 } 199 200 invariant { 201 assert 202 ( 203 ( _length > 0 && _head !is null && _tail !is null) || 204 ( _length == 0 && _tail is null && _tail is null) || 205 ( _length == 1 && _tail == _head && _head !is null ), 206 "length: %s, head: %s, tail: %s".format(_length, _head, _tail) 207 ); 208 } 209 210 ulong length() const pure nothrow @safe @nogc { 211 return _length; 212 } 213 214 Node!T* insert_last(T v) @safe nothrow 215 out 216 { 217 assert(_length>0); 218 assert(_head !is null && _tail !is null); 219 } 220 do 221 { 222 auto n = make!(Node!T)(allocator); 223 n.payload = v; 224 if ( _head is null ) { 225 _head = n; 226 } 227 n.prev = _tail; 228 if ( _tail !is null ) 229 { 230 _tail.next = n; 231 } 232 _tail = n; 233 _length++; 234 return n; 235 } 236 237 alias insertFront = insert_first; 238 Node!T* insert_first(T v) @safe nothrow 239 out 240 { 241 assert(_length>0); 242 assert(_head !is null && _tail !is null); 243 } 244 do 245 { 246 auto n = make!(Node!T)(allocator); 247 n.payload = v; 248 if ( _tail is null ) { 249 _tail = n; 250 } 251 n.next = _head; 252 if ( _head !is null ) 253 { 254 _head.prev = n; 255 } 256 _head = n; 257 _length++; 258 return n; 259 } 260 261 bool remove(Node!T* n) @safe @nogc 262 in {assert(_length>0);} 263 do { 264 if ( n.prev ) { 265 n.prev.next = n.next; 266 } 267 if ( n.next ) { 268 n.next.prev = n.prev; 269 } 270 if ( n == _tail ) { 271 _tail = n.prev; 272 } 273 if ( n == _head ) { 274 _head = n.next; 275 } 276 (() @trusted {dispose(allocator, n);})(); 277 _length--; 278 return true; 279 } 280 281 void move_to_tail(Node!T* n) @safe @nogc 282 in 283 { 284 assert(_length > 0); 285 assert(_head !is null && _tail !is null); 286 } 287 out { 288 assert(_tail == n && n.next is null); 289 } 290 do 291 { 292 if ( n == _tail ) { 293 return; 294 } 295 // unlink 296 if ( n.prev is null ) 297 { 298 _head = n.next; 299 } 300 else 301 { 302 n.prev.next = n.next; 303 } 304 if ( n.next is null ) 305 { 306 _tail = n.prev; 307 } 308 else 309 { 310 n.next.prev = n.prev; 311 } 312 // insert back 313 if ( _head is null ) { 314 _head = n; 315 } 316 n.prev = _tail; 317 if ( _tail !is null ) 318 { 319 _tail.next = n; 320 } 321 n.next = null; 322 _tail = n; 323 324 ////debug(cachetools) tracef("n: %s".format(*n)); 325 //assert(n.next !is null); 326 ////debug tracef("m-t-t: %s, tail: %s", *n, *_tail); 327 //assert(n.next, "non-tail entry have no 'next' pointer?"); 328 //if ( _head == n ) { 329 // assert(n.prev is null); 330 // _head = n.next; 331 //} else { 332 // n.prev.next = n.next; 333 //} 334 //// move this node to end 335 //n.next.prev = n.prev; 336 //n.next = null; 337 //tail.next = n; 338 //_tail = n; 339 } 340 341 Node!T* head() @safe @nogc nothrow { 342 return _head; 343 } 344 Node!T* tail() @safe @nogc nothrow { 345 return _tail; 346 } 347 } 348 349 struct SList(T, Allocator = Mallocator) { 350 this(this) @safe 351 { 352 // copy items 353 _Node!T* __newFirst, __newLast; 354 auto f = _first; 355 while(f) 356 { 357 auto v = f.v; 358 auto n = make!(_Node!T)(allocator, v); 359 if ( __newLast !is null ) { 360 __newLast._next = n; 361 } else { 362 __newFirst = n; 363 } 364 __newLast = n; 365 f = f._next; 366 } 367 _first = __newFirst; 368 _last = __newLast; 369 } 370 371 package { 372 struct _Node(T) { 373 T v; 374 _Node!T *_next; 375 } 376 alias allocator = Allocator.instance; 377 378 ulong _length; 379 _Node!T *_first; 380 _Node!T *_last; 381 } 382 383 invariant { 384 assert 385 ( 386 ( _length > 0 && _first !is null && _last !is null) || 387 ( _length == 0 && _first is null && _last is null) 388 ); 389 } 390 ~this() 391 { 392 clear(); 393 } 394 ulong length() const pure @nogc @safe nothrow { 395 return _length; 396 } 397 398 bool empty() @nogc @safe const { 399 return _length == 0; 400 } 401 402 T front() pure @nogc @safe { 403 return _first.v; 404 } 405 406 T back() pure @nogc @safe { 407 return _last.v; 408 } 409 410 T popFront() @nogc @safe nothrow 411 in { assert(_first !is null); } 412 do { 413 T v = _first.v; 414 auto next = _first._next; 415 (() @trusted {dispose(allocator, _first);})(); 416 _first = next; 417 if ( _first is null ) { 418 _last = null; 419 } 420 _length--; 421 return v; 422 } 423 void clear() @nogc @safe { 424 _Node!T* n = _first; 425 while( n !is null ) { 426 auto next = n._next; 427 (() @trusted {dispose(allocator, n);})(); 428 n = next; 429 } 430 } 431 private struct Range(T) { 432 private { 433 _Node!T *current; 434 } 435 auto front() pure nothrow @safe @nogc @property { 436 return ¤t.v; 437 } 438 void popFront() @safe @nogc nothrow { 439 current = current._next; 440 } 441 bool empty() pure const nothrow @safe @nogc @property { 442 return current is null; 443 } 444 } 445 alias opSlice = range; 446 auto range() { 447 return Range!T(_first); 448 } 449 450 void insertFront(T v) @safe nothrow 451 out{ assert(_first !is null && _last !is null);} 452 do { 453 auto n = make!(_Node!T)(allocator, v); 454 if ( _first !is null ) { 455 n._next = _first; 456 } 457 _first = n; 458 if ( _last is null ) { 459 _last = n; 460 } 461 _length++; 462 } 463 464 void insertBack(T v) @safe nothrow 465 out{ assert(_first !is null && _last !is null);} 466 do { 467 auto n = make!(_Node!T)(allocator, v); 468 if ( _last !is null ) { 469 _last._next = n; 470 } else { 471 _first = n; 472 } 473 _last = n; 474 _length++; 475 } 476 bool remove_by_predicate(scope bool delegate(T) @safe @nogc nothrow f) @nogc @trusted nothrow { 477 bool removed; 478 _Node!T *current = _first; 479 _Node!T *prev = null; 480 while (current !is null) { 481 auto next = current._next; 482 if ( !f(current.v) ) { 483 prev = current; 484 current = next; 485 continue; 486 } 487 // do remove 488 _length--; 489 removed = true; 490 dispose(allocator, current); 491 if ( prev is null ) { 492 _first = next; 493 } else { 494 prev._next = next; 495 } 496 if ( next is null ) { 497 _last = prev; 498 } 499 } 500 return removed; 501 } 502 } 503 504 @safe @nogc unittest { 505 SList!int l; 506 assert(l.length() == 0); 507 l.insertBack(1); 508 assert(l.front() == 1); 509 assert(l.length() == 1); 510 l.insertBack(2); 511 assert(l.front() == 1); 512 assert(l.back() == 2); 513 assert(l.length() == 2); 514 //log(l.range()); 515 l.popFront(); 516 assert(l.front() == 2); 517 assert(l.back() == 2); 518 assert(l.length() == 1); 519 l.insertBack(3); 520 l.insertBack(4); 521 //foreach(v; l[]){ 522 // log("v=%d\n", *v); 523 //} 524 //log("---\n"); 525 bool removed; 526 removed = l.remove_by_predicate((n){return n==2;}); 527 foreach(v; l[]){ 528 //log("v=%d\n", *v); 529 } 530 assert(removed); 531 assert(l.length()==2); 532 //log("---\n"); 533 removed = l.remove_by_predicate((n){return n==4;}); 534 foreach(v; l[]){ 535 //log("v=%d\n", *v); 536 } 537 assert(removed); 538 assert(l.length()==1); 539 //log("---\n"); 540 removed = l.remove_by_predicate((n){return n==3;}); 541 foreach(v; l[]){ 542 //log("v=%d\n", *v); 543 } 544 assert(removed); 545 assert(l.length()==0); 546 //log("---\n"); 547 removed = l.remove_by_predicate((n){return n==3;}); 548 foreach(v; l[]){ 549 //log("v=%d\n", *v); 550 } 551 assert(!removed); 552 assert(l.length()==0); 553 auto l1 = SList!int(); 554 foreach(i;0..100) { 555 l1.insertBack(i); 556 } 557 while(l.length) { 558 l1.popFront(); 559 } 560 foreach(i;0..100) { 561 l1.insertFront(i); 562 } 563 while(l.length) { 564 l1.popFront(); 565 } 566 } 567 568 @safe unittest { 569 DList!int dlist; 570 auto n1 = dlist.insert_last(1); 571 assert(dlist.length == 1); 572 dlist.remove(n1); 573 assert(dlist.length == 0); 574 575 n1 = dlist.insert_first(1); 576 assert(dlist.length == 1); 577 dlist.remove(n1); 578 assert(dlist.length == 0); 579 580 n1 = dlist.insert_first(1); 581 auto n2 = dlist.insert_last(2); 582 assert(dlist.length == 2); 583 dlist.move_to_tail(n1); 584 assert(dlist.head.payload == 2); 585 assert(dlist.tail.payload == 1); 586 }