1 /******************************************************************************* 2 3 copyright: Copyright (c) 2004 Kris Bell. All rights reserved 4 5 license: BSD style: $(LICENSE) 6 7 version: Mar 2004: Initial release$(BR) 8 Dec 2006: Outback release 9 10 authors: Kris 11 12 *******************************************************************************/ 13 14 module tango.io.stream.Buffered; 15 16 public import tango.io.model.IConduit; 17 18 private import tango.io.device.Conduit; 19 20 /****************************************************************************** 21 22 ******************************************************************************/ 23 24 public alias BufferedInput Bin; /// Shorthand aliases. 25 public alias BufferedOutput Bout; /// ditto 26 27 /****************************************************************************** 28 29 ******************************************************************************/ 30 31 extern (C) 32 { 33 int printf (char*, ...); 34 private void * memmove (void *dst, const(void) *src, size_t); 35 } 36 37 /****************************************************************************** 38 39 ******************************************************************************/ 40 41 private __gshared immutable immutable(char)[] underflow = "input buffer is empty"; 42 private __gshared immutable immutable(char)[] eofRead = "end-of-flow whilst reading"; 43 private __gshared immutable immutable(char)[] eofWrite = "end-of-flow whilst writing"; 44 private __gshared immutable immutable(char)[] overflow = "output buffer is full"; 45 46 47 /******************************************************************************* 48 49 Buffers the flow of data from a upstream input. A downstream 50 neighbour can locate and use this buffer instead of creating 51 another instance of their own. 52 53 (Note that upstream is closer to the source, and downstream is 54 further away.) 55 56 *******************************************************************************/ 57 58 class BufferedInput : InputFilter, InputBuffer 59 { 60 alias flush clear; /// Clear/flush are the same. 61 alias InputFilter.input input; /// Access the source. 62 63 private void[] data; // The raw data buffer. 64 private size_t index; // Current read position. 65 private size_t extent; // Limit of valid content. 66 private size_t dimension; // Maximum extent of content. 67 68 /*********************************************************************** 69 70 Ensure the buffer remains valid between method calls. 71 72 ***********************************************************************/ 73 74 invariant() 75 { 76 assert (index <= extent); 77 assert (extent <= dimension); 78 } 79 80 /*********************************************************************** 81 82 Construct a buffer. 83 84 Params: 85 stream = An input stream. 86 capacity = Desired buffer capacity. 87 88 Remarks: 89 Construct a Buffer upon the provided input stream. 90 91 ***********************************************************************/ 92 93 this (InputStream stream) 94 { 95 assert (stream); 96 this (stream, stream.conduit.bufferSize); 97 } 98 99 /*********************************************************************** 100 101 Construct a buffer. 102 103 Params: 104 stream = An input stream. 105 capacity = Desired buffer capacity. 106 107 Remarks: 108 Construct a Buffer upon the provided input stream. 109 110 ***********************************************************************/ 111 112 this (InputStream stream, size_t capacity) 113 { 114 set (new ubyte[capacity], 0); 115 super (source = stream); 116 } 117 118 /*********************************************************************** 119 120 Attempt to share an upstream Buffer, and create an instance 121 where there's not one available. 122 123 Params: 124 stream = An input stream. 125 126 Remarks: 127 If an upstream Buffer instances is visible, it will be shared. 128 Otherwise, a new instance is created based upon the bufferSize 129 exposed by the stream endpoint (conduit). 130 131 ***********************************************************************/ 132 133 static InputBuffer create (InputStream stream) 134 { 135 auto source = stream; 136 auto conduit = source.conduit; 137 while (cast(Mutator) source is null) 138 { 139 auto b = cast(InputBuffer) source; 140 if (b) 141 return b; 142 if (source is conduit) 143 break; 144 source = source.input; 145 assert (source); 146 } 147 148 return new BufferedInput (stream, conduit.bufferSize); 149 } 150 151 /*********************************************************************** 152 153 Place more data from the source stream into this buffer, and 154 return the number of bytes added. This does not compress the 155 current buffer content, so consider doing that explicitly. 156 157 Returns: Number of bytes added, which will be Eof when there 158 is no further input available. Zero is also a valid 159 response, meaning no data was actually added. 160 161 ***********************************************************************/ 162 163 final size_t populate () 164 { 165 return writer (&input.read); 166 } 167 168 /*********************************************************************** 169 170 Return a void[] slice of the buffer from start to end, where 171 end is exclusive. 172 173 ***********************************************************************/ 174 175 final void[] opSlice (size_t start, size_t end) 176 { 177 assert (start <= extent && end <= extent && start <= end); 178 return data [start .. end]; 179 } 180 181 /*********************************************************************** 182 183 Retrieve the valid content. 184 185 Returns: 186 A void[] slice of the buffer. 187 188 Remarks: 189 Return a void[] slice of the buffer, from the current position 190 up to the limit of valid content. The content remains in the 191 buffer for future extraction. 192 193 ***********************************************************************/ 194 195 final void[] slice () 196 { 197 return data [index .. extent]; 198 } 199 200 /*********************************************************************** 201 202 Access buffer content. 203 204 Params: 205 size = Number of bytes to access. 206 eat = Whether to consume the content or not. 207 208 Returns: 209 The corresponding buffer slice when successful, or 210 null if there's not enough data available (Eof; Eob). 211 212 Remarks: 213 Read a slice of data from the buffer, loading from the 214 conduit as necessary. The specified number of bytes is 215 sliced from the buffer, and marked as having been read 216 when the 'eat' parameter is set true. When 'eat' is set 217 false, the read position is not adjusted. 218 219 Note that the slice cannot be larger than the size of 220 the buffer ~ use method fill(void[]) instead where you 221 simply want the content copied, or use conduit.read() 222 to extract directly from an attached conduit. Also note 223 that if you need to retain the slice, then it should be 224 .dup'd before the buffer is compressed or repopulated. 225 226 Examples: 227 --- 228 // create a buffer with some content 229 auto buffer = new Buffer ("hello world"); 230 231 // consume everything unread 232 auto slice = buffer.slice (buffer.readable); 233 --- 234 235 ***********************************************************************/ 236 237 final void[] slice (size_t size, bool eat = true) 238 { 239 if (size > readable) 240 { 241 // make some space? This will try to leave as much content 242 // in the buffer as possible, such that entire records may 243 // be aliased directly from within. 244 if (size > (dimension - index)) 245 { 246 if (size <= dimension) 247 compress; 248 else 249 conduit.error (underflow); 250 } 251 // populate tail of buffer with new content 252 do { 253 if (writer (&source.read) is Eof) 254 conduit.error (eofRead); 255 } while (size > readable); 256 } 257 258 auto i = index; 259 if (eat) 260 index += size; 261 return data [i .. i + size]; 262 } 263 264 /*********************************************************************** 265 266 Read directly from this buffer. 267 268 Params: 269 dg = Callback to provide buffer access to. 270 271 Returns: 272 Returns whatever the delegate returns. 273 274 Remarks: 275 Exposes the raw data buffer at the current _read position. The 276 delegate is provided with a void[] representing the available 277 data, and should return zero to leave the current _read position 278 intact. 279 280 If the delegate consumes data, it should return the number of 281 bytes consumed; or IConduit.Eof to indicate an error. 282 283 ***********************************************************************/ 284 285 final size_t reader (scope size_t delegate (const(void)[]) dg) 286 { 287 auto count = dg (data [index..extent]); 288 289 if (count != Eof) 290 { 291 index += count; 292 assert (index <= extent); 293 } 294 return count; 295 } 296 297 /*********************************************************************** 298 299 Write into this buffer. 300 301 Params: 302 dg = The callback to provide buffer access to. 303 304 Returns: 305 Returns whatever the delegate returns. 306 307 Remarks: 308 Exposes the raw data buffer at the current _write position, 309 The delegate is provided with a void[] representing space 310 available within the buffer at the current _write position. 311 312 The delegate should return the appropriate number of bytes 313 if it writes valid content, or IConduit.Eof on error. 314 315 ***********************************************************************/ 316 317 public size_t writer (scope size_t delegate (void[]) dg) 318 { 319 auto count = dg (data [extent..dimension]); 320 321 if (count != Eof) 322 { 323 extent += count; 324 assert (extent <= dimension); 325 } 326 return count; 327 } 328 329 /*********************************************************************** 330 331 Transfer content into the provided dst. 332 333 Params: 334 dst = Destination of the content. 335 336 Returns: 337 Return the number of bytes read, which may be less than 338 dst.length. Eof is returned when no further content is 339 available. 340 341 Remarks: 342 Populates the provided array with content. We try to 343 satisfy the request from the buffer content, and read 344 directly from an attached conduit when the buffer is 345 empty. 346 347 ***********************************************************************/ 348 349 final override size_t read (void[] dst) 350 { 351 size_t content = readable; 352 if (content) 353 { 354 if (content >= dst.length) 355 content = dst.length; 356 357 // transfer buffer content 358 dst [0 .. content] = data [index .. index + content]; 359 index += content; 360 } 361 else 362 // pathological cases read directly from conduit 363 if (dst.length > dimension) 364 content = source.read (dst); 365 else 366 { 367 if (writable is 0) 368 index = extent = 0; // same as clear, without call-chain 369 370 // keep buffer partially populated 371 if ((content = writer (&source.read)) != Eof && content > 0) 372 content = read (dst); 373 } 374 return content; 375 } 376 377 /********************************************************************** 378 379 Fill the provided buffer. Returns the number of bytes 380 actually read, which will be less that dst.length when 381 Eof has been reached and Eof thereafter. 382 383 Params: 384 dst = Where data should be placed. 385 exact = Whether to throw an exception when dst is not 386 filled (an Eof occurs first). Defaults to false. 387 388 **********************************************************************/ 389 390 final size_t fill (void[] dst, bool exact = false) 391 { 392 size_t len = 0; 393 394 while (len < dst.length) 395 { 396 size_t i = read (dst [len .. $]); 397 if (i is Eof) 398 { 399 if (exact && len < dst.length) 400 conduit.error (eofRead); 401 return (len > 0) ? len : Eof; 402 } 403 len += i; 404 } 405 return len; 406 } 407 408 /*********************************************************************** 409 410 Move the current read location. 411 412 Params: 413 size = The number of bytes to move. 414 415 Returns: 416 Returns true if successful, false otherwise. 417 418 Remarks: 419 Skip ahead by the specified number of bytes, streaming from 420 the associated conduit as necessary. 421 422 Can also reverse the read position by 'size' bytes, when size 423 is negative. This may be used to support lookahead operations. 424 Note that a negative size will fail where there is not sufficient 425 content available in the buffer (can't _skip beyond the beginning). 426 427 ***********************************************************************/ 428 429 final bool skip (ptrdiff_t size) 430 { 431 if (size < 0) 432 { 433 size = -size; 434 if (index >= size) 435 { 436 index -= size; 437 return true; 438 } 439 return false; 440 } 441 return slice(size) !is null; 442 } 443 444 /*********************************************************************** 445 446 Move the current read location. 447 448 ***********************************************************************/ 449 450 final override long seek (long offset, Anchor start = Anchor.Begin) 451 { 452 if (start is Anchor.Current) 453 { 454 // handle this specially because we know this is 455 // buffered - we should take into account the buffer 456 // position when seeking 457 offset -= readable; 458 auto bpos = offset + limit; 459 460 if (bpos >= 0 && bpos < limit) 461 { 462 // the new position is within the current 463 // buffer, skip to that position. 464 skip (cast(int) bpos - cast(int) position); 465 466 // see if we can return a valid offset 467 auto pos = source.seek (0, Anchor.Current); 468 if (pos != Eof) 469 return pos - readable; 470 return Eof; 471 } 472 // else, position is outside the buffer. Do a real 473 // seek using the adjusted position. 474 } 475 476 clear(); 477 return source.seek (offset, start); 478 } 479 480 /*********************************************************************** 481 482 Iterator support. 483 484 Params: 485 scan = The delegate to invoke with the current content. 486 487 Returns: 488 Returns true if a token was isolated, false otherwise. 489 490 Remarks: 491 Upon success, the delegate should return the byte-based 492 index of the consumed pattern (tail end of it). Failure 493 to match a pattern should be indicated by returning an 494 Eof 495 496 Each pattern is expected to be stripped of the delimiter. 497 An end-of-file condition causes trailing content to be 498 placed into the token. Requests made beyond Eof result 499 in empty matches (length is zero). 500 501 Note that additional iterator and/or reader instances 502 will operate in lockstep when bound to a common buffer. 503 504 ***********************************************************************/ 505 506 final bool next (scope size_t delegate (const(void)[]) scan) 507 { 508 while (reader(scan) is Eof) 509 { 510 // did we start at the beginning? 511 if (position) 512 // yep - move partial token to start of buffer 513 compress; 514 else 515 // no more space in the buffer? 516 if (writable is 0) 517 conduit.error ("BufferedInput.next :: input buffer is full"); 518 519 // read another chunk of data 520 if (writer(&source.read) is Eof) 521 return false; 522 } 523 return true; 524 } 525 526 /*********************************************************************** 527 528 Reserve the specified space within the buffer, compressing 529 existing content as necessary to make room. 530 531 Returns the current read point, after compression if that 532 was required. 533 534 ***********************************************************************/ 535 536 final size_t reserve (size_t space) 537 { 538 assert (space < dimension); 539 540 if ((dimension - index) < space) 541 compress; 542 return index; 543 } 544 545 /*********************************************************************** 546 547 Compress buffer space. 548 549 Returns: 550 The buffer instance. 551 552 Remarks: 553 If we have some data left after an export, move it to 554 front-of-buffer and set position to be just after the 555 remains. This is for supporting certain conduits which 556 choose to write just the initial portion of a request. 557 558 Limit is set to the amount of data remaining. Position 559 is always reset to zero. 560 561 ***********************************************************************/ 562 563 @property final BufferedInput compress () 564 { 565 auto r = readable; 566 567 if (index > 0 && r > 0) 568 // content may overlap ... 569 memmove (&data[0], &data[index], r); 570 571 index = 0; 572 extent = r; 573 return this; 574 } 575 576 /*********************************************************************** 577 578 Drain buffer content to the specific conduit. 579 580 Returns: 581 Returns the number of bytes written, or Eof. 582 583 Remarks: 584 Write as much of the buffer that the associated conduit 585 can consume. The conduit is not obliged to consume all 586 content, so some may remain within the buffer. 587 588 ***********************************************************************/ 589 590 final size_t drain (OutputStream dst) 591 { 592 assert (dst); 593 594 size_t ret = reader (&dst.write); 595 compress; 596 return ret; 597 } 598 599 /*********************************************************************** 600 601 Access buffer limit. 602 603 Returns: 604 Returns the limit of readable content within this buffer. 605 606 Remarks: 607 Each buffer has a capacity, a limit, and a position. The 608 capacity is the maximum content a buffer can contain, limit 609 represents the extent of valid content, and position marks 610 the current read location. 611 612 ***********************************************************************/ 613 614 @property final const size_t limit () 615 { 616 return extent; 617 } 618 619 /*********************************************************************** 620 621 Access buffer capacity. 622 623 Returns: 624 Returns the maximum capacity of this buffer. 625 626 Remarks: 627 Each buffer has a capacity, a limit, and a position. The 628 capacity is the maximum content a buffer can contain, limit 629 represents the extent of valid content, and position marks 630 the current read location. 631 632 ***********************************************************************/ 633 634 final const size_t capacity () 635 { 636 return dimension; 637 } 638 639 /*********************************************************************** 640 641 Access buffer read position. 642 643 Returns: 644 Returns the current read-position within this buffer. 645 646 Remarks: 647 Each buffer has a capacity, a limit, and a position. The 648 capacity is the maximum content a buffer can contain, limit 649 represents the extent of valid content, and position marks 650 the current read location. 651 652 ***********************************************************************/ 653 654 @property final const size_t position () 655 { 656 return index; 657 } 658 659 /*********************************************************************** 660 661 Available content. 662 663 Remarks: 664 Return count of _readable bytes remaining in buffer. This is 665 calculated simply as limit() - position(). 666 667 ***********************************************************************/ 668 669 @property final const size_t readable () 670 { 671 return extent - index; 672 } 673 674 /*********************************************************************** 675 676 Cast to a target type without invoking the wrath of the 677 runtime checks for misalignment. Instead, we truncate the 678 array length. 679 680 ***********************************************************************/ 681 682 static inout(T)[] convert(T)(inout(void)[] x) 683 { 684 return (cast(inout(T)*) x.ptr) [0 .. (x.length / T.sizeof)]; 685 } 686 687 /*********************************************************************** 688 689 Clear buffer content. 690 691 Remarks: 692 Reset 'position' and 'limit' to zero. This effectively 693 clears all content from the buffer. 694 695 ***********************************************************************/ 696 697 final override BufferedInput flush () 698 { 699 index = extent = 0; 700 701 // clear the filter chain also 702 if (source) 703 super.flush(); 704 return this; 705 } 706 707 /*********************************************************************** 708 709 Set the input stream. 710 711 ***********************************************************************/ 712 713 @property final void input (InputStream source) 714 { 715 this.source = source; 716 } 717 718 /*********************************************************************** 719 720 Load the bits from a stream, up to an indicated length, and 721 return them all in an array. The function may consume more 722 than the indicated size where additional data is available 723 during a block read operation, but will not wait for more 724 than specified. An Eof terminates the operation. 725 726 Returns an array representing the content, and throws 727 IOException on error. 728 729 ***********************************************************************/ 730 731 final override void[] load (size_t max = size_t.max) 732 { 733 load (super.input, super.conduit.bufferSize, max); 734 return slice(); 735 } 736 737 /*********************************************************************** 738 739 Import content from the specified conduit, expanding 740 as necessary up to the indicated maximum or until an 741 Eof occurs. 742 743 Returns the number of bytes contained. 744 745 ***********************************************************************/ 746 747 private size_t load (InputStream src, size_t increment, size_t max) 748 { 749 size_t len, 750 count; 751 752 // make some room 753 compress; 754 755 // explicitly resize? 756 if (max != max.max) 757 if ((len = writable) < max) 758 increment = max - len; 759 760 while (count < max) 761 { 762 if (! writable) 763 { 764 dimension += increment; 765 data.length = dimension; 766 } 767 if ((len = writer(&src.read)) is Eof) 768 break; 769 else 770 count += len; 771 } 772 return count; 773 } 774 775 /*********************************************************************** 776 777 Reset the buffer content. 778 779 Params: 780 data = The backing array to buffer within. 781 readable = The number of bytes within data considered 782 valid. 783 784 Returns: 785 The buffer instance. 786 787 Remarks: 788 Set the backing array with some content readable. Writing 789 to this will either flush it to an associated conduit, or 790 raise an Eof condition. Use clear() to reset the content 791 (make it all writable). 792 793 ***********************************************************************/ 794 795 private final BufferedInput set (void[] data, size_t readable) 796 { 797 this.data = data; 798 this.extent = readable; 799 this.dimension = data.length; 800 801 // reset to start of input 802 this.index = 0; 803 804 return this; 805 } 806 807 /*********************************************************************** 808 809 Available space. 810 811 Remarks: 812 Return count of _writable bytes available in buffer. This is 813 calculated simply as capacity() - limit(). 814 815 ***********************************************************************/ 816 817 @property private final const size_t writable () 818 { 819 return dimension - extent; 820 } 821 } 822 823 824 825 /******************************************************************************* 826 827 Buffers the flow of data from a upstream output. A downstream 828 neighbour can locate and use this buffer instead of creating 829 another instance of their own. 830 831 (Note that upstream is closer to the source, and downstream is 832 further away.) 833 834 Don't forget to flush() buffered content before closing. 835 836 *******************************************************************************/ 837 838 class BufferedOutput : OutputFilter, OutputBuffer 839 { 840 alias OutputFilter.output output; /// access the sink 841 842 private void[] data; // the raw data buffer 843 private size_t index; // current read position 844 private size_t extent; // limit of valid content 845 private size_t dimension; // maximum extent of content 846 847 /*********************************************************************** 848 849 Ensure the buffer remains valid between method calls. 850 851 ***********************************************************************/ 852 853 invariant() 854 { 855 assert (index <= extent); 856 assert (extent <= dimension); 857 } 858 859 /*********************************************************************** 860 861 Construct a buffer. 862 863 Params: 864 stream = An input stream. 865 capacity = Desired buffer capacity. 866 867 Remarks: 868 Construct a Buffer upon the provided input stream. 869 870 ***********************************************************************/ 871 872 this (OutputStream stream) 873 { 874 assert (stream); 875 this (stream, stream.conduit.bufferSize); 876 } 877 878 /*********************************************************************** 879 880 Construct a buffer. 881 882 Params: 883 stream = An input stream. 884 capacity = Desired buffer capacity. 885 886 Remarks: 887 Construct a Buffer upon the provided input stream. 888 889 ***********************************************************************/ 890 891 this (OutputStream stream, size_t capacity) 892 { 893 set (new ubyte[capacity], 0); 894 super (sink = stream); 895 } 896 897 /*********************************************************************** 898 899 Attempts to share an upstream BufferedOutput, and creates a new 900 instance where there's not a shared one available. 901 902 Params: 903 stream = An output stream. 904 905 Remarks: 906 Where an upstream instance is visible it will be returned. 907 Otherwise, a new instance is created based upon the bufferSize 908 exposed by the associated conduit 909 910 ***********************************************************************/ 911 912 static OutputBuffer create (OutputStream stream) 913 { 914 auto sink = stream; 915 auto conduit = sink.conduit; 916 while (cast(Mutator) sink is null) 917 { 918 auto b = cast(OutputBuffer) sink; 919 if (b) 920 return b; 921 if (sink is conduit) 922 break; 923 sink = sink.output; 924 assert (sink); 925 } 926 927 return new BufferedOutput (stream, conduit.bufferSize); 928 } 929 930 /*********************************************************************** 931 932 Retrieve the valid content. 933 934 Returns: 935 A void[] slice of the buffer. 936 937 Remarks: 938 Return a void[] slice of the buffer, from the current position 939 up to the limit of valid content. The content remains in the 940 buffer for future extraction. 941 942 ***********************************************************************/ 943 944 final void[] slice () 945 { 946 return data [index .. extent]; 947 } 948 949 /*********************************************************************** 950 951 Emulate OutputStream.write(). 952 953 Params: 954 src = The content to write. 955 956 Returns: 957 Return the number of bytes written, which may be less than 958 provided (conceptually). 959 960 Remarks: 961 Appends src content to the buffer, flushing to an attached 962 conduit as necessary. An IOException is thrown upon write 963 failure. 964 965 ***********************************************************************/ 966 967 final override size_t write (const(void)[] src) 968 { 969 append (src.ptr, src.length); 970 return src.length; 971 } 972 973 /*********************************************************************** 974 975 Append content. 976 977 Params: 978 src = The content to _append. 979 980 Returns a chaining reference if all content was written. 981 Throws an IOException indicating Eof or Eob if not. 982 983 Remarks: 984 Append an array to this buffer, and flush to the 985 conduit as necessary. This is often used in lieu of 986 a Writer. 987 988 ***********************************************************************/ 989 990 final BufferedOutput append (const(void)[] src) 991 { 992 return append (src.ptr, src.length); 993 } 994 995 /*********************************************************************** 996 997 Append content. 998 999 Params: 1000 src = The content to _append. 1001 length = The number of bytes in src. 1002 1003 Returns a chaining reference if all content was written. 1004 Throws an IOException indicating Eof or Eob if not. 1005 1006 Remarks: 1007 Append an array to this buffer, and flush to the 1008 conduit as necessary. This is often used in lieu of 1009 a Writer. 1010 1011 ***********************************************************************/ 1012 1013 final BufferedOutput append (const(void)* src, size_t length) 1014 { 1015 if (length > writable) 1016 { 1017 flush(); 1018 1019 // check for pathological case 1020 if (length > dimension) 1021 do { 1022 auto written = sink.write (src [0 .. length]); 1023 if (written is Eof) 1024 conduit.error (eofWrite); 1025 length -= written; 1026 src += written; 1027 } while (length > dimension); 1028 } 1029 1030 // avoid "out of bounds" test on zero length 1031 if (length) 1032 { 1033 // content may overlap ... 1034 memmove (&data[extent], src, length); 1035 extent += length; 1036 } 1037 return this; 1038 } 1039 1040 /*********************************************************************** 1041 1042 Available space. 1043 1044 Remarks: 1045 Return count of _writable bytes available in buffer. This is 1046 calculated as capacity() - limit(). 1047 1048 ***********************************************************************/ 1049 1050 @property final const size_t writable () 1051 { 1052 return dimension - extent; 1053 } 1054 1055 /*********************************************************************** 1056 1057 Access buffer limit. 1058 1059 Returns: 1060 Returns the limit of readable content within this buffer. 1061 1062 Remarks: 1063 Each buffer has a capacity, a limit, and a position. The 1064 capacity is the maximum content a buffer can contain, limit 1065 represents the extent of valid content, and position marks 1066 the current read location. 1067 1068 ***********************************************************************/ 1069 1070 @property final const size_t limit () 1071 { 1072 return extent; 1073 } 1074 1075 /*********************************************************************** 1076 1077 Access buffer capacity. 1078 1079 Returns: 1080 Returns the maximum capacity of this buffer. 1081 1082 Remarks: 1083 Each buffer has a capacity, a limit, and a position. The 1084 capacity is the maximum content a buffer can contain, limit 1085 represents the extent of valid content, and position marks 1086 the current read location. 1087 1088 ***********************************************************************/ 1089 1090 final const size_t capacity () 1091 { 1092 return dimension; 1093 } 1094 1095 /*********************************************************************** 1096 1097 Truncate buffer content. 1098 1099 Remarks: 1100 Truncate the buffer within its extent. Returns true if 1101 the new length is valid, false otherwise. 1102 1103 ***********************************************************************/ 1104 1105 final bool truncate (size_t length) 1106 { 1107 if (length <= data.length) 1108 { 1109 extent = length; 1110 return true; 1111 } 1112 return false; 1113 } 1114 1115 /*********************************************************************** 1116 1117 Cast to a target type without invoking the wrath of the 1118 runtime checks for misalignment. Instead, we truncate the 1119 array length. 1120 1121 ***********************************************************************/ 1122 1123 static T[] convert(T)(void[] x) 1124 { 1125 return (cast(T*) x.ptr) [0 .. (x.length / T.sizeof)]; 1126 } 1127 1128 /*********************************************************************** 1129 1130 Flush all buffer content to the specific conduit. 1131 1132 Remarks: 1133 Flush the contents of this buffer. This will block until 1134 all content is actually flushed via the associated conduit, 1135 whereas drain() will not. 1136 1137 Throws an IOException on premature Eof. 1138 1139 ***********************************************************************/ 1140 1141 final override BufferedOutput flush () 1142 { 1143 while (readable > 0) 1144 { 1145 auto ret = reader (&sink.write); 1146 if (ret is Eof) 1147 conduit.error (eofWrite); 1148 } 1149 1150 // flush the filter chain also 1151 clear(); 1152 super.flush(); 1153 return this; 1154 } 1155 1156 /*********************************************************************** 1157 1158 Copy content via this buffer from the provided src 1159 conduit. 1160 1161 Remarks: 1162 The src conduit has its content transferred through 1163 this buffer via a series of fill & drain operations, 1164 until there is no more content available. The buffer 1165 content should be explicitly flushed by the caller. 1166 1167 Throws an IOException on premature Eof. 1168 1169 ***********************************************************************/ 1170 1171 final override BufferedOutput copy (InputStream src, size_t max = -1) 1172 { 1173 size_t chunk, 1174 copied; 1175 1176 while (copied < max && (chunk = writer(&src.read)) != Eof) 1177 { 1178 copied += chunk; 1179 1180 // don't drain until we actually need to 1181 if (writable is 0) 1182 if (drain(sink) is Eof) 1183 conduit.error (eofWrite); 1184 } 1185 return this; 1186 } 1187 1188 /*********************************************************************** 1189 1190 Drain buffer content to the specific conduit. 1191 1192 Returns: 1193 Returns the number of bytes written, or Eof. 1194 1195 Remarks: 1196 Write as much of the buffer that the associated conduit 1197 can consume. The conduit is not obliged to consume all 1198 content, so some may remain within the buffer. 1199 1200 ***********************************************************************/ 1201 1202 final size_t drain (OutputStream dst) 1203 { 1204 assert (dst); 1205 1206 size_t ret = reader (&dst.write); 1207 compress; 1208 return ret; 1209 } 1210 1211 /*********************************************************************** 1212 1213 Clear buffer content. 1214 1215 Remarks: 1216 Reset 'position' and 'limit' to zero. This effectively 1217 clears all content from the buffer. 1218 1219 ***********************************************************************/ 1220 1221 final BufferedOutput clear () 1222 { 1223 index = extent = 0; 1224 return this; 1225 } 1226 1227 /*********************************************************************** 1228 1229 Set the output stream. 1230 1231 ***********************************************************************/ 1232 1233 @property final void output (OutputStream sink) 1234 { 1235 this.sink = sink; 1236 } 1237 1238 /*********************************************************************** 1239 1240 Seek within this stream. Any and all buffered output is 1241 disposed before the upstream is invoked. Use an explicit 1242 flush() to emit content prior to seeking. 1243 1244 ***********************************************************************/ 1245 1246 final override long seek (long offset, Anchor start = Anchor.Begin) 1247 { 1248 clear(); 1249 return super.seek (offset, start); 1250 } 1251 1252 /*********************************************************************** 1253 1254 Write into this buffer. 1255 1256 Params: 1257 dg = The callback to provide buffer access to. 1258 1259 Returns: 1260 Returns whatever the delegate returns. 1261 1262 Remarks: 1263 Exposes the raw data buffer at the current _write position, 1264 The delegate is provided with a void[] representing space 1265 available within the buffer at the current _write position. 1266 1267 The delegate should return the appropriate number of bytes 1268 if it writes valid content, or Eof on error. 1269 1270 ***********************************************************************/ 1271 1272 final size_t writer (scope size_t delegate (void[]) dg) 1273 { 1274 auto count = dg (data [extent..dimension]); 1275 1276 if (count != Eof) 1277 { 1278 extent += count; 1279 assert (extent <= dimension); 1280 } 1281 return count; 1282 } 1283 1284 /*********************************************************************** 1285 1286 Read directly from this buffer. 1287 1288 Params: 1289 dg = Callback to provide buffer access to. 1290 1291 Returns: 1292 Returns whatever the delegate returns. 1293 1294 Remarks: 1295 Exposes the raw data buffer at the current _read position. The 1296 delegate is provided with a void[] representing the available 1297 data, and should return zero to leave the current _read position 1298 intact. 1299 1300 If the delegate consumes data, it should return the number of 1301 bytes consumed; or Eof to indicate an error. 1302 1303 ***********************************************************************/ 1304 1305 private final size_t reader (scope size_t delegate (const(void)[]) dg) 1306 { 1307 auto count = dg (data [index..extent]); 1308 1309 if (count != Eof) 1310 { 1311 index += count; 1312 assert (index <= extent); 1313 } 1314 return count; 1315 } 1316 1317 /*********************************************************************** 1318 1319 Available content. 1320 1321 Remarks: 1322 Return count of _readable bytes remaining in buffer. This is 1323 calculated simply as limit() - position(). 1324 1325 ***********************************************************************/ 1326 1327 @property private final const size_t readable () 1328 { 1329 return extent - index; 1330 } 1331 1332 /*********************************************************************** 1333 1334 Reset the buffer content. 1335 1336 Params: 1337 data = The backing array to buffer within. 1338 readable = The number of bytes within data considered 1339 valid. 1340 1341 Returns: 1342 The buffer instance. 1343 1344 Remarks: 1345 Set the backing array with some content readable. Writing 1346 to this will either flush it to an associated conduit, or 1347 raise an Eof condition. Use clear() to reset the content 1348 (make it all writable). 1349 1350 ***********************************************************************/ 1351 1352 private final BufferedOutput set (void[] data, size_t readable) 1353 { 1354 this.data = data; 1355 this.extent = readable; 1356 this.dimension = data.length; 1357 1358 // reset to start of input 1359 this.index = 0; 1360 1361 return this; 1362 } 1363 1364 /*********************************************************************** 1365 1366 Compress buffer space. 1367 1368 Returns: 1369 The buffer instance. 1370 1371 Remarks: 1372 If we have some data left after an export, move it to 1373 front-of-buffer and set position to be just after the 1374 remains. This is for supporting certain conduits which 1375 choose to write just the initial portion of a request. 1376 1377 Limit is set to the amount of data remaining. Position 1378 is always reset to zero. 1379 1380 ***********************************************************************/ 1381 1382 @property private final BufferedOutput compress () 1383 { 1384 size_t r = readable; 1385 1386 if (index > 0 && r > 0) 1387 // content may overlap ... 1388 memmove (&data[0], &data[index], r); 1389 1390 index = 0; 1391 extent = r; 1392 return this; 1393 } 1394 } 1395 1396 1397 1398 /****************************************************************************** 1399 1400 ******************************************************************************/ 1401 1402 debug (Buffered) 1403 { 1404 void main() 1405 { 1406 auto input = new BufferedInput (null); 1407 auto output = new BufferedOutput (null); 1408 } 1409 }