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 }