1 /++ 2 Containers and thereto related functionality. 3 4 Example: 5 --- 6 { 7 Buffer!string buffer; 8 9 buffer.put("abc"); 10 buffer.put("def"); 11 assert(!buffer.empty); 12 assert(buffer.front == "abc"); 13 buffer.popFront(); 14 assert(buffer.front == "def"); 15 buffer.popFront(); 16 assert(buffer.empty); 17 } 18 { 19 Buffer!(char, Yes.dynamic, 3) buffer; 20 21 assert(!buffer.buf.length); 22 buffer ~= 'a'; 23 assert(buffer.buf.length == 3); 24 buffer ~= 'b'; 25 buffer ~= 'c'; 26 assert(buffer.length == 3); 27 buffer ~= 'd'; 28 assert(buffer.buf.length > 3); 29 assert(buffer[0..5] == "abcd"); 30 buffer.clear(); 31 assert(buffer.empty); 32 } 33 --- 34 +/ 35 module lu.container; 36 37 private: 38 39 import std.typecons : Flag, No, Yes; 40 41 public: 42 43 @safe: 44 45 46 // Buffer 47 /++ 48 Simple buffer/queue for storing and fetching items of any type `T`. 49 Does not use manual memory allocation. 50 51 It can use a static array internally to store elements on the stack, which 52 imposes a hard limit on how many items can be added, or a dynamic heap one 53 with a resizable buffer. 54 55 Example: 56 --- 57 Buffer!(string, No.dynamic, 16) buffer; 58 59 buffer.put("abc"); 60 buffer ~= "def"; 61 assert(!buffer.empty); 62 assert(buffer.front == "abc"); 63 buffer.popFront(); 64 assert(buffer.front == "def"); 65 buffer.popFront(); 66 assert(buffer.empty); 67 --- 68 69 Params: 70 T = Buffer item type. 71 dynamic = Whether to use a dynamic array whose size can be grown at 72 runtime, or to use a static array with a fixed size. Trying to add 73 more elements than there is room for will cause an assert. 74 Defaults to `No.dynamic`; a static bufferCurrent position in the array.. 75 originalSize = How many items to allocate space for. If `No.dynamic` was 76 passed it will assert if you attempt to store anything past this amount. 77 +/ 78 struct Buffer(T, Flag!"dynamic" dynamic = No.dynamic, size_t originalSize = 128) 79 { 80 pure nothrow: 81 static if (dynamic) 82 { 83 /++ 84 By how much to grow the buffer when we reach the end of it. 85 +/ 86 private enum growthFactor = 1.5; 87 88 /++ 89 Internal buffer dynamic array. 90 +/ 91 T[] buf; 92 93 /++ 94 Variable buffer size. 95 +/ 96 size_t bufferSize; 97 } 98 else 99 { 100 /++ 101 Internal buffer static array. 102 +/ 103 T[bufferSize] buf; 104 105 /++ 106 Static buffer size. 107 +/ 108 alias bufferSize = originalSize; 109 } 110 111 /++ 112 Current position in the array. 113 +/ 114 ptrdiff_t pos; 115 116 /++ 117 Position of last entry in the array. 118 +/ 119 ptrdiff_t end; 120 121 static if (dynamic) 122 { 123 // put 124 /++ 125 Append an item to the end of the buffer. 126 127 If it would be put beyond the end of the buffer, it will be resized to fit. 128 129 Params: 130 more = Item to add. 131 +/ 132 void put(/*const*/ T more) 133 { 134 if (end == bufferSize) 135 { 136 bufferSize = !bufferSize ? originalSize : cast(size_t)(bufferSize * growthFactor); 137 buf.length = bufferSize; 138 } 139 140 buf[end++] = more; 141 } 142 } 143 else 144 { 145 // put 146 /++ 147 Append an item to the end of the buffer. 148 149 If it would be put beyond the end of the buffer, it will assert. 150 151 Params: 152 more = Item to add. 153 +/ 154 void put(/*const*/ T more) @nogc 155 in ((end < bufferSize), '`' ~ typeof(this).stringof ~ "` buffer overflow") 156 { 157 buf[end++] = more; 158 } 159 } 160 161 static if (dynamic) 162 { 163 // reserve 164 /++ 165 Reserves enough room for the specified number of elements. If there 166 is already enough room, nothing is done. Otherwise the buffer is grown. 167 168 Params: 169 reserveSize = Number of elements to reserve size for. 170 +/ 171 void reserve(const size_t reserveSize) 172 { 173 if (bufferSize < reserveSize) 174 { 175 bufferSize = reserveSize; 176 buf.length = bufferSize; 177 } 178 } 179 } 180 181 // opOpAssign 182 /++ 183 Implements `buf ~= someT` (appending) by wrapping `put`. 184 185 Params: 186 op = Operation type, here specialised to "`~`". 187 more = Item to add. 188 +/ 189 void opOpAssign(string op : "~")(const T more) 190 { 191 return put(more); 192 } 193 194 // front 195 /++ 196 Fetches the item at the current position of the buffer. 197 198 Returns: 199 An item T. 200 +/ 201 T front() const @nogc 202 in ((end > 0), '`' ~ typeof(this).stringof ~ "` buffer underrun") 203 { 204 return buf[pos]; 205 } 206 207 // popFront 208 /++ 209 Advances the current position to the next item in the buffer. 210 +/ 211 void popFront() @nogc 212 { 213 if (++pos == end) reset(); 214 } 215 216 // length 217 /++ 218 Returns what amounts to the current length of the buffer; the distance 219 between the current position `pos` and the last element `end`. 220 221 Returns: 222 The buffer's current length. 223 +/ 224 size_t length() const @nogc 225 { 226 return (end - pos); 227 } 228 229 // empty 230 /++ 231 Returns whether or not the container is considered empty. 232 233 Mind that the buffer may well still contain old contents. Use `clear` 234 to zero it out. 235 236 Returns: 237 `true` if there are items available to get via `front`, 238 `false` if not. 239 +/ 240 bool empty() const @nogc 241 { 242 return (end == 0); 243 } 244 245 // reset 246 /++ 247 Resets the array positions, effectively soft-emptying the buffer. 248 249 The old elements' values are still there, they will just be overwritten 250 as the buffer is appended to. 251 +/ 252 void reset() @nogc 253 { 254 pos = 0; 255 end = 0; 256 } 257 258 // clear 259 /++ 260 Zeroes out the buffer's elements, getting rid of old contents. 261 +/ 262 void clear() @nogc 263 { 264 reset(); 265 buf[] = T.init; 266 } 267 } 268 269 /// 270 unittest 271 { 272 { 273 Buffer!(bool, No.dynamic, 4) buffer; 274 275 assert(buffer.empty); 276 buffer.put(true); 277 buffer.put(false); 278 assert(buffer.length == 2); 279 buffer.put(true); 280 buffer.put(false); 281 282 assert(!buffer.empty); 283 assert(buffer.front == true); 284 buffer.popFront(); 285 assert(buffer.front == false); 286 buffer.popFront(); 287 assert(buffer.front == true); 288 buffer.popFront(); 289 assert(buffer.front == false); 290 buffer.popFront(); 291 assert(buffer.empty); 292 assert(buffer.buf == [ true, false, true, false ]); 293 buffer.put(false); 294 assert(buffer.buf == [ false, false, true, false ]); 295 buffer.reset(); 296 assert(buffer.empty); 297 buffer.clear(); 298 assert(buffer.buf == [ false, false, false, false ]); 299 } 300 { 301 Buffer!(string, No.dynamic, 4) buffer; 302 303 assert(buffer.empty); 304 buffer.put("abc"); 305 buffer.put("def"); 306 buffer.put("ghi"); 307 308 assert(!buffer.empty); 309 assert(buffer.front == "abc"); 310 buffer.popFront(); 311 assert(buffer.front == "def"); 312 buffer.popFront(); 313 buffer.put("JKL"); 314 assert(buffer.front == "ghi"); 315 buffer.popFront(); 316 assert(buffer.front == "JKL"); 317 buffer.popFront(); 318 assert(buffer.empty); 319 assert(buffer.buf == [ "abc", "def", "ghi", "JKL" ]); 320 buffer.put("MNO"); 321 assert(buffer.buf == [ "MNO", "def", "ghi", "JKL" ]); 322 buffer.clear(); 323 assert(buffer.buf == [ string.init, string.init, string.init, string.init ]); 324 } 325 { 326 Buffer!(char, No.dynamic, 64) buffer; 327 buffer ~= 'a'; 328 buffer ~= 'b'; 329 buffer ~= 'c'; 330 assert(buffer.buf[0..3] == "abc"); 331 332 foreach (char_; buffer) 333 { 334 assert((char_ == 'a') || (char_ == 'b') || (char_ == 'c')); 335 } 336 } 337 { 338 Buffer!(int, Yes.dynamic, 3) buffer; 339 assert(!buffer.buf.length); 340 buffer ~= 1; 341 assert(buffer.buf.length == 3); 342 buffer ~= 2; 343 buffer ~= 3; 344 assert(buffer.front == 1); 345 buffer.popFront(); 346 assert(buffer.front == 2); 347 buffer.popFront(); 348 assert(buffer.front == 3); 349 buffer.popFront(); 350 assert(buffer.empty); 351 buffer.reserve(64); 352 assert(buffer.buf.length == 64); 353 buffer.reserve(63); 354 assert(buffer.buf.length == 64); 355 } 356 { 357 Buffer!(char, No.dynamic, 4) buffer; 358 buffer ~= 'a'; 359 buffer ~= 'b'; 360 buffer ~= 'c'; 361 buffer ~= 'd'; 362 assert(buffer.buf == "abcd"); 363 assert(buffer.length == 4); 364 buffer.reset(); 365 assert(buffer.buf == "abcd"); 366 buffer.clear(); 367 assert(buffer.buf == typeof(buffer.buf).init); 368 } 369 } 370 371 372 // CircularBuffer 373 /++ 374 Simple circular-ish buffer for storing items of type `T` that discards elements 375 when the maximum size is reached. Does not use manual memory allocation. 376 377 It can use a static array internally to store elements on the stack, which 378 imposes a hard limit on how many items can be added, or a dynamic heap one 379 with a resizable buffer. 380 381 Example: 382 --- 383 CircularBuffer!(int, Yes.dynamic) buf; 384 buf.resize(3); 385 buf.put(1); 386 buf.put(2); 387 buf.put(3); 388 but.put(4); 389 assert(buf.front == 4); 390 assert(buf.buf == [ 4, 2, 3 ]); 391 --- 392 393 Params: 394 T = Buffer item type. 395 dynamic = Whether to use a dynamic array whose size can be grown at 396 runtime, or to use a static array with a fixed size. Trying to add 397 more elements than there is room for will wrap around and discard elements. 398 Defaults to `No.dynamic`; a static buffer. 399 originalSize = How many items to allocate space for in the case of a 400 static array. 401 +/ 402 struct CircularBuffer(T, Flag!"dynamic" dynamic = No.dynamic, size_t originalSize = 16) 403 if (originalSize > 1) 404 { 405 private: 406 static if (dynamic) 407 { 408 // buf 409 /++ 410 Internal buffer dynamic array. 411 +/ 412 T[] buf; 413 } 414 else 415 { 416 // buf 417 /++ 418 Internal buffer static array. 419 +/ 420 T[originalSize] buf; 421 } 422 423 // head 424 /++ 425 Head position in the internal buffer. 426 +/ 427 size_t head; 428 429 // tail 430 /++ 431 Tail position in the internal buffer. 432 +/ 433 size_t tail; 434 435 // caughtUp 436 /++ 437 Whether or not [head] and [tail] point to the same position in the 438 context of a circular array. 439 +/ 440 bool caughtUp; 441 442 // initialised 443 /++ 444 Whether or not at least one element has been added. 445 +/ 446 bool initialised; 447 448 public: 449 // front 450 /++ 451 Returns the front element. 452 453 Returns: 454 An item T. 455 +/ 456 auto front() const 457 in ((buf.length > 0), "Tried to get `front` from a zero-sized " ~ typeof(this).stringof) 458 { 459 return buf[head]; 460 } 461 462 // put 463 /++ 464 Append an item to the buffer. 465 466 If it would be put beyond the end of the buffer, it will wrap around and 467 truncate old values. 468 469 Params: 470 item = Item to add. 471 +/ 472 void put(T item) pure @safe @nogc nothrow 473 in ((buf.length > 0), "Tried to `put` something into a zero-sized " ~ typeof(this).stringof) 474 { 475 if (initialised) ++head %= buf.length; 476 buf[head] = item; 477 tail = head; 478 caughtUp = true; 479 initialised = true; 480 } 481 482 // popFront 483 /++ 484 Advances the current position to the next item in the buffer. 485 +/ 486 void popFront() pure @safe @nogc nothrow 487 in ((buf.length > 0), "Tried to `popFront` a zero-sized " ~ typeof(this).stringof) 488 in (!empty, "Tried to `popFront` an empty " ~ typeof(this).stringof) 489 { 490 if (head == 0) 491 { 492 head = (buf.length + (-1)); 493 caughtUp = false; 494 } 495 else 496 { 497 --head; 498 } 499 } 500 501 static if (dynamic) 502 { 503 // resize 504 /++ 505 Resizes the internal buffer to a specified size. 506 507 Params: 508 size = New size. 509 +/ 510 void resize(const size_t size) pure @safe nothrow 511 { 512 buf.length = size; 513 if (head >= buf.length) head = buf.length +(-1); 514 if (tail >= buf.length) tail = buf.length +(-1); 515 } 516 } 517 518 // opOpAssign 519 /++ 520 Implements `buf ~= someT` (appending) by wrapping `put`. 521 522 Params: 523 op = Operation type, here specialised to "`~`". 524 more = Item to add. 525 +/ 526 void opOpAssign(string op : "~")(const T more) pure @safe @nogc nothrow 527 { 528 return put(more); 529 } 530 531 // size 532 /++ 533 Returns the size of the internal buffer. 534 535 Returns: 536 Internal buffer size. 537 +/ 538 auto size() const 539 { 540 return buf.length; 541 } 542 543 // empty 544 /++ 545 Returns whether or not the container is considered empty. 546 547 Mind that the buffer may well still contain old contents. Use `clear` 548 to zero it out. 549 550 Returns: 551 `true` if there are items available to get via `front`, 552 `false` if not. 553 +/ 554 auto empty() const 555 { 556 return !caughtUp && (head == tail); 557 } 558 559 // save 560 /++ 561 Implements Range `save`. 562 563 Returns: 564 A shallow copy of the container. 565 +/ 566 auto save() 567 { 568 return this; 569 } 570 571 // dup 572 /++ 573 Makes a deep(er) duplicate of the container. 574 575 Returns: 576 A copy of the current container with the internal buffer explicitly `.dup`ed. 577 +/ 578 auto dup() 579 { 580 auto copy = this; 581 582 static if (dynamic) 583 { 584 copy.buf = this.buf.dup; 585 } 586 587 return copy; 588 } 589 590 // clear 591 /++ 592 Resets the buffer pointers but doesn't clear the contents. 593 +/ 594 void reset() pure @safe @nogc nothrow 595 { 596 head = 0; 597 tail = 0; 598 } 599 600 // clear 601 /++ 602 Zeroes out the buffer's elements, getting rid of old contents. 603 +/ 604 void clear() pure @safe @nogc nothrow 605 { 606 reset(); 607 buf[] = T.init; 608 } 609 } 610 611 /// 612 unittest 613 { 614 import std.conv : text; 615 616 { 617 CircularBuffer!(int, Yes.dynamic) buf; 618 buf.resize(3); 619 620 buf.put(1); 621 assert((buf.front == 1), buf.front.text); 622 buf.put(2); 623 assert((buf.front == 2), buf.front.text); 624 buf.put(3); 625 assert((buf.front == 3), buf.front.text); 626 buf ~= 4; 627 assert((buf.front == 4), buf.front.text); 628 assert((buf.buf[] == [ 4, 2, 3 ]), buf.buf.text); 629 buf ~= 5; 630 assert((buf.front == 5), buf.front.text); 631 buf ~= 6; 632 assert((buf.front == 6), buf.front.text); 633 assert((buf.buf[] == [ 4, 5, 6 ]), buf.buf.text); 634 buf.popFront(); 635 buf.popFront(); 636 buf.popFront(); 637 assert(buf.empty); 638 } 639 { 640 CircularBuffer!(int, No.dynamic, 3) buf; 641 //buf.resize(3); 642 643 buf.put(1); 644 assert((buf.front == 1), buf.front.text); 645 buf.put(2); 646 assert((buf.front == 2), buf.front.text); 647 buf.put(3); 648 assert((buf.front == 3), buf.front.text); 649 buf ~= 4; 650 assert((buf.front == 4), buf.front.text); 651 assert((buf.buf[] == [ 4, 2, 3 ]), buf.buf.text); 652 buf.popFront(); 653 buf.popFront(); 654 buf.popFront(); 655 assert(buf.empty); 656 } 657 { 658 CircularBuffer!(int, No.dynamic, 2) buf; 659 //buf.resize(2); 660 661 buf.put(1); 662 assert((buf.front == 1), buf.front.text); 663 buf.put(2); 664 assert((buf.front == 2), buf.front.text); 665 buf.put(3); 666 assert((buf.front == 3), buf.front.text); 667 buf ~= 4; 668 assert((buf.front == 4), buf.front.text); 669 assert((buf.buf[] == [ 3, 4 ]), buf.buf.text); 670 buf.popFront(); 671 buf.popFront(); 672 assert(buf.empty); 673 //buf.popFront(); // AssertError 674 } 675 { 676 CircularBuffer!(int, No.dynamic, 2) buf; 677 //buf.resize(2); 678 679 buf.put(1); 680 assert((buf.front == 1), buf.front.text); 681 buf.put(2); 682 assert((buf.front == 2), buf.front.text); 683 buf.put(3); 684 assert((buf.front == 3), buf.front.text); 685 buf ~= 4; 686 assert((buf.front == 4), buf.front.text); 687 assert((buf.buf[] == [ 3, 4 ]), buf.buf.text); 688 auto savedBuf = buf.save(); 689 buf.popFront(); 690 buf.popFront(); 691 assert(buf.empty); 692 assert((savedBuf.front == 4), savedBuf.front.text); 693 savedBuf.popFront(); 694 auto savedBuf2 = savedBuf.save(); 695 savedBuf.popFront(); 696 assert(savedBuf.empty); 697 assert((savedBuf2.front == 3), savedBuf2.front.text); 698 savedBuf2.popFront(); 699 assert(savedBuf2.empty); 700 } 701 }