| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720 | /******************************************************************************* copyright: Copyright (c) 2004 Kris Bell. All rights reserved license: BSD style: $(LICENSE) version: Mar 2004: Initial release$(BR) Dec 2006: Outback release author: Kris *******************************************************************************/ module tango.io.device.Array; private import tango.core.Exception; private import tango.io.device.Conduit; /****************************************************************************** ******************************************************************************/ extern (C) { protected void * memcpy (void *dst, const(void) *src, size_t); } /******************************************************************************* Array manipulation typically involves appending, as in the following example: --- // create a small buffer auto buf = new Array (256); auto foo = "to write some D"; // append some text directly to it buf.append ("now is the time for all good men ").append(foo); --- Alternatively, one might use a formatter to append content: --- auto output = new TextOutput (new Array(256)); output.format ("now is the time for {} good men {}", 3, foo); --- A slice() method returns all valid content within the array. *******************************************************************************/ class Array : Conduit, InputBuffer, OutputBuffer, Conduit.Seek { private void[] data; // the raw data buffer private size_t index; // current read position private size_t extent; // limit of valid content private size_t dimension; // maximum extent of content private size_t expansion; // for growing instances private enum immutable(char)[] overflow = "output buffer is full"; private enum immutable(char)[] underflow = "input buffer is empty"; private enum immutable(char)[] eofRead = "end-of-flow while reading"; private enum immutable(char)[] eofWrite = "end-of-flow while writing"; /*********************************************************************** Ensure the buffer remains valid between method calls. ***********************************************************************/ invariant() { assert (index <= extent); assert (extent <= dimension); } /*********************************************************************** Construct a buffer. Params: capacity = The number of bytes to make available. growing = Chunk size of a growable instance, or zero to prohibit expansion. Remarks: Construct a Buffer with the specified number of bytes and expansion policy. ***********************************************************************/ this (size_t capacity, size_t growing = 0) { assign (new ubyte[capacity], 0); expansion = growing; } /*********************************************************************** Construct a buffer. Params: data = The backing array to buffer within. Remarks: Prime a buffer with an application-supplied array. All content is considered valid for reading, and thus there is no writable space initially available. ***********************************************************************/ this (void[] data) { assign (data, data.length); } /*********************************************************************** Construct a buffer. Params: data = The backing array to buffer within. readable = The number of bytes initially made readable. Remarks: Prime buffer with an application-supplied array, and indicate how much readable data is already there. A write operation will begin writing immediately after the existing readable content. This is commonly used to attach a Buffer instance to a local array. ***********************************************************************/ this (void[] data, size_t readable) { assign (data, readable); } /*********************************************************************** Return the name of this conduit. ***********************************************************************/ final override immutable(char)[] toString () { return "<array>"; } /*********************************************************************** Transfer content into the provided dst. Params: dst = Destination of the content. Returns: Return the number of bytes read, which may be less than dst.length. Eof is returned when no further content is available. Remarks: Populates the provided array with content. We try to satisfy the request from the buffer content, and read directly from an attached conduit when the buffer is empty. ***********************************************************************/ final override size_t read (void[] dst) { auto content = readable; if (content) { if (content >= dst.length) content = dst.length; // transfer buffer content dst [0 .. content] = data [index .. index + content]; index += content; } else content = IConduit.Eof; return content; } /*********************************************************************** Emulate OutputStream.write(). Params: src = The content to write. Returns: Return the number of bytes written, which may be less than provided (conceptually). Returns Eof when the buffer becomes full. Remarks: Appends src content to the buffer, expanding as required if configured to do so (via the ctor). ***********************************************************************/ final override size_t write (const(void)[] src) { auto len = src.length; if (len) { if (len > writable) if (expand(len) < len) return Eof; // content may overlap ... memcpy (&data[extent], src.ptr, len); extent += len; } return len; } /*********************************************************************** Return a preferred size for buffering conduit I/O. ***********************************************************************/ final override const size_t bufferSize () { return data.length; } /*********************************************************************** Release external resources. ***********************************************************************/ override void detach () { } /*********************************************************************** Seek within the constraints of assigned content. ***********************************************************************/ override long seek (long offset, Anchor anchor = Anchor.Begin) { if (offset > cast(long) limit) offset = limit; switch (anchor) { case Anchor.End: index = cast(size_t) (limit - offset); break; case Anchor.Begin: index = cast(size_t) offset; break; case Anchor.Current: long o = cast(size_t) (index + offset); if (o < 0) o = 0; if (o > cast(long) limit) o = limit; index = cast(size_t) o; break; default: break; } return index; } /*********************************************************************** Reset the buffer content. Params: data = The backing array to buffer within. All content is considered valid. Returns: The buffer instance. Remarks: Set the backing array with all content readable. ***********************************************************************/ Array assign (void[] data) { return assign (data, data.length); } /*********************************************************************** Reset the buffer content Params: data = The backing array to buffer within. readable = The number of bytes within data considered valid. Returns: The buffer instance. Remarks: Set the backing array with some content readable. Use clear() to reset the content (make it all writable). ***********************************************************************/ Array assign (void[] data, size_t readable) { this.data = data; this.extent = readable; this.dimension = data.length; // reset to start of input this.expansion = 0; this.index = 0; return this; } /*********************************************************************** Access buffer content. Remarks: Return the entire backing array. ***********************************************************************/ final void[] assign () { return data; } /*********************************************************************** Return a void[] read of the buffer from start to end, where end is exclusive. ***********************************************************************/ final void[] opSlice (size_t start, size_t end) { assert (start <= extent && end <= extent && start <= end); return data [start .. end]; } /*********************************************************************** Retrieve all readable content. Returns: A void[] read of the buffer. Remarks: Return a void[] read of the buffer, from the current position up to the limit of valid content. The content remains in the buffer for future extraction. ***********************************************************************/ final void[] slice () { return data [index .. extent]; } /*********************************************************************** Access buffer content. Params: size = Number of bytes to access. eat = Whether to consume the content or not. Returns: The corresponding buffer slice when successful, or null if there's not enough data available (Eof; Eob). Remarks: Slices readable data. The specified number of bytes is readd from the buffer, and marked as having been read when the 'eat' parameter is set true. When 'eat' is set false, the read position is not adjusted. Note that the slice cannot be larger than the size of the buffer ~ use method read(void[]) instead where you simply want the content copied. Note also that the slice should be .dup'd if you wish to retain it. Examples: --- // create a buffer with some content auto buffer = new Buffer ("hello world"); // consume everything unread auto slice = buffer.slice (buffer.readable); --- ***********************************************************************/ final void[] slice (size_t size, bool eat = true) { if (size > readable) error (underflow); auto i = index; if (eat) index += size; return data [i .. i + size]; } /*********************************************************************** Append content. Params: src = The content to _append. length = The number of bytes in src. Returns: A chaining reference if all content was written. Throws an IOException indicating eof or eob if not. Remarks: Append an array to this buffer. ***********************************************************************/ final Array append (const(void)[] src) { if (write(src) is Eof) error (overflow); return this; } /*********************************************************************** Iterator support. Params: scan = The delagate to invoke with the current content Returns: Returns true if a token was isolated, false otherwise. Remarks: Upon success, the delegate should return the byte-based index of the consumed pattern (tail end of it). Failure to match a pattern should be indicated by returning an IConduit.Eof. Note that additional iterator and/or reader instances will operate in lockstep when bound to a common buffer. ***********************************************************************/ final bool next (scope size_t delegate (const(void)[]) scan) { return reader (scan) != IConduit.Eof; } /*********************************************************************** Available content. Remarks: Return count of _readable bytes remaining in buffer. This is calculated simply as limit() - position(). ***********************************************************************/ @property final const size_t readable () { return extent - index; } /*********************************************************************** Available space. Remarks: Return count of _writable bytes available in buffer. This is calculated simply as capacity() - limit(). ***********************************************************************/ @property final const size_t writable () { return dimension - extent; } /*********************************************************************** Access buffer limit. Returns: Returns the limit of readable content within this buffer. Remarks: Each buffer has a capacity, a limit, and a position. The capacity is the maximum content a buffer can contain, limit represents the extent of valid content, and position marks the current read location. ***********************************************************************/ @property final const size_t limit () { return extent; } /*********************************************************************** Access buffer capacity. Returns: Returns the maximum capacity of this buffer. Remarks: Each buffer has a capacity, a limit, and a position. The capacity is the maximum content a buffer can contain, limit represents the extent of valid content, and position marks the current read location. ***********************************************************************/ final const size_t capacity () { return dimension; } /*********************************************************************** Access buffer read position. Returns: Returns the current read-position within this buffer Remarks: Each buffer has a capacity, a limit, and a position. The capacity is the maximum content a buffer can contain, limit represents the extent of valid content, and position marks the current read location. ***********************************************************************/ final const size_t position () { return index; } /*********************************************************************** Clear array content. Remarks: Reset 'position' and 'limit' to zero. This effectively clears all content from the array. ***********************************************************************/ final Array clear () { index = extent = 0; return this; } /*********************************************************************** Emit/purge buffered content. ***********************************************************************/ final override Array flush () { return this; } /*********************************************************************** Write into this buffer. Params: dg = The callback to provide buffer access to. Returns: Returns whatever the delegate returns. Remarks: Exposes the raw data buffer at the current _write position, The delegate is provided with a void[] representing space available within the buffer at the current _write position. The delegate should return the appropriate number of bytes if it writes valid content, or IConduit.Eof on error. ***********************************************************************/ final size_t writer (scope size_t delegate (void[]) dg) { auto count = dg (data [extent..dimension]); if (count != IConduit.Eof) { extent += count; assert (extent <= dimension); } return count; } /*********************************************************************** Read directly from this buffer. Params: dg = Callback to provide buffer access to. Returns: Returns whatever the delegate returns. Remarks: Exposes the raw data buffer at the current _read position. The delegate is provided with a void[] representing the available data, and should return zero to leave the current _read position intact. If the delegate consumes data, it should return the number of bytes consumed; or IConduit.Eof to indicate an error. ***********************************************************************/ final size_t reader (scope size_t delegate (const(void)[]) dg) { auto count = dg (data [index..extent]); if (count != IConduit.Eof) { index += count; assert (index <= extent); } return count; } /*********************************************************************** Expand existing buffer space. Returns: Available space, without any expansion. Remarks: Make some additional room in the buffer, of at least the given size. Should not be public in order to avoid issues with non-growable subclasses. ***********************************************************************/ private final size_t expand (size_t size) { if (expansion) { if (size < expansion) size = expansion; dimension += size; data.length = dimension; } return writable; } /*********************************************************************** Cast to a target type without invoking the wrath of the runtime checks for misalignment. Instead, we truncate the array length. ***********************************************************************/ private static T[] convert(T)(void[] x) { return (cast(T*) x.ptr) [0 .. (x.length / T.sizeof)]; } } /****************************************************************************** ******************************************************************************/ debug (Array) { import tango.io.Stdout; void main() { auto b = new Array(6, 10); b.seek (0); b.write ("fubar"); Stdout.formatln ("extent {}, pos {}, read {}, bufsize {}", b.limit, b.position, cast(char[]) b.slice, b.bufferSize); b.write ("fubar"); Stdout.formatln ("extent {}, pos {}, read {}, bufsize {}", b.limit, b.position, cast(char[]) b.slice, b.bufferSize); } } |