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 }