| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504 | /******************************************************************************* copyright: Copyright (c) 2007 Kris Bell. All rights reserved license: BSD style: $(LICENSE) version: Initial release: Oct 2007 author: Kris These classes represent a simple means of reading and writing discrete data types as binary values, with an option to invert the endian order of numeric values. Arrays are treated as untyped byte streams, with an optional length-prefix, and should otherwise be explicitly managed at the application level. We'll add additional support for arrays and aggregates in future. *******************************************************************************/ module tango.io.stream.Data; private import tango.core.ByteSwap; private import tango.io.device.Conduit; private import tango.io.stream.Buffered; version = DataIntArrayLength; /******************************************************************************* A simple way to read binary data from an arbitrary InputStream, such as a file: --- auto input = new DataInput (new File ("path")); auto x = input.int32; auto y = input.float64; auto l = input.read (buffer); // read raw data directly auto s = cast(char[]) input.array; // read length, allocate space input.close; --- *******************************************************************************/ class DataInput : InputFilter { public alias array get; /// Old name aliases. public alias boolean getBool; /// ditto public alias int8 getByte; /// ditto public alias int16 getShort; /// ditto public alias int32 getInt; /// ditto public alias int64 getLong; /// ditto public alias float32 getFloat; /// ditto public alias float64 getDouble; /// ditto /// Endian variations. public enum { Native = 0, /// Network = 1, /// Big = 1, /// Little = 2 /// } private bool flip; protected InputStream input; private Allocate allocator; private alias void[] delegate (size_t) Allocate; /*********************************************************************** Propagate ctor to superclass. ***********************************************************************/ this (InputStream stream) { super (input = BufferedInput.create (stream)); allocator = (size_t bytes){return new void[bytes];}; } /*********************************************************************** Set the array allocator. ***********************************************************************/ final DataInput allocate (Allocate allocate) { allocator = allocate; return this; } /*********************************************************************** Set current endian translation. ***********************************************************************/ final DataInput endian (int e) { version (BigEndian) flip = e is Little; else flip = e is Network; return this; } /*********************************************************************** Read an array back into a user-provided workspace. The space must be sufficiently large enough to house all of the array, and the actual number of bytes is returned. Note that the size of the array is written as an integer prefixing the array content itself. Use read(void[]) to eschew this prefix. ***********************************************************************/ final size_t array (void[] dst) { version(DataIntArrayLength) auto len = cast(size_t)int32(); else auto len = cast(size_t)int64(); if (len > dst.length) conduit.error ("DataInput.readArray :: dst array is too small"); eat (dst.ptr, len); return len; } /*********************************************************************** Read an array back from the source, with the assumption it has been written using DataOutput.put() or otherwise prefixed with an integer representing the total number of bytes within the array content. That's *bytes*, not elements. An array of the appropriate size is allocated either via the provided delegate, or from the heap, populated and returned to the caller. Casting the return value to an appropriate type will adjust the number of elements as required: --- auto text = cast(char[]) input.get; --- ***********************************************************************/ final void[] array () { version(DataIntArrayLength) auto len = cast(size_t)int32(); else auto len = cast(size_t)int64(); auto dst = allocator (len); eat (dst.ptr, len); return dst; } /*********************************************************************** ***********************************************************************/ final bool boolean () { bool x; eat (&x, x.sizeof); return x; } /*********************************************************************** ***********************************************************************/ final byte int8 () { byte x; eat (&x, x.sizeof); return x; } /*********************************************************************** ***********************************************************************/ final short int16 () { short x; eat (&x, x.sizeof); if (flip) ByteSwap.swap16(&x, x.sizeof); return x; } /*********************************************************************** ***********************************************************************/ final int int32 () { int x; eat (&x, x.sizeof); if (flip) ByteSwap.swap32(&x, x.sizeof); return x; } /*********************************************************************** ***********************************************************************/ final long int64 () { long x; eat (&x, x.sizeof); if (flip) ByteSwap.swap64(&x, x.sizeof); return x; } /*********************************************************************** ***********************************************************************/ final float float32 () { float x; eat (&x, x.sizeof); if (flip) ByteSwap.swap32(&x, x.sizeof); return x; } /*********************************************************************** ***********************************************************************/ final double float64 () { double x; eat (&x, x.sizeof); if (flip) ByteSwap.swap64(&x, x.sizeof); return x; } /*********************************************************************** ***********************************************************************/ final override size_t read (void[] data) { eat (data.ptr, data.length); return data.length; } /*********************************************************************** ***********************************************************************/ private final void eat (void* dst, size_t bytes) { while (bytes) { auto i = input.read (dst [0 .. bytes]); if (i is Eof) input.conduit.error ("DataInput :: unexpected eof while reading"); bytes -= i; dst += i; } } } /******************************************************************************* A simple way to write binary data to an arbitrary OutputStream, such as a file: --- auto output = new DataOutput (new File ("path", File.WriteCreate)); output.int32 (1024); output.float64 (3.14159); output.array ("string with length prefix"); output.write ("raw array, no prefix"); output.close; --- *******************************************************************************/ class DataOutput : OutputFilter { public alias array put; /// Old name aliases. public alias boolean putBool; /// ditto public alias int8 putByte; /// ditto public alias int16 putShort; /// ditto public alias int32 putInt; /// ditto public alias int64 putLong; /// ditto public alias float32 putFloat; /// ditto public alias float64 putFloat; /// ditto /// Endian variations. public enum { Native = 0, /// Network = 1, /// Big = 1, /// Little = 2 /// } private bool flip; private OutputStream output; /*********************************************************************** Propagate ctor to superclass. ***********************************************************************/ this (OutputStream stream) { super (output = BufferedOutput.create (stream)); } /*********************************************************************** Set current endian translation. ***********************************************************************/ final DataOutput endian (int e) { version (BigEndian) flip = e is Little; else flip = e is Network; return this; } /*********************************************************************** Write an array to the target stream. Note that the size of the array is written as an integer prefixing the array content itself. Use write(void[]) to eschew this prefix. ***********************************************************************/ final ulong array (const(void)[] src) { auto len = src.length; version(DataIntArrayLength) int32 (cast(int)len); else int64 (len); output.write (src); return len; } /*********************************************************************** ***********************************************************************/ final void boolean (bool x) { eat (&x, x.sizeof); } /*********************************************************************** ***********************************************************************/ final void int8 (byte x) { eat (&x, x.sizeof); } /*********************************************************************** ***********************************************************************/ final void int16 (short x) { if (flip) ByteSwap.swap16 (&x, x.sizeof); eat (&x, x.sizeof); } /*********************************************************************** ***********************************************************************/ final void int32 (int x) { if (flip) ByteSwap.swap32 (&x, x.sizeof); eat (&x, uint.sizeof); } /*********************************************************************** ***********************************************************************/ final void int64 (long x) { if (flip) ByteSwap.swap64 (&x, x.sizeof); eat (&x, x.sizeof); } /*********************************************************************** ***********************************************************************/ final void float32 (float x) { if (flip) ByteSwap.swap32 (&x, x.sizeof); eat (&x, x.sizeof); } /*********************************************************************** ***********************************************************************/ final void float64 (double x) { if (flip) ByteSwap.swap64 (&x, x.sizeof); eat (&x, x.sizeof); } /*********************************************************************** ***********************************************************************/ final override size_t write (const(void)[] data) { eat (data.ptr, data.length); return data.length; } /*********************************************************************** ***********************************************************************/ private final void eat (in void* src, size_t bytes) { auto count = output.write (src[0..bytes]); assert (count is bytes); } } /******************************************************************************* *******************************************************************************/ debug (UnitTest) { import tango.io.device.Array; unittest { auto buf = new Array(32); auto output = new DataOutput (buf); output.array ("blah blah".dup); output.int32 (1024); auto input = new DataInput (buf); assert (input.array(new char[9]) is 9); assert (input.int32() is 1024); } } /******************************************************************************* *******************************************************************************/ debug (Data) { import tango.io.device.Array; void main() { auto buf = new Array(64); auto output = new DataOutput (buf); output.array ("blah blah"); output.int32 (1024); auto input = new DataInput (buf); assert (input.array.length is 9); assert (input.int32 is 1024); } } |