1 /++ 2 Some data structures 3 +/ 4 module utils.ds; 5 6 import std.file; 7 import std.stdio; 8 import std.conv : to; 9 import utils.misc; 10 11 /// Used to read some data type as `ubyte[x]` 12 union ByteUnion(T){ 13 T data; /// data 14 ubyte[T.sizeof] array; /// array of bytes 15 /// constructor 16 this(T data){ 17 this.data = data; 18 } 19 /// ditto 20 this(ubyte[T.sizeof] array){ 21 this.array = array; 22 } 23 } 24 25 /// Use to manage dynamic arrays that frequently change lengths 26 /// 27 /// Provides more functionality for arrays, like searching in arrays, removing elements... 28 class List(T){ 29 private: 30 T[] list; /// the actual list 31 uinteger taken=0; /// how many elements are actually stored in the list 32 uinteger extraAlloc; /// how many extra elements to make space for when list length runs out 33 uinteger _seek = 0; /// where to read/write next if index isn't specified 34 public: 35 /// constructor 36 /// 37 /// extraCount is the number of extra space to make for new elements when making "room" for new elements 38 this(uinteger extraCount = 4){ 39 extraAlloc = extraCount; 40 } 41 /// appends an element to the list 42 void append(T dat){ 43 if (taken==list.length){ 44 list.length+=extraAlloc; 45 } 46 list[taken] = dat; 47 taken++; 48 } 49 /// appends an array to the list 50 void append(T[] dat){ 51 list = list[0 .. taken]~dat.dup; 52 taken = list.length; 53 } 54 /// Changes the value of element at an index. 55 /// 56 /// Arguments: 57 /// `dat` is the new data 58 /// 59 /// Returns: false if index is out of bounds, true if successful 60 bool set(uinteger index, T dat){ 61 if (index >= taken){ 62 return false; 63 } 64 list[index]=dat; 65 return true; 66 } 67 /// Changes the value of element at seek. Seek is increased by one 68 /// 69 /// Arguments: 70 /// `value` is the new value 71 /// 72 /// Returns: true if successful, false if seek if out of bounds 73 bool write (T value){ 74 if (_seek >= taken){ 75 return false; 76 } 77 list[_seek] = value; 78 _seek ++; 79 return true; 80 } 81 /// Changes the value of elements starting at index=seek. Seek is increased by number of elements affected 82 /// 83 /// Arguments: 84 /// `elements` is the array of new values of the elements 85 /// 86 /// Returns: true if successful, false if not enough elements in list or seek out of bounds 87 bool write (T[] elements){ 88 if (_seek + elements.length >= taken){ 89 return false; 90 } 91 list[_seek .. _seek + elements.length] = elements.dup; 92 _seek += elements.length; 93 return true; 94 } 95 /// Reads an element at index=seek. Seek is increased by one 96 /// 97 /// Returns: the read element 98 /// 99 /// Throws: Exception if seek is out of bounds 100 T read(){ 101 if (_seek >= taken){ 102 throw new Exception ("seek out of bounds"); 103 } 104 T r = list[_seek]; 105 _seek ++; 106 return r; 107 } 108 /// Reads a number of elements starting at index=seek. Seek is increased by number of elements 109 /// 110 /// Arguments: 111 /// `buffer` is the array into which the elements will be read. set `buffer.length` to number of elements to read 112 /// 113 /// Returns: number of elements read into the buffer 114 uinteger read(ref T[] buffer){ 115 if (_seek >= taken || buffer.length == 0){ 116 return 0; 117 } 118 uinteger count = _seek + buffer.length < taken ? buffer.length : taken - _seek; 119 buffer = list[_seek .. _seek + count].dup; 120 _seek += count; 121 return count; 122 } 123 /// The seek position 124 @property uinteger seek(){ 125 return _seek; 126 } 127 /// ditto 128 @property uinteger seek(uinteger newSeek){ 129 return _seek = newSeek; 130 } 131 /// Removes last elements(s) starting from an index 132 /// 133 /// Arguments: 134 /// `count ` is number of elements to remove 135 /// 136 /// Returns: false if range is out of bounds, true if successful 137 bool remove(uinteger index, uinteger count=1){ 138 if (index + count >= taken){ 139 return false; 140 } 141 integer i; 142 integer till=taken-count; 143 for (i=index;i<till;i++){ 144 list[i] = list[i+count]; 145 } 146 list.length-=count; 147 taken-=count; 148 return true; 149 } 150 /// Removes number of elements from end of list 151 /// 152 /// Returns: true if successful, false if not enough elements to remove 153 bool removeLast(uinteger count = 1){ 154 if (count > taken){ 155 return false; 156 } 157 taken -= count; 158 return true; 159 } 160 /// shrinks the size of the list, removing last elements. 161 /// 162 /// Returns: true if shrunk, false if not for example if `newSize` was greater than actual size 163 bool shrink(uinteger newSize){ 164 if (newSize < taken){ 165 list.length=newSize; 166 taken = list.length; 167 return true; 168 } 169 return false; 170 } 171 /// Returns: how many elements can be appended before list length needs to increase 172 @property uinteger freeSpace(){ 173 return list.length - taken; 174 } 175 /// make more free space for new elements, or reduce it. To reduce, use n as negative. To decrease by 2, `n=-2` 176 /// 177 /// Returns: true if done, false if not done, for example if there wasn't enough free space in list to be removed 178 bool setFreeSpace(integer n){ 179 if (n < 0 && -n > list.length - taken){ 180 return false; 181 } 182 try{ 183 list.length = list.length + n; 184 }catch (Exception e){ 185 .destroy (e); 186 return false; 187 } 188 return true; 189 } 190 /// removes the free space, if any, for adding new elements. Call this when done with adding to list. 191 void clearFreeSpace(){ 192 list.length = taken; 193 } 194 /// Inserts an array into this list 195 /// 196 /// Returns: true if done, false if index out of bounds, or not done 197 bool insert(uinteger index, T[] dat){ 198 if (index >= taken){ 199 return false; 200 } 201 list = list[0 .. index] ~ dat.dup ~ list[index .. taken]; 202 taken = list.length; 203 return true; 204 } 205 /// Inserts an element into this list 206 /// 207 /// Returns: true if done, false if index out of bounds, or not done 208 bool insert(uinteger index, T dat){ 209 if (index >= taken){ 210 return false; 211 } 212 list = list[0 .. index] ~ dat ~ list[index .. taken]; 213 taken = list.length; 214 return true; 215 } 216 /// Writes the list to a file. 217 /// 218 /// Arguemnts: 219 /// `s` is the filename 220 /// `sp` is the separator, it will be added to the end of each list-element 221 /// 222 /// Returns: true if done, false if not due to some Exception 223 bool saveFile(string s, T sp){ 224 try{ 225 File f = File(s,"w"); 226 uinteger i; 227 for (i=0;i<taken;i++){ 228 f.write(list[i],sp); 229 } 230 f.close; 231 }catch (Exception e){ 232 .destroy(e); 233 return false; 234 } 235 return true; 236 } 237 /// Reads an element at an index 238 /// 239 /// Returns: the element read 240 /// 241 /// Throws: Exception if index out of bounds 242 T read(uinteger index){ 243 if (index >= taken){ 244 throw new Exception("index out of bounds"); 245 } 246 return list[index]; 247 } 248 /// Read a slice from the list. 249 /// 250 /// Returns: the elements read 251 /// 252 /// Throws: Exception if index out of bounds 253 T[] read(uinteger index,uinteger i2){ 254 if (index >= taken){ 255 throw new Exception("index out of bounds"); 256 } 257 return list[index .. i2].dup; 258 } 259 /// Returns: pointer to element at an index 260 /// 261 /// Be careful that the pointer might not be valid after the list has been resized, so try only to use it after all appending is done 262 /// 263 /// Throws: Exception if index out of bounds 264 T* readPtr(uinteger index){ 265 if (index >= taken){ 266 throw new Exception ("index out of bounds"); 267 } 268 return &(list[index]); 269 } 270 /// Reads the last element in list. 271 /// 272 /// Returns: the last element in list 273 /// 274 /// Throws: Exception if list length is zero 275 T readLast(){ 276 if (taken == 0){ 277 throw new Exception ("List has no elements, can not readLast"); 278 } 279 return list[taken-1]; 280 } 281 /// Reads number of elements from end of list 282 /// 283 /// Returns: the elements read 284 /// 285 /// Throws: Exception if not enough elements i.e range out of bounds 286 T[] readLast(uinteger count){ 287 if (count > taken){ 288 throw new Exception ("range out of bounds"); 289 } 290 return list[taken-count..taken].dup; 291 } 292 /// Returns: length of the list 293 @property integer length(){ 294 return taken; 295 } 296 /// Exports this list into a array 297 /// 298 /// Returns: the array containing the elements in this list 299 T[] toArray(){ 300 return list[0 .. taken].dup; 301 } 302 /// Loads list from an array 303 void loadArray(T[] newList){ 304 uinteger i; 305 list = newList.dup; 306 taken = newList.length; 307 _seek = 0; 308 } 309 /// empties the list 310 void clear(){ 311 list = []; 312 taken = 0; 313 _seek = 0; 314 } 315 /// Returns: index of the first matching element. -1 if not found 316 /// 317 /// Arguments: 318 /// `dat` is the element to search for 319 /// `i` is the index from where to start, default is 0 320 /// `forward` if true, searches in a forward direction, from lower index to higher 321 integer indexOf(bool forward=true)(T dat, integer i=0){ 322 static if (forward){ 323 for (;i<taken;i++){ 324 if (list[i]==dat){break;} 325 if (i==taken-1){i=-1;break;} 326 } 327 }else{ 328 for (;i>=0;i--){ 329 if (list[i]==dat){break;} 330 if (i==0){i=-1;break;} 331 } 332 } 333 if (taken==0){ 334 i=-1; 335 } 336 return i; 337 } 338 } 339 /// 340 unittest{ 341 List!ubyte list = new List!ubyte(4); 342 //`List.insert` and `List.add` and `List.toArray` 343 list.append(0); 344 list.append(1); 345 list.insert(1, 2); 346 assert(list.toArray() == [0, 2, 1]); 347 //`List.indexOf` 348 assert(list.indexOf(1) == 2); 349 //`List.clear` 350 list.clear; 351 assert(list.length == 0); 352 //`List.loadArray` 353 list.loadArray([0, 1, 2, 3]); 354 assert(list.length == 4); 355 assert(list.indexOf(3) == 3); 356 //`List.addArray` 357 list.append([4, 5, 6, 7, 8]); 358 assert(list.length == 9); 359 //`List.set` and `List.read` 360 list.set(0, 1); 361 assert(list.read(0) == 1); 362 //`List.readLast` 363 assert(list.readLast() == 8); 364 assert(list.readLast(2) == [7, 8]); 365 //`List.readRange` 366 assert(list.read(0, 2) == [1, 1]); 367 //`List.remove` 368 list.remove(0, 2); 369 assert(list.read(0) == 2); 370 //`List.removeLast` 371 list.removeLast(2); 372 assert(list.readLast() == 6); 373 //`List.freeSpace` 374 list.clear; 375 foreach (i; cast(ubyte[])[0,1,2]) 376 list.append(i); 377 assert(list.freeSpace == 1, to!string(list.freeSpace)); 378 list.append(3); 379 assert(list.freeSpace == 0); 380 list.setFreeSpace(6); 381 assert(list.freeSpace == 6 && list.length == 4); 382 list.setFreeSpace(-3); 383 assert(list.freeSpace == 3); 384 assert(list.setFreeSpace(-10) == false); 385 //reading/writing with seek 386 list.clear; 387 assert(list.seek == 0); 388 list.append([0,1,2,3,4,5,6,7,8]); 389 assert(list.seek == 0); 390 ubyte[] buffer; 391 buffer.length = 4; 392 assert(list.read(buffer) == 4); 393 assert(buffer == [0,1,2,3]); 394 assert(list.seek == 4); 395 assert(list.read == 4); 396 assert(list.write(5) == true); 397 assert(list.read(buffer) == 3); 398 assert(buffer[0 .. 3] == [6,7,8]); 399 assert(list.seek == 9); 400 //`List.readPtr` 401 list.clear; 402 list.append ([0,1,2,3,4,5]); 403 ubyte* ptr = list.readPtr(5); 404 *ptr = 4; 405 assert (list.toArray == [0,1,2,3,4,4]); 406 407 destroy(list); 408 } 409 410 /// A basic stack with push, and pop 411 class Stack(T){ 412 private: 413 struct stackItem(T){ 414 T data; /// the data this item holds 415 stackItem* prev; /// pointer to previous stackItem 416 } 417 stackItem!(T)* lastItemPtr; 418 uinteger itemCount; 419 public: 420 this(){ 421 lastItemPtr = null; 422 itemCount = 0; 423 } 424 ~this(){ 425 clear; 426 } 427 /// Appends an item to the stack 428 void push(T item){ 429 stackItem!(T)* newItem = new stackItem!T; 430 (*newItem).data = item; 431 (*newItem).prev = lastItemPtr; 432 lastItemPtr = newItem; 433 //increase count 434 itemCount ++; 435 } 436 /// Appends an array of items to the stack 437 void push(T[] items){ 438 // put them all in stackItem[] 439 stackItem!(T)*[] newItems; 440 newItems.length = items.length; 441 for (uinteger i = 0; i < items.length; i ++){ 442 newItems[i] = new stackItem!T; 443 (*newItems[i]).data = items[i]; 444 } 445 // make them all point to their previous item, except for the first one, which should point to `lastItemPtr` 446 for (uinteger i = newItems.length - 1; i > 0; i --){ 447 (*newItems[i]).prev = newItems[i-1]; 448 } 449 (*newItems[0]).prev = lastItemPtr; 450 lastItemPtr = newItems[newItems.length - 1]; 451 //increase count 452 itemCount += newItems.length; 453 } 454 /// pops an item from stack 455 /// 456 /// Returns: the item poped 457 /// 458 /// Throws: Exception if stack is empty 459 T pop(){ 460 // make sure its not null 461 if (lastItemPtr !is null){ 462 T r = (*lastItemPtr).data; 463 // delete it from stack 464 stackItem!(T)* prevItem = (*lastItemPtr).prev; 465 destroy(*lastItemPtr); 466 lastItemPtr = prevItem; 467 //decrease count 468 itemCount --; 469 return r; 470 }else{ 471 throw new Exception("Cannot pop from empty stack"); 472 } 473 } 474 /// Reads and removes an array of items from the stack, 475 /// 476 /// Throws: Exception if there are not enough items in stack 477 /// 478 /// Returns: the items read 479 /// 480 /// Arguments: 481 /// `count` is the number of elements to return 482 /// `reverse`, if true, elements are read in reverse, last-pushed is last in array 483 T[] pop(bool reverse=false)(uinteger count){ 484 //make sure there are enough items 485 if (itemCount >= count){ 486 T[] r; 487 r.length = count; 488 stackItem!(T)* ptr = lastItemPtr; 489 static if (reverse){ 490 for (integer i = count-1; i >= 0; i --){ 491 r[i] = (*ptr).data; 492 ptr = (*ptr).prev; 493 // delete this item 494 .destroy(*lastItemPtr); 495 lastItemPtr = ptr; 496 } 497 }else{ 498 for (uinteger i = 0; i < count; i ++){ 499 r[i] = (*ptr).data; 500 ptr = (*ptr).prev; 501 //delete it 502 .destroy(*lastItemPtr); 503 lastItemPtr = ptr; 504 } 505 } 506 //decrease count 507 itemCount -= r.length; 508 return r; 509 }else{ 510 throw new Exception("Not enough items in stack"); 511 } 512 } 513 /// Empties the stack, pops all items 514 void clear(){ 515 // go through all items and delete em 516 stackItem!(T)* ptr; 517 ptr = lastItemPtr; 518 while (ptr !is null){ 519 stackItem!(T)* prevPtr = (*ptr).prev; 520 destroy(*ptr); 521 ptr = prevPtr; 522 } 523 lastItemPtr = null; 524 itemCount = 0; 525 } 526 /// Number of items in stack 527 @property uinteger count(){ 528 return itemCount; 529 } 530 } 531 /// 532 unittest{ 533 Stack!ubyte stack = new Stack!ubyte; 534 //`Stack.push` and `Stack.pop` 535 stack.push(0); 536 stack.push([1, 2]); 537 assert(stack.pop == 2); 538 assert(stack.pop(2) == [1, 0]); 539 stack.push([1, 0]); 540 assert(stack.pop!(true)(2) == [1, 0]); 541 //`Stack.clear` && `Stack.count` 542 stack.push(0); 543 assert(stack.count == 1); 544 stack.clear; 545 assert(stack.count == 0); 546 stack.destroy; 547 } 548 549 /// A FIFO (First In is First Out, first element pushed will be removed first) stack 550 class FIFOStack(T){ 551 private: 552 /// to store data in a linked manner 553 struct StackElement(T){ 554 T data; /// the data stored 555 StackElement!(T)* next = null; /// pointer to data which was pushed after it 556 } 557 /// pointer to first item (first pushed, the one to pop next) 558 StackElement!(T)* firstItemPtr; 559 /// pointer to last item (last pushed) 560 StackElement!(T)* lastItemPtr; 561 /// stores number of elements pushed 562 uinteger _count; 563 public: 564 /// constructor 565 this (){ 566 firstItemPtr = null; 567 lastItemPtr = null; 568 _count = 0; 569 } 570 /// destructor 571 ~this (){ 572 // clear the whole stack 573 clear; 574 } 575 /// clears the whole stack, pops all items 576 void clear(){ 577 for (StackElement!(T)* i = firstItemPtr, next = null; i !is null; i = next){ 578 next = (*i).next; 579 .destroy(*i); 580 } 581 _count = 0; 582 } 583 /// Returns: number of items in stack 584 @property uinteger count(){ 585 return _count; 586 } 587 /// pushes an element to stack 588 void push(T element){ 589 StackElement!(T)* toPush = new StackElement!(T); 590 (*toPush).data = element; 591 (*toPush).next = null; 592 // check if stack is empty 593 if (lastItemPtr is null){ 594 firstItemPtr = toPush; 595 }else{ 596 (*lastItemPtr).next = toPush; 597 } 598 lastItemPtr = toPush; 599 _count ++; 600 } 601 /// pushes an array of elements to stack 602 void push(T[] elements){ 603 StackElement!(T)*[] toPush; 604 toPush.length = elements.length; 605 if (toPush.length > 0){ 606 // make a linked stack for just these elements first 607 foreach (i, element; elements){ 608 toPush[i] = new StackElement!(T); 609 (*toPush[i]).data = element; 610 if (i > 0){ 611 (*toPush[i-1]).next = toPush[i]; 612 } 613 } 614 (*toPush[toPush.length-1]).next = null; 615 // now "insert" it 616 if (lastItemPtr is null){ 617 firstItemPtr = toPush[0]; 618 }else{ 619 (*lastItemPtr).next = toPush[0]; 620 } 621 lastItemPtr = toPush[toPush.length-1]; 622 _count += elements.length; 623 } 624 } 625 /// pops an item from the stack (from bottom of stack, since it's a FIFO stack) 626 /// 627 /// Returns: the element pop-ed 628 /// 629 /// Throws: Exception if the stack is empty 630 T pop(){ 631 if (firstItemPtr is null){ 632 throw new Exception("Cannot pop from empty stack"); 633 } 634 T r = (*firstItemPtr).data; 635 StackElement!(T)* toDestroy = firstItemPtr; 636 firstItemPtr = (*firstItemPtr).next; 637 .destroy(toDestroy); 638 _count --; 639 // check if list is now empty 640 if (firstItemPtr is null){ 641 lastItemPtr = null; 642 } 643 return r; 644 } 645 /// pops a number of items from the stack (from bottom since it's a FIFO Stack) 646 /// 647 /// If there aren't enoguh items in stack, all the items are poped, and the returned array's length is less than `popCount` 648 /// 649 /// Returns: the elements poped 650 /// 651 /// Throws: Exception if stack is empty 652 T[] pop(uinteger popCount){ 653 if (count == 0){ 654 throw new Exception("Cannot pop from empty stack"); 655 } 656 if (_count < popCount){ 657 popCount = _count; 658 } 659 uinteger i = 0; 660 StackElement!(T)* item = firstItemPtr; 661 T[] r; 662 r.length = popCount; 663 while (i < popCount && item !is null){ 664 StackElement!(T)* toDestroy = item; 665 r[i] = (*item).data; 666 item = (*item).next; 667 .destroy(toDestroy); 668 i ++; 669 } 670 firstItemPtr = item; 671 _count -= popCount; 672 // check if list is empty now 673 if (firstItemPtr is null){ 674 lastItemPtr = null; 675 } 676 return r; 677 } 678 } 679 /// 680 unittest{ 681 FIFOStack!int stack = new FIFOStack!int; 682 stack.push(0); 683 stack.push([1,2,3,4]); 684 assert(stack.count == 5); 685 assert(stack.pop == 0); 686 assert(stack.count == 4); 687 assert(stack.pop(2) == [1,2]); 688 assert(stack.count == 2); 689 assert(stack.pop(2) == [3,4]); 690 assert(stack.count == 0); 691 stack.push([0,1,2]); 692 assert(stack.count == 3); 693 assert(stack.pop(3) == [0,1,2]); 694 } 695 696 /// To manage allocating extra for cases like lists where you need to create new objects often. Also manages initializing the objects 697 /// through a init function. 698 /// Creates a number of extra objects at one time, so it has to allocate memory less often. 699 class ExtraAlloc(T){ 700 private: 701 /// stores the free objects. 702 FIFOStack!T _store; 703 /// number of elements to allocate at one time 704 uinteger _allocCount; 705 /// max number of free elements present at one time, if more are present, extra are freed 706 uinteger _maxCount; 707 /// the delegate that will be called to get a new object 708 T delegate() _initFunction; 709 public: 710 /// constructor 711 this (uinteger extraAllocCount, uinteger maxAllocCount, T delegate() initFunction){ 712 _store = new FIFOStack!T; 713 _allocCount = extraAllocCount; 714 _maxCount = maxAllocCount; 715 _initFunction = initFunction; 716 } 717 /// destructor. Destroys all objects created by this 718 ~this (){ 719 while (_store.count > 0){ 720 .destroy(_store.pop); 721 } 722 .destroy(_store); 723 } 724 /// allocates and initializes objects to fill extraAllocCount 725 /// 726 /// Returns: true if more objects were allocated, false if the queue is already full, or if the queue had more than maxAlloc, and they were freed 727 bool allocate(){ 728 if (_store.count < _allocCount){ 729 T[] allocated; 730 allocated.length = _allocCount - _store.count; 731 for (uinteger i = 0; i < allocated.length; i ++){ 732 allocated[i] = _initFunction(); 733 } 734 _store.push(allocated); 735 return true; 736 } 737 while (_store.count > _maxCount){ 738 .destroy(_store.pop); 739 } 740 return false; 741 } 742 /// Returns: an object 743 T get(){ 744 if (_store.count == 0){ 745 allocate(); 746 } 747 return _store.pop; 748 } 749 /// Marks an object as free. Frees is if there are already enough free objects 750 void free(T obj){ 751 _store.push(obj); 752 if (_store.count > _maxCount){ 753 allocate(); 754 } 755 } 756 } 757 758 /// A linked list, used where only reading in the forward direction is required 759 class LinkedList(T){ 760 private: 761 ///represents an item in a linked list. contains the item, and pointer to the next item's container 762 struct LinkedItem(T){ 763 T data; 764 LinkedItem!(T)* next = null;//mark it null to show the list has ended 765 } 766 LinkedItem!(T)* firstItemPtr; 767 LinkedItem!(T)* lastItemPtr;//the pointer of the last item, used for appending new items 768 LinkedItem!(T)* nextReadPtr;//the pointer of the next item to be read 769 LinkedItem!(T)* lastReadPtr;//the pointer to the last item that was read 770 771 uinteger itemCount;//stores the total number of items 772 773 LinkedItem!(T)*[uinteger] bookmarks; 774 public: 775 this(){ 776 firstItemPtr = null; 777 lastItemPtr = null; 778 nextReadPtr = null; 779 lastReadPtr = null; 780 itemCount = 0; 781 } 782 ~this(){ 783 //free all the memory occupied 784 clear(); 785 } 786 /// clears/resets the list, by deleting all elements 787 void clear(){ 788 //make sure that the list is populated 789 if (firstItemPtr !is null){ 790 LinkedItem!(T)* nextPtr; 791 for (nextReadPtr = firstItemPtr; nextReadPtr !is null; nextReadPtr = nextPtr){ 792 nextPtr = (*nextReadPtr).next; 793 destroy(*nextReadPtr); 794 } 795 //reset all variables 796 firstItemPtr = null; 797 lastItemPtr = null; 798 nextReadPtr = null; 799 lastReadPtr = null; 800 itemCount = 0; 801 } 802 } 803 /// adds a new node/element to the end of the list 804 void append(T item){ 805 LinkedItem!(T)* ptr = new LinkedItem!(T); 806 (*ptr).data = item; 807 (*ptr).next = null; 808 //add it to the list 809 if (firstItemPtr is null){ 810 firstItemPtr = ptr; 811 nextReadPtr = firstItemPtr; 812 }else{ 813 (*lastItemPtr).next = ptr; 814 } 815 //mark this item as last 816 lastItemPtr = ptr; 817 //increase item count 818 itemCount ++; 819 } 820 /// adds new nodes/items at end of list 821 void append(T[] items){ 822 if (items.length > 0){ 823 LinkedItem!(T)*[] newNodes; 824 newNodes.length = items.length; 825 // put nodes inside the LinkedItem list 826 for (uinteger i = 0; i < items.length; i++){ 827 newNodes[i] = new LinkedItem!T; 828 (*newNodes[i]).data = items[i]; 829 } 830 // make them point to their next node 831 for (uinteger i = 0, end = newNodes.length-1; i < end; i ++){ 832 (*newNodes[i]).next = newNodes[i+1]; 833 } 834 // make last item from newNodes point to null 835 (*newNodes[newNodes.length-1]).next = null; 836 // make the last item point to first item in newNodes 837 if (firstItemPtr is null){ 838 firstItemPtr = newNodes[0]; 839 nextReadPtr = newNodes[0]; 840 }else{ 841 (*lastItemPtr).next = newNodes[0]; 842 } 843 // mark the last item in newNodes as last in list 844 lastItemPtr = newNodes[newNodes.length-1]; 845 //increase count 846 itemCount += newNodes.length; 847 } 848 } 849 /// removes the first node in list 850 /// 851 /// If the list is empty, this function does nothing 852 void removeFirst(){ 853 //make sure list is populated 854 if (firstItemPtr !is null){ 855 LinkedItem!(T)* first; 856 first = firstItemPtr; 857 //mark the second item as first, if there isn't a second item, it'll automatically be marked null 858 firstItemPtr = (*firstItemPtr).next; 859 //if nextReadPtr is firstItemPtr, move it to next as well 860 if (nextReadPtr is first){ 861 nextReadPtr = firstItemPtr; 862 } 863 // if the last-read is pointing to first item, null it 864 if (lastReadPtr is first){ 865 lastReadPtr = null; 866 } 867 //free memory occupied by first 868 destroy(*first); 869 //decrease count 870 itemCount --; 871 } 872 } 873 /// removes the node that was last read using `LinkedList.read`. The last node cannot be removed using this. 874 /// 875 /// It works by moving contents of next item into the last-read one, and removing the next node 876 /// 877 /// Returns: true in case the node/item was removed, false if not 878 bool removeLastRead(){ 879 bool r = false; 880 if (lastReadPtr !is null){ 881 LinkedItem!(T)* thisItem = lastReadPtr;// the item to delete 882 LinkedItem!(T)* nextItem = (*thisItem).next;// the item after last read 883 // make sure that the item to be deleted isn't last 884 if (nextItem !is null){ 885 // move contents of next to this item 886 thisItem.data = nextItem.data; 887 // set the pointer to the item after next 888 thisItem.next = nextItem.next; 889 // if nextItem is last item, move last item pointer to thisItem 890 if (nextItem is lastItemPtr){ 891 lastItemPtr = thisItem; 892 } 893 // delete nextItem 894 destroy(*nextItem); 895 896 r = true; 897 }else{ 898 // if there is only one item, or the pointer to second-last item is available, then it can be deleted 899 if (itemCount == 1){ 900 // just clearing the list will do the job 901 this.clear(); 902 // but we must increase the item count because at the end, it will be deceased by one 903 itemCount ++;// a workaround... 904 r = true; 905 }else{ 906 //we'll have to read till second-last item to get be able to remove the last item 907 LinkedItem!(T)* item = firstItemPtr; 908 for (uinteger i = 0, end = itemCount-2; i < end; i ++){ 909 item = item.next; 910 } 911 // now `item` is pointing to second last item, make sure this is true 912 if (item.next == lastItemPtr){ 913 //make the list end here 914 item.next = null; 915 // destroy last one 916 destroy(*lastItemPtr); 917 lastItemPtr = item; 918 919 r = true; 920 }/*else{ 921 something that shouldn't have gone wrong went wrong with `LinkedList.itemCount` 922 }*/ 923 924 } 925 } 926 //decrease count 927 if (r){ 928 itemCount --; 929 //since the last-read has been removed, null that pointer, to prevent segFault 930 lastReadPtr = null; 931 } 932 } 933 return r; 934 } 935 /// finds an element, if found, deletes it 936 /// 937 /// any function that works based on last-item-read should not be called while this is running, like in another thread... 938 /// 939 /// Arguments: 940 /// `toRemove` is the data to search for and delete 941 /// `count` is the number of times to search for it and delete it again. if 0, every element which is `==toRemove` is deleted 942 /// 943 /// Returns: true if was found and deleted, false if not found 944 /// 945 /// Throws: Exception if failed to delete an element 946 bool remove(T toRemove, uinteger count=0){ 947 LinkedItem!(T)* ptr = firstItemPtr, prev = null; 948 bool r = false; 949 uinteger removedCount = 0; 950 // I'll just use a "hack" and use removeLastRead to remove it 951 LinkedItem!(T)* actualLastRead = lastReadPtr; 952 while (ptr && ( (count > 0 && removedCount < count) || count == 0 )){ 953 LinkedItem!(T)* next = (*ptr).next; 954 if ((*ptr).data == toRemove){ 955 lastReadPtr = ptr; 956 r = this.removeLastRead(); 957 removedCount ++; 958 if (!r){ 959 throw new Exception("Failed to delete element in LinkedList->remove->removeLastRead"); 960 } 961 ptr = prev; 962 if (!ptr){ 963 ptr = firstItemPtr; 964 } 965 continue; 966 } 967 prev = ptr; 968 ptr = ptr.next; 969 } 970 lastReadPtr = actualLastRead; 971 return r; 972 } 973 /// searches the whole list, and any element that matches with elements in the array are deleted 974 /// 975 /// any function that works based on last-item-read should not be called while this is running, like in another thread... 976 /// 977 /// Arguments: 978 /// `toRemove` is the array containing the elements to delete 979 /// 980 /// Returns: true on success, false if no elements matched 981 /// 982 /// Throws: Exception if failed to delete an element 983 bool remove(T[] toRemove){ 984 LinkedItem!(T)* ptr = firstItemPtr, prev = null; 985 bool r = false; 986 uinteger removedCount = 0; 987 // I'll just use a "hack" and use removeLastRead to remove it 988 LinkedItem!(T)* actualLastRead = lastReadPtr; 989 while (ptr){ 990 LinkedItem!(T)* next = (*ptr).next; 991 if (toRemove.hasElement((*ptr).data)){ 992 lastReadPtr = ptr; 993 r = this.removeLastRead(); 994 if (!r){ 995 throw new Exception("Failed to delete element in LinkedList->remove->removeLastRead"); 996 } 997 ptr = prev; 998 if (!ptr){ 999 ptr = firstItemPtr; 1000 } 1001 continue; 1002 } 1003 prev = ptr; 1004 ptr = ptr.next; 1005 } 1006 lastReadPtr = actualLastRead; 1007 return r; 1008 } 1009 /// Returns: number of items that the list is holding 1010 @property uinteger count(){ 1011 return itemCount; 1012 } 1013 ///resets the read position, i.e: set reading position to first node, and nulls the last-read-ptr 1014 void resetRead(){ 1015 nextReadPtr = firstItemPtr; 1016 lastReadPtr = null; 1017 } 1018 /// Returns: pointer of next node to be read, null if there are no more nodes 1019 /// 1020 /// increments the read-position by 1, so next time it's called, the next item is read 1021 T* read(){ 1022 T* r; 1023 if (nextReadPtr !is null){ 1024 r = &((*nextReadPtr).data); 1025 //mark this item as last read 1026 lastReadPtr = nextReadPtr; 1027 //move read position 1028 nextReadPtr = (*nextReadPtr).next; 1029 }else{ 1030 r = null; 1031 lastReadPtr = null; 1032 } 1033 return r; 1034 } 1035 /// Returns: the pointer to the first node in the list 1036 T* readFirst(){ 1037 if (firstItemPtr !is null){ 1038 lastReadPtr = firstItemPtr; 1039 return &((*firstItemPtr).data); 1040 }else{ 1041 return null; 1042 } 1043 } 1044 /// Returns: the pointer to the last node in the list 1045 T* readLast(){ 1046 if (lastItemPtr !is null){ 1047 lastReadPtr = lastItemPtr; 1048 return &((*lastItemPtr).data); 1049 }else{ 1050 return null; 1051 } 1052 } 1053 /// Reads the list into an array 1054 /// 1055 /// Returns: the array formed from this list 1056 T[] toArray(){ 1057 LinkedItem!(T)* currentNode = firstItemPtr; 1058 uinteger i = 0; 1059 T[] r; 1060 r.length = itemCount; 1061 while (currentNode !is null){ 1062 r[i] = (*currentNode).data; 1063 // move to next node 1064 currentNode = (*currentNode).next; 1065 i ++; 1066 } 1067 return r; 1068 } 1069 /// Inserts a node after the position of last-read-node, i.e, to insert at position from where next item is to be read 1070 /// 1071 /// To insert at beginning, call `resetRead` before inserting 1072 /// For inserting more than one nodes, use `LinkedList.insert([...])` 1073 void insert(T node){ 1074 LinkedItem!(T)* newNode = new LinkedItem!T; 1075 (*newNode).data = node; 1076 // check if has to insert at beginning or at after last-read 1077 if (lastReadPtr !is null){ 1078 // make new node point to the current next-to-be-read node 1079 (*newNode).next = lastReadPtr.next; 1080 // make last read node point to new node 1081 (*lastReadPtr).next = newNode; 1082 }else{ 1083 // make this item point to first-item 1084 (*newNode).next = firstItemPtr; 1085 // mark this as first item 1086 firstItemPtr = newNode; 1087 } 1088 // make next read point to this node now 1089 nextReadPtr = newNode; 1090 //increase count 1091 itemCount ++; 1092 } 1093 /// Inserts nodes after the position of last-read-node, i.e, to insert at position from where next item is to be read 1094 /// 1095 /// If there is no last-read-item, the item is inserted at beginning. To do this, call `resetRead` before inserting 1096 /// 1097 /// Returns: true on success, false on failure 1098 void insert(T[] nodes){ 1099 if (nodes.length > 0){ 1100 LinkedItem!(T)*[] newNodes; 1101 newNodes.length = nodes.length; 1102 // put nodes inside the LinkedItem list 1103 for (uinteger i = 0; i < nodes.length; i++){ 1104 newNodes[i] = new LinkedItem!T; 1105 (*newNodes[i]).data = nodes[i]; 1106 } 1107 // make them point to their next node 1108 for (uinteger i = 0, end = newNodes.length-1; i < end; i ++){ 1109 (*newNodes[i]).next = newNodes[i+1]; 1110 } 1111 // check if has to insert at beginning or at after last-read 1112 if (lastReadPtr !is null && nodes.length > 0){ 1113 // and make the last node in list point to the node after last-read 1114 (*newNodes[newNodes.length-1]).next = (*lastReadPtr).next; 1115 // make last read node point to the first new-node 1116 (*lastReadPtr).next = newNodes[0]; 1117 }else{ 1118 // insert at beginning 1119 (*newNodes[newNodes.length-1]).next = firstItemPtr; 1120 // make this the first node 1121 firstItemPtr = newNodes[0]; 1122 } 1123 //make next read point to this 1124 nextReadPtr = newNodes[0]; 1125 //increase count 1126 itemCount += nodes.length; 1127 } 1128 } 1129 /// Returns: true if list contains a node, i.e searches for a node and returns true if found 1130 bool hasElement(T node){ 1131 bool r = false; 1132 LinkedItem!(T)* currentNode = firstItemPtr; 1133 while (currentNode !is null){ 1134 if ((*currentNode).data == node){ 1135 r = true; 1136 break; 1137 } 1138 // move to next node 1139 currentNode = (*currentNode).next; 1140 } 1141 return r; 1142 } 1143 /// matches all elements from an array to elements in list, to see if all elements in array are present in the list 1144 /// 1145 /// If the same element is present at more than one index in array, it won't work 1146 /// 1147 /// Returns: true if list contains all elements provided in an array, else, false 1148 bool hasElements(T[] nodes){ 1149 bool r = false; 1150 nodes = nodes.dup; 1151 // go through the list and match as many elements as possible 1152 LinkedItem!(T)* currentNode = firstItemPtr; 1153 while (currentNode !is null){ 1154 // check if current node matches any in array 1155 integer index = nodes.indexOf((*currentNode).data); 1156 if (index >= 0){ 1157 // this node matched, so remove it from the array 1158 nodes = nodes.deleteElement(index); 1159 } 1160 // check if all elements have been checked against 1161 if (nodes.length == 0){ 1162 break; 1163 } 1164 // move to next node 1165 currentNode = (*currentNode).next; 1166 } 1167 // Now check if the nodes array is empty, if yes, then all nodes were matched 1168 if (nodes.length == 0){ 1169 r = true; 1170 } 1171 return r; 1172 } 1173 /// Sets a "bookmark" 1174 /// 1175 /// the returned ID can later be used to go back to the reading position at which the bookmark was placed 1176 /// and be careful not to remove an item to which bookmark is pointing, because then if you moveToBookmark, it'll segfault. 1177 /// 1178 /// Returns: the bookmark-ID 1179 /// 1180 /// Throws: Exception if there is no last-read item 1181 uinteger placeBookmark(){ 1182 if (lastReadPtr is null){ 1183 throw new Exception("no last-read-item to place bookmark on"); 1184 }else{ 1185 // go through bookmarks list to find empty slot, or create a new one 1186 uinteger id = 0; 1187 while (true){ 1188 if (id in bookmarks){ 1189 id ++; 1190 }else{ 1191 break; 1192 } 1193 } 1194 bookmarks[id] = lastReadPtr.next; 1195 return id; 1196 } 1197 } 1198 /// moves read position back to a bookmark using the bookmark ID 1199 /// 1200 /// Does NOT delete the bookmark. Use `LinkedList.removeBookmark` to delete 1201 /// 1202 /// Returns: true if successful, false if the bookmark no longer exists 1203 bool moveToBookmark(uinteger id){ 1204 if (id !in bookmarks){ 1205 return false; 1206 }else{ 1207 nextReadPtr = bookmarks[id]; 1208 return true; 1209 } 1210 } 1211 /// removes a bookmark using the bookmark id 1212 /// 1213 /// Returns: true if bookmark is removed, false if it doesn't exist 1214 bool removeBookmark(uinteger id){ 1215 if (id !in bookmarks){ 1216 return false; 1217 }else{ 1218 bookmarks.remove(id); 1219 return true; 1220 } 1221 } 1222 /// Removes all bookmarks 1223 void clearBookmarks(){ 1224 foreach(key; bookmarks.keys){ 1225 bookmarks.remove(key); 1226 } 1227 } 1228 } 1229 /// 1230 unittest{ 1231 import std.conv : to; 1232 LinkedList!ubyte list = new LinkedList!ubyte; 1233 //`LinkedList.append` and `LinkedList.read` and `LinkedList.readFirst` and `LinkedList.readLast` and `LinkedList.resetRead` 1234 list.append(0); 1235 list.append(1); 1236 list.append(2); 1237 assert(*(list.readFirst()) == 0); 1238 assert(*(list.readLast()) == 2); 1239 assert(list.count == 3); 1240 list.read();// to skip, we wanna read the node at index 1 (2nd node) 1241 assert(*(list.read()) == 1); 1242 list.resetRead(); 1243 assert(*(list.read()) == 0); 1244 // `LinkedList.append(T[])`: 1245 list.clear(); 1246 list.append(0); 1247 list.append([1, 2, 3]); 1248 assert(list.count == 4); 1249 assert(list.toArray ==[0, 1, 2, 3]); 1250 list.clear; 1251 list.append([0, 1, 2]); 1252 list.append(3); 1253 assert(list.count == 4); 1254 assert(list.toArray == [0, 1, 2, 3]); 1255 //`LinkedList.clear` 1256 list.clear(); 1257 list.append(3); 1258 list.append(4); 1259 assert(*(list.read()) == 3); 1260 assert(list.count == 2); 1261 list.clear(); 1262 //`LinkedList.removeLastRead` and `Linkedlist.removeFirst` 1263 list.append(0); 1264 list.append(1); 1265 list.append(2); 1266 list.read(); 1267 list.read(); 1268 list.removeLastRead(); 1269 list.resetRead(); 1270 assert(*(list.read()) == 0); 1271 assert(*(list.read()) == 2); 1272 assert(list.count == 2); 1273 list.removeFirst(); 1274 list.resetRead(); 1275 assert(*(list.read()) == 2); 1276 assert(list.count == 1); 1277 list.removeLastRead(); 1278 assert(list.count == 0); 1279 //`LinkedList.toArray` and `LinkedList.insertNode` and `LinkedList.insertNodes` 1280 list.clear();// to reset stuff 1281 list.append(0); 1282 list.append(4); 1283 list.read(); 1284 list.insert(1); 1285 assert(*(list.read()) == 1); 1286 list.insert([2, 3]); 1287 list.resetRead(); 1288 assert(list.count == 5); 1289 assert(list.toArray == [0, 1, 2, 3, 4]); 1290 //`Linkedlist.hasElement` and `LinkedList.hasElements` 1291 assert(list.hasElement(0) == true); 1292 assert(list.hasElement(4) == true); 1293 assert(list.hasElement(5) == false); 1294 assert(list.hasElement(7) == false); 1295 assert(list.hasElements([3, 1, 2, 0, 4]) == true); 1296 assert(list.hasElements([0, 1, 2, 6]) == false); 1297 // `LinkedList.insert` at beginning 1298 list.clear; 1299 list.insert([1, 2]); 1300 list.insert(0); 1301 assert(list.count == 3); 1302 assert(list.toArray == [0, 1, 2]); 1303 //destroying last item 1304 list.clear(); 1305 list.append(0); 1306 list.append(1); 1307 list.append(2); 1308 list.read(); 1309 list.read(); 1310 list.read(); 1311 assert(list.removeLastRead() == true); 1312 assert(list.toArray() == [0, 1]); 1313 //bookmarks 1314 list.clear; 1315 list.append([0, 1, 2, 3, 4, 5]); 1316 assert(*list.read == 0); 1317 assert(*list.read == 1); 1318 assert(*list.read == 2); 1319 { 1320 uinteger id = list.placeBookmark; 1321 assert(*list.read == 3); 1322 assert(list.moveToBookmark(id + 1) == false); 1323 assert(list.moveToBookmark(id) == true); 1324 assert(*list.read == 3); 1325 assert(list.removeBookmark(id) == true); 1326 } 1327 // now to test LinkedList.remove 1328 list.clear; 1329 list.append([0,0,1,1,2,3,3,4,5,6,0,0]); 1330 assert(list.remove(0,2) == true); 1331 assert(list.toArray == [1,1,2,3,3,4,5,6,0,0], to!string(list.toArray)); 1332 assert(list.remove(0) == true); 1333 assert(list.toArray == [1,1,2,3,3,4,5,6]); 1334 assert(list.remove([1,3]) == true); 1335 assert(list.toArray == [2,4,5,6]); 1336 destroy(list); 1337 } 1338 1339 /// Used in log display widgets (like in dub package `qui` `qui.widgets.LogWidget`) 1340 /// 1341 /// Holds up to a certain number of items, after which it starts over-writing older ones 1342 deprecated class LogList(T){ 1343 private: 1344 List!T list; 1345 uinteger readFrom, maxLen; 1346 public: 1347 this(uinteger maxLength=100){ 1348 list = new List!T; 1349 readFrom = 0; 1350 maxLen = maxLength; 1351 } 1352 ~this(){ 1353 delete list; 1354 } 1355 /// adds an item to the log 1356 void add(T dat){ 1357 if (list.length>=maxLen){ 1358 list.set(readFrom,dat); 1359 readFrom++; 1360 }else{ 1361 list.add(dat); 1362 } 1363 } 1364 /// Returns: array containing items 1365 T[] read(uinteger count=0){ 1366 T[] r; 1367 if (count>list.length){ 1368 count = list.length; 1369 } 1370 if (count > 0){ 1371 uinteger i; 1372 if (count>list.length){ 1373 count = list.length; 1374 } 1375 r.length = count; 1376 for (i = readFrom; i < count; i++){ 1377 r[i] = list.read((readFrom+i)%count); 1378 } 1379 }else{ 1380 r = null; 1381 } 1382 return r; 1383 } 1384 /// resets and clears the log 1385 void reset(){ 1386 list.clear; 1387 readFrom = 0; 1388 } 1389 /// Returns: the max number of items that can be stored 1390 @property uinteger maxCapacity(){ 1391 return maxLen; 1392 } 1393 } 1394 1395 /// TODO: do something about this 1396 /// For reading large files which otherwise, would take too much memory 1397 /// 1398 /// Aside from reading, it can also write to files. TODO make it ready 1399 /*class FileReader{ 1400 private: 1401 File file; /// the file currently loaded 1402 bool closeOnDestroy; /// stores if the file will be closed when this object is destroyed 1403 uinteger _minSeek; /// stores the minimum value of seek, if zero, it has no effect 1404 uinteger _maxSeek; /// stores the maximum value of seek, if zero, it has no effect 1405 uinteger _maxSize; /// stores the max size of the file in case _minSeek and _maxSeek are set non-zero 1406 string _filename; /// the filename of the file opened 1407 public: 1408 /// prepares a file for reading/writing through this class 1409 /// 1410 /// if filename does not exists, attempts to create it 1411 /// 1412 /// Throws: Exception (ErrnoException) if some error occurs 1413 this(string filename){ 1414 file = File (filename, filename.exists ? "r+" : "w+"); 1415 closeOnDestroy = true; 1416 _filename = filename; 1417 _minSeek = 0; 1418 _maxSeek = 0; 1419 } 1420 /// prepares this object for reading/writing an already opened file 1421 /// 1422 /// When this constructor is used, file will not be closed when this object is destroyed 1423 /// and keep in mind that modifying the seek of `f` will also modify it in this object, so try not to use `f` outside, 1424 /// or do so with some precaution. 1425 this (File f){ 1426 file = f; 1427 closeOnDestroy = false; 1428 _minSeek = 0; 1429 _maxSeek = 0; 1430 } 1431 /// prepares this object for reading/writing an already opened file, where the read/write can only take place between a 1432 /// certain range. 1433 /// 1434 /// When this constructor is used, file will not be closed when this object is destroyed 1435 /// and keep in mind that modifying the seek of `f` will also modify it in this object, so try not to use `f` outside, 1436 /// or do so with some precaution. 1437 /// The object will treat the File segment as the whole file in the functions: 1438 /// * seek will return relative to minSeek. i.e, if actual seek is `minSeek + 1`, it will return `1` 1439 /// * size will return `(maxSeek - minSeek) + 1` if the actual size is greater than maxSeek, otherwise, it will be `size - maxSeek` 1440 /// * `lock()` (locking whole file) will only lock the segment 1441 /// * `unlock()` (unlocking whole file) will only unlock the segment 1442 /// 1443 /// Arguments: 1444 /// `f` if the File to do reading/writing on 1445 /// `minSeek` is the index from where reading/writing can begin from. 1446 /// `maxSeek` is the index after which no reading writing can be done. 1447 this (File f, uinteger minSeek, uinteger maxSeek){ 1448 file = f; 1449 assert (minSeek < maxSeek, "minSeek must be smaller than maxSeek"); 1450 this._minSeek = minSeek; 1451 this._maxSeek = maxSeek; 1452 this._maxSize = (maxSeek - minSeek) + 1; 1453 } 1454 /// destructor 1455 ~this(){ 1456 if (closeOnDestroy) 1457 file.close(); 1458 } 1459 /// locks a file segment (readWrite lock) 1460 /// 1461 /// Throws: Exception if this FileReader is only for a segment and it tries to access outdside that segment 1462 /// 1463 /// Returns: true if lock was successful, false if already locked 1464 bool lock(uinteger start, uinteger length){ 1465 if (_minSeek + _maxSeek > 0){ 1466 start = start + _minSeek; 1467 } 1468 // make sure it's not accessing anything outside the segment, if there is a segment limit 1469 if (start + length > _maxSeek + 1){ 1470 throw new Exception ("trying to access outside _maxSeek"); 1471 } 1472 return file.tryLock(LockType.readWrite, start, length); 1473 } 1474 /// locks the whole file (readWrite lock) 1475 /// 1476 /// Returns: true if lock was successful, false if alrady locked 1477 bool lock(){ 1478 if (_minSeek + _maxSeek == 0){ 1479 return file.tryLock(LockType.readWrite, 0, 0); 1480 } 1481 return file.tryLock(LockType.readWrite, _minSeek, _maxSize); 1482 } 1483 /// unlocks a file segment 1484 /// 1485 /// Throws: Exception if this FileReader is only for a segment and it tries to access outdside that segment 1486 void unlock (uinteger start, uinteger length){ 1487 if (_minSeek + _maxSeek > 0){ 1488 start = start + _minSeek; 1489 } 1490 // make sure it's not accessing anything outside the segment, if there is a segment limit 1491 if (start + length > _maxSeek + 1){ 1492 throw new Exception ("trying to access outside _maxSeek"); 1493 } 1494 file.unlock (start, length); 1495 } 1496 /// unlocks the whole file 1497 void unlock (){ 1498 if (_minSeek + _maxSeek == 0){ 1499 file.unlock (0, file.size); 1500 }else if (file.size > 0){ 1501 file.unlock(_minSeek, _maxSize); 1502 } 1503 } 1504 /// reads a number of bytes 1505 /// 1506 /// Returns: the bytes read. If there were not enough bytes left to read in the file, an array of smaller size is returned 1507 /// 1508 /// Throws: Exception (ErrnoException) in case of an error 1509 ubyte[] read (uinteger n){ 1510 ubyte[] buffer; 1511 buffer.length = this.size - this.seek > n ? n : this.size - this.seek; 1512 file.rawRead(buffer); 1513 return buffer; 1514 } 1515 /// reads till a specific byte is reached, or if eof is reached 1516 /// 1517 /// Returns: the bytes read including the terminating byte 1518 /// 1519 /// Throws: Exception (ErrnoException) in case of an error 1520 ubyte[] read (ubyte terminateByte){ 1521 ubyte[] r; 1522 while (this.seek < this.size){ 1523 ubyte[1] currentByte; 1524 file.rawRead(currentByte); 1525 r = r ~ currentByte[0]; 1526 if (currentByte[0] == terminateByte){ 1527 break; 1528 } 1529 } 1530 return r; 1531 } 1532 /// writes some bytes 1533 /// 1534 /// Throws: Exception (ErrnoException) in case of an error 1535 void write (ubyte[] buffer){ 1536 // make sure it won't overflow the _maxSeek 1537 if (buffer.length + this.seek > _maxSize){ 1538 buffer = buffer.dup; 1539 buffer.length = _maxSize - this.seek; 1540 } 1541 file.rawWrite(buffer); 1542 } 1543 /// Removes a number of bytes from the file, starting at an index. 1544 /// 1545 /// Arguments: 1546 /// `index` is index to begin removing from 1547 /// `length` is number of bytes to remove 1548 /// `chunkSize` is the number of bytes to shift in one iteration 1549 /// 1550 /// Returns: true if done, false if not, or index was out of bounds TODO add tests for this 1551 bool remove (uinteger index, uinteger length){ 1552 if (this.size <= index || this.size - index < length) 1553 return false; 1554 try{ 1555 for (this.seek = index+length; this.seek < this.size;){ 1556 ubyte[] toShift = this.read(length); 1557 this.seek = this.seek - length; 1558 this.write (toShift); 1559 } 1560 truncate(this.size - length); 1561 }catch(Exception e){ 1562 .destroy (e); 1563 return false; 1564 } 1565 return true; 1566 } 1567 /// inserts some bytes at the seek. i.e, shifts existing data from index=seek+1 onwards and makes space for new data, and writes it 1568 /// 1569 /// Does not work if minSeek or maxSeek is set 1570 /// 1571 /// Returns: true if successful, false if not 1572 bool insert (ubyte[] data){ 1573 // TODO make this 1574 } 1575 /// truncates a file, i.e removes last byte(s) from file. 1576 /// 1577 /// Does not work if minSeek and/or maxSeek were non-zero. 1578 /// 1579 /// TODO: read file `byChunk`, write it to new file, excluding last byte(s), replace old file with newfile; will be faster 1580 /// 1581 /// Arguments: 1582 /// `newSize` is the new number of bytes in file. 1583 /// `onFailTrySlow` if true, when `SetEndOfFile` or `ftruncate` fails, it'll use a slower method that might work 1584 /// 1585 /// Returns: true if file was truncated, false if not, for example if the file size was less than newSize TODO add tests 1586 bool truncate(uinteger newSize, bool onFailTrySlow=false){ 1587 if (_minSeek + _maxSeek != 0 || newSize < this.size){ 1588 return false; 1589 } 1590 try{ 1591 version (Posix){ 1592 import core.sys.posix.unistd: ftruncate; 1593 ftruncate(file.fileno, newSize); 1594 } 1595 version (Windows){ 1596 import core.sys.windows.windows: SetEndOfFile; 1597 uinteger oldSeek = this.seek; 1598 this.seek = newSize-1; 1599 SetEndOfFile (file.HANDLE); 1600 this.seek = oldSeek; 1601 } 1602 }catch (Exception e){ 1603 return false; 1604 } 1605 return (file.size == newSize); 1606 } 1607 /// from where the next byte will be read/write 1608 @property ulong seek (){ 1609 if (_maxSeek + _minSeek == 0){ 1610 return file.tell(); 1611 } 1612 ulong actualSeek = file.tell(); 1613 if (actualSeek < _minSeek){ 1614 this.seek = 0; 1615 }else if (actualSeek > _maxSeek){ 1616 this.seek = _maxSeek; 1617 } 1618 return file.tell() - _minSeek; 1619 } 1620 /// from where the next byte will be read/write 1621 @property ulong seek (ulong newSeek){ 1622 if (_maxSeek + _minSeek == 0){ 1623 file.seek (newSeek, SEEK_SET); 1624 return file.tell(); 1625 } 1626 newSeek += _minSeek; 1627 if (newSeek > _maxSeek){ 1628 newSeek = _maxSeek; 1629 } 1630 file.seek (newSeek, SEEK_SET); 1631 return file.tell() - _minSeek; 1632 } 1633 /// number of bytes in file 1634 @property ulong size (){ 1635 ulong actualSize = file.size(); 1636 if (actualSize > _maxSize){ 1637 actualSize = _maxSize; 1638 } 1639 return actualSize; 1640 } 1641 /// the filename currently being read/written 1642 @property string filename (){ 1643 return _filename; 1644 } 1645 } 1646 /// 1647 unittest{ 1648 import std.path : dirSeparator; 1649 import std.conv : to; 1650 // delete the file if it already exists, so it wont mess up the tests 1651 string fname = tempDir ~ dirSeparator ~ "utilsfilereader"; 1652 if (fname.exists){ 1653 remove (fname); 1654 } 1655 FileReader fread = new FileReader(fname); 1656 assert (fread.seek == 0, "seek is not zero at file open"); 1657 assert (fread.size == 0, "size is not zero for newly created file"); 1658 assert (fread.filename == fname, "filename is not "~fname); 1659 // first fill it with some data 1660 fread.write ([1,3,4,5,6,7,8,0,8,7,6,5,4,3,2,1]); 1661 assert (fread.seek == 16); 1662 assert (fread.size == 16); 1663 fread.seek = 1; 1664 fread.write ([2]); 1665 assert (fread.seek == 2); 1666 assert (fread.size == 16); 1667 // close it, and see if opening existing files works 1668 .destroy (fread); 1669 fread = new FileReader(fname); 1670 assert (fread.size == 16); 1671 assert (fread.filename == fname); 1672 assert (fread.seek == 0); 1673 /// test read-until-terminator 1674 assert (fread.read(cast(ubyte)0) == [1,2,4,5,6,7,8,0]); 1675 assert (fread.seek == 8); 1676 /// test read-number of bytes 1677 assert (fread.read(cast(uinteger)5) == [8,7,6,5,4]); 1678 assert (fread.seek == 13); 1679 assert (fread.read(999) == [3,2,1]); 1680 /// test move-seek and read 1681 fread.seek = 2; 1682 assert (fread.read(cast(ubyte)0) == [4,5,6,7,8,0]); 1683 /// close it 1684 .destroy (fread); 1685 remove (fname); 1686 }*/ 1687 1688 /// For reading/writing sequentially to a ubyte[] 1689 /// 1690 /// be careful using maxSize and grow, they're not tested 1691 class ByteStream{ 1692 private: 1693 ubyte[] _stream; 1694 uinteger _seek; 1695 bool _grow; 1696 uinteger _maxSize; 1697 public: 1698 /// constructor 1699 /// 1700 /// `grow` is whether the stream is allowed to grow in size while writing 1701 /// `maxSize` is the maximum size stream is allowed to grow to (0 for no limit) 1702 this(bool grow = true, uinteger maxSize = 0){ 1703 _grow = grow; 1704 _maxSize = maxSize; 1705 } 1706 ~this(){ 1707 .destroy(_stream); 1708 } 1709 /// Seek position (i.e: next read/write index) 1710 @property uinteger seek(){ 1711 return _seek; 1712 } 1713 /// ditto 1714 @property uinteger seek(uinteger newVal){ 1715 return _seek = newVal > _stream.length ? _stream.length : newVal; 1716 } 1717 /// if the stream is allowed to grow in size while writing 1718 @property bool grow(){ 1719 return _grow; 1720 } 1721 /// ditto 1722 @property bool grow(bool newVal){ 1723 return _grow = newVal; 1724 } 1725 /// maximum size stream is allowed to grow to, 0 for no limit. 1726 /// 1727 /// This is enforced while writing, or changing `ByteStream.size` 1728 @property uinteger maxSize(){ 1729 return _maxSize; 1730 } 1731 /// ditto 1732 @property uinteger maxSize(uinteger newVal){ 1733 _maxSize = newVal; 1734 if (_seek > _maxSize) 1735 _seek = _maxSize; 1736 return _maxSize; 1737 } 1738 /// The stream 1739 @property ubyte[] stream(){ 1740 return _stream; 1741 } 1742 /// ditto 1743 @property ubyte[] stream(ubyte[] newVal){ 1744 return _stream = newVal; 1745 } 1746 /// Size, in bytes, of stream 1747 @property uinteger size(){ 1748 return _stream.length; 1749 } 1750 /// Size, setter. if new size is >maxSize, size is set to maxSize 1751 @property uinteger size(uinteger newVal){ 1752 _stream.length = _maxSize < newVal ? _maxSize : newVal; 1753 if (_seek > _stream.length) 1754 _seek = _stream.length; 1755 return _stream.length; 1756 } 1757 /// Writes this stream to a file 1758 /// 1759 /// Returns: true if successful, false if not 1760 bool toFile(string fname){ 1761 try{ 1762 std.file.write(fname, _stream); 1763 }catch (Exception e){ 1764 .destroy(e); 1765 return false; 1766 } 1767 return true; 1768 } 1769 /// Reads a stream from file. 1770 /// if successful, seek and maxSize are set to 0; 1771 /// 1772 /// Returns: true if done successfully, false if not 1773 bool fromFile(string fname){ 1774 try{ 1775 _stream = cast(ubyte[])std.file.read(fname); 1776 }catch (Exception e){ 1777 .destroy(e); 1778 return false; 1779 } 1780 _seek = 0; 1781 _maxSize = 0; 1782 return true; 1783 } 1784 /// Reads a slice from the stream into buffer. Will read number of bytes so as to fill `buffer` 1785 /// 1786 /// Returns: number of bytes read 1787 uinteger readRaw(ubyte[] buffer){ 1788 immutable uinteger len = _seek + buffer.length > _stream.length ? _stream.length - _seek : buffer.length; 1789 buffer[0 .. len] = _stream[_seek .. _seek + len]; 1790 _seek += len; 1791 return len; 1792 } 1793 /// Reads at a seek without changing seek. **Does not work for dynamic arrays** 1794 /// 1795 /// Will still return an invalid value if reading outside stream 1796 /// Sets `incompleteRead` to true if there were less bytes in stream that T.sizeof 1797 /// 1798 /// Returns: the data read at position 1799 T readAt(T)(uinteger at, ref bool incompleteRead){ 1800 ByteUnion!T r; 1801 immutable uinteger prevSeek = _seek; 1802 at = at > _stream.length ? _stream.length : at; 1803 immutable uinteger len = at + r.array.length > _stream.length ? _stream.length - at : r.array.length; 1804 incompleteRead = len < r.array.length; 1805 r.array[0 .. len] = _stream[at .. at + len]; 1806 return r.data; 1807 } 1808 /// ditto 1809 T readAt(T)(uinteger at){ 1810 bool dummyBool; 1811 return readAt!T(at, dummyBool); 1812 } 1813 /// Reads a data type T from current seek. **Do not use this for reading arrays** 1814 /// 1815 /// Will return invalid data if there are insufficient bytes to read from. 1816 /// Sets `incompleteRead` to true if there were less bytes in stream that T.sizeof 1817 /// If value of `n` is non-zero, that number of bytes will be read. 1818 /// 1819 /// Returns: the read data 1820 T read(T)(ref bool incompleteRead, ubyte n=0){ 1821 ByteUnion!T u; 1822 uinteger readCount; 1823 if (n == 0 || n > T.sizeof) 1824 readCount = readRaw(u.array); 1825 else 1826 readCount = readRaw(u.array[0 .. n]); 1827 incompleteRead = readCount < n; 1828 if (n > T.sizeof) 1829 _seek += n - T.sizeof; 1830 return u.data; 1831 } 1832 /// ditto 1833 T read(T)(ubyte n=0){ 1834 bool dummyBool; 1835 return read!T(dummyBool, n); 1836 } 1837 /// Reads an array. 1838 /// 1839 /// in case of insufficient bytes in stream, will return array of correct length but missing bytes at end. 1840 /// `readCount` is the number of elements that were actually read (this can be < length if stream doesnt have enough bytes) 1841 /// `n` is the number of bytes to read for length of array, default(`0`) is `size_t.sizeof` 1842 /// 1843 /// Returns: the read array 1844 T[] readArray(T)(ref uinteger readCount, ubyte n=0){ 1845 immutable uinteger len = read!uinteger(n); 1846 T[] r; 1847 r.length = len / T.sizeof; 1848 readCount = readRaw((cast(ubyte*)r.ptr)[0 .. r.length * T.sizeof]) / T.sizeof; 1849 return r; 1850 } 1851 /// ditto 1852 T[] readArray(T)(ubyte n=0){ 1853 uinteger dummyUint; 1854 return readArray!T(dummyUint, n); 1855 } 1856 /// Writes data at seek. **Do not use this for arrays** 1857 /// 1858 /// `n` is number of bytes to actually write, default (0) is `T.sizeof` 1859 /// 1860 /// Returns: true if written, false if not (could be because stream not allowed to grow, or max size reached) 1861 bool write(T)(T data, ubyte n=0){ 1862 ByteUnion!T u; 1863 u.data = data; 1864 immutable uinteger newSize = _seek + (n == 0 ? u.array.length : n); // size after writing 1865 if (newSize > _stream.length){ 1866 if (!_grow || (_maxSize && newSize > _maxSize)) 1867 return false; 1868 _stream.length = newSize; 1869 } 1870 if (n == 0 || n > u.array.length){ 1871 _stream[_seek .. _seek + u.array.length] = u.array; 1872 _seek += u.array.length; 1873 if (n <= u.array.length) 1874 return true; 1875 n -= u.array.length; 1876 if (n){ 1877 _stream[_seek .. _seek + n] = 0; 1878 _seek += n; 1879 } 1880 return true; 1881 } 1882 _stream[_seek .. _seek + n] = u.array[0 .. n]; 1883 _seek += n; 1884 return true; 1885 } 1886 /// Writes an array without its size. 1887 /// 1888 /// Returns: number of bytes written, **not the number of elements** 1889 uinteger writeRaw(T)(T[] data){ 1890 uinteger len = data.length * T.sizeof; 1891 if (_seek + len > _stream.length){ 1892 if (!_grow) 1893 len = _stream.length - _seek; 1894 else if (_maxSize && _seek + len > _maxSize) 1895 len = _maxSize - _seek; 1896 _stream.length = _seek + len; 1897 } 1898 _stream[_seek .. _seek + len] = (cast(ubyte*)data.ptr)[0 .. len]; 1899 _seek += len; 1900 return len; 1901 } 1902 /// Writes (overwriting existing) data `at` a seek, without changing seek. 1903 /// 1904 /// `n` is number of bytes to actually write. default (0) is T.sizeof 1905 /// Will append to end of stream if `at` is outside stream 1906 /// 1907 /// Returns: true if written successfully, false if not 1908 bool writeAt(T)(uinteger at, T data, ubyte n = 0){ 1909 // writing is bit complicated, so just use `write` and change seek back to original after 1910 immutable uinteger prevSeek = _seek; 1911 _seek = at > _stream.length ? _stream.length : at; 1912 immutable bool r = this.write(data, n); 1913 _seek = prevSeek; 1914 return r; 1915 } 1916 /// Writes an array at seek. 1917 /// 1918 /// `n` is the number of bytes to use for storing length of array, default (`0`) is `size_t.sizeof` 1919 /// 1920 /// Returns: true if written, false if not (due to maxSize reached or not allowed to grow) 1921 bool writeArray(T)(T[] data, ubyte n=0){ 1922 immutable uinteger newSize = _seek + (n == 0 ? uinteger.sizeof : n) + (data.length * T.sizeof); 1923 if (newSize > _stream.length){ 1924 if (!_grow || (_maxSize && newSize > _maxSize)) 1925 return false; 1926 _stream.length = newSize; 1927 } 1928 if (this.write(data.length * T.sizeof, n)){ 1929 return writeRaw(data) == data.length * T.sizeof; 1930 } 1931 return false; // something bad went wrong, while writing size 1932 } 1933 } 1934 /// 1935 unittest{ 1936 ByteStream stream = new ByteStream(); 1937 ubyte[] buffer; 1938 uint[] uintArray = [12_345, 123_456, 1_234_567, 12_345_678, 123_456_789]; 1939 1940 stream.write(1024, 8); // 1024 as ulong 1941 stream.seek = 0; 1942 assert(stream.read!uint(8) == 1024); 1943 assert(stream.seek == 8, stream.seek.to!string); 1944 stream.writeRaw(uintArray); 1945 stream.seek = 8; 1946 buffer.length = 50; 1947 stream.readRaw(buffer); 1948 assert((cast(uint*)buffer.ptr)[0 .. 5] == uintArray); 1949 1950 stream.seek = 0; 1951 stream.writeArray(uintArray, 6); 1952 assert(stream.seek == (uintArray.length * 4) + 6); 1953 stream.seek = 0; 1954 uintArray = stream.readArray!(uint)(6); 1955 assert (uintArray == [12_345, 123_456, 1_234_567, 12_345_678, 123_456_789], uintArray.to!string); 1956 1957 stream.seek = 0; 1958 stream.writeRaw(uintArray); 1959 stream.writeAt(0, cast(uint)50); 1960 buffer.length = uintArray.length * uint.sizeof; 1961 stream.seek = 0; 1962 assert(stream.readRaw(buffer) == buffer.length); 1963 uintArray = (cast(uint*)buffer.ptr)[0 .. uintArray.length]; 1964 assert(uintArray[0] == 50 && uintArray[1 .. $] == [123_456, 1_234_567, 12_345_678, 123_456_789]); 1965 assert(stream.readAt!uint(4) == 123_456); 1966 } 1967 1968 /// used by Tree class to hold individual nodes in the tree 1969 struct TreeNode(T){ 1970 T data; /// the data stored 1971 TreeNode!(T)* parentPtr; /// pointer to the parent node, if this is null, this is the root of the tree 1972 TreeNode!(T)*[] childNodes; /// stores child nodes 1973 /// constructor 1974 this(T dataToStore){ 1975 data = dataToStore; 1976 } 1977 /// constructor 1978 this(TreeNode!(T)* parent){ 1979 parentPtr = parent; 1980 } 1981 /// constructor 1982 this(TreeNode!(T)*[] children){ 1983 childNodes = children.dup; 1984 } 1985 /// constructor 1986 this(T dataToStore, TreeNode!(T)*[] children, TreeNode!(T)* parent){ 1987 data = dataToStore; 1988 childNodes = children.dup; 1989 parentPtr = parent; 1990 } 1991 } 1992 /// To make reading a Tree (made up of TreeNode) a bit easier 1993 /// 1994 /// and while using it, make sure you do not make a loop in TreeNodes by putting a parent or parent's parent in a node's childNodes, 1995 /// doing so will cause an infinite loop, TreeReader cannot currently handle this 1996 struct TreeReader(T){ 1997 /// the root node 1998 TreeNode!(T)* root; 1999 /// .destroy()s children of the root, including children of children and so on, the root is also .destroy-ed 2000 void clear(){ 2001 /// called by iterate to destroy a node 2002 static bool destroyNode(TreeNode!(T)* node){ 2003 .destroy(*node); 2004 return true; 2005 } 2006 // start killing every node 2007 this.iterate(&destroyNode); 2008 } 2009 /// counts and returns number of nodes in the tree 2010 /// 2011 /// Returns: the number of nodes in the tree, counting all child-nodes and their child-nodes and so on 2012 uinteger count(){ 2013 // stores the count 2014 uinteger r = 0; 2015 /// used to "receive" nodes from iterate 2016 bool increaseCount(TreeNode!(T)* node){ 2017 r ++; 2018 return true; 2019 } 2020 // start counting 2021 iterate(&increaseCount); 2022 return r; 2023 } 2024 /// counts and returns number of nodes in the tree 2025 /// 2026 /// if `doCount` is not null, only nodes for which `doCount` function returns true will be counted 2027 /// 2028 /// Returns: number of nodes for which `doCount(node)` returned true 2029 uinteger count(bool function(TreeNode!(T)*) doCount=null){ 2030 /// stores the count 2031 uinteger r = 0; 2032 /// used to "receive" nodes from iterate 2033 bool increaseCount(TreeNode!(T)* node){ 2034 if (doCount !is null && (*doCount)(node)){ 2035 r ++; 2036 } 2037 return true; 2038 } 2039 // start counting 2040 iterate(&increaseCount); 2041 return r; 2042 } 2043 /// calls a function on every node 2044 /// 2045 /// loop is terminated as soon as false is returned from function 2046 /// No recursion is used, as it uses a stack to store which nodes it has to call a function on 2047 void iterate(bool function(TreeNode!(T)*) func){ 2048 if (func is null){ 2049 throw new Exception ("func cannot be null in iterate"); 2050 } 2051 /// stores all the nodes of whose childNodes's have to be sent 2052 Stack!(TreeNode!(T)*) nodes = new Stack!(TreeNode!(T)*); 2053 /// start from root 2054 nodes.push(root); 2055 while (nodes.count > 0){ 2056 /// the node whose childs are being currently being "sent": 2057 TreeNode!(T)* currentNode = nodes.pop; 2058 // "send" this node 2059 func(currentNode); 2060 // and have to send their childNodes too 2061 foreach (childPtr; (*currentNode).childNodes){ 2062 nodes.push(childPtr); 2063 } 2064 } 2065 .destroy(nodes); 2066 } 2067 /// calls a delegate on every node 2068 /// 2069 /// loop is terminated as soon as false is returned from function 2070 /// No recursion is used, as it uses a stack to store which nodes it has to call a delegate on 2071 void iterate(bool delegate(TreeNode!(T)*) func){ 2072 if (func is null){ 2073 throw new Exception ("func cannot be null in iterate"); 2074 } 2075 /// stores all the nodes of whose childNodes's have to be sent 2076 Stack!(TreeNode!(T)*) nodes = new Stack!(TreeNode!(T)*); 2077 /// start from root 2078 nodes.push(root); 2079 while (nodes.count > 0){ 2080 /// the node whose childs are being currently being "sent": 2081 TreeNode!(T)* currentNode = nodes.pop; 2082 // "send" this node 2083 func(currentNode); 2084 // and have to send their childNodes too 2085 foreach (childPtr; (*currentNode).childNodes){ 2086 nodes.push(childPtr); 2087 } 2088 } 2089 .destroy(nodes); 2090 } 2091 } 2092 /// 2093 unittest{ 2094 TreeReader!int tree; 2095 // testing iterate 2096 // make a sample tree 2097 TreeNode!int rootNode; 2098 rootNode.data = 0; 2099 // childNodes of root 2100 TreeNode!int rootChild0 = TreeNode!int(1), rootChild1=TreeNode!int(2); 2101 // childNodes of rootChild0 2102 TreeNode!int child0child0 = TreeNode!int(3), child0child1 = TreeNode!int(4); 2103 // childNodes of rootChild1 2104 TreeNode!int child1child0 = TreeNode!int(5), child1child1 = TreeNode!int(6); 2105 // arrange them in a tree 2106 rootNode.childNodes = [&rootChild0, &rootChild1]; 2107 rootChild0.childNodes = [&child0child0, &child0child1]; 2108 rootChild1.childNodes = [&child1child0, &child1child1]; 2109 tree.root = &rootNode; 2110 // check if iterate's working 2111 int[] iteratedNodes; 2112 tree.iterate((TreeNode!(int)* node){iteratedNodes ~= (*node).data; return true;}); 2113 // make sure each number was iterated 2114 assert ([0,1,2,3,4,5,6].matchElements(iteratedNodes), "TreeReader.iterate did not iterate through all nodes"); 2115 /// now test count 2116 assert (tree.count == 7, "TreeReader.count returned invalid count"); 2117 /// thats all unit tests for now, so destroy all nodes now 2118 tree.clear; 2119 }