1 module xbuffer.buffer;
2 
3 import std.bitmanip : swapEndian;
4 import std.string : toUpper;
5 import std.system : Endian, endian;
6 import std.traits : isArray, isBoolean, isIntegral, isFloatingPoint, isSomeChar, Unqual;
7 
8 import xbuffer.memory : xmalloc, xrealloc, xfree;
9 import xbuffer.varint : isVar, Var;
10 
11 //TODO remove
12 import std.stdio : writeln;
13 
14 alias ForeachType(T) = typeof(T.init[0]);
15 
16 enum canSwapEndianness(T) = isBoolean!T || isIntegral!T || isFloatingPoint!T || isSomeChar!T || (is(T == struct) && canSwapEndiannessImpl!T);
17 
18 private bool canSwapEndiannessImpl(T)() {
19 	static if(is(T == struct)) {
20 		import std.traits : Fields;
21 		bool ret = true;
22 		foreach(field ; Fields!T) {
23 			if(!canSwapEndianness!field) ret = false;
24 		}
25 		return ret;
26 	} else {
27 		return false;
28 	}
29 }
30 
31 unittest {
32 	
33 	static assert(canSwapEndianness!byte);
34 	static assert(canSwapEndianness!int);
35 	static assert(canSwapEndianness!double);
36 	static assert(canSwapEndianness!char);
37 	static assert(canSwapEndianness!dchar);
38 	
39 }
40 
41 unittest {
42 	
43 	static struct A {}
44 	
45 	static struct B { int a, b; }
46 	
47 	static class C {}
48 	
49 	static struct D { int a; B b; }
50 	
51 	static struct E { B b; C c; }
52 	
53 	static struct F { void a(){} float b; }
54 	
55 	static struct G { @property Object a(){ return null; } }
56 	
57 	static struct H { ubyte[] a; }
58 	
59 	static struct I { int* a; }
60 	
61 	static assert(canSwapEndianness!A);
62 	static assert(canSwapEndianness!B);
63 	static assert(!canSwapEndianness!C);
64 	static assert(canSwapEndianness!D);
65 	static assert(!canSwapEndianness!E);
66 	static assert(canSwapEndianness!F);
67 	static assert(canSwapEndianness!G);
68 	static assert(!canSwapEndianness!H);
69 	static assert(!canSwapEndianness!I);
70 	
71 }
72 
73 private union EndianSwapper(T) if(canSwapEndianness!T) {
74 	
75 	enum builtInSwap = T.sizeof == 2 || T.sizeof == 4 || T.sizeof == 8;
76 	
77 	T value;
78 	void[T.sizeof] data;
79 	ubyte[T.sizeof] bytes;
80 
81 	this(T value) {
82 		this.value = value;
83 	}
84 
85 	this(void[] data) {
86 		this.data = data;
87 	}
88 	
89 	static if(T.sizeof == 2) ushort _swap;
90 	else static if(T.sizeof == 4) uint _swap;
91 	else static if(T.sizeof == 8) ulong _swap;
92 	
93 	void swap() {
94 		static if(builtInSwap) _swap = swapEndian(_swap);
95 		else static if(T.sizeof > 1) {
96 			import std.algorithm.mutation : swap;
97 			foreach(i ; 0..T.sizeof>>1) {
98 				swap(bytes[i], bytes[T.sizeof-i-1]);
99 			}
100 		}
101 	}
102 	
103 }
104 
105 unittest {
106 	
107 	static struct Test {
108 		
109 		int a, b, c;
110 		
111 	}
112 	
113 	static assert(Test.sizeof == 12);
114 	
115 	EndianSwapper!Test swapper;
116 	swapper.value = Test(1, 2, 3);
117 	
118 	version(BigEndian) assert(swapper.bytes == [0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3]);
119 	version(LittleEndian) assert(swapper.bytes == [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0]);
120 	
121 	swapper.swap();
122 	
123 	version(BigEndian) assert(swapper.bytes == [3, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0]);
124 	version(LittleEndian) assert(swapper.bytes == [0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 1]);
125 	
126 	assert(swapper.value == Test(3 << 24, 2 << 24, 1 << 24));
127 	
128 }
129 
130 /**
131  * Exception thrown when the buffer cannot read the requested
132  * data.
133  */
134 class BufferOverflowException : Exception {
135 	
136 	this(string file=__FILE__, size_t line=__LINE__) pure nothrow @safe @nogc {
137 		super("The buffer's index has exceeded its length", file, line);
138 	}
139 	
140 }
141 
142 static if(__traits(compiles, () @nogc { throw new Exception(""); })) version = DIP1008;
143 
144 /**
145  * Buffer for writing and reading binary data.
146  */
147 class Buffer {
148 	
149 	private immutable size_t chunk;
150 	
151 	private void[] _data;
152 	private size_t _rindex, _windex;
153 	
154 	/**
155 	 * Creates a buffer specifying the chunk size.
156 	 * This should be the default constructor for re-used buffers and
157 	 * input buffers.
158 	 */
159 	this(size_t chunk) pure nothrow @trusted @nogc {
160 		this.chunk = chunk == 0 ? 1 : chunk;
161 		//_data = malloc(this.chunk);
162 		_data = xrealloc(_data.ptr, this.chunk);
163 	}
164 	
165 	///
166 	pure nothrow @safe unittest {
167 		
168 		Buffer buffer = new Buffer(8);
169 		
170 		// 8 bytes allocated by the constructor
171 		assert(buffer.capacity == 8);
172 		
173 		// writing 4 bytes does not alter the capacity
174 		buffer.write(0);
175 		assert(buffer.capacity == 8);
176 		
177 		// writing 8 bytes requires a new allocation (because 4 + 8 is
178 		// higher than the current capacity of 8).
179 		// The new capacity is rounded up to the nearest multiple of the chunk size.
180 		buffer.write(0L);
181 		assert(buffer.capacity == 16);
182 		
183 	}
184 	
185 	/**
186 	 * Creates a buffer from an array of data.
187 	 * The chunk size is set to the size of the array.
188 	 */
189 	this(T)(in T[] data...) pure nothrow @trusted @nogc if(canSwapEndianness!T || is(T == void)) {
190 		this(data.length * T.sizeof);
191 		_windex = _data.length;
192 		_data[0..$] = cast(void[])data;
193 	}
194 	
195 	///
196 	pure nothrow @safe unittest {
197 
198 		Buffer buffer = new Buffer(cast(ubyte[])[1, 2, 3, 4]);
199 		assert(buffer.rindex == 0);
200 		assert(buffer.windex == 4);
201 
202 		buffer = new Buffer([1, 2]);
203 		assert(buffer.rindex == 0);
204 		assert(buffer.windex == 8);
205 		
206 	}
207 	
208 	private void resize(size_t requiredSize) pure nothrow @trusted @nogc {
209 		immutable rem = requiredSize / chunk;
210 		immutable size = (requiredSize + chunk - 1) / chunk * chunk;
211 		_data = xrealloc(_data.ptr, size);
212 	}
213 
214 	@property size_t rindex() pure nothrow @safe @nogc {
215 		return _rindex;
216 	}
217 
218 	@property size_t windex() pure nothrow @safe @nogc {
219 		return _windex;
220 	}
221 	
222 	@property T[] data(T=void)() pure nothrow @trusted @nogc if((canSwapEndianness!T || is(T == void)) && T.sizeof == 1) {
223 		return cast(T[])_data[_rindex.._windex];
224 	}
225 
226 	@property T[] data(T)() pure nothrow @nogc if(canSwapEndianness!T && T.sizeof != 1) {
227 		return cast(T[])_data[_rindex.._windex];
228 	}
229 	
230 	/**
231 	 * Sets new data and resets the index.
232 	 */
233 	@property auto data(T)(in T[] data) pure nothrow @trusted @nogc {
234 		_rindex = 0;
235 		_windex = data.length * T.sizeof;
236 		if(_windex > _data.length) this.resize(_windex);
237 		_data[0.._windex] = cast(void[])data;
238 		return data;
239 	}
240 	
241 	///
242 	pure nothrow @trusted unittest {
243 		
244 		Buffer buffer = new Buffer(2);
245 
246 		buffer.data = cast(ubyte[])[0, 0, 0, 1];
247 		assert(buffer.rindex == 0); // resetted when setting new data
248 		assert(buffer.windex == 4);
249 		version(BigEndian) assert(buffer.data!uint == [1]);
250 		version(LittleEndian) assert(buffer.data!uint == [1 << 24]);
251 
252 		buffer.data = "hello";
253 		assert(buffer.rindex == 0);
254 		assert(buffer.windex == 5);
255 		assert(buffer.data == "hello");
256 		
257 	}
258 	
259 	/**
260 	 * Gets the current write/read index of the buffer.
261 	 * The index can be set to 0 using the `reset` method.
262 	 */
263 	deprecated("Use rindex instead") @property size_t index() pure nothrow @safe @nogc {
264 		return _rindex;
265 	}
266 	
267 	/**
268 	 * Gets the length of the buffer.
269 	 */
270 	deprecated("Use windex instead") @property size_t length() pure nothrow @safe @nogc {
271 		return _windex;
272 	}
273 	
274 	/**
275 	 * Resets the buffer setting the index and its length to 0.
276 	 */
277 	void reset() pure nothrow @safe @nogc {
278 		_rindex = 0;
279 		_windex = 0;
280 	}
281 	
282 	/**
283 	 * Gets the size of the data allocated by the buffer.
284 	 */
285 	@property size_t capacity() pure nothrow @safe @nogc {
286 		return _data.length;
287 	}
288 
289 	void back(size_t amount) pure nothrow @safe @nogc {
290 		assert(amount <= _rindex);
291 		_rindex -= amount;
292 	}
293 
294 	// ----------
295 	// operations
296 	// ----------
297 
298 	/**
299 	 * Check whether the data of the buffer is equals to
300 	 * the given array.
301 	 */
302 	bool opEquals(T)(in T[] data) pure nothrow @safe @nogc if(T.sizeof == 1) {
303 		return this.data!T == data;
304 	}
305 
306 	/// ditto
307 	bool opEquals(T)(in T[] data) pure nothrow @nogc if(T.sizeof != 1) {
308 		return this.data!T == data;
309 	}
310 
311 	///
312 	pure nothrow @trusted unittest {
313 
314 		Buffer buffer = new Buffer([1, 2, 3]);
315 
316 		assert(buffer == [1, 2, 3]);
317 		version(BigEndian) assert(buffer == cast(ubyte[])[0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3]);
318 		version(LittleEndian) assert(buffer == cast(ubyte[])[1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0]);
319 
320 	}
321 	
322 	// -----
323 	// write
324 	// -----
325 	
326 	private void need(size_t size) pure nothrow @safe @nogc {
327 		size += _windex;
328 		if(size > this.capacity) this.resize(size);
329 	}
330 	
331 	private void writeDataImpl(in void[] data) pure nothrow @trusted @nogc {
332 		immutable start = _windex;
333 		_windex += data.length;
334 		_data[start.._windex] = data;
335 	}
336 	
337 	/**
338 	 * Writes data to the buffer and expands if it is not big enough.
339 	 */
340 	void writeData(in void[] data) pure nothrow @safe @nogc {
341 		this.need(data.length);
342 		this.writeDataImpl(data);
343 	}
344 	
345 	/**
346 	 * Writes data to buffer using the given endianness.
347 	 */
348 	void write(Endian endianness, T)(T value) pure nothrow @trusted @nogc if(canSwapEndianness!T) {
349 		EndianSwapper!T swapper = EndianSwapper!T(value);
350 		static if(endianness != endian && T.sizeof > 1) swapper.swap();
351 		this.writeData(swapper.data);
352 	}
353 	
354 	///
355 	unittest {
356 		
357 		Buffer buffer = new Buffer(4);
358 		buffer.write!(Endian.bigEndian)(4);
359 		buffer.write!(Endian.littleEndian)(4);
360 		assert(buffer.data!ubyte == [0, 0, 0, 4, 4, 0, 0, 0]);
361 		
362 	}
363 	
364 	/**
365 	 * Writes data to the buffer using the system's endianness.
366 	 */
367 	void write(T)(T value) pure nothrow @safe @nogc if(canSwapEndianness!T) {
368 		this.write!(endian, T)(value);
369 	}
370 	
371 	///
372 	pure nothrow @safe unittest {
373 		
374 		Buffer buffer = new Buffer(5);
375 		buffer.write(ubyte(5));
376 		buffer.write(10);
377 		version(BigEndian) assert(buffer.data!ubyte == [5, 0, 0, 0, 10]);
378 		version(LittleEndian) assert(buffer.data!ubyte == [5, 10, 0, 0, 0]);
379 		
380 	}
381 	
382 	/**
383 	 * Writes an array using the given endianness.
384 	 */
385 	void write(Endian endianness, T)(in T value) pure nothrow @trusted @nogc if(isArray!T && (is(ForeachType!T : void) || canSwapEndianness!(ForeachType!T))) {
386 		static if(endianness == endian || T.sizeof <= 1) {
387 			this.writeData(value);
388 		} else {
389 			this.need(value.length * ForeachType!T.sizeof);
390 			foreach(element ; value) {
391 				auto swapper = EndianSwapper!(ForeachType!T)(element);
392 				swapper.swap();
393 				this.writeDataImpl(swapper.data);
394 			}
395 		}
396 	}
397 	
398 	///
399 	pure nothrow @safe unittest {
400 		
401 		Buffer buffer = new Buffer(8);
402 		buffer.write!(Endian.bigEndian)([1, 2, 3]);
403 		assert(buffer.capacity == 16);
404 		assert(buffer.data!ubyte == [0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3]);
405 		
406 		buffer.reset();
407 		buffer.write!(Endian.littleEndian)(cast(short[])[-2, 2]);
408 		assert(buffer.data!ubyte == [254, 255, 2, 0]);
409 
410 		buffer.reset();
411 		buffer.write!(Endian.bigEndian, wstring)("test"w);
412 		assert(buffer.data!ubyte == [0, 't', 0, 'e', 0, 's', 0, 't']);
413 		
414 	}
415 	
416 	/**
417 	 * Writes an array using the system's endianness.
418 	 */
419 	void write(T)(in T value) pure nothrow @safe @nogc if(isArray!T && (is(ForeachType!T : void) || canSwapEndianness!(ForeachType!T))) {
420 		this.write!(endian, T)(value);
421 	}
422 	
423 	///
424 	pure nothrow @safe unittest {
425 		
426 		Buffer buffer = new Buffer(8);
427 		buffer.write(cast(ubyte[])[1, 2, 3, 4]);
428 		buffer.write("test");
429 		assert(buffer.data!ubyte == [1, 2, 3, 4, 't', 'e', 's', 't']);
430 		buffer.write([1, 2]);
431 		version(BigEndian) assert(buffer.data!ubyte == [1, 2, 3, 4, 't', 'e', 's', 't', 0, 0, 0, 1, 0, 0, 0, 2]);
432 		version(LittleEndian) assert(buffer.data!ubyte == [1, 2, 3, 4, 't', 'e', 's', 't', 1, 0, 0, 0, 2, 0, 0, 0]);
433 		
434 	}
435 	
436 	/**
437 	 * Writes a varint.
438 	 */
439 	void writeVar(T)(T value) pure nothrow @safe @nogc if(isIntegral!T && T.sizeof > 1) {
440 		Var!T.encode(this, value);
441 	}
442 
443 	/// ditto
444 	void write(T:Var!B, B)(B value) pure nothrow @safe @nogc {
445 		this.writeVar!(T.Base)(value);
446 	}
447 	
448 	///
449 	pure nothrow @safe unittest {
450 		
451 		import xbuffer.varint;
452 		
453 		Buffer buffer = new Buffer(8);
454 		buffer.writeVar(1);
455 		buffer.write!varuint(1);
456 		assert(buffer.data!ubyte == [2, 1]);
457 		
458 	}
459 
460 	/**
461 	 * Writes data at the given index.
462 	 */
463 	void write(alias E=endian, T)(in T value, size_t index) pure nothrow @trusted @nogc if(is(typeof(E) : Endian) && (canSwapEndianness!T || is(T == void) || isArray!T && (canSwapEndianness!(ForeachType!T) || is(ForeachType!T == void))) || is(E == struct) && isVar!E) {
464 		void[] shift = xmalloc(this.data.length - index);
465 		shift[0..$] = this.data[index..$];
466 		_windex = _rindex + index;
467 		this.write!E(value);
468 		this.writeData(shift);
469 		xfree(shift.ptr);
470 	}
471 
472 	/// ditto
473 	pure @trusted unittest {
474 
475 		import xbuffer.varint;
476 
477 		Buffer buffer = new Buffer([1, 2, 3]);
478 		buffer.write(0, 0);
479 		assert(buffer.data!int == [0, 1, 2, 3]);
480 
481 		buffer.data = cast(ubyte[])[0, 1, 2, 5];
482 		buffer.write(cast(ubyte[])[3, 4], 3);
483 		assert(buffer.data!ubyte == [0, 1, 2, 3, 4, 5]);
484 
485 		buffer.data = cast(ubyte[])[1, 1, 2];
486 		buffer.read!ubyte();
487 		buffer.write!varuint(118485, 1);
488 		assert(buffer.data!ubyte == [1, 213, 157, 7, 2]);
489 
490 		buffer.data = "hellld";
491 		buffer.write('o', 4);
492 		buffer.write(" wor", 5);
493 		assert(buffer.data == "hello world");
494 
495 	}
496 	
497 	// ----
498 	// read
499 	// ----
500 	
501 	/**
502 	 * Indicates whether an array of length `size` or the given type
503 	 * can be read without any exceptions thrown.
504 	 */
505 	bool canRead(size_t size) pure nothrow @safe @nogc {
506 		return _rindex + size <= _windex;
507 	}
508 	
509 	/// ditto
510 	bool canRead(T)() pure nothrow @safe @nogc if(canSwapEndianness!T) {
511 		return this.canRead(T.sizeof);
512 	}
513 	
514 	///
515 	unittest {
516 		
517 		import xbuffer.varint;
518 		
519 		Buffer buffer = new Buffer(cast(ubyte[])[128, 200, 3]);
520 		assert(buffer.canRead(2));
521 		assert(buffer.canRead(3));
522 		assert(!buffer.canRead(4));
523 		assert(buffer.canRead!byte());
524 		assert(buffer.canRead!short());
525 		assert(!buffer.canRead!int());
526 		
527 	}
528 
529 	/**
530 	 * Reads the amount of data requested.
531 	 * Throws: BufferOverflowException if there isn't enough data to read.
532 	 */
533 	void[] readData(size_t size) pure @safe {
534 		if(!this.canRead(size)) throw new BufferOverflowException();
535 		_rindex += size;
536 		return _data[_rindex-size.._rindex];
537 	}
538 
539 	///
540 	pure @safe unittest {
541 		
542 		Buffer buffer = new Buffer([1]);
543 		assert(buffer.read!int() == 1);
544 		try {
545 			buffer.read!int(); assert(0);
546 		} catch(BufferOverflowException ex) {
547 			assert(ex.file == __FILE__);
548 		}
549 		
550 	}
551 
552 	/**
553 	 * Reads a value with the given endianness.
554 	 * Throws: BufferOverflowException if there isn't enough data to read.
555 	 */
556 	T read(Endian endianness, T)() pure @trusted if(canSwapEndianness!T) {
557 		EndianSwapper!T swapper = EndianSwapper!T(this.readData(T.sizeof));
558 		static if(endianness != endian) swapper.swap();
559 		return swapper.value;
560 	}
561 	
562 	///
563 	pure @safe unittest {
564 		
565 		Buffer buffer = new Buffer(cast(ubyte[])[0, 0, 0, 1, 1, 0]);
566 		assert(buffer.read!(Endian.bigEndian, int)() == 1);
567 		assert(buffer.read!(Endian.littleEndian, short)() == 1);
568 		
569 	}
570 	
571 	/**
572 	 * Reads a value using the system's endianness.
573 	 * Throws: BufferOverflowException if there isn't enough data to read.
574 	 */
575 	T read(T)() pure @safe if(canSwapEndianness!T) {
576 		return this.read!(endian, T)();
577 	}
578 	
579 	///
580 	pure @safe unittest {
581 		
582 		version(BigEndian) Buffer buffer = new Buffer([0, 0, 0, 1]);
583 		version(LittleEndian) Buffer buffer = new Buffer([1, 0, 0, 0]);
584 		assert(buffer.read!int() == 1);
585 		
586 	}
587 
588 	/**
589 	 * Reads an array using the given endianness.
590 	 * Throws: BufferOverflowException if there isn't enough data to read.
591 	 */
592 	T read(Endian endianness, T)(size_t length) pure @trusted if(isArray!T && (is(ForeachType!T : void) || canSwapEndianness!(ForeachType!T))) {
593 		T ret = cast(T)this.readData(length * ForeachType!T.sizeof);
594 		static if(endianness != endian && T.sizeof != 1) {
595 			foreach(ref element ; ret) {
596 				EndianSwapper!(ForeachType!T) swapper = EndianSwapper!(ForeachType!T)(element);
597 				swapper.swap();
598 				element = swapper.value;
599 			}
600 		}
601 		return ret;
602 	}
603 
604 	///
605 	pure @safe unittest {
606 
607 		Buffer buffer = new Buffer(16);
608 
609 		buffer.write!(Endian.bigEndian)(16);
610 		buffer.write!(Endian.bigEndian)(32);
611 		buffer.write!(Endian.littleEndian)(32);
612 		buffer.write!(Endian.littleEndian)(16);
613 		assert(buffer.data!ubyte == [0, 0, 0, 16, 0, 0, 0, 32, 32, 0, 0, 0, 16, 0, 0, 0]);
614 
615 		assert(buffer.read!(Endian.bigEndian, int[])(2) == [16, 32]);
616 		assert(buffer.read!(Endian.littleEndian, int[])(2) == [32, 16]);
617 
618 	}
619 
620 	/**
621 	 * Reads an array using the system's endianness.
622 	 * Throws: BufferOverflowException if there isn't enough data to read.
623 	 */
624 	T read(T)(size_t size) pure @trusted if(isArray!T && (is(ForeachType!T : void) || canSwapEndianness!(ForeachType!T))) {
625 		return this.read!(endian, T)(size);
626 	}
627 	
628 	///
629 	pure @safe unittest {
630 		
631 		Buffer buffer = new Buffer("!hello");
632 		assert(buffer.read!(ubyte[])(1) == [33]);
633 		assert(buffer.read!string(5) == "hello");
634 
635 		buffer.data = [1, 2, 3];
636 		version(BigEndian) assert(buffer.data!ubyte == [0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3]);
637 		version(LittleEndian) assert(buffer.data!ubyte == [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0]);
638 		assert(buffer.read!(int[])(3) == [1, 2, 3]);
639 
640 	}
641 	
642 	/**
643 	 * Reads a varint.
644 	 * Throws: BufferOverflowException if there isn't enough data to read.
645 	 */
646 	T readVar(T)() pure @safe if(isIntegral!T && T.sizeof > 1) {
647 		return Var!T.decode!true(this);
648 	}
649 	
650 	/// ditto
651 	B read(T:Var!B, B)() pure @safe {
652 		return this.readVar!B();
653 	}
654 	
655 	///
656 	pure @safe unittest {
657 		
658 		import xbuffer.varint;
659 		
660 		Buffer buffer = new Buffer(cast(ubyte[])[2, 1]);
661 		assert(buffer.readVar!int() == 1);
662 		assert(buffer.read!varuint() == 1);
663 		
664 	}
665 	
666 	// ----
667 	// peek
668 	// ----
669 	
670 	/**
671 	 * Peeks some data (read it but without changing the buffer's index).
672 	 * Throws: BufferOverflowException if there isn't enough data to read.
673 	 */
674 	void[] peekData(size_t size) pure {
675 		if(!this.canRead(size)) throw new BufferOverflowException();
676 		return _data[_rindex.._rindex+size];
677 	}
678 	
679 	unittest {
680 		
681 		Buffer buffer = new Buffer([1]);
682 		assert(buffer.rindex == 0);
683 		buffer.peekData(4);
684 		assert(buffer.rindex == 0);
685 		
686 	}
687 	
688 	/**
689 	 * Peeks a value using the given endianness.
690 	 * Throws: BufferOverflowException if there isn't enough data to read.
691 	 */
692 	T peek(Endian endianness, T)() pure @trusted if(canSwapEndianness!T) {
693 		EndianSwapper!T swapper = EndianSwapper!T(this.peekData(T.sizeof));
694 		static if(endianness != endian) swapper.swap();
695 		return swapper.value;
696 	}
697 
698 	///
699 	pure @safe unittest {
700 
701 		Buffer buffer = new Buffer(cast(ubyte[])[0, 0, 0, 1]);
702 		assert(buffer.peek!(Endian.bigEndian, int)() == 1);
703 		assert(buffer.peek!(Endian.littleEndian, int)() == 1 << 24);
704 		assert(buffer.peek!(Endian.bigEndian, short)() == 0);
705 
706 	}
707 
708 	/**
709 	 * Peeks some data using the system's endianness.
710 	 * Throws: BufferOverflowException if there isn't enough data to read.
711 	 */
712 	T peek(T)() pure @safe if(canSwapEndianness!T) {
713 		return this.peek!(endian, T)();
714 	}
715 	
716 	///
717 	pure @safe unittest {
718 		
719 		Buffer buffer = new Buffer([1, 2]);
720 		assert(buffer.peek!int() == 1);
721 		assert(buffer.rindex == 0);
722 		assert(buffer.peek!int() == buffer.read!int());
723 		assert(buffer.rindex == 4);
724 		assert(buffer.peek!int() == 2);
725 		
726 	}
727 
728 	/**
729 	 * Peeks a varint.
730 	 * Throws: BufferOverflowException if there isn't enough data to read.
731 	 */
732 	T peekVar(T)() pure @safe if(isIntegral!T && T.sizeof > 1) {
733 		return Var!T.decode!false(this);
734 	}
735 
736 	/// ditto
737 	B peek(T:Var!B, B)() pure @safe {
738 		return this.peekVar!B();
739 	}
740 
741 	///
742 	pure @safe unittest {
743 
744 		import xbuffer.varint;
745 
746 		Buffer buffer = new Buffer(cast(ubyte[])[2]);
747 		assert(buffer.peekVar!int() == 1);
748 		assert(buffer.peek!varuint() == 2);
749 
750 	}
751 
752 	// -----------
753 	// destruction
754 	// -----------
755 	
756 	void free() pure nothrow @nogc {
757 		xfree(_data.ptr);
758 	}
759 	
760 	void __xdtor() pure nothrow @nogc {
761 		this.free();
762 	}
763 	
764 	~this() {
765 		this.free();
766 	}
767 	
768 }
769 
770 ///
771 unittest {
772 	
773 	import xbuffer.memory : xalloc;
774 	
775 	// a buffer can be garbage collected
776 	Buffer gc = new Buffer(16);
777 	
778 	// or manually allocated
779 	// alloc is a function provided by the xbuffer.memory module
780 	Buffer b = xalloc!Buffer(16);
781 	
782 	// the memory is realsed with free, which is called by the garbage
783 	// collector or by the `free` function in the `xbuffer.memory` module
784 	xfree(b);
785 	
786 }
787 
788 unittest {
789 	
790 	import xbuffer.memory : xalloc;
791 	
792 	void[] data = xmalloc(923);
793 	
794 	auto buffer = xalloc!Buffer(1024);
795 	assert(buffer.windex == 0);
796 
797 	buffer.writeData(data);
798 	assert(buffer.rindex == 0);
799 	assert(buffer.windex == 923);
800 	assert(buffer.capacity == 1024);
801 	
802 	buffer.writeData(data);
803 	assert(buffer.windex == 1846);
804 	assert(buffer.capacity == 2048);
805 	
806 	data = xrealloc(data.ptr, 1);
807 	
808 	buffer.data = data;
809 	assert(buffer.windex == 1);
810 	assert(buffer.capacity == 2048);
811 	
812 	data = xrealloc(data.ptr, 2049);
813 	
814 	buffer.data = data;
815 	assert(buffer.windex == 2049);
816 	assert(buffer.capacity == 3072);
817 	
818 	xfree(data.ptr);
819 	xfree(buffer);
820 	
821 }