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 }