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 (!begin) return; 76 static if (__VERSION__ >= 2067) 77 { 78 if (use_secure_mem) SecureMem.free(begin[0 .. end - begin]); 79 else 80 { 81 if (zeroize_on_free) 82 { 83 import std.c.string : memset; 84 memset(begin, 0, end-begin); 85 } 86 Mem.free(begin[0 .. end - begin]); 87 } 88 } 89 else { 90 if (zeroize_on_free || use_secure_mem) 91 { 92 import std.c.string : memset; 93 memset(begin, 0, end-begin); 94 } 95 Mem.free(begin[0 .. end - begin]); 96 } 97 begin = null; 98 } 99 100 /* 101 * Extends buffer so that capacity() returns at least 102 * |new_cap|. If extensions took place, buffer pointers in |buf| will 103 * change. 104 */ 105 void reserve(size_t new_cap) 106 { 107 if (!new_cap) return; 108 109 ubyte[] new_buf; 110 size_t cap; 111 112 cap = capacity; 113 114 if (cap >= new_cap) { 115 return; 116 } 117 118 new_cap = max(new_cap, cap * 2); 119 120 static if (__VERSION__ >= 2067) 121 { 122 if (use_secure_mem) { 123 if (begin) 124 new_buf = SecureMem.realloc(begin[0 .. end - begin], new_cap); 125 else 126 new_buf = SecureMem.alloc!(ubyte[])(new_cap); 127 } 128 else { 129 if (begin) { 130 new_buf = Mem.realloc(begin[0 .. end - begin], new_cap); 131 if (zeroize_on_free && (new_buf.ptr > end || new_buf.ptr + new_cap < begin)) 132 { 133 import std.c.string : memset; 134 memset(begin, 0, end - begin); 135 } 136 } 137 else 138 new_buf = Mem.alloc!(ubyte[])(new_cap); 139 } 140 } 141 else { 142 if (begin) { 143 new_buf = Mem.realloc(begin[0 .. end - begin], new_cap); 144 if ((zeroize_on_free || use_secure_mem) && (new_buf.ptr > end || new_buf.ptr + new_cap < begin)) 145 { 146 import std.c.string : memset; 147 memset(begin, 0, end - begin); 148 } 149 } 150 else 151 new_buf = Mem.alloc!(ubyte[])(new_cap); 152 } 153 ubyte* ptr = new_buf.ptr; 154 155 pos = ptr + (pos - begin); 156 last = ptr + (last - begin); 157 mark = ptr + (mark - begin); 158 begin = ptr; 159 end = ptr + new_cap; 160 161 return; 162 } 163 164 /// Resets pos, last, mark member of |buf| to buf.begin. 165 void reset() 166 { 167 pos = last = mark = begin; 168 } 169 170 /* 171 * Initializes Buffer using supplied buffer |buf|. Semantically, 172 * the application should not call *_reserve() or free() functions. 173 */ 174 this(ubyte[] buf) { 175 begin = pos = last = mark = buf.ptr; 176 end = begin + buf.length; 177 } 178 179 /* 180 * Initializes the |buf| and allocates at least |initial| bytes of 181 * memory. 182 */ 183 this(size_t initial = 0) { 184 begin = null; 185 end = null; 186 pos = null; 187 last = null; 188 mark = null; 189 reserve(initial); 190 } 191 192 } 193 194 class Buffers { 195 196 class Chain { 197 Buffer buf; 198 Chain next; 199 static Chain opCall(size_t chunk_length = 0, bool use_secure_mem = false, bool zeroize_on_free = false) 200 { 201 Chain chain; 202 chain = Mem.alloc!(Buffers.Chain)(); 203 scope(failure) Mem.free(chain); 204 chain.next = null; 205 chain.buf = Buffer(chunk_length); 206 chain.buf.use_secure_mem = use_secure_mem; 207 chain.buf.zeroize_on_free = zeroize_on_free; 208 return chain; 209 } 210 211 void free() 212 { 213 buf.free(); 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 Chain chain = Chain(_chunk_length, _use_secure_mem, _zeroize_on_free); 265 266 offset = _offset; 267 268 head = chain; 269 cur = head; 270 271 cur.buf.shiftRight(offset); 272 273 chunk_length = _chunk_length; 274 chunk_used = 1; 275 max_chunk = _max_chunk; 276 chunk_keep = _chunk_keep; 277 } 278 279 /* 280 * Initializes Buffers using supplied buffer |buf|. 281 * The first buffer bufs.head uses buffer |begin|. The buffer size 282 * is fixed and no allocate extra chunk buffer is allocated. In other 283 * words, max_chunk = chunk_keep = 1. 284 */ 285 this(ubyte[] buf) 286 { 287 Chain chain = Chain(); 288 289 chain.next = null; 290 chain.buf = Buffer(buf); 291 292 dont_free = true; 293 offset = 0; 294 head = chain; 295 cur = head; 296 chunk_length = buf.length; 297 chunk_used = 1; 298 max_chunk = 1; 299 chunk_keep = 1; 300 } 301 302 /// Frees any related resources 303 void free() 304 { 305 Chain chain; 306 Chain next_chain; 307 308 if (!head) return; 309 310 if (dont_free) { 311 Mem.free(head); 312 head = null; 313 return; 314 } 315 316 for (chain = head; chain;) { 317 next_chain = chain.next; 318 chain.free(); 319 Mem.free(chain); 320 chain = next_chain; 321 } 322 323 head = null; 324 } 325 326 /* 327 * Reallocates internal buffer using |chunk_length|. The max_chunk, 328 * chunk_keep and offset do not change. After successful allocation 329 * of new buffer, previous buffers are deallocated without copying 330 * anything into new buffers. chunk_used is reset to 1. 331 * 332 * This function returns 0 if it succeeds, or one of the following 333 * negative error codes: 334 * 335 * ErrorCode.NOMEM 336 * Out of memory. 337 * ErrorCode.INVALID_ARGUMENT 338 * chunk_length < offset 339 */ 340 void realloc(size_t _chunk_length) 341 in { assert(_chunk_length >= offset, "Invalid Arguments"); } 342 body { 343 Chain chain = Chain(_chunk_length, use_secure_mem, zeroize_on_free); 344 345 free(); 346 347 head = chain; 348 cur = head; 349 350 cur.buf.shiftRight(offset); 351 352 chunk_length = _chunk_length; 353 chunk_used = 1; 354 } 355 356 /* 357 * Appends the |data| of length |len| to the |bufs|. The write starts 358 * at bufs.cur.buf.last. A new buffers will be allocated to store 359 * all data. 360 * 361 * This function returns 0 if it succeeds, or one of the following 362 * negative error codes: 363 * 364 * ErrorCode.NOMEM 365 * Out of memory. 366 * ErrorCode.BUFFER_ERROR 367 * Out of buffer space. 368 */ 369 ErrorCode add(in string data) 370 { 371 ErrorCode rv; 372 size_t nwrite; 373 Buffer* buf; 374 const(ubyte)* p; 375 int len = cast(int)data.length; 376 if (available < len) { 377 return ErrorCode.BUFFER_ERROR; 378 } 379 380 p = cast(const(ubyte)*)data.ptr; 381 382 while (len > 0) { 383 buf = &cur.buf; 384 385 nwrite = cast(size_t) min(buf.available, len); 386 if (nwrite == 0) { 387 rv = allocChain(); 388 if (rv != 0) { 389 return rv; 390 } 391 continue; 392 } 393 memcpy(buf.last, p, nwrite); 394 buf.last += nwrite; 395 p += len; 396 len -= nwrite; 397 } 398 399 return ErrorCode.OK; 400 } 401 402 /* 403 * Appends a single byte |b| to the Buffers. The write starts at 404 * cur.buf.last. A new buffers will be allocated to store all 405 * data. 406 * 407 * This function returns 0 if it succeeds, or one of the following 408 * negative error codes: 409 * 410 * ErrorCode.BUFFER_ERROR 411 * Out of buffer space. 412 */ 413 ErrorCode add(ubyte b) 414 { 415 ErrorCode rv; 416 417 rv = ensureAddByte(); 418 if (rv != 0) 419 return rv; 420 421 422 *cur.buf.last++ = b; 423 424 return ErrorCode.OK; 425 } 426 427 /* 428 * Behaves like addb(), but this does not update 429 * buf.last pointer. 430 */ 431 ErrorCode addHold(ubyte b) 432 { 433 ErrorCode rv; 434 435 rv = ensureAddByte(); 436 if (rv != 0) { 437 return rv; 438 } 439 440 *cur.buf.last = b; 441 442 return ErrorCode.OK; 443 } 444 445 void fastAdd(ubyte b) { 446 assert(cur.buf.last+1 <= cur.buf.end); 447 *cur.buf.last++ = b; 448 } 449 450 void fastAddHold(ubyte b) { 451 *cur.buf.last = b; 452 } 453 454 /* 455 * Performs bitwise-OR of |b| at cur.buf.last. A new buffers 456 * will be allocated if necessary. 457 * 458 * This function returns 0 if it succeeds, or one of the following 459 * negative error codes: 460 * 461 * ErrorCode.NOMEM 462 * Out of memory. 463 * ErrorCode.BUFFER_ERROR 464 * Out of buffer space. 465 */ 466 ErrorCode or(ubyte b) 467 { 468 ErrorCode rv; 469 470 rv = ensureAddByte(); 471 if (rv != 0) { 472 return rv; 473 } 474 475 *cur.buf.last++ |= b; 476 477 return ErrorCode.OK; 478 } 479 480 /* 481 * Behaves like orb(), but does not update buf.last 482 * pointer. 483 */ 484 ErrorCode orHold(ubyte b) 485 { 486 ErrorCode rv; 487 488 rv = ensureAddByte(); 489 if (rv != 0) { 490 return rv; 491 } 492 493 *cur.buf.last |= b; 494 495 return ErrorCode.OK; 496 } 497 498 void fastOr(ubyte b) { 499 assert(cur.buf.last+1 <= cur.buf.end); 500 *cur.buf.last++ |= b; 501 } 502 503 void fastOrHold(ubyte b) { 504 *cur.buf.last |= b; 505 } 506 507 /* 508 * Copies all data stored in Buffers to the contagious buffer. This 509 * function allocates the contagious memory to store all data in 510 * Buffers and returns it. 511 */ 512 ubyte[] remove() 513 { 514 size_t len; 515 Chain chain; 516 Buffer* buf; 517 ubyte[] res; 518 Buffer resbuf; 519 len = 0; 520 521 for (chain = head; chain; chain = chain.next) { 522 len += chain.buf.length; 523 } 524 if (!len) 525 res = null; 526 else { 527 res = Mem.alloc!(ubyte[])(len); 528 } 529 resbuf = Buffer(res); 530 531 for (chain = head; chain; chain = chain.next) { 532 buf = &chain.buf; 533 534 if (resbuf.last) { 535 assert(resbuf.available >= buf.length); 536 memcpy(resbuf.last, buf.pos, buf.length); 537 resbuf.last += buf.length; 538 } 539 540 buf.reset(); 541 chain.buf.shiftRight(offset); 542 } 543 544 cur = head; 545 return res; 546 } 547 548 /// Fills dst with a slice of the head chain's buffer, and frees the chain if it becomes empty 549 /// chunk_keep must be 1 for buffers to be emptied this way. 550 ubyte[] removeOne(ubyte[] dst) 551 in { 552 assert(chunk_keep <= 1, "Cannot use removeOne with a custom keep amount set"); 553 } 554 body { 555 Chain chain = head; 556 size_t len = min(chain.buf.length, dst.length); 557 ubyte[] data = chain.buf.pos[0 .. len]; 558 memcpy(dst.ptr, data.ptr, len); 559 560 chain.buf.pos += len; 561 562 if (chain.buf.length > 0) 563 return dst; 564 565 if (chain.next) { 566 head = chain.next; 567 chain.free(); 568 chunk_used--; 569 } else { 570 chain.buf.reset(); 571 chain.buf.shiftRight(offset); 572 cur = head = chain; 573 } 574 return dst[0 .. len]; 575 } 576 577 // The head buffer was already read, just remove it 578 void removeOne() 579 in { 580 assert(chunk_keep <= 1, "Cannot use removeOne with a custom keep amount set"); 581 } 582 body { 583 Chain chain = head; 584 585 if (chain.next) { 586 head = chain.next; 587 chain.free(); 588 chunk_used--; 589 } else { 590 chain.buf.reset(); 591 chain.buf.shiftRight(offset); 592 cur = head = chain; 593 } 594 } 595 596 /* 597 * Resets Buffers and makes the buffers empty. 598 */ 599 void reset() 600 { 601 Chain chain; 602 Chain ci; 603 size_t k; 604 605 k = chunk_keep; 606 for (ci = head; ci; ci = ci.next) { 607 ci.buf.reset(); 608 ci.buf.shiftRight(offset); 609 610 if (--k == 0) { 611 break; 612 } 613 } 614 615 if (ci) { 616 chain = ci.next; 617 ci.next = null; 618 619 for (ci = chain; ci;) { 620 chain = ci.next; 621 622 ci.free(); 623 Mem.free(ci); 624 ci = chain; 625 } 626 627 chunk_used = chunk_keep; 628 } 629 630 cur = head; 631 } 632 633 /* 634 * Moves cur to cur.next. If resulting cur is 635 * null, this function allocates new buffers and cur points to 636 * it. 637 * 638 * This function returns 0 if it succeeds, or one of the following 639 * negative error codes: 640 * 641 * ErrorCode.NOMEM 642 * Out of memory 643 * ErrorCode.BUFFER_ERROR 644 * Out of buffer space. 645 */ 646 ErrorCode advance() { return allocChain(); } 647 648 /* Sets cur to head */ 649 void rewind() { 650 cur = head; 651 } 652 653 654 /* 655 * Move cur, from the current position, using next member, to 656 * the last buf which has length > 0 without seeing buf 657 * which satisfies length == 0. If cur.buf.length == 0 or cur.next is null, 658 * cur is unchanged. 659 */ 660 void seekLastPresent() 661 { 662 Chain ci; 663 664 for (ci = cur; ci; ci = ci.next) { 665 if (ci.buf.length == 0) { 666 return; 667 } else { 668 cur = ci; 669 } 670 } 671 } 672 673 /* 674 * Returns true if cur.next is not empty. 675 */ 676 bool nextPresent() 677 { 678 Chain chain; 679 680 chain = cur.next; 681 682 return chain && chain.buf.length; 683 } 684 685 int curAvailable() { 686 return cur.buf.available(); 687 } 688 689 @property int length() 690 { 691 Chain ci; 692 int len; 693 694 len = 0; 695 for (ci = head; ci; ci = ci.next) { 696 len += ci.buf.length; 697 } 698 699 return len; 700 } 701 702 private: 703 @property int available() { 704 return cast(int)(cur.buf.available + (chunk_length - offset) * (max_chunk - chunk_used)); 705 706 } 707 708 ErrorCode ensureAddByte() 709 { 710 ErrorCode rv; 711 Buffer* buf; 712 713 buf = &cur.buf; 714 715 if (buf.available >= 1) { 716 return ErrorCode.OK; 717 } 718 719 rv = allocChain(); 720 if (rv != 0) 721 return rv; 722 return ErrorCode.OK; 723 } 724 725 ErrorCode allocChain() { 726 Chain chain; 727 728 if (cur.next) { 729 cur = cur.next; 730 return ErrorCode.OK; 731 } 732 733 if (max_chunk == chunk_used) 734 return ErrorCode.BUFFER_ERROR; 735 736 chain = Chain(chunk_length, use_secure_mem, zeroize_on_free); 737 738 LOGF("new buffer %d bytes allocated for bufs %s, used %d", chunk_length, this, chunk_used); 739 740 ++chunk_used; 741 742 cur.next = chain; 743 cur = chain; 744 cur.buf.shiftRight(offset); 745 746 return ErrorCode.OK; 747 } 748 749 }