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 }