1 /* 2 Copyright (c) 2016-2021 Eugene Wissner 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license (the "Software") to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 */ 28 29 /** 30 * Copyright: Eugene Wissner 2016-2021. 31 * License: $(LINK2 boost.org/LICENSE_1_0.txt, Boost License 1.0). 32 * Authors: Eugene Wissner 33 */ 34 module dlib.container.buffer; 35 36 import dlib.memory; 37 38 version (unittest) 39 { 40 private int fillBuffer(ubyte[] buffer, 41 in size_t size, 42 int start = 0, 43 int end = 10) @nogc pure nothrow 44 in 45 { 46 assert(start < end); 47 } 48 do 49 { 50 auto numberRead = end - start; 51 for (ubyte i; i < numberRead; ++i) 52 { 53 buffer[i] = cast(ubyte) (start + i); 54 } 55 return numberRead; 56 } 57 } 58 59 /** 60 * Interface for implemeting input/output buffers. 61 */ 62 interface Buffer 63 { 64 /** 65 * Returns: The size of the internal buffer. 66 */ 67 @property size_t capacity() const @nogc @safe pure nothrow; 68 69 /** 70 * Returns: Data size. 71 */ 72 @property size_t length() const @nogc @safe pure nothrow; 73 74 /** 75 * Returns: Available space. 76 */ 77 @property size_t free() const @nogc @safe pure nothrow; 78 79 /** 80 * Params: 81 * start = Start position. 82 * end = End position. 83 * 84 * Returns: Array between $(D_PARAM start) and $(D_PARAM end). 85 */ 86 @property ubyte[] opSlice(size_t start, size_t end) 87 in 88 { 89 assert(start <= end); 90 assert(end <= length); 91 } 92 93 /** 94 * Returns: Length of available data. 95 */ 96 @property size_t opDollar() const pure nothrow @safe @nogc; 97 98 /** 99 * Returns: Data chunk. 100 */ 101 @property ubyte[] opIndex(); 102 } 103 104 /** 105 * Self-expanding buffer, that can be used with functions returning the number 106 * of the read bytes. 107 * 108 * This buffer supports asynchronous reading. It means you can pass a new chunk 109 * to an asynchronous read function during you are working with already 110 * available data. But only one asynchronous call at a time is supported. Be 111 * sure to call $(D_PSYMBOL ReadBuffer.clear()) before you append the result 112 * of the pended asynchronous call. 113 */ 114 class ReadBuffer : Buffer 115 { 116 /// Internal buffer. 117 protected ubyte[] buffer_; 118 119 /// Filled buffer length. 120 protected size_t length_; 121 122 /// Start of available data. 123 protected size_t start; 124 125 /// Last position returned with $(D_KEYWORD []). 126 protected size_t ring; 127 128 /// Available space. 129 protected immutable size_t minAvailable; 130 131 /// Size by which the buffer will grow. 132 protected immutable size_t blockSize; 133 134 invariant 135 { 136 assert(length_ <= buffer_.length); 137 assert(blockSize > 0); 138 assert(minAvailable > 0); 139 } 140 141 /** 142 * Creates a new read buffer. 143 * 144 * Params: 145 * size = Initial buffer size and the size by which the buffer 146 * will grow. 147 * minAvailable = minimal size should be always available to fill. 148 * So it will reallocate if $(D_INLINECODE 149 * $(D_PSYMBOL free) < $(D_PARAM minAvailable) 150 * ). 151 */ 152 this(size_t size = 8192, 153 size_t minAvailable = 1024) 154 { 155 this.minAvailable = minAvailable; 156 this.blockSize = size; 157 defaultAllocator.resizeArray!ubyte(buffer_, size); 158 } 159 160 /** 161 * Deallocates the internal buffer. 162 */ 163 ~this() 164 { 165 defaultAllocator.dispose(buffer_); 166 } 167 168 /// 169 unittest 170 { 171 auto b = defaultAllocator.make!ReadBuffer; 172 assert(b.capacity == 8192); 173 assert(b.length == 0); 174 175 defaultAllocator.dispose(b); 176 } 177 178 /** 179 * Returns: The size of the internal buffer. 180 */ 181 @property size_t capacity() const @nogc @safe pure nothrow 182 { 183 return buffer_.length; 184 } 185 186 /** 187 * Returns: Data size. 188 */ 189 @property size_t length() const @nogc @safe pure nothrow 190 { 191 return length_ - start; 192 } 193 194 /** 195 * Clears the buffer. 196 * 197 * Returns: $(D_KEYWORD this). 198 */ 199 ReadBuffer clear() pure nothrow @safe @nogc 200 { 201 start = length_ = ring; 202 return this; 203 } 204 205 /** 206 * Returns: Available space. 207 */ 208 @property size_t free() const pure nothrow @safe @nogc 209 { 210 return length > ring ? capacity - length : capacity - ring; 211 } 212 213 /// 214 unittest 215 { 216 auto b = defaultAllocator.make!ReadBuffer; 217 size_t numberRead; 218 219 // Fills the buffer with values 0..10 220 assert(b.free == b.blockSize); 221 222 numberRead = fillBuffer(b[], b.free, 0, 10); 223 b += numberRead; 224 assert(b.free == b.blockSize - numberRead); 225 b.clear(); 226 assert(b.free == b.blockSize); 227 228 defaultAllocator.dispose(b); 229 } 230 231 /** 232 * Appends some data to the buffer. 233 * 234 * Params: 235 * length = Number of the bytes read. 236 * 237 * Returns: $(D_KEYWORD this). 238 */ 239 ReadBuffer opOpAssign(string op)(size_t length) 240 if (op == "+") 241 { 242 length_ += length; 243 ring = start; 244 return this; 245 } 246 247 /// 248 unittest 249 { 250 auto b = defaultAllocator.make!ReadBuffer; 251 size_t numberRead; 252 ubyte[] result; 253 254 // Fills the buffer with values 0..10 255 numberRead = fillBuffer(b[], b.free, 0, 10); 256 b += numberRead; 257 258 result = b[0..$]; 259 assert(result[0] == 0); 260 assert(result[1] == 1); 261 assert(result[9] == 9); 262 b.clear(); 263 264 // It shouldn't overwrite, but append another 5 bytes to the buffer 265 numberRead = fillBuffer(b[], b.free, 0, 10); 266 b += numberRead; 267 268 numberRead = fillBuffer(b[], b.free, 20, 25); 269 b += numberRead; 270 271 result = b[0..$]; 272 assert(result[0] == 0); 273 assert(result[1] == 1); 274 assert(result[9] == 9); 275 assert(result[10] == 20); 276 assert(result[14] == 24); 277 278 defaultAllocator.dispose(b); 279 } 280 281 /** 282 * Returns: Length of available data. 283 */ 284 @property size_t opDollar() const pure nothrow @safe @nogc 285 { 286 return length; 287 } 288 289 /** 290 * Params: 291 * start = Start position. 292 * end = End position. 293 * 294 * Returns: Array between $(D_PARAM start) and $(D_PARAM end). 295 */ 296 @property ubyte[] opSlice(size_t start, size_t end) pure nothrow @safe @nogc 297 { 298 return buffer_[this.start + start .. this.start + end]; 299 } 300 301 /** 302 * Returns a free chunk of the buffer. 303 * 304 * Add ($(D_KEYWORD +=)) the number of the read bytes after using it. 305 * 306 * Returns: A free chunk of the buffer. 307 */ 308 ubyte[] opIndex() 309 { 310 if (start > 0) 311 { 312 auto ret = buffer_[0..start]; 313 ring = 0; 314 return ret; 315 } 316 else 317 { 318 if (capacity - length < minAvailable) 319 { 320 defaultAllocator.resizeArray!ubyte(buffer_, capacity + blockSize); 321 } 322 ring = length_; 323 return buffer_[length_..$]; 324 } 325 } 326 327 /// 328 unittest 329 { 330 auto b = defaultAllocator.make!ReadBuffer; 331 size_t numberRead; 332 ubyte[] result; 333 334 // Fills the buffer with values 0..10 335 numberRead = fillBuffer(b[], b.free, 0, 10); 336 b += numberRead; 337 338 assert(b.length == 10); 339 result = b[0..$]; 340 assert(result[0] == 0); 341 assert(result[9] == 9); 342 b.clear(); 343 assert(b.length == 0); 344 345 defaultAllocator.dispose(b); 346 } 347 } 348 349 /** 350 * Circular, self-expanding buffer with overflow support. Can be used with 351 * functions returning returning the number of the transferred bytes. 352 * 353 * The buffer is optimized for situations where you read all the data from it 354 * at once (without writing to it occasionally). It can become ineffective if 355 * you permanently keep some data in the buffer and alternate writing and 356 * reading, because it may allocate and move elements. 357 */ 358 class WriteBuffer : Buffer 359 { 360 /// Internal buffer. 361 protected ubyte[] buffer_; 362 363 /// Buffer start position. 364 protected size_t start; 365 366 /// Buffer ring area size. After this position begins buffer overflow area. 367 protected size_t ring; 368 369 /// Size by which the buffer will grow. 370 protected immutable size_t blockSize; 371 372 /// The position of the free area in the buffer. 373 protected size_t position; 374 375 invariant 376 { 377 assert(blockSize > 0); 378 // position can refer to an element outside the buffer if the buffer is full. 379 assert(position <= buffer_.length); 380 } 381 382 /** 383 * Params: 384 * size = Initial buffer size and the size by which the buffer 385 * will grow. 386 */ 387 this(size_t size = 8192) 388 { 389 blockSize = size; 390 ring = size - 1; 391 defaultAllocator.resizeArray!ubyte(buffer_, size); 392 } 393 394 /** 395 * Deallocates the internal buffer. 396 */ 397 ~this() 398 { 399 defaultAllocator.dispose(buffer_); 400 } 401 402 /** 403 * Returns: The size of the internal buffer. 404 */ 405 @property size_t capacity() const @nogc @safe pure nothrow 406 { 407 return buffer_.length; 408 } 409 410 /** 411 * Note that $(D_PSYMBOL length) doesn't return the real length of the data, 412 * but only the array length that will be returned with $(D_PSYMBOL buffer) 413 * next time. Be sure to call $(D_PSYMBOL buffer) and set $(D_KEYWORD +=) 414 * until $(D_PSYMBOL length) returns 0. 415 * 416 * Returns: Data size. 417 */ 418 @property size_t length() const @nogc @safe pure nothrow 419 { 420 if (position > ring || position < start) // Buffer overflowed 421 { 422 return ring - start + 1; 423 } 424 else 425 { 426 return position - start; 427 } 428 } 429 430 /** 431 * Returns: Length of available data. 432 */ 433 @property size_t opDollar() const pure nothrow @safe @nogc 434 { 435 return length; 436 } 437 438 /// 439 unittest 440 { 441 auto b = defaultAllocator.make!WriteBuffer(4); 442 ubyte[3] buf = [48, 23, 255]; 443 444 b ~= buf; 445 assert(b.length == 3); 446 b += 2; 447 assert(b.length == 1); 448 449 b ~= buf; 450 assert(b.length == 2); 451 b += 2; 452 assert(b.length == 2); 453 454 b ~= buf; 455 assert(b.length == 5); 456 b += b.length; 457 assert(b.length == 0); 458 459 defaultAllocator.dispose(b); 460 } 461 462 /** 463 * Returns: Available space. 464 */ 465 @property size_t free() const @nogc @safe pure nothrow 466 { 467 return capacity - length; 468 } 469 470 /** 471 * Appends data to the buffer. 472 * 473 * Params: 474 * buffer = Buffer chunk got with $(D_PSYMBOL buffer). 475 */ 476 WriteBuffer opOpAssign(string op)(ubyte[] buffer) 477 if (op == "~") 478 { 479 size_t end, start; 480 481 if (position >= this.start && position <= ring) 482 { 483 auto afterRing = ring + 1; 484 485 end = position + buffer.length; 486 if (end > afterRing) 487 { 488 end = afterRing; 489 } 490 start = end - position; 491 buffer_[position..end] = buffer[0..start]; 492 if (end == afterRing) 493 { 494 position = this.start == 0 ? afterRing : 0; 495 } 496 else 497 { 498 position = end; 499 } 500 } 501 502 // Check if we have some free space at the beginning 503 if (start < buffer.length && position < this.start) 504 { 505 end = position + buffer.length - start; 506 if (end > this.start) 507 { 508 end = this.start; 509 } 510 auto areaEnd = end - position + start; 511 buffer_[position..end] = buffer[start..areaEnd]; 512 position = end == this.start ? ring + 1 : end - position; 513 start = areaEnd; 514 } 515 516 // And if we still haven't found any place, save the rest in the overflow area 517 if (start < buffer.length) 518 { 519 end = position + buffer.length - start; 520 if (end > capacity) 521 { 522 auto newSize = end / blockSize * blockSize + blockSize; 523 524 defaultAllocator.resizeArray!ubyte(buffer_, newSize); 525 } 526 buffer_[position..end] = buffer[start..$]; 527 position = end; 528 if (this.start == 0) 529 { 530 ring = capacity - 1; 531 } 532 } 533 534 return this; 535 } 536 537 /// 538 unittest 539 { 540 auto b = defaultAllocator.make!WriteBuffer(4); 541 ubyte[3] buf = [48, 23, 255]; 542 543 b ~= buf; 544 assert(b.capacity == 4); 545 assert(b.buffer_[0] == 48 && b.buffer_[1] == 23 && b.buffer_[2] == 255); 546 547 b += 2; 548 b ~= buf; 549 assert(b.capacity == 4); 550 assert(b.buffer_[0] == 23 && b.buffer_[1] == 255 551 && b.buffer_[2] == 255 && b.buffer_[3] == 48); 552 553 b += 2; 554 b ~= buf; 555 assert(b.capacity == 8); 556 assert(b.buffer_[0] == 23 && b.buffer_[1] == 255 557 && b.buffer_[2] == 48 && b.buffer_[3] == 23 && b.buffer_[4] == 255); 558 559 defaultAllocator.dispose(b); 560 561 b = make!WriteBuffer(defaultAllocator, 2); 562 563 b ~= buf; 564 assert(b.start == 0); 565 assert(b.capacity == 4); 566 assert(b.ring == 3); 567 assert(b.position == 3); 568 569 defaultAllocator.dispose(b); 570 } 571 572 /** 573 * Sets how many bytes were written. It will shrink the buffer 574 * appropriately. Always set this property after calling 575 * $(D_PSYMBOL buffer). 576 * 577 * Params: 578 * length = Length of the written data. 579 * 580 * Returns: $(D_KEYWORD this). 581 */ 582 @property WriteBuffer opOpAssign(string op)(size_t length) pure nothrow @safe @nogc 583 if (op == "+") 584 in 585 { 586 assert(length <= this.length); 587 } 588 do 589 { 590 auto afterRing = ring + 1; 591 auto oldStart = start; 592 593 if (length <= 0) 594 { 595 return this; 596 } 597 else if (position <= afterRing) 598 { 599 start += length; 600 if (start > 0 && position == afterRing) 601 { 602 position = oldStart; 603 } 604 } 605 else 606 { 607 auto overflow = position - afterRing; 608 609 if (overflow > length) { 610 buffer_[start.. start + length] = buffer_[afterRing.. afterRing + length]; 611 buffer_[afterRing.. afterRing + length] = buffer_[afterRing + length ..position]; 612 position -= length; 613 } 614 else if (overflow == length) 615 { 616 buffer_[start.. start + overflow] = buffer_[afterRing..position]; 617 position -= overflow; 618 } 619 else 620 { 621 buffer_[start.. start + overflow] = buffer_[afterRing..position]; 622 position = overflow; 623 } 624 start += length; 625 626 if (start == position) 627 { 628 if (position != afterRing) 629 { 630 position = 0; 631 } 632 start = 0; 633 ring = capacity - 1; 634 } 635 } 636 if (start > ring) 637 { 638 start = 0; 639 } 640 return this; 641 } 642 643 /// 644 unittest 645 { 646 auto b = defaultAllocator.make!WriteBuffer; 647 ubyte[6] buf = [23, 23, 255, 128, 127, 9]; 648 649 b ~= buf; 650 assert(b.length == 6); 651 b += 2; 652 assert(b.length == 4); 653 b += 4; 654 assert(b.length == 0); 655 656 defaultAllocator.dispose(b); 657 } 658 659 /** 660 * Returns a chunk with data. 661 * 662 * After calling it, set $(D_KEYWORD +=) to the length could be 663 * written. 664 * 665 * $(D_PSYMBOL buffer) may return only part of the data. You may need 666 * to call it (and set $(D_KEYWORD +=) several times until 667 * $(D_PSYMBOL length) is 0). If all the data can be written, 668 * maximally 3 calls are required. 669 * 670 * Returns: A chunk of data buffer. 671 */ 672 @property ubyte[] opSlice(size_t start, size_t end) pure nothrow @safe @nogc 673 { 674 immutable internStart = this.start + start; 675 676 if (position > ring || position < start) // Buffer overflowed 677 { 678 return buffer_[this.start.. ring + 1 - length + end]; 679 } 680 else 681 { 682 return buffer_[this.start.. this.start + end]; 683 } 684 } 685 686 /// 687 unittest 688 { 689 auto b = defaultAllocator.make!WriteBuffer(6); 690 ubyte[6] buf = [23, 23, 255, 128, 127, 9]; 691 692 b ~= buf; 693 assert(b[0..$] == buf[0..6]); 694 b += 2; 695 696 assert(b[0..$] == buf[2..6]); 697 698 b ~= buf; 699 assert(b[0..$] == buf[2..6]); 700 b += b.length; 701 702 assert(b[0..$] == buf[0..6]); 703 b += b.length; 704 705 defaultAllocator.dispose(b); 706 } 707 708 /** 709 * After calling it, set $(D_KEYWORD +=) to the length could be 710 * written. 711 * 712 * $(D_PSYMBOL buffer) may return only part of the data. You may need 713 * to call it (and set $(D_KEYWORD +=) several times until 714 * $(D_PSYMBOL length) is 0). If all the data can be written, 715 * maximally 3 calls are required. 716 * 717 * Returns: A chunk of data buffer. 718 */ 719 @property ubyte[] opIndex() pure nothrow @safe @nogc 720 { 721 return opSlice(0, length); 722 } 723 }