diff tango/tango/io/Buffer.d @ 132:1700239cab2e trunk

[svn r136] MAJOR UNSTABLE UPDATE!!! Initial commit after moving to Tango instead of Phobos. Lots of bugfixes... This build is not suitable for most things.
author lindquist
date Fri, 11 Jan 2008 17:57:40 +0100
parents
children 0e28624814e8
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tango/tango/io/Buffer.d	Fri Jan 11 17:57:40 2008 +0100
@@ -0,0 +1,1420 @@
+/*******************************************************************************
+
+        copyright:      Copyright (c) 2004 Kris Bell. All rights reserved
+
+        license:        BSD style: $(LICENSE)
+
+        version:        Mar 2004: Initial release
+                        Dec 2006: Outback release
+
+        authors:        Kris
+        
+*******************************************************************************/
+
+module tango.io.Buffer;
+
+private import  tango.core.Exception;
+
+public  import  tango.io.model.IBuffer,
+                tango.io.model.IConduit;
+
+/******************************************************************************
+
+******************************************************************************/
+
+extern (C)
+{
+        protected void * memcpy (void *dst, void *src, uint);
+}       
+
+/*******************************************************************************
+
+        Buffer is central concept in Tango I/O. Each buffer acts
+        as a queue (line) where items are removed from the front
+        and new items are added to the back. Buffers are modeled 
+        by tango.io.model.IBuffer, and a concrete implementation 
+        is provided by this class.
+        
+        Buffer can be read from and written to directly, though 
+        various data-converters and filters are often leveraged 
+        to apply structure to what might otherwise be simple raw 
+        data. 
+
+        Buffers may also be tokenized by applying an Iterator. 
+        This can be handy when one is dealing with text input, 
+        and/or the content suits a more fluid format than most 
+        typical converters support. Iterator tokens are mapped 
+        directly onto buffer content (sliced), making them quite 
+        efficient in practice. Like other types of buffer client, 
+        multiple iterators can be mapped onto one common buffer
+        and access will be serialized.
+
+        Buffers are sometimes memory-only, in which case there
+        is nothing left to do when a client has consumed all the 
+        content. Other buffers are themselves bound to an external
+        device called a conduit. When this is the case, a consumer 
+        will eventually cause a buffer to reload via its associated 
+        conduit and previous buffer content will be lost. 
+        
+        A similar approach is applied to clients which populate a
+        buffer, whereby the content of a full buffer will be flushed
+        to a bound conduit before continuing. Another variation is 
+        that of a memory-mapped buffer, whereby the buffer content 
+        is mapped directly to virtual memory exposed via the OS. This 
+        can be used to address large files as an array of content.
+
+        Direct buffer manipulation typically involves appending, 
+        as in the following example:
+        ---
+        // create a small buffer
+        auto buf = new Buffer (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 the buffer:
+        ---
+        auto output = new FormatOutput (new Buffer(256));
+        output.format ("now is the time for {} good men {}", 3, foo);
+        ---
+
+        A slice() method will return all valid content within a buffer.
+        GrowBuffer can be used instead, where one wishes to append beyond 
+        a specified limit. 
+        
+        A common usage of a buffer is in conjunction with a conduit, 
+        such as FileConduit. Each conduit exposes a preferred-size for 
+        its associated buffers, utilized during buffer construction:
+        ---
+        auto file = new FileConduit ("file.name");
+        auto buf = new Buffer (file);
+        ---
+
+        However, this is typically hidden by higher level constructors 
+        such as those exposed via the stream wrappers. For example:
+        ---
+        auto input = new DataInput (new FileInput("file.name"));
+        ---
+
+        There is indeed a buffer between the resultant stream and the 
+        file source, but explicit buffer construction is unecessary in 
+        common cases. 
+
+        An Iterator is constructed in a similar manner, where you provide 
+        it an input stream to operate upon. There's a variety of iterators 
+        available in the tango.text.stream package, and they are templated 
+        for each of utf8, utf16, and utf32. This example uses a line iterator 
+        to sweep a text file:
+        ---
+        auto lines = new LineInput (new FileInput("file.name"));
+        foreach (line; lines)
+                 Cout(line).newline;
+        ---                 
+
+        Buffers are useful for many purposes within Tango, but there
+        are times when it may be more appropriate to sidestep them. For 
+        such cases, all conduit derivatives (such as FileConduit) support 
+        direct array-based IO via a pair of read() and write() methods. 
+
+*******************************************************************************/
+
+class Buffer : IBuffer
+{
+        protected OutputStream  sink;           // optional data sink
+        protected InputStream   source;         // optional data source
+        protected void[]        data;           // the raw data buffer
+        protected uint          index;          // current read position
+        protected uint          extent;         // limit of valid content
+        protected uint          dimension;      // maximum extent of content
+
+        
+        protected static char[] overflow  = "output buffer is full";
+        protected static char[] underflow = "input buffer is empty";
+        protected static char[] eofRead   = "end-of-flow whilst reading";
+        protected static char[] eofWrite  = "end-of-flow whilst writing";
+
+        /***********************************************************************
+        
+                Ensure the buffer remains valid between method calls
+                 
+        ***********************************************************************/
+
+        invariant 
+        {
+                assert (index <= extent);
+                assert (extent <= dimension);
+        }
+
+        /***********************************************************************
+        
+                Construct a buffer
+
+                Params: 
+                conduit = the conduit to buffer
+
+                Remarks:
+                Construct a Buffer upon the provided conduit. A relevant 
+                buffer size is supplied via the provided conduit.
+
+        ***********************************************************************/
+
+        this (IConduit conduit)
+        {
+                assert (conduit);
+
+                this (conduit.bufferSize);
+                setConduit (conduit);
+        }
+
+        /***********************************************************************
+        
+                Construct a buffer
+
+                Params: 
+                stream = an input stream
+                capacity = desired buffer capacity
+
+                Remarks:
+                Construct a Buffer upon the provided input stream.
+
+        ***********************************************************************/
+
+        this (InputStream stream, uint capacity)
+        {
+                this (capacity);
+                input = stream;
+        }
+
+        /***********************************************************************
+        
+                Construct a buffer
+
+                Params: 
+                stream = an output stream
+                capacity = desired buffer capacity
+
+                Remarks:
+                Construct a Buffer upon the provided output stream.
+
+        ***********************************************************************/
+
+        this (OutputStream stream, uint capacity)
+        {
+                this (capacity);
+                output = stream;
+        }
+
+        /***********************************************************************
+        
+                Construct a buffer
+
+                Params: 
+                capacity = the number of bytes to make available
+
+                Remarks:
+                Construct a Buffer with the specified number of bytes. 
+
+        ***********************************************************************/
+
+        this (uint capacity = 0)
+        {
+                setContent (new ubyte[capacity], 0);              
+        }
+
+        /***********************************************************************
+        
+                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)
+        {
+                setContent (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, uint readable)
+        {
+                setContent (data, readable);
+        }
+
+        /***********************************************************************
+        
+                Attempt to share an upstream Buffer, and create an instance
+                where there not one available.
+
+                Params: 
+                stream = an input stream
+                size = a hint of the desired buffer size. Defaults to the
+                conduit-defined size
+
+                Remarks:
+                If an upstream Buffer instances is visible, it will be shared.
+                Otherwise, a new instance is created based upon the bufferSize
+                exposed by the stream endpoint (conduit).
+
+        ***********************************************************************/
+
+        static IBuffer share (InputStream stream, uint size=uint.max)
+        {
+                auto b = cast(Buffered) stream;
+                if (b)
+                    return b.buffer;
+
+                if (size is uint.max)
+                    size = stream.conduit.bufferSize;
+
+                return new Buffer (stream, size);
+        }
+
+        /***********************************************************************
+        
+                Attempt to share an upstream Buffer, and create an instance
+                where there not one available.
+
+                Params: 
+                stream = an output stream
+                size = a hint of the desired buffer size. Defaults to the
+                conduit-defined size
+
+                Remarks:
+                If an upstream Buffer instances is visible, it will be shared.
+                Otherwise, a new instance is created based upon the bufferSize
+                exposed by the stream endpoint (conduit).
+
+        ***********************************************************************/
+
+        static IBuffer share (OutputStream stream, uint size=uint.max)
+        {
+                auto b = cast(Buffered) stream;
+                if (b)
+                    return b.buffer;
+
+                if (size is uint.max)
+                    size = stream.conduit.bufferSize;
+
+                return new Buffer (stream, size);
+        }
+
+        /***********************************************************************
+
+                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. Writing
+                to this will either flush it to an associated conduit, or
+                raise an Eof condition. Use clear() to reset the content
+                (make it all writable).
+
+        ***********************************************************************/
+
+        IBuffer setContent (void[] data)
+        {
+                return setContent (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. Writing
+                to this will either flush it to an associated conduit, or
+                raise an Eof condition. Use clear() to reset the content
+                (make it all writable).
+
+        ***********************************************************************/
+
+        IBuffer setContent (void[] data, uint readable)
+        {
+                this.data = data;
+                this.extent = readable;
+                this.dimension = data.length;
+
+                // reset to start of input
+                this.index = 0;
+
+                return this;            
+        }
+
+        /***********************************************************************
+        
+                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:
+                Read a slice of data from the buffer, loading from the
+                conduit as necessary. The specified number of bytes is
+                sliced 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 fill(void[]) instead where you
+                simply want the content copied, or use conduit.read()
+                to extract directly from an attached conduit. Also note 
+                that if you need to retain the slice, then it should be 
+                .dup'd before the buffer is compressed or repopulated.
+
+                Examples:
+                ---
+                // create a buffer with some content 
+                auto buffer = new Buffer ("hello world");
+
+                // consume everything unread 
+                auto slice = buffer.slice (buffer.readable);
+                ---
+
+        ***********************************************************************/
+
+        void[] slice (uint size, bool eat = true)
+        {   
+                if (size > readable)
+                   {
+                   if (source is null)
+                       error (underflow);
+
+                   // make some space? This will try to leave as much content
+                   // in the buffer as possible, such that entire records may
+                   // be aliased directly from within. 
+                   if (size > writable)
+                      {
+                      if (size > dimension)
+                          error (underflow);
+                      compress ();
+                      }
+
+                   // populate tail of buffer with new content
+                   do {
+                      if (fill(source) is IConduit.Eof)
+                          error (eofRead);
+                      } while (size > readable);
+                   }
+
+                auto i = index;
+                if (eat)
+                    index += size;
+                return data [i .. i + size];               
+        }
+
+        /**********************************************************************
+
+                Fill the provided buffer. Returns the number of bytes
+                actually read, which will be less that dst.length when
+                Eof has been reached and IConduit.Eof thereafter
+
+        **********************************************************************/
+
+        uint fill (void[] dst)
+        {
+                uint len = 0;
+
+                while (len < dst.length)
+                      {
+                      uint i = read (dst [len .. $]);
+                      if (i is IConduit.Eof)
+                          return (len > 0) ? len : IConduit.Eof;
+                      len += i;
+                      }
+                return len;
+        }
+
+        /***********************************************************************
+
+                Copy buffer content into the provided dst
+
+                Params: 
+                dst = destination of the content
+                bytes = size of dst
+
+                Returns:
+                A reference to the populated content
+
+                Remarks:
+                Fill the provided array with content. We try to satisfy 
+                the request from the buffer content, and read directly
+                from an attached conduit where more is required.
+
+        ***********************************************************************/
+
+        void[] readExact (void* dst, uint bytes)
+        {
+                auto tmp = dst [0 .. bytes];
+                if (fill (tmp) != bytes)
+                    error (eofRead);
+
+                return tmp;
+        }
+        
+        /***********************************************************************
+        
+                Append content
+
+                Params:
+                src = the content to _append
+
+                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, and flush to the
+                conduit as necessary. This is often used in lieu of 
+                a Writer.
+
+        ***********************************************************************/
+
+        IBuffer append (void[] src)
+        {
+                return append (src.ptr, src.length);
+        }
+        
+        /***********************************************************************
+        
+                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, and flush to the
+                conduit as necessary. This is often used in lieu of 
+                a Writer.
+
+        ***********************************************************************/
+
+        IBuffer append (void* src, uint length)
+        {               
+                if (length > writable)
+                    // can we write externally?
+                    if (sink)
+                       {
+                       flush ();
+
+                       // check for pathological case
+                       if (length > dimension)
+                          {
+                          do {
+                             auto written = sink.write (src [0 .. length]);
+                             if (written is IConduit.Eof)
+                                 error (eofWrite);
+                             src += written, length -= written;
+                             } while (length > dimension);
+                          }
+                       }
+                    else
+                       error (overflow);
+
+                copy (src, length);
+                return this;
+        }
+
+        /***********************************************************************
+        
+                Append content
+
+                Params:
+                other = a buffer with content available
+
+                Returns:
+                Returns a chaining reference if all content was written. 
+                Throws an IOException indicating eof or eob if not.
+
+                Remarks:
+                Append another buffer to this one, and flush to the
+                conduit as necessary. This is often used in lieu of 
+                a Writer.
+
+        ***********************************************************************/
+
+        IBuffer append (IBuffer other)        
+        {               
+                return append (other.slice);
+        }
+
+        /***********************************************************************
+        
+                Consume content from a producer
+
+                Params:
+                The content to consume. This is consumed verbatim, and in
+                raw binary format ~ no implicit conversions are performed.
+
+                Remarks:
+                This is often used in lieu of a Writer, and enables simple
+                classes, such as FilePath and Uri, to emit content directly
+                into a buffer (thus avoiding potential heap activity)
+
+                Examples:
+                ---
+                auto path = new FilePath (somepath);
+
+                path.produce (&buffer.consume);
+                ---
+
+        ***********************************************************************/
+
+        void consume (void[] x)        
+        {   
+                append (x);
+        }
+                    
+        /***********************************************************************
+        
+                Retrieve the valid content
+
+                Returns:
+                a void[] slice of the buffer
+
+                Remarks:
+                Return a void[] slice of the buffer, from the current position 
+                up to the limit of valid content. The content remains in the
+                buffer for future extraction.
+
+        ***********************************************************************/
+
+        void[] slice ()
+        {       
+                return  data [index .. extent];
+        }
+
+        /***********************************************************************
+        
+                Move the current read location
+
+                Params:
+                size = the number of bytes to move
+
+                Returns:
+                Returns true if successful, false otherwise.
+
+                Remarks:
+                Skip ahead by the specified number of bytes, streaming from 
+                the associated conduit as necessary.
+        
+                Can also reverse the read position by 'size' bytes, when size
+                is negative. This may be used to support lookahead operations. 
+                Note that a negative size will fail where there is not sufficient
+                content available in the buffer (can't _skip beyond the beginning).
+
+        ***********************************************************************/
+
+        bool skip (int size)
+        {
+                if (size < 0)
+                   {
+                   size = -size;
+                   if (index >= size)
+                      {
+                      index -= size;
+                      return true;
+                      }
+                   return false;
+                   }
+                return slice(size) !is null;
+        }
+
+        /***********************************************************************
+        
+                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
+
+                Each pattern is expected to be stripped of the delimiter.
+                An end-of-file condition causes trailing content to be 
+                placed into the token. Requests made beyond Eof result
+                in empty matches (length is zero).
+
+                Note that additional iterator and/or reader instances
+                will operate in lockstep when bound to a common buffer.
+
+        ***********************************************************************/
+
+        bool next (uint delegate (void[]) scan)
+        {
+                while (read(scan) is IConduit.Eof)
+                       // not found - are we streaming?
+                       if (source)
+                          {
+                          // did we start at the beginning?
+                          if (position)
+                              // nope - move partial token to start of buffer
+                              compress ();
+                          else
+                             // no more space in the buffer?
+                             if (writable is 0)
+                                 error ("Token is too large to fit within buffer");
+
+                          // read another chunk of data
+                          if (fill(source) is IConduit.Eof)
+                              return false;
+                          }
+                       else
+                          return false;
+
+                return true;
+        }
+
+        /***********************************************************************
+        
+                Available content
+
+                Remarks:
+                Return count of _readable bytes remaining in buffer. This is 
+                calculated simply as limit() - position()
+
+        ***********************************************************************/
+
+        uint readable ()
+        {
+                return extent - index;
+        }               
+
+        /***********************************************************************
+        
+                Available space
+
+                Remarks:
+                Return count of _writable bytes available in buffer. This is 
+                calculated simply as capacity() - limit()
+
+        ***********************************************************************/
+
+        uint writable ()
+        {
+                return dimension - extent;
+        }               
+
+        /***********************************************************************
+
+                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.
+
+        ***********************************************************************/
+
+        uint write (uint delegate (void[]) dg)
+        {
+                int 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.
+
+        ***********************************************************************/
+
+        uint read (uint delegate (void[]) dg)
+        {
+                int count = dg (data [index..extent]);
+                
+                if (count != IConduit.Eof)
+                   {
+                   index += count;
+                   assert (index <= extent);
+                   }
+                return count;
+        }               
+
+        /***********************************************************************
+
+                Compress buffer space
+
+                Returns:
+                the buffer instance
+
+                Remarks:
+                If we have some data left after an export, move it to 
+                front-of-buffer and set position to be just after the 
+                remains. This is for supporting certain conduits which 
+                choose to write just the initial portion of a request.
+                
+                Limit is set to the amount of data remaining. Position 
+                is always reset to zero.
+
+        ***********************************************************************/
+
+        IBuffer compress ()
+        {
+                uint r = readable ();
+
+                if (index > 0 && r > 0)
+                    // content may overlap ...
+                    memcpy (&data[0], &data[index], r);
+
+                index = 0;
+                extent = r;
+                return this;
+        }               
+
+        /***********************************************************************
+
+                Fill buffer from the specific conduit
+               
+                Returns:
+                Returns the number of bytes read, or Conduit.Eof
+        
+                Remarks:
+                Try to _fill the available buffer with content from the 
+                specified conduit. We try to read as much as possible 
+                by clearing the buffer when all current content has been 
+                eaten. If there is no space available, nothing will be 
+                read.
+
+        ***********************************************************************/
+
+        uint fill (InputStream src)
+        {
+                if (src is null)
+                    return IConduit.Eof;
+
+                if (readable is 0)
+                    index = extent = 0;  // same as clear(), but without chain
+                else 
+                   if (writable is 0)
+                       return 0;
+
+                return write (&src.read);
+        } 
+
+        /***********************************************************************
+
+                Drain buffer content to the specific conduit
+
+                Returns:
+                Returns the number of bytes written
+
+                Remarks:
+                Write as much of the buffer that the associated conduit
+                can consume. The conduit is not obliged to consume all 
+                content, so some may remain within the buffer.
+
+                Throws an IOException on premature Eof.
+        
+        ***********************************************************************/
+
+        final uint drain (OutputStream dst)
+        {
+                if (dst is null)
+                    return IConduit.Eof;
+
+                uint ret = read (&dst.write);
+                if (ret is IConduit.Eof)
+                    error (eofWrite);
+
+                compress ();
+                return ret;
+        }
+
+        /***********************************************************************
+        
+                Truncate buffer content
+
+                Remarks:
+                Truncate the buffer within its extent. Returns true if
+                the new length is valid, false otherwise.
+
+        ***********************************************************************/
+
+        bool truncate (uint length)
+        {
+                if (length <= data.length)
+                   {
+                   extent = length;
+                   return true;
+                   }
+                return false;
+        }               
+
+        /***********************************************************************
+        
+                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.
+
+        ***********************************************************************/
+
+        uint 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.
+
+        ***********************************************************************/
+
+        uint 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.
+
+        ***********************************************************************/
+
+        uint position ()
+        {
+                return index;
+        }
+
+        /***********************************************************************
+        
+                Set external conduit
+
+                Params:
+                conduit = the conduit to attach to
+
+                Remarks:
+                Sets the external conduit associated with this buffer.
+
+                Buffers do not require an external conduit to operate, but 
+                it can be convenient to associate one. For example, methods
+                fill() & drain() use it to import/export content as necessary.
+
+        ***********************************************************************/
+
+        IBuffer setConduit (IConduit conduit)
+        {
+                sink = conduit.output;
+                source = conduit.input;
+                return this;
+        }
+
+        /***********************************************************************
+        
+                Set output stream
+
+                Params:
+                sink = the stream to attach to
+
+                Remarks:
+                Sets the external output stream associated with this buffer.
+
+                Buffers do not require an external stream to operate, but 
+                it can be convenient to associate one. For example, methods
+                fill & drain use them to import/export content as necessary.
+
+        ***********************************************************************/
+
+        final IBuffer output (OutputStream sink)
+        {
+                this.sink = sink;
+                return this;
+        }
+
+        /***********************************************************************
+        
+                Set input stream
+
+                Params:
+                source = the stream to attach to
+
+                Remarks:
+                Sets the external input stream associated with this buffer.
+
+                Buffers do not require an external stream to operate, but 
+                it can be convenient to associate one. For example, methods
+                fill & drain use them to import/export content as necessary.
+
+        ***********************************************************************/
+
+        final IBuffer input (InputStream source)
+        {
+                this.source = source;
+                return this;
+        }
+
+        /***********************************************************************
+                
+                Access buffer content
+
+                Remarks:
+                Return the entire backing array. Exposed for subclass usage 
+                only
+
+        ***********************************************************************/
+
+        protected void[] getContent ()
+        {
+                return data;
+        }
+
+        /***********************************************************************
+        
+                Copy content into buffer
+
+                Params:
+                src = the soure of the content
+                size = the length of content at src
+
+                Remarks:
+                Bulk _copy of data from 'src'. The new content is made 
+                available for reading. This is exposed for subclass use
+                only 
+
+        ***********************************************************************/
+
+        protected void copy (void *src, uint size)
+        {
+                // avoid "out of bounds" test on zero size
+                if (size)
+                   {
+                   // content may overlap ...
+                   memcpy (&data[extent], src, size);
+                   extent += size;
+                   }
+        }
+
+        /***********************************************************************
+
+                Cast to a target type without invoking the wrath of the 
+                runtime checks for misalignment. Instead, we truncate the 
+                array length
+
+        ***********************************************************************/
+
+        static T[] convert(T)(void[] x)
+        {
+                return (cast(T*) x.ptr) [0 .. (x.length / T.sizeof)];
+        }
+
+
+
+        /**********************************************************************/
+        /*********************** Buffered Interface ***************************/
+        /**********************************************************************/
+
+        IBuffer buffer ()
+        {
+                return this;
+        }
+
+
+        /**********************************************************************/
+        /******************** Stream & Conduit Interfaces *********************/
+        /**********************************************************************/
+
+
+        /***********************************************************************
+        
+                Return the name of this conduit
+
+        ***********************************************************************/
+
+        override char[] toString ()
+        {
+                return "<buffer>";
+        }
+                     
+        /***********************************************************************
+        
+                Generic IOException thrower
+                
+                Params: 
+                msg = a text message describing the exception reason
+
+                Remarks:
+                Throw an IOException with the provided message
+
+        ***********************************************************************/
+
+        final void error (char[] msg)
+        {
+                throw new IOException (msg);
+        }
+
+        /***********************************************************************
+        
+                Flush all buffer content to the specific conduit
+
+                Remarks:
+                Flush the contents of this buffer. This will block until 
+                all content is actually flushed via the associated conduit, 
+                whereas drain() will not.
+
+                Do nothing where a conduit is not attached, enabling memory
+                buffers to treat flush as a noop.
+
+                Throws an IOException on premature Eof.
+
+        ***********************************************************************/
+
+        override OutputStream flush ()
+        {
+                if (sink)
+                   {
+                   while (readable() > 0)
+                          drain (sink);
+
+                   // flush the filter chain also
+                   sink.flush;
+                   }
+                return this;
+        } 
+
+        /***********************************************************************
+        
+                Clear buffer content
+
+                Remarks:
+                Reset 'position' and 'limit' to zero. This effectively 
+                clears all content from the buffer.
+
+        ***********************************************************************/
+
+        override InputStream clear ()
+        {
+                index = extent = 0;
+
+                // clear the filter chain also
+                if (source)
+                    source.clear;
+                return this;
+        }               
+
+        /***********************************************************************
+        
+                Copy content via this buffer from the provided src
+                conduit.
+
+                Remarks:
+                The src conduit has its content transferred through 
+                this buffer via a series of fill & drain operations, 
+                until there is no more content available. The buffer
+                content should be explicitly flushed by the caller.
+
+                Throws an IOException on premature eof
+
+        ***********************************************************************/
+
+        override OutputStream copy (InputStream src)
+        {
+                while (fill(src) != IConduit.Eof)
+                       // don't drain until we actually need to
+                       if (writable is 0)
+                           if (sink)
+                               drain (sink);
+                           else
+                              error (overflow);
+                return this;
+        } 
+
+        /***********************************************************************
+
+                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.
+
+        ***********************************************************************/
+
+        override uint read (void[] dst)
+        {   
+                uint content = readable();
+                if (content)
+                   {
+                   if (content >= dst.length)
+                       content = dst.length;
+
+                   // transfer buffer content
+                   dst [0 .. content] = data [index .. index + content];
+                   index += content;
+                   }
+                else
+                   if (source) 
+                      {
+                      // pathological cases read directly from conduit
+                      if (dst.length > dimension) 
+                          content = source.read (dst);
+                      else
+                         // keep buffer partially populated
+                         if ((content = fill(source)) != IConduit.Eof && content > 0)
+                              content = read (dst);
+                      }
+                   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).
+
+                Remarks:
+                Appends src content to the buffer, flushing to an attached
+                conduit as necessary. An IOException is thrown upon write 
+                failure.
+
+        ***********************************************************************/
+
+        override uint write (void[] src)
+        {   
+                append (src.ptr, src.length);
+                return src.length;
+        }
+
+        /***********************************************************************
+        
+                Access configured conduit
+
+                Returns:
+                Returns the conduit associated with this buffer. Returns 
+                null if the buffer is purely memory based; that is, it's
+                not backed by some external medium.
+
+                Remarks:
+                Buffers do not require an external conduit to operate, but 
+                it can be convenient to associate one. For example, methods
+                fill() & drain() use it to import/export content as necessary.
+
+        ***********************************************************************/
+
+        final override IConduit conduit ()
+        {
+                if (sink)
+                    return sink.conduit;
+                else
+                   if (source)
+                       return source.conduit;
+                return this;
+        }
+
+        /***********************************************************************
+        
+                Return a preferred size for buffering conduit I/O
+
+        ***********************************************************************/
+
+        final override uint bufferSize ()
+        {
+                return 32 * 1024;
+        }
+                     
+        /***********************************************************************
+
+                Is the conduit alive?
+
+        ***********************************************************************/
+
+        final override bool isAlive ()
+        {       
+                return true;
+        }
+
+        /***********************************************************************
+        
+                Exposes configured output stream
+
+                Returns:
+                Returns the OutputStream associated with this buffer. Returns 
+                null if the buffer is not attached to an output; that is, it's
+                not backed by some external medium.
+
+                Remarks:
+                Buffers do not require an external stream to operate, but 
+                it can be convenient to associate them. For example, methods
+                fill & drain use them to import/export content as necessary.
+
+        ***********************************************************************/
+
+        final OutputStream output ()
+        {
+                return sink;
+        }
+
+        /***********************************************************************
+        
+                Exposes configured input stream
+
+                Returns:
+                Returns the InputStream associated with this buffer. Returns 
+                null if the buffer is not attached to an input; that is, it's
+                not backed by some external medium.
+
+                Remarks:
+                Buffers do not require an external stream to operate, but 
+                it can be convenient to associate them. For example, methods
+                fill & drain use them to import/export content as necessary.
+
+        ***********************************************************************/
+
+        final InputStream input ()
+        {
+                return source;
+        }
+
+        /***********************************************************************
+                
+                Release external resources
+
+        ***********************************************************************/
+
+        final override void detach ()
+        {
+        }
+
+        /***********************************************************************
+        
+                Close the stream
+
+                Remarks:
+                Propagate request to an attached OutputStream (this is a
+                requirement for the OutputStream interface)
+
+        ***********************************************************************/
+
+        override void close () 
+        {
+                if (sink)
+                    sink.close;
+                else
+                   if (source)
+                       source.close;
+        }
+}