Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

Protocols

Conduits expose typeless data streaming only. Tango provides support for typed streaming via a reader and writer pair; readers extract data-types from a stream whilst writers inject data types into a stream. Each of the native D types is supported, along with their one-dimensional array variants.

Readers and writers may be configured to converse in text or binary format, handle endian conversion etc, by furnishing them with a protocol. These protocols handle both encoding and decoding of typed data, and represent the core of the stream conversion facilities. Data encoded by one particular protocol can generally be decoded correctly only by the same protocol. Readers and writers are actually 'skins' around a protocol instance and, while not strictly necessary for the operation of a protocol, we tend to consider reader, writer and protocol as a combined entity.

Protocols are often attached to a conduit, where the intent is to stream D types to and/or from a file or socket – thus, a conduit is often attached at protocol construction time. In such cases, interacting with the protocol (directly, or via a reader/writer) will access the attached conduit accordingly.

Some protocols treat arrays in a specific manner: they prefix each with the number of elements contained. When reading, each protocol normally performs array allocation on behalf of the application, utilizing said element count. For the sake of simplicity, the default allocation strategy is to assign via the heap. However, the strategy itself is configurable and a handful of implementations are provided to choose from. One to note is the null strategy, where arrays are instead managed by the application and the length prefix is assumed to be missing from the stream. This allows for those cases where, for example, an array length might be declared in a header.

In contrast to the vararg style popularized by printf/readf et al, reader & writer methods are discrete for each element read or written, and support call chaining. Thus, basic protocol usage can be illustrated as shown below where input & output represent a protocol reader & writer pair, and where opCall is leveraged in a symmetrical style known as whisper:

int i;
double d;
char[] text;

output (i) (d) (text);
input  (i) (d) (text);

In addition, protocols support a simple object serialization strategy where compatible objects implement a specific interface. Such objects are capable of reading and/or writing their own content via the relevant interface method:

void read  (IReader input);
void write (IWriter output);

The interfaces involved are IReadable and IWritable, each of which declares one method relating to their behavior. Compatible objects are read & written in a manner similar to native data types. Following on from the prior example:

class  Wumpus : IReadable, IWritable 
{
    void read  (IReader input) {...}
    void write (IWriter output) {...}
}

auto wumpus = new Wumpus;
output (wumpus);
input  (wumpus);

Serialization of structs is handled in a similar manner, by submitting a delegate instead of an object:

struct Wombat 
{
    void read  (IReader input) {...}
    void write (IWriter output) {...}
}

Wombat wombat;

output (&wombat.write);
input  (&wombat.read);

Protocol Exceptions

Protocols generally expect data to be available when an application asks for it. Thus, exceptions are thrown on unexpected end-of-flow conditions. This is quite unlike direct conduit IO, which is stream oriented rather than element oriented.