1 module dcrypt.util.pack;
2 import std.traits;
3 
4 /// 
5 /// This module contains several methods to convert integer types into byte arrays
6 /// and vice versa.
7 /// 
8 
9 // TODO Replace with std.bitmanip?
10 
11 public pure nothrow @safe:
12 
13 /**
14  Converts big endian bytes to integral of type T
15  Params:	bs = the big endian bytes
16  Returns: integral of type T
17  */
18 @safe @nogc
19 T fromBigEndian(T)(in ubyte[] bs) if (isIntegral!T)
20 in {
21 	assert(bs.length >= T.sizeof, "input buffer too short");
22 }
23 body {
24 	version(BigEndian) {
25 		// data is already in memory as we want
26 		return (cast(const T[])bs)[0];
27 	}else {
28 		T n = 0;
29 		static if (T.sizeof >= short.sizeof) {
30 			n |= bs[0];
31 			n <<= 8;
32 			n |= bs[1];
33 		}
34 		static if (T.sizeof >= int.sizeof) {
35 			n <<= 8;
36 			n |= bs[2];
37 			n <<= 8;
38 			n |= bs[3];
39 		}
40 		static if (T.sizeof == long.sizeof) {
41 			n <<= 8;
42 			n |= bs[4];
43 			n <<= 8;
44 			n |= bs[5];
45 			n <<= 8;
46 			n |= bs[6];
47 			n <<= 8;
48 			n |= bs[7];
49 		}
50 		
51 		return n;
52 	}
53 }
54 
55 /**
56  Converts little endian bytes to integral of type T
57  Params:	bs = the little endian bytes
58  Returns: integral of type T
59  */
60 @safe @nogc
61 T fromLittleEndian(T)(in ubyte[] bs) if (isIntegral!T)
62 in {
63 	assert(bs.length >= T.sizeof, "input buffer too short");
64 }
65 body {
66 	version(LittleEndian) {
67 		// data is already in memory as we want
68 		return (cast(const T[])bs)[0];
69 	}else {
70 		T n = 0;
71 		static if (T.sizeof >= short.sizeof) {
72 			n |= bs[0];
73 			n |= cast(T)bs[1] << 8;
74 		}
75 		static if (T.sizeof >= int.sizeof) {
76 			n |= cast(T)bs[2] << 16;
77 			n |= cast(T)bs[3] << 24;
78 		}
79 		static if (T.sizeof == long.sizeof) {
80 			n |= cast(T)bs[4] << 32;
81 			n |= cast(T)bs[5] << 40;
82 			n |= cast(T)bs[6] << 48;
83 			n |= cast(T)bs[7] << 56;
84 		}
85 		
86 		return n;
87 	}
88 }
89 
90 /**
91  Converts big endian bytes to integrals of type T
92  size of bs has to match the size in bytes of output
93  Params:
94  bs = the big endian bytes
95  output = where the T's get written to
96  */
97 @safe @nogc
98 void fromBigEndian(T)(in ubyte[] bs, T[] output) if (isIntegral!T) 
99 in {
100 	assert(bs.length == output.length * T.sizeof, "size of input array does not match size of output array");
101 }
102 body {
103 	version(BigEndian) {
104 		// short cut on big endian systems
105 		const T[] casted = cast(const T[]) bs;
106 		output[] = casted[];
107 	} else {
108 		// for little endian systems
109 		foreach (i; 0 .. output.length)
110 		{
111 			output[i] = fromBigEndian!T(bs[4*i .. 4*i+4]);
112 		}
113 	}
114 }
115 
116 /**
117  Converts little endian bytes to integrals of type T
118  size of bs has to match the size in bytes of output
119  Params:
120  bs = the little endian bytes
121  output = where the T's get written to
122  */
123 @safe @nogc
124 void fromLittleEndian(T)(in ubyte[] bs, T[] output) if (isIntegral!T) 
125 in {
126 	assert(bs.length == output.length * T.sizeof, "size of input array does not match size of output array");
127 }
128 body {
129 	version(LittleEndian) {
130 		// short cut on little endian systems
131 		const T[] casted = cast(const T[]) bs;
132 		output[] = casted[];
133 	} else {
134 		// for big endian systems
135 		foreach (i; 0 .. output.length)
136 		{
137 			output[i] = fromLittleEndian!T(bs[4*i .. 4*i+4]);
138 		}
139 	}
140 }
141 
142 /**
143  convert a integral type T into an array of bytes.
144  Params:
145  n = the number
146  output = the buffer to write the bytes to
147  */
148 @safe @nogc
149 void toBigEndian(T)(T n, ubyte[] output) if(isIntegral!T) 
150 in {
151 	assert(output.length >= T.sizeof, "output buffer too small");
152 }
153 body {
154 	uint off = 0;
155 	
156 	static if(T.sizeof == long.sizeof) {
157 		output[off] = cast (ubyte) (n >>> 56);
158 		++off;
159 		output[off] = cast (ubyte) (n >>> 48);
160 		++off;
161 		output[off] = cast (ubyte) (n >>> 40);
162 		++off;
163 		output[off] = cast (ubyte) (n >>> 32);
164 		++off;
165 	}
166 	static if(T.sizeof >= int.sizeof) {
167 		output[off] = cast (ubyte) (n >>> 24);
168 		++off;
169 		output[off] = cast (ubyte) (n >>> 16);
170 		++off;
171 	}
172 	static if(T.sizeof >= short.sizeof) {
173 		output[off] = cast (ubyte) (n >>> 8);
174 		++off;
175 	}
176 	output[off] = cast (ubyte) (n);
177 }
178 
179 /**
180  convert a integral type T into an array of bytes.
181  Params:
182  n = the number
183  output = the buffer to write the bytes to
184  */
185 @safe @nogc
186 void toLittleEndian(T)(T n, ubyte[] output) if(isIntegral!T) 
187 in {
188 	assert(output.length >= T.sizeof, "output buffer too small");
189 }
190 body {
191 	output[0] = cast (ubyte) (n);
192 	n >>>= 8;
193 	static if(T.sizeof >= short.sizeof) {
194 		output[1] = cast (ubyte) (n);
195 		n >>>= 8;
196 	}
197 	static if(T.sizeof >= int.sizeof) {
198 		output[2] = cast (ubyte) (n);
199 		n >>>= 8;
200 		output[3] = cast (ubyte) (n);
201 		n >>>= 8;
202 	}
203 	static if(T.sizeof == long.sizeof) {
204 		output[4] = cast (ubyte) (n);
205 		n >>>= 8;
206 		output[5] = cast (ubyte) (n);
207 		n >>>= 8;
208 		output[6] = cast (ubyte) (n);
209 		n >>>= 8;
210 		output[7] = cast (ubyte) (n);
211 	}
212 }
213 
214 /**
215  convert a integral type T[] into an array of bytes.
216  Params:
217  ns = the numbers
218  output = the buffer to write the bytes to
219  */
220 @safe @nogc
221 void toBigEndian(T)(in T[] ns, ubyte[] output) if(isIntegral!T) 
222 in {
223 	assert(output.length >= T.sizeof*ns.length, "output buffer too small");
224 }
225 body {
226 	version(BigEndian) {
227 		// shortcut on BigEndian systems
228 		const ubyte[] casted = cast(const ubyte []) ns;
229 		output[] = casted[];
230 	}else{
231 		foreach(i, T n; ns) {
232 			toBigEndian!T(n, output[T.sizeof * i .. $]);
233 		}
234 	}
235 }
236 
237 /**
238  convert a integral type T[] into an array of bytes.
239  Params:
240  ns	the numbers
241  output	the buffer to write the bytes to
242  */
243 @safe @nogc
244 void toLittleEndian(T)(in T[] ns, ubyte[] output) if(isIntegral!T) 
245 in {
246 	assert(output.length >= T.sizeof*ns.length, "output buffer too small");
247 }
248 body {
249 	version(LittleEndian) {
250 		// shortcut on LittleEndian systems
251 		const ubyte[] casted = cast(const ubyte []) ns;
252 		output[] = casted[];
253 	}else{
254 		foreach(i, T n; ns) {
255 			toLittleEndian!T(n, output[T.sizeof * i .. $]);
256 		}
257 	}
258 }
259 
260 ubyte[] toBigEndian(T)(T n) if(isIntegral!T)
261 {
262 	ubyte[] bs = new ubyte[T.sizeof];
263 	toBigEndian!T(n, bs);
264 	return bs;
265 }
266 
267 ubyte[] toBigEndian(T)(in T[] ns) if(isIntegral!T)
268 {
269 	ubyte[] bs = new ubyte[T.sizeof * ns.length];
270 	toBigEndian!T(ns, bs);
271 	return bs;
272 }
273 
274 
275 ubyte[] toLittleEndian(T)(T n) if(isIntegral!T)
276 {
277 	ubyte[] bs = new ubyte[T.sizeof];
278 	toLittleEndian!T(n, bs);
279 	return bs;
280 }
281 
282 
283 ubyte[] toLittleEndian(T)(in T[] ns) if(isIntegral!T)
284 {
285 	ubyte[] bs = new ubyte[T.sizeof * ns.length];
286 	toLittleEndian!T(ns, bs);
287 	return bs;
288 }
289 
290 unittest {
291 	
292 	// int
293 	assert(toBigEndian(0x01020304) == [0x01,0x02,0x03,0x04], "intToBigEndian failed");
294 	assert(toLittleEndian(0x01020304) == [0x04,0x03,0x02,0x01], "intToLittleEndian failed");
295 	
296 	
297 	// long
298 	assert(toBigEndian(0x0102030405060708L) == [0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08], "longToBigEndian failed");
299 	assert(toLittleEndian(0x0807060504030201L) == [0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08], "longToLittleEndian failed");
300 	
301 	// bigEndian to short, int, long
302 	assert(fromBigEndian!ushort([0x01,0x02]) == 0x0102u);
303 	assert(fromBigEndian!uint([0x01,0x02,0x03,0x04]) == 0x01020304u);
304 	assert(fromBigEndian!ulong([0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08]) == 0x0102030405060708UL);
305 	
306 	// littleEndian to short, int, long
307 	assert(fromLittleEndian!ushort([0x02,0x01]) == 0x0102u);
308 	assert(fromLittleEndian!uint([0x04,0x03,0x02,0x01]) == 0x01020304u);
309 	assert(fromLittleEndian!ulong([0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08]) == 0x0807060504030201UL);
310 	
311 	// bigEndian: convert multiple ints
312 	uint[] output = new uint[2];
313 	immutable ubyte[] input = [0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08];
314 	fromBigEndian(input, output);
315 	assert(output == [0x01020304u, 0x05060708u], "fromBigEndian(ubyte[] input, int[] output) failed");
316 	
317 	// littleEndian: convert multiple ints
318 	output = new uint[2];
319 	fromLittleEndian(input, output);
320 	assert(output == [0x04030201u, 0x08070605u], "fromLittleEndian(ubyte[] input, int[] output) failed");
321 	
322 	
323 	immutable int i = 0xf1f2f3f4;
324 	int iResult;
325 	ubyte[] buf;
326 	
327 	// int to bigEndian
328 	buf = new ubyte[4];
329 	toBigEndian!int(i, buf);
330 	iResult = fromBigEndian!int(buf);
331 	assert(i == iResult);
332 	
333 	// int to littleEndian
334 	buf = new ubyte[4];
335 	toLittleEndian!int(i, buf);
336 	iResult = fromLittleEndian!int(buf);
337 	assert(i == iResult);
338 	
339 	
340 	
341 	immutable long l = 0xf1f2f3f4f5f6f7f8;
342 	long lResult;
343 	
344 	// long to bigEndian
345 	buf = new ubyte[8];
346 	toBigEndian!long(l, buf);
347 	lResult = fromBigEndian!long(buf);
348 	assert(l == lResult);
349 	
350 	// int to littleEndian
351 	buf = new ubyte[8];
352 	toLittleEndian!long(l, buf);
353 	lResult = fromLittleEndian!long(buf);
354 	assert(l == lResult);
355 }