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 		enum s = T.sizeof;
164 		foreach (i; 0 .. output.length)
165 		{
166 			output[i] = fromBigEndian!T(bs[s*i .. s*i+s]);
167 		}
168 	}
169 }
170 
171 /**
172  Converts little endian bytes to integrals of type T
173  size of bs has to match the size in bytes of output
174  Params:
175  bs = the little endian bytes
176  output = where the T's get written to
177  */
178 @safe @nogc
179 void fromLittleEndian(T)(in ubyte[] bs, T[] output) if (isIntegral!T) 
180 in {
181 	assert(bs.length == output.length * T.sizeof, "size of input array does not match size of output array");
182 }
183 body {
184 	version(LittleEndian) {
185 		// short cut on little endian systems
186 		const T[] casted = cast(const T[]) bs;
187 		output[] = casted[];
188 	} else {
189 		// for big endian systems
190 		enum s = T.sizeof;
191 		foreach (i; 0 .. output.length)
192 		{
193 			output[i] = fromLittleEndian!T(bs[s*i .. s*i+s]);
194 		}
195 	}
196 }
197 
198 /**
199  convert a integral type T into an array of bytes.
200  Params:
201  n = the number
202  output = the buffer to write the bytes to
203  */
204 @safe @nogc
205 void toBigEndian(T)(in T val, ubyte[] output) if(isIntegral!T) 
206 in {
207 	assert(output.length >= T.sizeof, "output buffer too small");
208 }
209 body {
210 	Unqual!T n = val;
211 	uint off = 0;
212 	
213 	static if(T.sizeof == long.sizeof) {
214 		output[off] = cast (ubyte) (n >>> 56);
215 		++off;
216 		output[off] = cast (ubyte) (n >>> 48);
217 		++off;
218 		output[off] = cast (ubyte) (n >>> 40);
219 		++off;
220 		output[off] = cast (ubyte) (n >>> 32);
221 		++off;
222 	}
223 	static if(T.sizeof >= int.sizeof) {
224 		output[off] = cast (ubyte) (n >>> 24);
225 		++off;
226 		output[off] = cast (ubyte) (n >>> 16);
227 		++off;
228 	}
229 	static if(T.sizeof >= short.sizeof) {
230 		output[off] = cast (ubyte) (n >>> 8);
231 		++off;
232 	}
233 	output[off] = cast (ubyte) (n);
234 }
235 
236 /**
237  convert a integral type T into an array of bytes.
238  Params:
239  n = the number
240  output = the buffer to write the bytes to
241  */
242 @safe @nogc
243 void toLittleEndian(T)(in T val, ubyte[] output) if(isIntegral!T) 
244 in {
245 	assert(output.length >= T.sizeof, "output buffer too small");
246 }
247 body {
248 	Unqual!T n = val;
249 	output[0] = cast (ubyte) (n);
250 	n >>>= 8;
251 	static if(T.sizeof >= short.sizeof) {
252 		output[1] = cast (ubyte) (n);
253 		n >>>= 8;
254 	}
255 	static if(T.sizeof >= int.sizeof) {
256 		output[2] = cast (ubyte) (n);
257 		n >>>= 8;
258 		output[3] = cast (ubyte) (n);
259 		n >>>= 8;
260 	}
261 	static if(T.sizeof == long.sizeof) {
262 		output[4] = cast (ubyte) (n);
263 		n >>>= 8;
264 		output[5] = cast (ubyte) (n);
265 		n >>>= 8;
266 		output[6] = cast (ubyte) (n);
267 		n >>>= 8;
268 		output[7] = cast (ubyte) (n);
269 	}
270 }
271 
272 /**
273  convert a integral type T[] into an array of bytes.
274  Params:
275  ns = the numbers
276  output = the buffer to write the bytes to
277  */
278 @safe @nogc
279 void toBigEndian(T)(in T[] ns, ubyte[] output) if(isIntegral!T) 
280 in {
281 	assert(output.length >= T.sizeof*ns.length, "output buffer too small");
282 }
283 body {
284 	version(BigEndian) {
285 		// shortcut on BigEndian systems
286 		const ubyte[] casted = cast(const ubyte []) ns;
287 		output[] = casted[];
288 	}else{
289 		foreach(i, const T n; ns) {
290 			toBigEndian!T(n, output[T.sizeof * i .. $]);
291 		}
292 	}
293 }
294 
295 /**
296  convert a integral type T[] into an array of bytes.
297  Params:
298  ns	the numbers
299  output	the buffer to write the bytes to
300  */
301 @safe @nogc
302 void toLittleEndian(T)(in T[] ns, ubyte[] output) if(isIntegral!T) 
303 in {
304 	assert(output.length >= T.sizeof*ns.length, "output buffer too small");
305 }
306 body {
307 	version(LittleEndian) {
308 		// shortcut on LittleEndian systems
309 		const ubyte[] casted = cast(const ubyte []) ns;
310 		output[] = casted[];
311 	}else{
312 		foreach(i, const T n; ns) {
313 			toLittleEndian!T(n, output[T.sizeof * i .. $]);
314 		}
315 	}
316 }
317 
318 ubyte[T.sizeof] toBigEndian(T)(in T n) pure nothrow @nogc
319 	if(isIntegral!T)
320 {
321 	ubyte[T.sizeof] bs;
322 	toBigEndian!T(n, bs);
323 	return bs;
324 }
325 
326 ubyte[] toBigEndian(T)(in T[] ns) if(isIntegral!T)
327 {
328 	ubyte[] bs = new ubyte[T.sizeof * ns.length];
329 	toBigEndian!T(ns, bs);
330 	return bs;
331 }
332 
333 
334 ubyte[T.sizeof] toLittleEndian(T)(in T n) pure nothrow @nogc
335 	if(isIntegral!T)
336 {
337 	ubyte[T.sizeof] bs;
338 	toLittleEndian!T(n, bs);
339 	return bs;
340 }
341 
342 
343 ubyte[] toLittleEndian(T)(in T[] ns) if(isIntegral!T)
344 {
345 	ubyte[] bs = new ubyte[T.sizeof * ns.length];
346 	toLittleEndian!T(ns, bs);
347 	return bs;
348 }
349 
350 unittest {
351 	
352 	// int
353 	assert(toBigEndian(0x01020304) == [0x01,0x02,0x03,0x04], "intToBigEndian failed");
354 	assert(toLittleEndian(0x01020304) == [0x04,0x03,0x02,0x01], "intToLittleEndian failed");
355 	
356 	
357 	// long
358 	assert(toBigEndian(0x0102030405060708L) == [0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08], "longToBigEndian failed");
359 	assert(toLittleEndian(0x0807060504030201L) == [0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08], "longToLittleEndian failed");
360 	
361 	// bigEndian to short, int, long
362 	assert(fromBigEndian!ushort([0x01,0x02]) == 0x0102u);
363 	assert(fromBigEndian!uint([0x01,0x02,0x03,0x04]) == 0x01020304u);
364 	assert(fromBigEndian!ulong([0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08]) == 0x0102030405060708UL);
365 	
366 	// littleEndian to short, int, long
367 	assert(fromLittleEndian!ushort([0x02,0x01]) == 0x0102u);
368 	assert(fromLittleEndian!uint([0x04,0x03,0x02,0x01]) == 0x01020304u);
369 	assert(fromLittleEndian!ulong([0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08]) == 0x0807060504030201UL);
370 	
371 	// bigEndian: convert multiple ints
372 	uint[] output = new uint[2];
373 	immutable ubyte[] input = [0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08];
374 	fromBigEndian(input, output);
375 	assert(output == [0x01020304u, 0x05060708u], "fromBigEndian(ubyte[] input, int[] output) failed");
376 	
377 	// littleEndian: convert multiple ints
378 	output = new uint[2];
379 	fromLittleEndian(input, output);
380 	assert(output == [0x04030201u, 0x08070605u], "fromLittleEndian(ubyte[] input, int[] output) failed");
381 	
382 	
383 	immutable int i = 0xf1f2f3f4;
384 	int iResult;
385 	ubyte[] buf;
386 	
387 	// int to bigEndian
388 	buf = new ubyte[4];
389 	toBigEndian!int(i, buf);
390 	iResult = fromBigEndian!int(buf);
391 	assert(i == iResult);
392 	
393 	// int to littleEndian
394 	buf = new ubyte[4];
395 	toLittleEndian!int(i, buf);
396 	iResult = fromLittleEndian!int(buf);
397 	assert(i == iResult);
398 	
399 	
400 	
401 	immutable long l = 0xf1f2f3f4f5f6f7f8;
402 	long lResult;
403 	
404 	// long to bigEndian
405 	buf = new ubyte[8];
406 	toBigEndian!long(l, buf);
407 	lResult = fromBigEndian!long(buf);
408 	assert(l == lResult);
409 	
410 	// int to littleEndian
411 	buf = new ubyte[8];
412 	toLittleEndian!long(l, buf);
413 	lResult = fromLittleEndian!long(buf);
414 	assert(l == lResult);
415 }