1 /** 2 * Buffers 3 * 4 * Copyright: 5 * (C) 2012-2015 Tatsuhiro Tsujikawa 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Distributed under the terms of the MIT license with an additional section 1.2 of the curl/libcurl project. 10 * Consult the provided LICENSE.md file for details 11 */ 12 module libhttp2.buffers; 13 14 import libhttp2.types; 15 import core.exception : onOutOfMemoryError; 16 import core.stdc..string : memcpy; 17 import memutils.utils; 18 import std.algorithm : min,max; 19 20 struct Buffer 21 { 22 /// This points to the beginning of the buffer. The effective range of buffer is [begin, end). 23 ubyte* begin; 24 /// This points to the memory one byte beyond the end of the buffer. 25 ubyte* end; 26 /// The position indicator for effective start of the buffer. pos <= last must be hold. 27 ubyte* pos; 28 /// The position indicator for effective one beyond of the end of the buffer. last <= end must be hold. 29 ubyte* last; 30 /// Mark arbitrary position in buffer [begin, end) 31 ubyte* mark; 32 /// true if the allocator must be secure 33 bool use_secure_mem; 34 /// true if the memory much be manually freed. use_secure_mem can be false if this is true 35 bool zeroize_on_free; 36 37 ubyte[] opSlice() { return pos[0 .. length]; } 38 39 @property int length() { 40 assert(last-pos >= 0); 41 return cast(int)(last - pos); 42 } 43 44 @property int available() { 45 return cast(int)(end - last); 46 } 47 48 @property int markAvailable() { 49 return cast(int)(mark - last); 50 } 51 52 @property int capacity() { 53 return cast(int)(end - begin); 54 } 55 56 @property int posOffset() { 57 return cast(int)(pos - begin); 58 } 59 @property int offset() { 60 return cast(int) (last - begin); 61 } 62 63 void shiftRight(size_t amount) { 64 pos += amount; 65 last += amount; 66 } 67 68 void shiftLeft(size_t amount) { 69 pos -= amount; 70 last -= amount; 71 } 72 73 void free() 74 { 75 if (!end || !begin) return; 76 assert(end >= begin); 77 size_t memlen = cast(size_t)((cast(void*)end) - (cast(void*)begin)); 78 static if (__VERSION__ >= 2067) 79 { 80 if (use_secure_mem) { 81 import core.stdc..string : memset; 82 // optimization, skips freeing the entire buffer in memutils for light buffer uses 83 if (capacity > 1024 && offset < capacity - capacity/128) 84 memset(begin, 0, offset); 85 SecureMem.free(begin[0 .. memlen]); 86 } 87 else 88 { 89 if (zeroize_on_free) 90 { 91 import core.stdc..string : memset; 92 memset(begin, 0, memlen); 93 } 94 Mem.free(begin[0 .. memlen]); 95 } 96 } 97 else { 98 if (zeroize_on_free || use_secure_mem) 99 { 100 import core.stdc..string : memset; 101 memset(begin, 0, memlen); 102 } 103 Mem.free(begin[0 .. memlen]); 104 } 105 begin = null; 106 end = null; 107 } 108 109 /* 110 * Extends buffer so that capacity() returns at least 111 * |new_cap|. If extensions took place, buffer pointers in |buf| will 112 * change. 113 */ 114 void reserve(size_t new_cap) 115 { 116 if (!new_cap) return; 117 118 ubyte[] new_buf; 119 size_t cap; 120 121 cap = capacity; 122 123 if (cap >= new_cap) { 124 return; 125 } 126 import std.algorithm : max; 127 new_cap = max(new_cap, cap * 2); 128 129 static if (__VERSION__ >= 2067) 130 { 131 if (use_secure_mem) { 132 if (begin) 133 new_buf = SecureMem.realloc(begin[0 .. end - begin], new_cap); 134 else 135 new_buf = SecureMem.alloc!(ubyte[])(new_cap); 136 } 137 else { 138 if (begin) { 139 new_buf = Mem.realloc(begin[0 .. end - begin], new_cap); 140 } 141 else 142 new_buf = Mem.alloc!(ubyte[])(new_cap); 143 } 144 } 145 else { 146 if (begin) { 147 new_buf = Mem.realloc(begin[0 .. end - begin], new_cap); 148 } 149 else 150 new_buf = Mem.alloc!(ubyte[])(new_cap); 151 } 152 ubyte* ptr = new_buf.ptr; 153 154 pos = ptr + (pos - begin); 155 last = ptr + (last - begin); 156 mark = ptr + (mark - begin); 157 begin = ptr; 158 end = ptr + new_cap; 159 160 return; 161 } 162 163 /// Resets pos, last, mark member of |buf| to buf.begin. 164 void reset() 165 { 166 pos = last = mark = begin; 167 } 168 169 /* 170 * Initializes Buffer using supplied buffer |buf|. Semantically, 171 * the application should not call *_reserve() or free() functions. 172 */ 173 this(ubyte[] buf) { 174 begin = pos = last = mark = buf.ptr; 175 end = begin + buf.length; 176 } 177 178 /* 179 * Initializes the |buf| and allocates at least |initial| bytes of 180 * memory. 181 */ 182 this(size_t initial, bool _use_secure_mem = false, bool _zeroise_on_free = false) { 183 use_secure_mem = _use_secure_mem; 184 zeroize_on_free = _zeroise_on_free; 185 begin = null; 186 end = null; 187 pos = null; 188 last = null; 189 mark = null; 190 reserve(initial); 191 } 192 193 } 194 195 class Buffers { 196 197 static class Chain { 198 Buffer buf; 199 Chain next; 200 static Chain opCall(size_t chunk_length = 0, bool _use_secure_mem = false, bool _zeroize_on_free = false) 201 { 202 Chain chain; 203 chain = Mem.alloc!(typeof(this))(); 204 scope(failure) Mem.free(chain); 205 chain.next = null; 206 chain.buf = Buffer(chunk_length, _use_secure_mem, _zeroize_on_free); 207 return chain; 208 } 209 210 void free() 211 { 212 buf.free(); 213 buf.destroy(); 214 } 215 } 216 217 /// Points to the first buffer 218 Chain head; 219 /// Buffer pointer where write occurs. 220 Chain cur; 221 /// The buffer capacity of each Buffer 222 size_t chunk_length; 223 /// The maximum number of `Chain`s 224 size_t max_chunk; 225 /// The number of `Chain`s allocated 226 size_t chunk_used; 227 /// The number of `Chain`s to keep on reset 228 size_t chunk_keep; 229 /// pos offset from begin in each buffers. On initialization and reset, buf.pos and buf.last are positioned at buf.begin + offset. 230 size_t offset; 231 232 /// true if the buffers were initialized with a pre-allocated ubyte[] which mustn't be freed 233 bool dont_free; 234 /// true if we should secure the Buffer allocations 235 bool use_secure_mem; 236 /// true if the Buffer allocations should be zeroized 237 bool zeroize_on_free; 238 239 /// This is the same as calling init2 with the given arguments and offset = 0. 240 this(size_t _chunk_length, size_t _max_chunk, bool _use_secure_mem = false, bool _zeroize_on_free = false) { 241 this(_chunk_length, _max_chunk, _max_chunk, 0, _use_secure_mem, _zeroize_on_free); 242 } 243 244 /// This is the same as calling init3 with the given arguments and chunk_keep = max_chunk. 245 this(size_t _chunk_length, size_t _max_chunk, size_t _offset, bool _use_secure_mem = false, bool _zeroize_on_free = false) 246 { 247 this(_chunk_length, _max_chunk, _max_chunk, _offset, _use_secure_mem, _zeroize_on_free); 248 } 249 250 /** 251 * Initializes Buffers. Each buffer size is given in the 252 * |chunk_length|. The maximum number of buffers is given in the 253 * |max_chunk|. On reset, first |chunk_keep| buffers are kept and 254 * remaining buffers are deleted. Each buffer will have bufs.pos and 255 * bufs.last shifted to left by |offset| bytes on creation and reset. 256 * 257 * This function allocates first buffer. bufs.head and bufs.cur 258 * will point to the first buffer after this call. 259 */ 260 this(size_t _chunk_length, size_t _max_chunk, size_t _chunk_keep, size_t _offset, bool _use_secure_mem = false, bool _zeroize_on_free = false) 261 in { assert(!(_chunk_keep == 0 || _max_chunk < _chunk_keep || _chunk_length < _offset), "Invalid Arguments"); } 262 body 263 { 264 use_secure_mem = _use_secure_mem; 265 zeroize_on_free = _zeroize_on_free; 266 Chain chain = Chain(_chunk_length, _use_secure_mem, _zeroize_on_free); 267 268 offset = _offset; 269 270 head = chain; 271 cur = head; 272 273 cur.buf.shiftRight(offset); 274 275 chunk_length = _chunk_length; 276 chunk_used = 1; 277 max_chunk = _max_chunk; 278 chunk_keep = _chunk_keep; 279 } 280 281 /* 282 * Initializes Buffers using supplied buffer |buf|. 283 * The first buffer bufs.head uses buffer |begin|. The buffer size 284 * is fixed and no allocate extra chunk buffer is allocated. In other 285 * words, max_chunk = chunk_keep = 1. 286 */ 287 this(ubyte[] buf) 288 { 289 Chain chain = Chain(); 290 291 chain.next = null; 292 chain.buf = Buffer(buf); 293 294 dont_free = true; 295 offset = 0; 296 head = chain; 297 cur = head; 298 chunk_length = buf.length; 299 chunk_used = 1; 300 max_chunk = 1; 301 chunk_keep = 1; 302 } 303 304 ~this() { if (head) free(); } 305 306 /// Frees any related resources 307 void free() 308 { 309 Chain chain; 310 Chain next_chain; 311 312 if (!head) return; 313 314 if (dont_free) { 315 Mem.free(head); 316 head = null; 317 return; 318 } 319 320 for (chain = head; chain;) { 321 next_chain = chain.next; 322 chain.free(); 323 Mem.free(chain); 324 chain = next_chain; 325 } 326 327 head = null; 328 } 329 330 /* 331 * Reallocates internal buffer using |chunk_length|. The max_chunk, 332 * chunk_keep and offset do not change. After successful allocation 333 * of new buffer, previous buffers are deallocated without copying 334 * anything into new buffers. chunk_used is reset to 1. 335 * 336 * This function returns 0 if it succeeds, or one of the following 337 * negative error codes: 338 * 339 * ErrorCode.NOMEM 340 * Out of memory. 341 * ErrorCode.INVALID_ARGUMENT 342 * chunk_length < offset 343 */ 344 void realloc(size_t _chunk_length) 345 in { assert(_chunk_length >= offset, "Invalid Arguments"); } 346 body { 347 Chain chain = Chain(_chunk_length, use_secure_mem, zeroize_on_free); 348 349 free(); 350 351 head = chain; 352 cur = head; 353 354 cur.buf.shiftRight(offset); 355 356 chunk_length = _chunk_length; 357 chunk_used = 1; 358 } 359 360 /* 361 * Appends the |data| of length |len| to the |bufs|. The write starts 362 * at bufs.cur.buf.last. A new buffers will be allocated to store 363 * all data. 364 * 365 * This function returns 0 if it succeeds, or one of the following 366 * negative error codes: 367 * 368 * ErrorCode.NOMEM 369 * Out of memory. 370 * ErrorCode.BUFFER_ERROR 371 * Out of buffer space. 372 */ 373 ErrorCode add(in string data) 374 { 375 ErrorCode rv; 376 size_t nwrite; 377 Buffer* buf; 378 const(ubyte)* p; 379 int len = cast(int)data.length; 380 if (available < len) { 381 return ErrorCode.BUFFER_ERROR; 382 } 383 384 p = cast(const(ubyte)*)data.ptr; 385 386 while (len > 0) { 387 buf = &cur.buf; 388 389 import std.algorithm : min; 390 nwrite = cast(size_t) min(buf.available, len); 391 if (nwrite == 0) { 392 rv = allocChain(); 393 if (rv != 0) { 394 return rv; 395 } 396 continue; 397 } 398 memcpy(buf.last, p, nwrite); 399 buf.last += nwrite; 400 p += nwrite; 401 len -= nwrite; 402 } 403 404 return ErrorCode.OK; 405 } 406 407 /* 408 * Appends a single byte |b| to the Buffers. The write starts at 409 * cur.buf.last. A new buffers will be allocated to store all 410 * data. 411 * 412 * This function returns 0 if it succeeds, or one of the following 413 * negative error codes: 414 * 415 * ErrorCode.BUFFER_ERROR 416 * Out of buffer space. 417 */ 418 ErrorCode add(ubyte b) 419 { 420 ErrorCode rv; 421 422 rv = ensureAddByte(); 423 if (rv != 0) 424 return rv; 425 426 427 *cur.buf.last++ = b; 428 429 return ErrorCode.OK; 430 } 431 432 /* 433 * Behaves like addb(), but this does not update 434 * buf.last pointer. 435 */ 436 ErrorCode addHold(ubyte b) 437 { 438 ErrorCode rv; 439 440 rv = ensureAddByte(); 441 if (rv != 0) { 442 return rv; 443 } 444 445 *cur.buf.last = b; 446 447 return ErrorCode.OK; 448 } 449 450 void fastAdd(ubyte b) { 451 assert(cur.buf.last+1 <= cur.buf.end); 452 *cur.buf.last++ = b; 453 } 454 455 void fastAddHold(ubyte b) { 456 *cur.buf.last = b; 457 } 458 459 /* 460 * Performs bitwise-OR of |b| at cur.buf.last. A new buffers 461 * will be allocated if necessary. 462 * 463 * This function returns 0 if it succeeds, or one of the following 464 * negative error codes: 465 * 466 * ErrorCode.NOMEM 467 * Out of memory. 468 * ErrorCode.BUFFER_ERROR 469 * Out of buffer space. 470 */ 471 ErrorCode or(ubyte b) 472 { 473 ErrorCode rv; 474 475 rv = ensureAddByte(); 476 if (rv != 0) { 477 return rv; 478 } 479 480 *cur.buf.last++ |= b; 481 482 return ErrorCode.OK; 483 } 484 485 /* 486 * Behaves like orb(), but does not update buf.last 487 * pointer. 488 */ 489 ErrorCode orHold(ubyte b) 490 { 491 ErrorCode rv; 492 493 rv = ensureAddByte(); 494 if (rv != 0) { 495 return rv; 496 } 497 498 *cur.buf.last |= b; 499 500 return ErrorCode.OK; 501 } 502 503 void fastOr(ubyte b) { 504 assert(cur.buf.last+1 <= cur.buf.end); 505 *cur.buf.last++ |= b; 506 } 507 508 void fastOrHold(ubyte b) { 509 *cur.buf.last |= b; 510 } 511 512 /* 513 * Copies all data stored in Buffers to the contagious buffer. This 514 * function allocates the contagious memory to store all data in 515 * Buffers and returns it. 516 */ 517 ubyte[] remove() 518 { 519 size_t len; 520 Chain chain; 521 Buffer* buf; 522 ubyte[] res; 523 Buffer resbuf; 524 len = 0; 525 526 for (chain = head; chain; chain = chain.next) { 527 len += chain.buf.length; 528 } 529 if (!len) 530 res = null; 531 else { 532 res = Mem.alloc!(ubyte[])(len); 533 } 534 resbuf = Buffer(res); 535 536 for (chain = head; chain; chain = chain.next) { 537 buf = &chain.buf; 538 539 if (resbuf.last) { 540 assert(resbuf.available >= buf.length); 541 memcpy(resbuf.last, buf.pos, buf.length); 542 resbuf.last += buf.length; 543 } 544 545 buf.reset(); 546 chain.buf.shiftRight(offset); 547 } 548 549 cur = head; 550 return res; 551 } 552 553 /// Fills dst with a slice of the head chain's buffer, and frees the chain if it becomes empty 554 /// chunk_keep must be 1 for buffers to be emptied this way. 555 ubyte[] removeOne(ubyte[] dst) 556 in { 557 assert(chunk_keep <= 1, "Cannot use removeOne with a custom keep amount set"); 558 } 559 body { 560 Chain chain = head; 561 import std.algorithm : min; 562 size_t len = min(chain.buf.length, dst.length); 563 ubyte[] data = chain.buf.pos[0 .. len]; 564 memcpy(dst.ptr, data.ptr, len); 565 566 chain.buf.pos += len; 567 568 if (chain.buf.available == 0 && chain.buf.length == 0) 569 { 570 if (chain.next) { 571 head = chain.next; 572 chain.free(); 573 Mem.free(chain); 574 chunk_used--; 575 } else { 576 chain.buf.reset(); 577 chain.buf.shiftRight(offset); 578 cur = head = chain; 579 } 580 } 581 582 if (dst.length == len) 583 return dst; 584 return dst[0 .. len]; 585 } 586 587 // The head buffer was already read, just remove it 588 void removeOne() 589 in { 590 assert(chunk_keep <= 1, "Cannot use removeOne with a custom keep amount set"); 591 } 592 body { 593 Chain chain = head; 594 595 if (chain.next) { 596 head = chain.next; 597 chain.free(); 598 Mem.free(chain); 599 chunk_used--; 600 } else { 601 chain.buf.reset(); 602 chain.buf.shiftRight(offset); 603 cur = head = chain; 604 } 605 } 606 607 /* 608 * Resets Buffers and makes the buffers empty. 609 */ 610 void reset() 611 { 612 Chain chain; 613 Chain ci; 614 size_t k; 615 616 k = chunk_keep; 617 for (ci = head; ci; ci = ci.next) { 618 ci.buf.reset(); 619 ci.buf.shiftRight(offset); 620 621 if (--k == 0) { 622 break; 623 } 624 } 625 626 if (ci) { 627 chain = ci.next; 628 ci.next = null; 629 630 for (ci = chain; ci;) { 631 chain = ci.next; 632 633 ci.free(); 634 Mem.free(ci); 635 ci = chain; 636 } 637 638 chunk_used = chunk_keep; 639 } 640 641 cur = head; 642 } 643 644 /* 645 * Moves cur to cur.next. If resulting cur is 646 * null, this function allocates new buffers and cur points to 647 * it. 648 * 649 * This function returns 0 if it succeeds, or one of the following 650 * negative error codes: 651 * 652 * ErrorCode.NOMEM 653 * Out of memory 654 * ErrorCode.BUFFER_ERROR 655 * Out of buffer space. 656 */ 657 ErrorCode advance() { return allocChain(); } 658 659 /* Sets cur to head */ 660 void rewind() { 661 cur = head; 662 } 663 664 665 /* 666 * Move cur, from the current position, using next member, to 667 * the last buf which has length > 0 without seeing buf 668 * which satisfies length == 0. If cur.buf.length == 0 or cur.next is null, 669 * cur is unchanged. 670 */ 671 void seekLastPresent() 672 { 673 Chain ci; 674 675 for (ci = cur; ci; ci = ci.next) { 676 if (ci.buf.length == 0) { 677 return; 678 } else { 679 cur = ci; 680 } 681 } 682 } 683 684 /* 685 * Returns true if cur.next is not empty. 686 */ 687 bool nextPresent() 688 { 689 Chain chain; 690 691 chain = cur.next; 692 693 return chain && chain.buf.length; 694 } 695 696 int curAvailable() { 697 return cur.buf.available(); 698 } 699 700 @property int length() 701 { 702 Chain ci; 703 int len; 704 705 len = 0; 706 for (ci = head; ci; ci = ci.next) { 707 len += ci.buf.length; 708 } 709 710 return len; 711 } 712 713 @property int available() { 714 return cast(int)(cur.buf.available + (chunk_length - offset) * (max_chunk - chunk_used)); 715 716 } 717 718 private: 719 ErrorCode ensureAddByte() 720 { 721 ErrorCode rv; 722 Buffer* buf; 723 724 buf = &cur.buf; 725 726 if (buf.available >= 1) { 727 return ErrorCode.OK; 728 } 729 730 rv = allocChain(); 731 if (rv != 0) 732 return rv; 733 return ErrorCode.OK; 734 } 735 736 ErrorCode allocChain() { 737 Chain chain; 738 739 if (cur.next) { 740 cur = cur.next; 741 return ErrorCode.OK; 742 } 743 744 if (max_chunk == chunk_used) 745 return ErrorCode.BUFFER_ERROR; 746 747 chain = Chain(chunk_length, use_secure_mem, zeroize_on_free); 748 749 LOGF("new buffer %d bytes allocated for bufs %s, used %d", chunk_length, this, chunk_used); 750 751 ++chunk_used; 752 753 cur.next = chain; 754 cur = chain; 755 cur.buf.shiftRight(offset); 756 757 return ErrorCode.OK; 758 } 759 760 }