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