1 module binary.reader;
2 
3 import std.algorithm;
4 import std.array;
5 import std.range;
6 import std.traits;
7 import binary.common;
8 
9 
10 
11 /**
12  * Creates new instance of Binary reader.
13  * 
14  * Params:
15  *  range = Input range to read from
16  *  order = Byte order to use
17  */
18 auto binaryReader(Range)(auto ref Range range, ByteOrder order = ByteOrder.Native)
19 {
20 	return BinaryReader!(Range)(range, order);
21 }
22 
23 
24 /**
25  * Reads binary encoded data.
26  */
27 struct BinaryReader(Range = ubyte[])
28 	if(isInputRange!Range && is(ElementType!Range == ubyte))
29 {
30 	/**
31 	 * InputRange of ubytes to read from.
32 	 */
33 	private Range _source;
34 
35 	
36 	/**
37 	 * Current position in stream.
38 	 */
39 	private ulong _position;
40 
41 	
42 	/**
43 	 * Used byte order
44 	 */
45 	ByteOrder byteOrder;
46 
47 	
48 
49 	/**
50 	 * Creates instance of BinaryReader.
51 	 * 
52 	 * Params:
53 	 *  source = Range of ubytes to read from.
54 	 */
55 	this(ref Range source, ByteOrder byteOrder = ByteOrder.Native)
56 	{
57 		_source 	= source;
58 		_position   = 0;
59 		this.byteOrder  = byteOrder;
60 	}
61 
62 	
63 	/**
64 	 * Creates instance of BinaryReader.
65 	 * 
66 	 * Params:
67 	 *  source = Range of ubytes to read from.
68 	 */
69 	/*this(Range source, ByteOrder byteOrder = ByteOrder.Native)
70 	 {
71 	 this(source, byteOrder);
72 	 }*/
73 
74 	
75 	/**
76 	 * Skips `num` bytes from source.
77 	 * 
78 	 * Params:
79 	 *  num = Number of bytes to skip.
80 	 */
81 	void skipBytes(size_t num)
82 	{
83 		_source.popFrontN(num);
84 	}
85 
86 	
87 	/**
88 	 * Moves cursor to specified position.
89 	 * 
90 	 * If specified position is behind current cursor position, 
91 	 * nothing happens.
92 	 * 
93 	 * Params:
94 	 *  offset = Offset to align to
95 	 */
96 	void skipTo(size_t offset)
97 	{
98 		if (cast(long)(offset - _position) < 0)
99 			return;
100 
101 		skipBytes(cast(size_t)(offset - _position));
102 	}
103 
104 	
105 	/**
106 	 * Reads specified value from input stream.
107 	 * 
108 	 * If there is insufficient data in input stream, DecodeException is thrown.
109 	 * 
110 	 * Throws:
111 	 *  DecodeException
112 	 * 
113 	 * Params:
114 	 *  value = Value to read to
115 	 */
116 	void read(T)(ref T value)
117 	{
118 		if (empty)
119 			throw new DecodeException("Input stream is empty.");
120 
121 		static if (isStaticArray!T) {
122 			foreach(ref el; value)
123 				read!(ElementEncodingType!T)(el);
124 		}
125 		else static if (isDynamicArray!T) {
126 			uint size;
127 			read(size);
128 			value.length = cast(size_t)size;
129 			readArray!(ElementEncodingType!T)(value, value.length);
130 		}
131 		else {
132 			value = decodeBinary!T(readBytes(T.sizeof), byteOrder);
133 		}
134 	}
135 
136 
137 	/**
138 	 * Reads array with `length` elements.
139 	 * 
140 	 * Throws:
141 	 *  DecodeException
142 	 * 
143 	 * Params:
144 	 *  arr = Array to read to.
145 	 *  length = Number of elements to read.
146 	 */
147 	void readArray(T)(ref T[] arr, size_t length)
148 	{
149 		arr.length = length;
150 		for(size_t i=0; i<length; i++) {
151 			read!T(arr[i]);
152 		}
153 	}
154 
155 
156 	/**
157 	 * Reads string into `str`.
158 	 * 
159 	 * Reads until null terminator. If not found, DecodeException is thrown.
160 	 * 
161 	 * Throws:
162 	 *  DecodeException
163 	 * 
164 	 * Params:
165 	 *  str = String to read to
166 	 */
167 	void readString(T)(ref T str)
168 		if (isSomeString!T)
169 	{
170 		char[] data = cast(char[])readUntil(0);
171 		str = cast(T)data;
172 	}
173 
174 	
175 	/**
176 	 * Reads T type from stream and returns it.
177 	 * 
178 	 * If there is insufficient data in input stream, DecodeException is thrown.
179 	 * 
180 	 * Throws:
181 	 *  DecodeException
182 	 * 
183 	 * Returns:
184 	 *  Read value of type T.
185 	 */
186 	T read(T)()
187 	{
188 		T value;
189 		read!T(value);
190 		return value;
191 	}
192 
193 	/**
194 	 * Reads array of type T with `num` elemetns and returns it.
195 	 * 
196 	 * Throws:
197 	 *  DecodeException
198 	 * 
199 	 * Returns:
200 	 *  Array with `num` elements.
201 	 */
202 	T[] readArray(T)(size_t num)
203 	{
204 		T[] arr = new T[num];
205 		readArray(arr, num);
206 		return arr;
207 	}
208 
209 
210 	/**
211 	 * Reads string and returns it.
212 	 * 
213 	 * See_Also:
214 	 *  `readString`
215 	 * 
216 	 * Returns:
217 	 *  Read string.
218 	 */
219 	string readString()
220 	{
221 		string str;
222 		readString(str);
223 		return str;
224 	}
225 
226 	
227 	/**
228 	 * Reads specified values from input stream.
229 	 * 
230 	 * Throws:
231 	 *  DecodeException
232 	 * 
233 	 * Params:
234 	 *  value = Tuple of values to read to
235 	 */
236 	void read(T...)(ref T values)
237 		if(T.length > 1)
238 	{
239 		foreach(ref value; values)
240 			read(value);
241 	}
242 	
243 	
244 	/**
245 	 * Determines if input stream is empty.
246 	 */
247 	bool empty()
248 	{
249 		return source.empty;
250 	}
251 
252 	
253 	/**
254 	 * Clears source range and position.
255 	 */
256 	void clear()
257 	{
258 		_source = _source.init;
259 		_position = 0;
260 	}
261 
262 	
263 	/**
264 	 * Reads array of bytes from input stream.
265 	 * 
266 	 * Returned array may be smaller than requested if end
267 	 * of input occured.
268 	 * 
269 	 * Params:
270 	 *  bytes = Number of bytes to read
271 	 * 
272 	 * Returns:
273 	 *  Array of bytes read
274 	 */
275 	ubyte[] readBytes(size_t bytes)
276 	{
277 		ubyte[] arr = _source.take(bytes).array;
278 		
279 		static if (isForwardRange!Range)
280 			_source.popFrontN(bytes);
281 
282 		_position += arr.length;
283 
284 		return arr;
285 	}
286 	
287 
288 	/**
289 	 * Reads bytes until `stop` is found.
290 	 * 
291 	 * Returned array can be empty if input stream is empty.
292 	 * 
293 	 * Params:
294 	 *  stop  = Value to read until
295 	 *  next  = If true, input stream is moved to next byte.
296 	 * 
297 	 * Returns:
298 	 *  Array of bytes. 
299 	 */
300 	ubyte[] readUntil(ubyte stop, bool next = true)
301 	{
302 		ubyte[] arr = _source.until(0).array;
303 		
304 		static if(isForwardRange!Range)
305 			_source.popFrontN(arr.length);
306 		
307 		if (!empty && next && _source.front == 0) {
308 			_source.popFront();
309 			_position += 1;
310 		}
311 
312 		_position += arr.length;
313 
314 		return arr;
315 	}
316 
317 	
318 	/**
319 	 * Gets source range used.
320 	 */
321 	Range source() @property
322 	{
323 		return _source;
324 	}
325 
326 	
327 	/**
328 	 * Sets new source range to read from.
329 	 */
330 	void source(ref Range source) @property
331 	{
332 		_position = 0;
333 		_source = source;
334 	}
335 
336 	
337 	/**
338 	 * Sets new source range to read from.
339 	 */
340 	void source(Range source) @property
341 	{
342 		_position = 0;
343 		_source = source;
344 	}
345 
346 	
347 	/**
348 	 * Gets current position in stream.
349 	 */
350 	ulong position() @property
351 	{
352 		return _position;
353 	}
354 }
355 
356 
357 
358 unittest
359 {
360 	import std.exception;
361 
362 	short sh;
363 	auto reader = binaryReader(cast(ubyte[])[15, 0, 0, 30]);
364 	reader.byteOrder = ByteOrder.LittleEndian;
365 	reader.read(sh);
366 	assert(sh == 15);
367 	assert(!reader.empty);
368 	reader.byteOrder = ByteOrder.BigEndian;
369 	reader.read(sh);
370 	assert(sh == 30);
371 	assert(reader.empty);
372 
373 	assertThrown!DecodeException(reader.read(sh), "Input stream is empty.");
374 	reader.source = [9];
375 	assertThrown!DecodeException(reader.read(sh), 
376 	                             "Unexpected end of input stream. Trying to read 2 bytes (type short), but got 1.");
377 
378 	char[] str;
379 	reader.source = ['a', 'b', 'c'];
380 	reader.readArray(str, cast(size_t)3);
381 	assert(str == "abc".dup);
382 	assert(reader.empty);
383 
384 	reader.source = ['x', 'y', 'z', 0, 90, 0];
385 	reader.byteOrder = ByteOrder.LittleEndian;
386 	reader.readString(str);
387 	reader.read(sh);
388 	assert(str == "xyz".dup);
389 	assert(sh  == 90);
390 	assert(reader.empty);
391 
392 	reader.clear();
393 	assert(reader.empty);
394 	assert(reader.position == 0);
395 
396 	long l;
397 	reader.source = [1, 56, 0, 0, 0,  0, 0, 0, 0];
398 	assert(!reader.empty);
399 	assert(reader.source.front == 1);
400 	reader.skipBytes(1);
401 	assert(reader.source.front == 56);
402 	assert(!reader.empty);
403 	reader.read(l);
404 	assert(l == 56);
405 	assert(reader.empty);
406 
407 	reader.source = ['a', 'b', 'c', 0,  0, 0, 0, 0, 15, 0];
408 	assert(reader.position == 0);
409 	assert(reader.readString == "abc".dup);
410 	assert(reader.source == [0, 0, 0, 0, 15, 0]);
411 	reader.skipTo(8);
412 	assert(reader.source == [15, 0]);
413 	assert(reader.read!short == 15);
414 
415 	reader.source = [10, 20, 30, 40];
416 	assert(reader.readArray!byte(4) == [10, 20, 30, 40]);
417 
418 	reader.byteOrder = ByteOrder.BigEndian;
419 	reader.source = [0, 0, 0, 3, 0, 99, 0, 55, 0, 44];
420 	assert(reader.read!(ushort[])() == [99, 55, 44]);
421 
422 	reader.source = [0, 0, 0, 5, 'H', 'e', 'l', 'l', 'o'];
423 	reader.read(str);
424 	assert(str == "Hello".dup);
425 }