1 module xbuffer.util;
2 
3 import std.range : OutputRange;
4 import std.system : Endian, endian;
5 import std.traits : isArray;
6 
7 import xbuffer.buffer : ForeachType, Buffer, canSwapEndianness;
8 
9 /**
10  * Extension template for a buffer that provides methods
11  * and properties useful for working with a single type
12  * of data.
13  */
14 class Typed(T, B:Buffer=Buffer) : B, OutputRange!T if(canSwapEndianness!T) {
15 	
16 	this(size_t chunk) pure nothrow @safe @nogc {
17 		super(chunk * T.sizeof);
18 	}
19 	
20 	this(in T[] data) pure nothrow @safe @nogc {
21 		super(data);
22 	}
23 	
24 	/**
25 	 * Gets the data as an array of the template's type.
26 	 * The data can also be obtained in the specified format.
27 	 * Example:
28 	 * ---
29 	 * auto buffer = new Typed!char("hello");
30 	 * assert(buffer.data == "hello");
31 	 * assert(buffer.data!ubyte == [104, 101, 108, 108, 111]);
32 	 * ---
33 	 */
34 	@property D[] data(D=T)() pure nothrow @trusted @nogc if(D.sizeof == 1) {
35 		return super.data!D;
36 	}
37 	
38 	/// ditto
39 	@property D[] data(D=T)() pure nothrow @nogc if(D.sizeof != 1) {
40 		return super.data!D;
41 	}
42 	
43 	/**
44 	 * Writes a value of type `T` or array of type `T[]`.
45 	 * Example:
46 	 * ---
47 	 * auto buffer = new Typed!short(4);
48 	 * buffer.put(1);
49 	 * buffer.put(2, 3);
50 	 * buffer.put([4, 5]);
51 	 * assert(buffer.data == [1, 2, 3, 4, 5]);
52 	 * ---
53 	 */
54 	void put(T value) pure nothrow @safe @nogc {
55 		this.write(value);
56 	}
57 	
58 	/// ditto
59 	void put(in T[] value...) pure nothrow @safe @nogc {
60 		this.write(value);
61 	}
62 	
63 	/**
64 	 * Reads a value of type `T` or an array of type `T[]`.
65 	 * Example:
66 	 * ---
67 	 * auto buffer = new Typed!int(4);
68 	 * buffer.data = [1, 2, 3, 4, 5];
69 	 * assert(buffer.get == 1);
70 	 * assert(buffer.get == 2);
71 	 * assert(buffer.get(3) == [3, 4, 5]);
72 	 * ---
73 	 */
74 	T get() pure @safe {
75 		return this.read!T();
76 	}
77 	
78 	/// ditto
79 	T[] get(size_t length) pure @safe {
80 		return this.read!(T[])(length);
81 	}
82 	
83 }
84 
85 /// ditto
86 template Typed(T, B:Buffer=Buffer) if(isArray!T && canSwapEndianness!(ForeachType!T)) { alias Typed = Typed!(ForeachType!T, B); }
87 
88 ///
89 pure @safe unittest {
90 
91 	import std.range;
92 	
93 	alias ByteBuffer = Typed!ubyte;
94 
95 	static assert(isOutputRange!(ByteBuffer, ubyte));
96 
97 	auto buffer = new ByteBuffer([0, 0, 0, 4]);
98 	assert(buffer.read!(Endian.bigEndian, uint)() == 4);
99 	
100 	buffer.reset();
101 	buffer.put(1);
102 	buffer.put([2, 3]);
103 	buffer.put(4, 5, 6);
104 	assert(buffer.data == [1, 2, 3, 4, 5, 6]);
105 	assert(buffer.get == 1);
106 	assert(buffer.get(3) == [2, 3, 4]);
107 	
108 }
109 
110 ///
111 pure @safe unittest {
112 	
113 	alias IntBuffer = Typed!int;
114 	
115 	auto buffer = new IntBuffer(2);
116 	assert(buffer.capacity == 8);
117 	buffer.write(1);
118 	buffer.write(2);
119 	version(BigEndian) assert(buffer.data!ubyte == [0, 0, 0, 1, 0, 0, 0, 2]);
120 	version(LittleEndian) assert(buffer.data!ubyte == [1, 0, 0, 0, 2, 0, 0, 0]);
121 	assert(buffer.read!int() == 1);
122 	assert(buffer.read!int() == 2);
123 	
124 }
125 
126 ///
127 pure @trusted unittest {
128 	
129 	static struct Test {
130 		
131 		int a;
132 		short b;
133 		
134 	}
135 	
136 	static assert(Test.sizeof == 8); // because of the alignment
137 	
138 	alias TestBuffer = Typed!Test;
139 	
140 	auto buffer = new TestBuffer(1);
141 	buffer.put(Test(1, 2));
142 	version(BigEndian) assert(buffer.data!ubyte == [0, 0, 0, 1, 0, 2, 0, 0]);
143 	version(LittleEndian) assert(buffer.data!ubyte == [1, 0, 0, 0, 2, 0, 0, 0]);
144 	assert(buffer.data == [Test(1, 2)]);
145 	assert(buffer.get == Test(1, 2));
146 	
147 	buffer.reset();
148 	buffer.write!(cast(Endian)!endian)(Test(1, 2));
149 	version(BigEndian) assert(buffer.data!ubyte == [0, 0, 2, 0, 1, 0, 0, 0]);
150 	version(LittleEndian) assert(buffer.data!ubyte == [0, 0, 0, 2, 0, 0, 0, 1]);
151 	
152 }
153 
154 ///
155 pure nothrow @safe @nogc unittest {
156 	
157 	static struct Test {
158 		
159 		int a;
160 		ubyte[] b;
161 		
162 	}
163 	
164 	static assert(!canSwapEndianness!Test);
165 	
166 }
167 
168 ///
169 pure @safe nothrow unittest {
170 
171 	alias StringBuffer = Typed!string;
172 
173 	static assert(is(StringBuffer == Typed!(immutable(char))));
174 
175 	auto buffer = new StringBuffer(64);
176 	buffer.put("hello");
177 	assert(buffer.data!char == ['h', 'e', 'l', 'l', 'o']);
178 
179 }