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 std.algorithm : max, min; 16 import core.exception : onOutOfMemoryError; 17 import std.c.string : memcpy; 18 import memutils.utils; 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 std.c.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 std.c.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 std.c.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 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 = 0, 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 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!(Buffers.Chain)(); 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 nwrite = cast(size_t) min(buf.available, len); 390 if (nwrite == 0) { 391 rv = allocChain(); 392 if (rv != 0) { 393 return rv; 394 } 395 continue; 396 } 397 memcpy(buf.last, p, nwrite); 398 buf.last += nwrite; 399 p += nwrite; 400 len -= nwrite; 401 } 402 403 return ErrorCode.OK; 404 } 405 406 /* 407 * Appends a single byte |b| to the Buffers. The write starts at 408 * cur.buf.last. A new buffers will be allocated to store all 409 * data. 410 * 411 * This function returns 0 if it succeeds, or one of the following 412 * negative error codes: 413 * 414 * ErrorCode.BUFFER_ERROR 415 * Out of buffer space. 416 */ 417 ErrorCode add(ubyte b) 418 { 419 ErrorCode rv; 420 421 rv = ensureAddByte(); 422 if (rv != 0) 423 return rv; 424 425 426 *cur.buf.last++ = b; 427 428 return ErrorCode.OK; 429 } 430 431 /* 432 * Behaves like addb(), but this does not update 433 * buf.last pointer. 434 */ 435 ErrorCode addHold(ubyte b) 436 { 437 ErrorCode rv; 438 439 rv = ensureAddByte(); 440 if (rv != 0) { 441 return rv; 442 } 443 444 *cur.buf.last = b; 445 446 return ErrorCode.OK; 447 } 448 449 void fastAdd(ubyte b) { 450 assert(cur.buf.last+1 <= cur.buf.end); 451 *cur.buf.last++ = b; 452 } 453 454 void fastAddHold(ubyte b) { 455 *cur.buf.last = b; 456 } 457 458 /* 459 * Performs bitwise-OR of |b| at cur.buf.last. A new buffers 460 * will be allocated if necessary. 461 * 462 * This function returns 0 if it succeeds, or one of the following 463 * negative error codes: 464 * 465 * ErrorCode.NOMEM 466 * Out of memory. 467 * ErrorCode.BUFFER_ERROR 468 * Out of buffer space. 469 */ 470 ErrorCode or(ubyte b) 471 { 472 ErrorCode rv; 473 474 rv = ensureAddByte(); 475 if (rv != 0) { 476 return rv; 477 } 478 479 *cur.buf.last++ |= b; 480 481 return ErrorCode.OK; 482 } 483 484 /* 485 * Behaves like orb(), but does not update buf.last 486 * pointer. 487 */ 488 ErrorCode orHold(ubyte b) 489 { 490 ErrorCode rv; 491 492 rv = ensureAddByte(); 493 if (rv != 0) { 494 return rv; 495 } 496 497 *cur.buf.last |= b; 498 499 return ErrorCode.OK; 500 } 501 502 void fastOr(ubyte b) { 503 assert(cur.buf.last+1 <= cur.buf.end); 504 *cur.buf.last++ |= b; 505 } 506 507 void fastOrHold(ubyte b) { 508 *cur.buf.last |= b; 509 } 510 511 /* 512 * Copies all data stored in Buffers to the contagious buffer. This 513 * function allocates the contagious memory to store all data in 514 * Buffers and returns it. 515 */ 516 ubyte[] remove() 517 { 518 size_t len; 519 Chain chain; 520 Buffer* buf; 521 ubyte[] res; 522 Buffer resbuf; 523 len = 0; 524 525 for (chain = head; chain; chain = chain.next) { 526 len += chain.buf.length; 527 } 528 if (!len) 529 res = null; 530 else { 531 res = Mem.alloc!(ubyte[])(len); 532 } 533 resbuf = Buffer(res); 534 535 for (chain = head; chain; chain = chain.next) { 536 buf = &chain.buf; 537 538 if (resbuf.last) { 539 assert(resbuf.available >= buf.length); 540 memcpy(resbuf.last, buf.pos, buf.length); 541 resbuf.last += buf.length; 542 } 543 544 buf.reset(); 545 chain.buf.shiftRight(offset); 546 } 547 548 cur = head; 549 return res; 550 } 551 552 /// Fills dst with a slice of the head chain's buffer, and frees the chain if it becomes empty 553 /// chunk_keep must be 1 for buffers to be emptied this way. 554 ubyte[] removeOne(ubyte[] dst) 555 in { 556 assert(chunk_keep <= 1, "Cannot use removeOne with a custom keep amount set"); 557 } 558 body { 559 Chain chain = head; 560 size_t len = min(chain.buf.length, dst.length); 561 ubyte[] data = chain.buf.pos[0 .. len]; 562 memcpy(dst.ptr, data.ptr, len); 563 564 chain.buf.pos += len; 565 566 if (chain.buf.available == 0 && chain.buf.length == 0) 567 { 568 if (chain.next) { 569 head = chain.next; 570 chain.free(); 571 Mem.free(chain); 572 chunk_used--; 573 } else { 574 chain.buf.reset(); 575 chain.buf.shiftRight(offset); 576 cur = head = chain; 577 } 578 } 579 580 if (dst.length == len) 581 return dst; 582 return dst[0 .. len]; 583 } 584 585 // The head buffer was already read, just remove it 586 void removeOne() 587 in { 588 assert(chunk_keep <= 1, "Cannot use removeOne with a custom keep amount set"); 589 } 590 body { 591 Chain chain = head; 592 593 if (chain.next) { 594 head = chain.next; 595 chain.free(); 596 Mem.free(chain); 597 chunk_used--; 598 } else { 599 chain.buf.reset(); 600 chain.buf.shiftRight(offset); 601 cur = head = chain; 602 } 603 } 604 605 /* 606 * Resets Buffers and makes the buffers empty. 607 */ 608 void reset() 609 { 610 Chain chain; 611 Chain ci; 612 size_t k; 613 614 k = chunk_keep; 615 for (ci = head; ci; ci = ci.next) { 616 ci.buf.reset(); 617 ci.buf.shiftRight(offset); 618 619 if (--k == 0) { 620 break; 621 } 622 } 623 624 if (ci) { 625 chain = ci.next; 626 ci.next = null; 627 628 for (ci = chain; ci;) { 629 chain = ci.next; 630 631 ci.free(); 632 Mem.free(ci); 633 ci = chain; 634 } 635 636 chunk_used = chunk_keep; 637 } 638 639 cur = head; 640 } 641 642 /* 643 * Moves cur to cur.next. If resulting cur is 644 * null, this function allocates new buffers and cur points to 645 * it. 646 * 647 * This function returns 0 if it succeeds, or one of the following 648 * negative error codes: 649 * 650 * ErrorCode.NOMEM 651 * Out of memory 652 * ErrorCode.BUFFER_ERROR 653 * Out of buffer space. 654 */ 655 ErrorCode advance() { return allocChain(); } 656 657 /* Sets cur to head */ 658 void rewind() { 659 cur = head; 660 } 661 662 663 /* 664 * Move cur, from the current position, using next member, to 665 * the last buf which has length > 0 without seeing buf 666 * which satisfies length == 0. If cur.buf.length == 0 or cur.next is null, 667 * cur is unchanged. 668 */ 669 void seekLastPresent() 670 { 671 Chain ci; 672 673 for (ci = cur; ci; ci = ci.next) { 674 if (ci.buf.length == 0) { 675 return; 676 } else { 677 cur = ci; 678 } 679 } 680 } 681 682 /* 683 * Returns true if cur.next is not empty. 684 */ 685 bool nextPresent() 686 { 687 Chain chain; 688 689 chain = cur.next; 690 691 return chain && chain.buf.length; 692 } 693 694 int curAvailable() { 695 return cur.buf.available(); 696 } 697 698 @property int length() 699 { 700 Chain ci; 701 int len; 702 703 len = 0; 704 for (ci = head; ci; ci = ci.next) { 705 len += ci.buf.length; 706 } 707 708 return len; 709 } 710 711 @property int available() { 712 return cast(int)(cur.buf.available + (chunk_length - offset) * (max_chunk - chunk_used)); 713 714 } 715 716 private: 717 ErrorCode ensureAddByte() 718 { 719 ErrorCode rv; 720 Buffer* buf; 721 722 buf = &cur.buf; 723 724 if (buf.available >= 1) { 725 return ErrorCode.OK; 726 } 727 728 rv = allocChain(); 729 if (rv != 0) 730 return rv; 731 return ErrorCode.OK; 732 } 733 734 ErrorCode allocChain() { 735 Chain chain; 736 737 if (cur.next) { 738 cur = cur.next; 739 return ErrorCode.OK; 740 } 741 742 if (max_chunk == chunk_used) 743 return ErrorCode.BUFFER_ERROR; 744 745 chain = Chain(chunk_length, use_secure_mem, zeroize_on_free); 746 747 LOGF("new buffer %d bytes allocated for bufs %s, used %d", chunk_length, this, chunk_used); 748 749 ++chunk_used; 750 751 cur.next = chain; 752 cur = chain; 753 cur.buf.shiftRight(offset); 754 755 return ErrorCode.OK; 756 } 757 758 }