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 author: Kris 11 12 *******************************************************************************/ 13 14 module tango.io.device.Array; 15 16 private import tango.core.Exception; 17 18 private import tango.io.device.Conduit; 19 20 /****************************************************************************** 21 22 ******************************************************************************/ 23 24 extern (C) 25 { 26 protected void * memcpy (void *dst, const(void) *src, size_t); 27 } 28 29 /******************************************************************************* 30 31 Array manipulation typically involves appending, as in the 32 following example: 33 --- 34 // create a small buffer 35 auto buf = new Array (256); 36 37 auto foo = "to write some D"; 38 39 // append some text directly to it 40 buf.append ("now is the time for all good men ").append(foo); 41 --- 42 43 Alternatively, one might use a formatter to append content: 44 --- 45 auto output = new TextOutput (new Array(256)); 46 output.format ("now is the time for {} good men {}", 3, foo); 47 --- 48 49 A slice() method returns all valid content within the array. 50 51 *******************************************************************************/ 52 53 class Array : Conduit, InputBuffer, OutputBuffer, Conduit.Seek 54 { 55 private void[] data; // the raw data buffer 56 private size_t index; // current read position 57 private size_t extent; // limit of valid content 58 private size_t dimension; // maximum extent of content 59 private size_t expansion; // for growing instances 60 61 private __gshared immutable immutable(char)[] overflow = "output buffer is full"; 62 private __gshared immutable immutable(char)[] underflow = "input buffer is empty"; 63 private __gshared immutable immutable(char)[] eofRead = "end-of-flow while reading"; 64 private __gshared immutable immutable(char)[] eofWrite = "end-of-flow while writing"; 65 66 /*********************************************************************** 67 68 Ensure the buffer remains valid between method calls. 69 70 ***********************************************************************/ 71 72 invariant() 73 { 74 assert (index <= extent); 75 assert (extent <= dimension); 76 } 77 78 /*********************************************************************** 79 80 Construct a buffer. 81 82 Params: 83 capacity = The number of bytes to make available. 84 growing = Chunk size of a growable instance, or zero 85 to prohibit expansion. 86 87 Remarks: 88 Construct a Buffer with the specified number of bytes 89 and expansion policy. 90 91 ***********************************************************************/ 92 93 this (size_t capacity, size_t growing = 0) 94 { 95 assign (new ubyte[capacity], 0); 96 expansion = growing; 97 } 98 99 /*********************************************************************** 100 101 Construct a buffer. 102 103 Params: 104 data = The backing array to buffer within. 105 106 Remarks: 107 Prime a buffer with an application-supplied array. All content 108 is considered valid for reading, and thus there is no writable 109 space initially available. 110 111 ***********************************************************************/ 112 113 this (void[] data) 114 { 115 assign (data, data.length); 116 } 117 118 /*********************************************************************** 119 120 Construct a buffer. 121 122 Params: 123 data = The backing array to buffer within. 124 readable = The number of bytes initially made 125 readable. 126 127 Remarks: 128 Prime buffer with an application-supplied array, and 129 indicate how much readable data is already there. A 130 write operation will begin writing immediately after 131 the existing readable content. 132 133 This is commonly used to attach a Buffer instance to 134 a local array. 135 136 ***********************************************************************/ 137 138 this (void[] data, size_t readable) 139 { 140 assign (data, readable); 141 } 142 143 /*********************************************************************** 144 145 Return the name of this conduit. 146 147 ***********************************************************************/ 148 149 final override immutable(char)[] toString () 150 { 151 return "<array>"; 152 } 153 154 /*********************************************************************** 155 156 Transfer content into the provided dst. 157 158 Params: 159 dst = Destination of the content. 160 161 Returns: 162 Return the number of bytes read, which may be less than 163 dst.length. Eof is returned when no further content is 164 available. 165 166 Remarks: 167 Populates the provided array with content. We try to 168 satisfy the request from the buffer content, and read 169 directly from an attached conduit when the buffer is 170 empty. 171 172 ***********************************************************************/ 173 174 final override size_t read (void[] dst) 175 { 176 auto content = readable; 177 if (content) 178 { 179 if (content >= dst.length) 180 content = dst.length; 181 182 // transfer buffer content 183 dst [0 .. content] = data [index .. index + content]; 184 index += content; 185 } 186 else 187 content = IConduit.Eof; 188 return content; 189 } 190 191 /*********************************************************************** 192 193 Emulate OutputStream.write(). 194 195 Params: 196 src = The content to write. 197 198 Returns: 199 Return the number of bytes written, which may be less than 200 provided (conceptually). Returns Eof when the buffer becomes 201 full. 202 203 Remarks: 204 Appends src content to the buffer, expanding as required if 205 configured to do so (via the ctor). 206 207 ***********************************************************************/ 208 209 final override size_t write (const(void)[] src) 210 { 211 auto len = src.length; 212 if (len) 213 { 214 if (len > writable) 215 if (expand(len) < len) 216 return Eof; 217 218 // content may overlap ... 219 memcpy (&data[extent], src.ptr, len); 220 extent += len; 221 } 222 return len; 223 } 224 225 /*********************************************************************** 226 227 Return a preferred size for buffering conduit I/O. 228 229 ***********************************************************************/ 230 231 final override const size_t bufferSize () 232 { 233 return data.length; 234 } 235 236 /*********************************************************************** 237 238 Release external resources. 239 240 ***********************************************************************/ 241 242 override void detach () 243 { 244 } 245 246 /*********************************************************************** 247 248 Seek within the constraints of assigned content. 249 250 ***********************************************************************/ 251 252 override long seek (long offset, Anchor anchor = Anchor.Begin) 253 { 254 if (offset > cast(long) limit) 255 offset = limit; 256 257 switch (anchor) 258 { 259 case Anchor.End: 260 index = cast(size_t) (limit - offset); 261 break; 262 263 case Anchor.Begin: 264 index = cast(size_t) offset; 265 break; 266 267 case Anchor.Current: 268 long o = cast(size_t) (index + offset); 269 if (o < 0) 270 o = 0; 271 if (o > cast(long) limit) 272 o = limit; 273 index = cast(size_t) o; 274 break; 275 default: 276 break; 277 } 278 return index; 279 } 280 281 /*********************************************************************** 282 283 Reset the buffer content. 284 285 Params: 286 data = The backing array to buffer within. All content 287 is considered valid. 288 289 Returns: 290 The buffer instance. 291 292 Remarks: 293 Set the backing array with all content readable. 294 295 ***********************************************************************/ 296 297 Array assign (void[] data) 298 { 299 return assign (data, data.length); 300 } 301 302 /*********************************************************************** 303 304 Reset the buffer content 305 306 Params: 307 data = The backing array to buffer within. 308 readable = The number of bytes within data considered 309 valid. 310 311 Returns: 312 The buffer instance. 313 314 Remarks: 315 Set the backing array with some content readable. Use clear() 316 to reset the content (make it all writable). 317 318 ***********************************************************************/ 319 320 Array assign (void[] data, size_t readable) 321 { 322 this.data = data; 323 this.extent = readable; 324 this.dimension = data.length; 325 326 // reset to start of input 327 this.expansion = 0; 328 this.index = 0; 329 return this; 330 } 331 332 /*********************************************************************** 333 334 Access buffer content. 335 336 Remarks: 337 Return the entire backing array. 338 339 ***********************************************************************/ 340 341 final void[] assign () 342 { 343 return data; 344 } 345 346 /*********************************************************************** 347 348 Return a void[] read of the buffer from start to end, where 349 end is exclusive. 350 351 ***********************************************************************/ 352 353 final void[] opSlice (size_t start, size_t end) 354 { 355 assert (start <= extent && end <= extent && start <= end); 356 return data [start .. end]; 357 } 358 359 /*********************************************************************** 360 361 Retrieve all readable content. 362 363 Returns: 364 A void[] read of the buffer. 365 366 Remarks: 367 Return a void[] read of the buffer, from the current position 368 up to the limit of valid content. The content remains in the 369 buffer for future extraction. 370 371 ***********************************************************************/ 372 373 final void[] slice () 374 { 375 return data [index .. extent]; 376 } 377 378 /*********************************************************************** 379 380 Access buffer content. 381 382 Params: 383 size = Number of bytes to access. 384 eat = Whether to consume the content or not. 385 386 Returns: 387 The corresponding buffer slice when successful, or 388 null if there's not enough data available (Eof; Eob). 389 390 Remarks: 391 Slices readable data. The specified number of bytes is 392 readd from the buffer, and marked as having been read 393 when the 'eat' parameter is set true. When 'eat' is set 394 false, the read position is not adjusted. 395 396 Note that the slice cannot be larger than the size of 397 the buffer ~ use method read(void[]) instead where you 398 simply want the content copied. 399 400 Note also that the slice should be .dup'd if you wish to 401 retain it. 402 403 Examples: 404 --- 405 // create a buffer with some content 406 auto buffer = new Buffer ("hello world"); 407 408 // consume everything unread 409 auto slice = buffer.slice (buffer.readable); 410 --- 411 412 ***********************************************************************/ 413 414 final void[] slice (size_t size, bool eat = true) 415 { 416 if (size > readable) 417 error (underflow); 418 419 auto i = index; 420 if (eat) 421 index += size; 422 return data [i .. i + size]; 423 } 424 425 /*********************************************************************** 426 427 Append content. 428 429 Params: 430 src = The content to _append. 431 length = The number of bytes in src. 432 433 Returns: 434 A chaining reference if all content was written. 435 Throws an IOException indicating eof or eob if not. 436 437 Remarks: 438 Append an array to this buffer. 439 440 ***********************************************************************/ 441 442 final Array append (const(void)[] src) 443 { 444 if (write(src) is Eof) 445 error (overflow); 446 return this; 447 } 448 449 /*********************************************************************** 450 451 Iterator support. 452 453 Params: 454 scan = The delagate to invoke with the current content 455 456 Returns: 457 Returns true if a token was isolated, false otherwise. 458 459 Remarks: 460 Upon success, the delegate should return the byte-based 461 index of the consumed pattern (tail end of it). Failure 462 to match a pattern should be indicated by returning an 463 IConduit.Eof. 464 465 Note that additional iterator and/or reader instances 466 will operate in lockstep when bound to a common buffer. 467 468 ***********************************************************************/ 469 470 final bool next (scope size_t delegate (const(void)[]) scan) 471 { 472 return reader (scan) != IConduit.Eof; 473 } 474 475 /*********************************************************************** 476 477 Available content. 478 479 Remarks: 480 Return count of _readable bytes remaining in buffer. This is 481 calculated simply as limit() - position(). 482 483 ***********************************************************************/ 484 485 @property final const size_t readable () 486 { 487 return extent - index; 488 } 489 490 /*********************************************************************** 491 492 Available space. 493 494 Remarks: 495 Return count of _writable bytes available in buffer. This is 496 calculated simply as capacity() - limit(). 497 498 ***********************************************************************/ 499 500 @property final const size_t writable () 501 { 502 return dimension - extent; 503 } 504 505 /*********************************************************************** 506 507 Access buffer limit. 508 509 Returns: 510 Returns the limit of readable content within this buffer. 511 512 Remarks: 513 Each buffer has a capacity, a limit, and a position. The 514 capacity is the maximum content a buffer can contain, limit 515 represents the extent of valid content, and position marks 516 the current read location. 517 518 ***********************************************************************/ 519 520 @property final const size_t limit () 521 { 522 return extent; 523 } 524 525 /*********************************************************************** 526 527 Access buffer capacity. 528 529 Returns: 530 Returns the maximum capacity of this buffer. 531 532 Remarks: 533 Each buffer has a capacity, a limit, and a position. The 534 capacity is the maximum content a buffer can contain, limit 535 represents the extent of valid content, and position marks 536 the current read location. 537 538 ***********************************************************************/ 539 540 final const size_t capacity () 541 { 542 return dimension; 543 } 544 545 /*********************************************************************** 546 547 Access buffer read position. 548 549 Returns: 550 Returns the current read-position within this buffer 551 552 Remarks: 553 Each buffer has a capacity, a limit, and a position. The 554 capacity is the maximum content a buffer can contain, limit 555 represents the extent of valid content, and position marks 556 the current read location. 557 558 ***********************************************************************/ 559 560 final const size_t position () 561 { 562 return index; 563 } 564 565 /*********************************************************************** 566 567 Clear array content. 568 569 Remarks: 570 Reset 'position' and 'limit' to zero. This effectively 571 clears all content from the array. 572 573 ***********************************************************************/ 574 575 final Array clear () 576 { 577 index = extent = 0; 578 return this; 579 } 580 581 /*********************************************************************** 582 583 Emit/purge buffered content. 584 585 ***********************************************************************/ 586 587 final override Array flush () 588 { 589 return this; 590 } 591 592 /*********************************************************************** 593 594 Write into this buffer. 595 596 Params: 597 dg = The callback to provide buffer access to. 598 599 Returns: 600 Returns whatever the delegate returns. 601 602 Remarks: 603 Exposes the raw data buffer at the current _write position, 604 The delegate is provided with a void[] representing space 605 available within the buffer at the current _write position. 606 607 The delegate should return the appropriate number of bytes 608 if it writes valid content, or IConduit.Eof on error. 609 610 ***********************************************************************/ 611 612 final size_t writer (scope size_t delegate (void[]) dg) 613 { 614 auto count = dg (data [extent..dimension]); 615 616 if (count != IConduit.Eof) 617 { 618 extent += count; 619 assert (extent <= dimension); 620 } 621 return count; 622 } 623 624 /*********************************************************************** 625 626 Read directly from this buffer. 627 628 Params: 629 dg = Callback to provide buffer access to. 630 631 Returns: 632 Returns whatever the delegate returns. 633 634 Remarks: 635 Exposes the raw data buffer at the current _read position. The 636 delegate is provided with a void[] representing the available 637 data, and should return zero to leave the current _read position 638 intact. 639 640 If the delegate consumes data, it should return the number of 641 bytes consumed; or IConduit.Eof to indicate an error. 642 643 ***********************************************************************/ 644 645 final size_t reader (scope size_t delegate (const(void)[]) dg) 646 { 647 auto count = dg (data [index..extent]); 648 649 if (count != IConduit.Eof) 650 { 651 index += count; 652 assert (index <= extent); 653 } 654 return count; 655 } 656 657 /*********************************************************************** 658 659 Expand existing buffer space. 660 661 Returns: 662 Available space, without any expansion. 663 664 Remarks: 665 Make some additional room in the buffer, of at least the 666 given size. Should not be public in order to avoid issues 667 with non-growable subclasses. 668 669 ***********************************************************************/ 670 671 private final size_t expand (size_t size) 672 { 673 if (expansion) 674 { 675 if (size < expansion) 676 size = expansion; 677 dimension += size; 678 data.length = dimension; 679 } 680 return writable; 681 } 682 683 /*********************************************************************** 684 685 Cast to a target type without invoking the wrath of the 686 runtime checks for misalignment. Instead, we truncate the 687 array length. 688 689 ***********************************************************************/ 690 691 private static T[] convert(T)(void[] x) 692 { 693 return (cast(T*) x.ptr) [0 .. (x.length / T.sizeof)]; 694 } 695 } 696 697 698 /****************************************************************************** 699 700 ******************************************************************************/ 701 702 debug (Array) 703 { 704 import tango.io.Stdout; 705 706 void main() 707 { 708 auto b = new Array(6, 10); 709 b.seek (0); 710 b.write ("fubar"); 711 712 Stdout.formatln ("extent {}, pos {}, read {}, bufsize {}", 713 b.limit, b.position, cast(char[]) b.slice, b.bufferSize); 714 715 b.write ("fubar"); 716 Stdout.formatln ("extent {}, pos {}, read {}, bufsize {}", 717 b.limit, b.position, cast(char[]) b.slice, b.bufferSize); 718 } 719 }