416 lines
9.7 KiB
D
416 lines
9.7 KiB
D
module angel.utils.cryptography.bitmanip;
|
|
|
|
import std.traits;
|
|
///
|
|
/// This module contains several methods to convert integer types into byte arrays
|
|
/// and vice versa.
|
|
///
|
|
///
|
|
|
|
alias rotateLeft rol;
|
|
alias rotateRight ror;
|
|
|
|
/// rot shift to the left
|
|
/// Params:
|
|
/// x = integer to shift
|
|
/// shiftAmount = number of bits to shift
|
|
@safe
|
|
@nogc
|
|
T rotateLeft(T)(T x, uint shiftAmount) pure nothrow
|
|
{
|
|
enum nbits = T.sizeof*8;
|
|
//shiftAmount %= nbits;
|
|
return cast(T)(x << shiftAmount) | (x >>> (nbits-shiftAmount));
|
|
}
|
|
|
|
/// test rotateLeft
|
|
unittest {
|
|
ubyte b0 = 0b10000001;
|
|
ubyte b1 = 0b00000011;
|
|
ubyte b2 = 0b00000110;
|
|
ubyte b7 = 0b11000000;
|
|
|
|
assert(rotateLeft(b0,0) == b0);
|
|
assert(rotateLeft(b0,1) == b1);
|
|
assert(rotateLeft(b0,2) == b2);
|
|
assert(rotateLeft(b0,7) == b7);
|
|
assert(rotateLeft(b0,8) == b0);
|
|
}
|
|
|
|
/// rot shift to the right
|
|
/// Params:
|
|
/// x = integer to shift
|
|
/// shiftAmount = number of bits to shift
|
|
@safe
|
|
@nogc
|
|
T rotateRight(T)(T x, uint shiftAmount) pure nothrow
|
|
{
|
|
enum nbits = T.sizeof*8;
|
|
//shiftAmount %= nbits;
|
|
return cast(T)((x >>> shiftAmount) | (x << (nbits-shiftAmount)));
|
|
}
|
|
|
|
/// test rotateRight
|
|
unittest {
|
|
ubyte b0 = 0b00000101;
|
|
ubyte b1 = 0b10000010;
|
|
ubyte b2 = 0b01000001;
|
|
ubyte b7 = 0b00001010;
|
|
|
|
assert(rotateRight(b0,0) == b0);
|
|
assert(rotateRight(b0,1) == b1);
|
|
assert(rotateRight(b0,2) == b2);
|
|
assert(rotateRight(b0,7) == b7);
|
|
assert(rotateRight(b0,8) == b0);
|
|
}
|
|
|
|
|
|
/**
|
|
Converts big endian bytes to integral of type T
|
|
Params: bs = the big endian bytes
|
|
Returns: integral of type T
|
|
*/
|
|
@safe @nogc
|
|
T fromBigEndian(T)(in ubyte[] bs) if (isIntegral!T)
|
|
in {
|
|
assert(bs.length >= T.sizeof, "input buffer too short");
|
|
}
|
|
body {
|
|
version(BigEndian) {
|
|
// data is already in memory as we want
|
|
return (cast(const T[])bs)[0];
|
|
}else {
|
|
Unqual!T n = 0;
|
|
static if (T.sizeof >= short.sizeof) {
|
|
n |= bs[0];
|
|
n <<= 8;
|
|
n |= bs[1];
|
|
}
|
|
static if (T.sizeof >= int.sizeof) {
|
|
n <<= 8;
|
|
n |= bs[2];
|
|
n <<= 8;
|
|
n |= bs[3];
|
|
}
|
|
static if (T.sizeof == long.sizeof) {
|
|
n <<= 8;
|
|
n |= bs[4];
|
|
n <<= 8;
|
|
n |= bs[5];
|
|
n <<= 8;
|
|
n |= bs[6];
|
|
n <<= 8;
|
|
n |= bs[7];
|
|
}
|
|
|
|
return n;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Converts little endian bytes to integral of type T
|
|
Params: bs = the little endian bytes
|
|
Returns: integral of type T
|
|
*/
|
|
@safe @nogc
|
|
T fromLittleEndian(T)(in ubyte[] bs) if (isIntegral!T)
|
|
in {
|
|
assert(bs.length >= T.sizeof, "input buffer too short");
|
|
}
|
|
body {
|
|
version(LittleEndian) {
|
|
// data is already in memory as we want
|
|
return (cast(const T[])bs)[0];
|
|
}else {
|
|
Unqual!T n = 0;
|
|
static if (T.sizeof >= short.sizeof) {
|
|
n |= bs[0];
|
|
n |= cast(T)bs[1] << 8;
|
|
}
|
|
static if (T.sizeof >= int.sizeof) {
|
|
n |= cast(T)bs[2] << 16;
|
|
n |= cast(T)bs[3] << 24;
|
|
}
|
|
static if (T.sizeof == long.sizeof) {
|
|
n |= cast(T)bs[4] << 32;
|
|
n |= cast(T)bs[5] << 40;
|
|
n |= cast(T)bs[6] << 48;
|
|
n |= cast(T)bs[7] << 56;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Converts big endian bytes to integrals of type T
|
|
size of bs has to match the size in bytes of output
|
|
Params:
|
|
bs = the big endian bytes
|
|
output = where the T's get written to
|
|
*/
|
|
@safe @nogc
|
|
void fromBigEndian(T)(in ubyte[] bs, T[] output) if (isIntegral!T)
|
|
in {
|
|
assert(bs.length == output.length * T.sizeof, "size of input array does not match size of output array");
|
|
}
|
|
body {
|
|
version(BigEndian) {
|
|
// short cut on big endian systems
|
|
const T[] casted = cast(const T[]) bs;
|
|
output[] = casted[];
|
|
} else {
|
|
// for little endian systems
|
|
enum s = T.sizeof;
|
|
foreach (i; 0 .. output.length)
|
|
{
|
|
output[i] = fromBigEndian!T(bs[s*i .. s*i+s]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Converts little endian bytes to integrals of type T
|
|
size of bs has to match the size in bytes of output
|
|
Params:
|
|
bs = the little endian bytes
|
|
output = where the T's get written to
|
|
*/
|
|
@safe @nogc
|
|
void fromLittleEndian(T)(in ubyte[] bs, T[] output) if (isIntegral!T)
|
|
in {
|
|
assert(bs.length == output.length * T.sizeof, "size of input array does not match size of output array");
|
|
}
|
|
body {
|
|
version(LittleEndian) {
|
|
// short cut on little endian systems
|
|
const T[] casted = cast(const T[]) bs;
|
|
output[] = casted[];
|
|
} else {
|
|
// for big endian systems
|
|
enum s = T.sizeof;
|
|
foreach (i; 0 .. output.length)
|
|
{
|
|
output[i] = fromLittleEndian!T(bs[s*i .. s*i+s]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
convert a integral type T into an array of bytes.
|
|
Params:
|
|
n = the number
|
|
output = the buffer to write the bytes to
|
|
*/
|
|
@safe @nogc
|
|
void toBigEndian(T)(in T val, ubyte[] output) if(isIntegral!T)
|
|
in {
|
|
assert(output.length >= T.sizeof, "output buffer too small");
|
|
}
|
|
body {
|
|
Unqual!T n = val;
|
|
uint off = 0;
|
|
|
|
static if(T.sizeof == long.sizeof) {
|
|
output[off] = cast (ubyte) (n >>> 56);
|
|
++off;
|
|
output[off] = cast (ubyte) (n >>> 48);
|
|
++off;
|
|
output[off] = cast (ubyte) (n >>> 40);
|
|
++off;
|
|
output[off] = cast (ubyte) (n >>> 32);
|
|
++off;
|
|
}
|
|
static if(T.sizeof >= int.sizeof) {
|
|
output[off] = cast (ubyte) (n >>> 24);
|
|
++off;
|
|
output[off] = cast (ubyte) (n >>> 16);
|
|
++off;
|
|
}
|
|
static if(T.sizeof >= short.sizeof) {
|
|
output[off] = cast (ubyte) (n >>> 8);
|
|
++off;
|
|
}
|
|
output[off] = cast (ubyte) (n);
|
|
}
|
|
|
|
/**
|
|
convert a integral type T into an array of bytes.
|
|
Params:
|
|
n = the number
|
|
output = the buffer to write the bytes to
|
|
*/
|
|
@safe @nogc
|
|
void toLittleEndian(T)(in T val, ubyte[] output) if(isIntegral!T)
|
|
in {
|
|
assert(output.length >= T.sizeof, "output buffer too small");
|
|
}
|
|
body {
|
|
Unqual!T n = val;
|
|
output[0] = cast (ubyte) (n);
|
|
n >>>= 8;
|
|
static if(T.sizeof >= short.sizeof) {
|
|
output[1] = cast (ubyte) (n);
|
|
n >>>= 8;
|
|
}
|
|
static if(T.sizeof >= int.sizeof) {
|
|
output[2] = cast (ubyte) (n);
|
|
n >>>= 8;
|
|
output[3] = cast (ubyte) (n);
|
|
n >>>= 8;
|
|
}
|
|
static if(T.sizeof == long.sizeof) {
|
|
output[4] = cast (ubyte) (n);
|
|
n >>>= 8;
|
|
output[5] = cast (ubyte) (n);
|
|
n >>>= 8;
|
|
output[6] = cast (ubyte) (n);
|
|
n >>>= 8;
|
|
output[7] = cast (ubyte) (n);
|
|
}
|
|
}
|
|
|
|
/**
|
|
convert a integral type T[] into an array of bytes.
|
|
Params:
|
|
ns = the numbers
|
|
output = the buffer to write the bytes to
|
|
*/
|
|
@safe @nogc
|
|
void toBigEndian(T)(in T[] ns, ubyte[] output) if(isIntegral!T)
|
|
in {
|
|
assert(output.length >= T.sizeof*ns.length, "output buffer too small");
|
|
}
|
|
body {
|
|
version(BigEndian) {
|
|
// shortcut on BigEndian systems
|
|
const ubyte[] casted = cast(const ubyte []) ns;
|
|
output[] = casted[];
|
|
}else{
|
|
foreach(i, const T n; ns) {
|
|
toBigEndian!T(n, output[T.sizeof * i .. $]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
convert a integral type T[] into an array of bytes.
|
|
Params:
|
|
ns the numbers
|
|
output the buffer to write the bytes to
|
|
*/
|
|
@safe @nogc
|
|
void toLittleEndian(T)(in T[] ns, ubyte[] output) if(isIntegral!T)
|
|
in {
|
|
assert(output.length >= T.sizeof*ns.length, "output buffer too small");
|
|
}
|
|
body {
|
|
version(LittleEndian) {
|
|
// shortcut on LittleEndian systems
|
|
const ubyte[] casted = cast(const ubyte []) ns;
|
|
output[] = casted[];
|
|
}else{
|
|
foreach(i, const T n; ns) {
|
|
toLittleEndian!T(n, output[T.sizeof * i .. $]);
|
|
}
|
|
}
|
|
}
|
|
|
|
ubyte[T.sizeof] toBigEndian(T)(in T n) pure nothrow @nogc
|
|
if(isIntegral!T)
|
|
{
|
|
ubyte[T.sizeof] bs;
|
|
toBigEndian!T(n, bs);
|
|
return bs;
|
|
}
|
|
|
|
ubyte[] toBigEndian(T)(in T[] ns) if(isIntegral!T)
|
|
{
|
|
ubyte[] bs = new ubyte[T.sizeof * ns.length];
|
|
toBigEndian!T(ns, bs);
|
|
return bs;
|
|
}
|
|
|
|
|
|
ubyte[T.sizeof] toLittleEndian(T)(in T n) pure nothrow @nogc
|
|
if(isIntegral!T)
|
|
{
|
|
ubyte[T.sizeof] bs;
|
|
toLittleEndian!T(n, bs);
|
|
return bs;
|
|
}
|
|
|
|
|
|
ubyte[] toLittleEndian(T)(in T[] ns) if(isIntegral!T)
|
|
{
|
|
ubyte[] bs = new ubyte[T.sizeof * ns.length];
|
|
toLittleEndian!T(ns, bs);
|
|
return bs;
|
|
}
|
|
|
|
unittest {
|
|
|
|
// int
|
|
assert(toBigEndian(0x01020304) == [0x01,0x02,0x03,0x04], "intToBigEndian failed");
|
|
assert(toLittleEndian(0x01020304) == [0x04,0x03,0x02,0x01], "intToLittleEndian failed");
|
|
|
|
|
|
// long
|
|
assert(toBigEndian(0x0102030405060708L) == [0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08], "longToBigEndian failed");
|
|
assert(toLittleEndian(0x0807060504030201L) == [0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08], "longToLittleEndian failed");
|
|
|
|
// bigEndian to short, int, long
|
|
assert(fromBigEndian!ushort([0x01,0x02]) == 0x0102u);
|
|
assert(fromBigEndian!uint([0x01,0x02,0x03,0x04]) == 0x01020304u);
|
|
assert(fromBigEndian!ulong([0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08]) == 0x0102030405060708UL);
|
|
|
|
// littleEndian to short, int, long
|
|
assert(fromLittleEndian!ushort([0x02,0x01]) == 0x0102u);
|
|
assert(fromLittleEndian!uint([0x04,0x03,0x02,0x01]) == 0x01020304u);
|
|
assert(fromLittleEndian!ulong([0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08]) == 0x0807060504030201UL);
|
|
|
|
// bigEndian: convert multiple ints
|
|
uint[] output = new uint[2];
|
|
immutable ubyte[] input = [0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08];
|
|
fromBigEndian(input, output);
|
|
assert(output == [0x01020304u, 0x05060708u], "fromBigEndian(ubyte[] input, int[] output) failed");
|
|
|
|
// littleEndian: convert multiple ints
|
|
output = new uint[2];
|
|
fromLittleEndian(input, output);
|
|
assert(output == [0x04030201u, 0x08070605u], "fromLittleEndian(ubyte[] input, int[] output) failed");
|
|
|
|
|
|
immutable int i = 0xf1f2f3f4;
|
|
int iResult;
|
|
ubyte[] buf;
|
|
|
|
// int to bigEndian
|
|
buf = new ubyte[4];
|
|
toBigEndian!int(i, buf);
|
|
iResult = fromBigEndian!int(buf);
|
|
assert(i == iResult);
|
|
|
|
// int to littleEndian
|
|
buf = new ubyte[4];
|
|
toLittleEndian!int(i, buf);
|
|
iResult = fromLittleEndian!int(buf);
|
|
assert(i == iResult);
|
|
|
|
|
|
|
|
immutable long l = 0xf1f2f3f4f5f6f7f8;
|
|
long lResult;
|
|
|
|
// long to bigEndian
|
|
buf = new ubyte[8];
|
|
toBigEndian!long(l, buf);
|
|
lResult = fromBigEndian!long(buf);
|
|
assert(l == lResult);
|
|
|
|
// int to littleEndian
|
|
buf = new ubyte[8];
|
|
toLittleEndian!long(l, buf);
|
|
lResult = fromLittleEndian!long(buf);
|
|
assert(l == lResult);
|
|
} |