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