Files
angelc2/bitmanip.d
2025-12-22 15:13:52 +00:00

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);
}