1 /*******************************************************************************
2 
3         These classes represent a simple means of reading and writing
4         discrete data types as binary values, with an option to invert
5         the endian order of numeric values.
6 
7         Arrays are treated as untyped byte streams, with an optional
8         length-prefix, and should otherwise be explicitly managed at
9         the application level. We'll add additional support for arrays
10         and aggregates in future.
11 
12         Copyright:
13             Copyright (c) 2007 Kris Bell.
14             Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH.
15             All rights reserved.
16 
17         License:
18             Tango Dual License: 3-Clause BSD License / Academic Free License v3.0.
19             See LICENSE_TANGO.txt for details.
20 
21         Version: Initial release: Oct 2007
22 
23         Authors: Kris
24 
25 *******************************************************************************/
26 
27 module ocean.io.stream.Data;
28 
29 import ocean.meta.types.Qualifiers;
30 
31 import ocean.core.Verify;
32 
33 import ocean.core.ByteSwap;
34 
35 import ocean.io.device.Conduit;
36 
37 import ocean.io.stream.Buffered;
38 
39 version (unittest) import ocean.core.Test;
40 
41 /*******************************************************************************
42 
43         A simple way to read binary data from an arbitrary InputStream,
44         such as a file:
45         ---
46         auto input = new DataInput (new File ("path"));
47         auto x = input.int32;
48         auto y = input.float64;
49         auto l = input.read (buffer);           // read raw data directly
50         auto s = cast(char[]) input.array;      // read length, allocate space
51         input.close;
52         ---
53 
54 *******************************************************************************/
55 
56 class DataInput : InputFilter
57 {
58         public alias array     get;             /// Old name aliases.
59         public alias boolean   getBool;         /// ditto
60         public alias int8      getByte;         /// ditto
61         public alias int16     getShort;        /// ditto
62         public alias int32     getInt;          /// ditto
63         public alias int64     getLong;         /// ditto
64         public alias float32   getFloat;        /// ditto
65         public alias float64   getDouble;       /// ditto
66 
67         /// Endian variations.
68         public enum
69         {
70                 Native  = 0, ///
71                 Network = 1, ///
72                 Big     = 1, ///
73                 Little  = 2  ///
74         }
75 
76         private bool            flip;
77         protected InputStream   input;
78         private Allocate        allocator;
79 
80         private alias void[] delegate (uint) Allocate;
81 
82         /***********************************************************************
83 
84                 Propagate ctor to superclass.
85 
86         ***********************************************************************/
87 
88         this (InputStream stream)
89         {
90                 super (input = BufferedInput.create (stream));
91 
92                 allocator = (uint bytes){return new void[bytes];};
93         }
94 
95         /***********************************************************************
96 
97                 Set the array allocator.
98 
99         ***********************************************************************/
100 
101         final DataInput allocate (Allocate allocate)
102         {
103                 allocator = allocate;
104                 return this;
105         }
106 
107         /***********************************************************************
108 
109                 Set current endian translation.
110 
111         ***********************************************************************/
112 
113         final DataInput endian (int e)
114         {
115                 version (BigEndian)
116                          flip = e is Little;
117                    else
118                       flip = e is Network;
119                 return this;
120         }
121 
122         /***********************************************************************
123 
124                 Read an array back into a user-provided workspace. The
125                 space must be sufficiently large enough to house all of
126                 the array, and the actual number of bytes is returned.
127 
128                 Note that the size of the array is written as an integer
129                 prefixing the array content itself.  Use read(void[]) to
130                 eschew this prefix.
131 
132         ***********************************************************************/
133 
134         final uint array (void[] dst)
135         {
136                 auto len = int32;
137                 if (len > dst.length)
138                     conduit.error ("DataInput.readArray :: dst array is too small");
139                 eat (dst.ptr, len);
140                 return len;
141         }
142 
143         /***********************************************************************
144 
145                 Read an array back from the source, with the assumption
146                 it has been written using DataOutput.put() or otherwise
147                 prefixed with an integer representing the total number
148                 of bytes within the array content. That's *bytes*, not
149                 elements.
150 
151                 An array of the appropriate size is allocated either via
152                 the provided delegate, or from the heap, populated and
153                 returned to the caller. Casting the return value to an
154                 appropriate type will adjust the number of elements as
155                 required:
156                 ---
157                 auto text = cast(char[]) input.get;
158                 ---
159 
160         ***********************************************************************/
161 
162         final void[] array ()
163         {
164                 auto len = int32;
165                 auto dst = allocator (len);
166                 eat (dst.ptr, len);
167                 return dst;
168         }
169 
170         /***********************************************************************
171 
172         ***********************************************************************/
173 
174         final bool boolean ()
175         {
176                 bool x;
177                 eat (&x, x.sizeof);
178                 return x;
179         }
180 
181         /***********************************************************************
182 
183         ***********************************************************************/
184 
185         final byte int8 ()
186         {
187                 byte x;
188                 eat (&x, x.sizeof);
189                 return x;
190         }
191 
192         /***********************************************************************
193 
194         ***********************************************************************/
195 
196         final short int16 ()
197         {
198                 short x;
199                 eat (&x, x.sizeof);
200                 if (flip)
201                     ByteSwap.swap16(&x, x.sizeof);
202                 return x;
203         }
204 
205         /***********************************************************************
206 
207         ***********************************************************************/
208 
209         final int int32 ()
210         {
211                 int x;
212                 eat (&x, x.sizeof);
213                 if (flip)
214                     ByteSwap.swap32(&x, x.sizeof);
215                 return x;
216         }
217 
218         /***********************************************************************
219 
220         ***********************************************************************/
221 
222         final long int64 ()
223         {
224                 long x;
225                 eat (&x, x.sizeof);
226                 if (flip)
227                     ByteSwap.swap64(&x, x.sizeof);
228                 return x;
229         }
230 
231         /***********************************************************************
232 
233         ***********************************************************************/
234 
235         final float float32 ()
236         {
237                 float x;
238                 eat (&x, x.sizeof);
239                 if (flip)
240                     ByteSwap.swap32(&x, x.sizeof);
241                 return x;
242         }
243 
244         /***********************************************************************
245 
246         ***********************************************************************/
247 
248         final double float64 ()
249         {
250                 double x;
251                 eat (&x, x.sizeof);
252                 if (flip)
253                     ByteSwap.swap64(&x, x.sizeof);
254                 return x;
255         }
256 
257         /***********************************************************************
258 
259         ***********************************************************************/
260 
261         final override size_t read (void[] data)
262         {
263                 eat (data.ptr, data.length);
264                 return data.length;
265         }
266 
267         /***********************************************************************
268 
269         ***********************************************************************/
270 
271         private final void eat (void* dst, size_t bytes)
272         {
273                 while (bytes)
274                       {
275                       auto i = input.read (dst [0 .. bytes]);
276                       if (i is Eof)
277                           input.conduit.error ("DataInput :: unexpected eof while reading");
278                       bytes -= i;
279                       dst += i;
280                       }
281          }
282 }
283 
284 
285 /*******************************************************************************
286 
287         A simple way to write binary data to an arbitrary OutputStream,
288         such as a file:
289         ---
290         auto output = new DataOutput (new File ("path", File.WriteCreate));
291         output.int32   (1024);
292         output.float64 (3.14159);
293         output.array   ("string with length prefix");
294         output.write   ("raw array, no prefix");
295         output.close;
296         ---
297 
298 *******************************************************************************/
299 
300 class DataOutput : OutputFilter
301 {
302         public alias array      put;            /// Old name aliases.
303         public alias boolean    putBool;        /// ditto
304         public alias int8       putByte;        /// ditto
305         public alias int16      putShort;       /// ditto
306         public alias int32      putInt;         /// ditto
307         public alias int64      putLong;        /// ditto
308         public alias float32    putFloat;       /// ditto
309         public alias float64    putFloat;       /// ditto
310 
311         /// Endian variations.
312         public enum
313         {
314                 Native  = 0, ///
315                 Network = 1, ///
316                 Big     = 1, ///
317                 Little  = 2  ///
318         }
319 
320         private bool            flip;
321         private OutputStream    output;
322 
323         /***********************************************************************
324 
325                 Propagate ctor to superclass.
326 
327         ***********************************************************************/
328 
329         this (OutputStream stream)
330         {
331                 super (output = BufferedOutput.create (stream));
332         }
333 
334         /***********************************************************************
335 
336                 Set current endian translation.
337 
338         ***********************************************************************/
339 
340         final DataOutput endian (int e)
341         {
342                 version (BigEndian)
343                          flip = e is Little;
344                    else
345                       flip = e is Network;
346                 return this;
347         }
348 
349         /***********************************************************************
350 
351                 Write an array to the target stream. Note that the size
352                 of the array is written as an integer prefixing the array
353                 content itself. Use write(void[]) to eschew this prefix.
354 
355         ***********************************************************************/
356 
357         final uint array (const(void)[] src)
358         {
359                 auto len = src.length;
360                 int32 (cast(int) len);
361                 output.write (src);
362                 return cast(uint) len;
363         }
364 
365         /***********************************************************************
366 
367         ***********************************************************************/
368 
369         final void boolean (bool x)
370         {
371                 eat (&x, x.sizeof);
372         }
373 
374         /***********************************************************************
375 
376         ***********************************************************************/
377 
378         final void int8 (byte x)
379         {
380                 eat (&x, x.sizeof);
381         }
382 
383         /***********************************************************************
384 
385         ***********************************************************************/
386 
387         final void int16 (short x)
388         {
389                 if (flip)
390                     ByteSwap.swap16 (&x, x.sizeof);
391                 eat (&x, x.sizeof);
392         }
393 
394         /***********************************************************************
395 
396         ***********************************************************************/
397 
398         final void int32 (int x)
399         {
400                 if (flip)
401                     ByteSwap.swap32 (&x, x.sizeof);
402                 eat (&x, uint.sizeof);
403         }
404 
405         /***********************************************************************
406 
407         ***********************************************************************/
408 
409         final void int64 (long x)
410         {
411                 if (flip)
412                     ByteSwap.swap64 (&x, x.sizeof);
413                 eat (&x, x.sizeof);
414         }
415 
416         /***********************************************************************
417 
418         ***********************************************************************/
419 
420         final void float32 (float x)
421         {
422                 if (flip)
423                     ByteSwap.swap32 (&x, x.sizeof);
424                 eat (&x, x.sizeof);
425         }
426 
427         /***********************************************************************
428 
429         ***********************************************************************/
430 
431         final void float64 (double x)
432         {
433                 if (flip)
434                     ByteSwap.swap64 (&x, x.sizeof);
435                 eat (&x, x.sizeof);
436         }
437 
438         /***********************************************************************
439 
440         ***********************************************************************/
441 
442         final override size_t write (const(void)[] data)
443         {
444                 eat (data.ptr, data.length);
445                 return data.length;
446         }
447 
448         /***********************************************************************
449 
450         ***********************************************************************/
451 
452         private final void eat (const(void)* src, size_t bytes)
453         {
454                 auto count = output.write (src[0..bytes]);
455                 verify(count == bytes);
456         }
457 }
458 
459 
460 /*******************************************************************************
461 
462 *******************************************************************************/
463 
464 version (unittest)
465 {
466     import ocean.io.device.Array;
467 }
468 
469 unittest
470 {
471     auto buf = new Array(32);
472 
473     auto output = new DataOutput (buf);
474     output.array ("blah blah");
475     output.int32 (1024);
476 
477     auto input = new DataInput (buf);
478     test (input.array(new char[9]) is 9);
479     test (input.int32 is 1024);
480 }