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 }