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