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 }