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