License:
BSD style: see license.txt
Version:
Initial release: March 2004
Authors:
Kris
- class
Buffer
: tango.io.model.IBuffer.IBuffer;
- The premise behind this IO package is as follows:
A central concept is that of a buffer. 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.
Buffers can be read and written directly, but a Reader,
Iterator, and/or Writer are often leveraged to apply
structure to what might otherwise be simple raw data.
Readers & writers are bound to a buffer; often the same
buffer. It's also perfectly legitimate to bind multiple
readers to the same buffer; they will access buffer
content serially as one would expect. This also applies to
multiple writers on the same buffer. Readers and writers
support three styles of IO: put/get, the C++ style << &
>> operators, and the () whisper style. All operations
can be chained.
Any class can be made compatable with the reader/writer
framework by implementing the IReadable and/or IWritable
interfaces. Each of these specify just a single method.
Once compatable, the class can simply be passed to the
reader/writer as if it were native 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 readers & writers support. Iterator tokens
are mapped directly onto buffer content (sliced), making
them quite efficient in practice. Like Readers, multiple
iterators can be mapped onto a common buffer; access is
serialized in a similar fashion.
Conduits provide virtualized access to external content,
and represent things like files or Internet connections.
They are just a different kind of stream. Conduits are
modelled by tango.io.model.IConduit, and implemented via
classes FileConduit, SocketConduit, ConsoleConduit, and
so on. Additional conduit varieties are easy to construct:
one either subclasses tango.io.Conduit, or implements
tango.io.model.IConduit ~ depending upon which is the most
convenient to use. Each conduit reads and writes from/to
a buffer in big chunks (typically the entire buffer).
Conduits may have one or more filters attached. These
will process content as it flows back and forth across
the conduit. Examples of filters include compression, utf
transcoding, and endian transformation. These filters
apply to the entire scope of the conduit, rather than
being specific to one data-type or another. Specific
data-type transformations are applied by readers and
writers instead, and include operations such as
endian-conversion.
Buffers are sometimes memory-only, in which case there
is nothing left to do when a reader (or iterator) hits
end of buffer conditions. Other buffers are themselves
bound to a Conduit. When this is the case, a reader will
eventually cause the buffer to reload via its associated
conduit. Previous buffer content will thus be lost. The
same approach is applied to writers, whereby they flush
the content of a full buffer 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.
Readers & writers may have a transcoder attached. The
role of a transcoder is to aid in converting between each
representation of text (utf8, utf16, utf32). They're used
to normalize string I/O according to a standard text-type.
By default there is no transcoder attached, and the type
is therefore considered "raw".
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 Writer to append the buffer.
This is an example of the 'whisper' style supported by the
IO package:
auto write = new Writer (new Buffer(256));
write ("now is the time for all good men "c) (foo);
Or, using printf-like formatting via a text oriented DisplayWriter:
auto write = new DisplayWriter (new Buffer(256));
write.print ("now is the time for %d good men %s", 3, foo);
One might use a GrowBuffer instead, where one wishes to append
beyond the specified size.
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 of Reader and Writer derivitives. For example:
auto file = new FileConduit ("file.name");
auto read = new Reader (file);
There is indeed a buffer between the Reader and Conduit, but
explicit construction is unecessary in common cases. See both
Reader and Writer for examples of formatted IO.
Stdout is a predefined DisplayWriter, attached to a conduit
representing the console. Thus, all conduit operations are
legitimate on Stdout and Stderr. For example:
Stdout.conduit.copy (new FileConduit ("readme.txt"));
Because Stdout is an instance of DisplayWriter, it also has
support for formatted output:
Stdout.println ("now is the time for %d good men %s", 3, foo);
The Stdout writer is attached to a specific buffer, which in
turn is attached to a specific conduit. This buffer is known
as Cout, and it is attached to a conduit representing the
console. Cout can be used directly, bypassing the writer layer
if so desired.
Cout has relatives named Cerr and Cin, which are attached to
the corresponding console conduits. Writer Stderr, and reader
Stdin are mapped onto Cerr and Cin respectively, ensuring
console IO is buffered in one common area.
Cout ("what is your name?");
char[] name;
Cin (name);
Cout ("hello ") (name) .newline;
An Iterator is constructed in a similar manner to a Reader; you
provide it with a buffer or a conduit. There's a variety of
iterators available in the tango.text package, and they are each
templated for utf8, utf16, and utf32 ~ this example uses a line
iterator to sweep a text file:
auto file = new FileConduit ("file.name");
foreach (line; new LineIterator (file))
Cout(line).newline;
Buffers are useful for many purposes within Mango, but there
are times when it may be more appropriate to sidestep them. For
such cases, conduit derivatives (such as FileConduit) support
direct array-based IO via a pair of read() and write() methods.
These alternate methods will also invoke any attached filters.
- this(IConduit conduit);
- Construct a buffer
Params:
IConduit conduit |
the conduit to buffer |
Remarks:
Construct a Buffer upon the provided conduit. A relevant
buffer size is supplied via the provided conduit.
- this(uint capacity = cast(uint)0);
- Construct a buffer
Params:
uint capacity |
the number of bytes to make available |
Remarks:
Construct a Buffer with the specified number of bytes.
- this(void[] data);
- Construct a buffer
Params:
void[] 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, uint readable);
- Construct a buffer
Params:
void[] data |
the backing array to buffer within |
uint 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.
- final void
error
(char[] msg);
- Generic IOException thrower
Params:
char[] msg |
a text message describing the exception reason |
Remarks:
Throw an IOException with the provided message
- Style
getStyle
();
- Access the buffer style
Returns:
the style of this buffer
Remarks:
Return style of buffer. This is either Text, Binary, or Raw.
The style is initially set via a constructor
- IBuffer
setValidContent
(void[] data);
- Reset the buffer content
Params:
void[] 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, uint readable = cast(uint)0);
- Reset the buffer content
Params:
void[] data |
the backing array to buffer within |
uint 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).
- void[]
get
(uint size, bool eat = true);
- Access buffer content
Params:
uint size |
number of bytes to access |
bool 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 get(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.get (buffer.readable);
- uint
get
(void[] dst);
- Access buffer content
Params:
void[] dst |
destination of the content |
Returns:
return the number of bytes read, which will be less than
dst.length when the content has been consumed (Eof, Eob)
and zero thereafter.
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.
- IBuffer
wait
();
- Wait for input
Returns:
the buffer instance
Remarks:
Wait for something to arrive in the buffer. This may stall
the current thread forever, although usage of SocketConduit
will take advantage of the timeout facilities provided there.
Note that this is not intended to provide 'barrier' style
semantics for multi-threaded producer/consumer environments.
- IBuffer
append
(void[] src);
- Append content
Params:
void[] src |
the content to append |
Returns:
the buffer instance
Remarks:
Append an array of data to this buffer, and flush to the
conduit as necessary. Returns a chaining reference if all
data was written; throws an IOException indicating eof or
eob if not.
This is often used in lieu of a Writer.
- IBuffer
append
(IBuffer other);
- Append content
Params:
IBuffer other |
a buffer with content available |
Returns:
the buffer instance
Remarks:
Append another buffer to this one, and flush to the
conduit as necessary. Returns a chaining reference if all
data was written; throws an IOException indicating eof or
eob if not.
This is often used in lieu of a Writer.
- char[]
toString
();
- Retrieve the current content as a string
Returns:
a char[][ slice of the buffer
Remarks:
Return a char[] slice of the buffer, from the current position
up to the limit of valid content. The content remains in the
buffer for future extraction.
- bool
skip
(int size);
- Move the current read location
Params:
int 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
next
(uint delegate(void[]) scan);
- Iterator support
Params:
uint delegate(void[]) 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 stay in lockstep when bound to a common buffer.
- uint
readable
();
- Available content
Remarks:
Return count of readable bytes remaining in buffer. This is
calculated simply as limit() - position()
- uint
writable
();
- Available space
Remarks:
Return count of writable bytes available in buffer. This is
calculated simply as capacity() - limit()
- uint
write
(uint delegate(void[]) dg);
- Write into this buffer
Params:
uint delegate(void[]) 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
read
(uint delegate(void[]) dg);
- Read directly from this buffer
Params:
uint delegate(void[]) 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.
- IBuffer
compress
();
- 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.
- uint
fill
();
- Fill buffer from conduit
Returns:
Returns the number of bytes read, or Eof if there's no
more data available. Where no conduit is attached, Eof
is always returned.
Remarks:
Try to fill the available buffer with content from the
attached conduit. In particular, we will never ask to
read less than 32 bytes. This permits conduit-filters
to operate within a known environment.
- uint
fill
(IConduit conduit);
- Fill buffer from conduit
Returns:
Returns the number of bytes read, or Conduit.Eof
Remarks:
Try to fill the available buffer with content from the
specified conduit. In particular, we will never ask to
read less than 32 bytes ~ this permits conduit-filters
to operate within a known environment. We also try to
read as much as possible by clearing the buffer when
all current content has been eaten.
- uint
makeRoom
(uint space);
- Try to make space available
Params:
uint space |
number of bytes required |
Returns:
The number of bytes actually made available
Remarks:
Make some room in the buffer. The requested space is simply
an indicator of how much is desired. A subclass may or may
not fulfill the request directly.
For example, the base-class simply performs a drain() on the
buffer.
- uint
drain
();
- Drain buffer content
Returns:
Returns the number of bytes written, or Conduit.Eof
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.
- IBuffer
flush
();
- Flush all buffer content
Remarks:
Flush the contents of this buffer. This will block until
all content is actually flushed via the associated conduit,
whereas drain() will not.
Throws an IOException on premature eof.
- IBuffer
clear
();
- Clear buffer content
Returns:
the buffer instance
Remarks:
Reset 'position' and 'limit' to zero. This effectively clears
all content from the buffer.
- bool
truncate
(uint extent);
- Truncate buffer content
Remarks:
Truncate the buffer within its extent. Returns true if
the new 'extent' is valid, false otherwise.
- uint
getLimit
();
- 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
getCapacity
();
- 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
getPosition
();
- 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.
- IConduit
getConduit
();
- 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.
- IBuffer
setConduit
(IConduit conduit);
- Set external conduit
Params:
IConduit 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.
- protected void[]
getContent
();
- Access buffer content
Remarks:
Return the entire backing array. Exposed for subclass usage
only
- protected void
copy
(void* src, uint size);
- Copy content into buffer
Params:
void* src |
the soure of the content |
uint 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
|