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 }