Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A custom int type supporting various signed number representations as well as different endiannesses
module customint;
import std.traits : Unsigned;
import std.algorithm : among;
import std.conv : to;
/// The representation used for an instance of CustomInt.
/// Details on how these work can be found on https://en.wikipedia.org/wiki/Signed_number_representations
enum Representation
{
Unsigned,
TwosComplement,
SignedMagnitude,
OnesComplement,
OffsetBinary
}
enum Endianness
{
LittleEndian,
BigEndian
}
version (LittleEndian)
enum DefaultEndian = Endianness.LittleEndian;
else
enum DefaultEndian = Endianness.BigEndian;
/// A custom int type that can specify endianness, number of bits and the representation of signed numbers.
template CustomInt(
size_t bits,
Representation representation = Representation.TwosComplement,
ulong offset = 0,
Endianness endian = DefaultEndian)
if (representation.among(Representation.TwosComplement, Representation.Unsigned)
&& bits.among(8,16,32,64)
&& (offset == 0)
&& endian == DefaultEndian)
{
static if (bits == 8)
alias SignedType = byte;
else static if (bits == 16)
alias SignedType = short;
else static if (bits == 32)
alias SignedType = int;
else static if (bits == 64)
alias SignedType = long;
static if (representation == Representation.Unsigned)
alias CustomInt = Unsigned!SignedType;
else
alias CustomInt = SignedType;
}
/// ditto
struct CustomInt(
size_t bits,
Representation representation = Representation.TwosComplement,
ulong offset = 0,
Endianness endian = DefaultEndian)
if (!representation.among(Representation.TwosComplement, Representation.Unsigned)
|| !bits.among(8,16,32,64)
&& bits > 0 && bits <= 64
&& (offset == 0 || representation == Representation.OffsetBinary)
|| endian != DefaultEndian)
{
private enum size_t size = (bits+7)/8;
private ubyte[size] _payload;
static if (representation == Representation.Unsigned)
enum min = CustomInt(0);
else static if (representation == Representation.TwosComplement)
enum min = CustomInt(-(1uL << (bits-1)));
else static if (representation == Representation.OffsetBinary)
enum min = CustomInt(-offset);
else
enum min = CustomInt(-((1uL << (bits-1)) - 1));
static if (representation == Representation.Unsigned)
enum max = CustomInt(-1uL >>> (64-bits));
else static if (representation == Representation.TwosComplement)
enum max = CustomInt( (1uL << (bits-1)) - 1);
else static if (representation == Representation.OffsetBinary)
enum max = CustomInt((-1uL >>> (64-bits)) - offset);
else
enum max = CustomInt( (1uL << (bits-1)) - 1);
this(T)(T value)
{
this = value;
}
CustomInt opAssign(T)(T value)
{
_convert(value);
return this;
}
CustomInt opOpAssign(string op, T)(T rhs)
{
this = mixin("this "~op~" rhs");
return this;
}
string toString() const
{
return _convert.to!string();
}
alias _convert this;
@trusted private CustomInt _convert(long value)
{
static if (representation == Representation.Unsigned)
{
_payload = trimBits!representation(value.toArray!size, bits);
}
static if (representation == Representation.TwosComplement)
{
_payload = trimBits!representation(value.toArray!size, bits-1);
if (value < 0)
_payload[$-1] |= (1uL << ((bits+7) % 8));
}
static if (representation == Representation.SignedMagnitude)
{
_payload = trimBits!representation((value < 0 ? -value : value).toArray!size, bits-1);
if (value < 0)
_payload[$-1] |= (1uL << ((bits+7) % 8));
}
static if (representation == Representation.OnesComplement)
{
long tmp = value < 0 ? ~-value : value;
_payload = tmp.toArray!size;
_payload = trimBits!representation(_payload, bits-1);
if (value < 0)
_payload[$-1] |= (1uL << ((bits+7) % 8));
}
static if (representation == Representation.OffsetBinary)
_payload = trimBits!representation((value + offset).toArray!size, bits);
static if (endian != DefaultEndian)
{
import std.algorithm.mutation : reverse;
_payload[].reverse();
}
return this;
}
@trusted private long _convert() const
{
long result;
ubyte[size] tmp = _payload;
static if (endian != DefaultEndian)
{
import std.algorithm.mutation : reverse;
tmp[].reverse();
}
result.asArray = tmp;
static if (representation == Representation.TwosComplement)
if (tmp[$-1] & (1uL << ((bits+7) % 8)))
result |= ~((1uL << bits)-1);
static if (representation == Representation.SignedMagnitude)
{
result.asArray = trimBits!representation(result.toArray!size, bits-1);
if (tmp[$-1] & (1uL << ((bits+7) % 8)))
result = -result;
}
static if (representation == Representation.OnesComplement)
{
if (tmp[$-1] & (1uL << ((bits+7) % 8)))
{
result.asArray = trimBits!representation(result.toArray!size, bits-1);
result |= ~((1uL << (bits-1))-1);
result = -~result;
}
}
static if (representation == Representation.OffsetBinary)
result -= offset;
return result;
}
}
private ubyte[size] trimBits(Representation representation, size_t size)(ubyte[size] data, size_t bitLength)
{
if (bitLength % 8 != 0)
data[$-1] &= (1uL << (bitLength % 8)) - 1;
else if (representation != Representation.Unsigned && representation != Representation.OffsetBinary)
data[$-1] &= 0x7F;
return data;
}
private ubyte[size] toArray(size_t size)(long value)
{
ubyte[size] result;
foreach (i, ref e; result)
e = (value >> i*8) & 0xFF;
return result;
}
private long asArray(size_t size)(out long result, ubyte[size] value)
{
foreach (i, e; value)
result |= cast(ulong)e << (i*8);
return result;
}
pure:
nothrow:
@safe:
@nogc
unittest
{
import std.traits : EnumMembers;
alias Representations = EnumMembers!Representation;
static foreach (bits; 1..64)
static foreach (rep; Representations)
{{
enum offset = rep == Representation.OffsetBinary ? 1 : 0;
CustomInt!(bits, rep, offset) a;
static if (rep == Representation.Unsigned)
{
static assert(a.min == 0);
static assert(a.max == (1uL << bits) - 1);
}
static if (rep == Representation.TwosComplement)
{
static assert(a.min == -(1uL << (bits-1)));
static assert(a.max == (1uL << (bits-1))-1);
}
static if (rep == Representation.OnesComplement)
{
static assert(a.min == -(1uL << (bits-1))+1);
static assert(a.max == (1uL << (bits-1))-1);
}
static if (rep == Representation.OffsetBinary)
{
static assert(a.min == -offset);
static assert(a.max == (-1uL >>> (64-bits)) - offset);
}
a = a.max;
assert(a == a.max);
a = a.min;
assert(a == a.min);
static if (bits > 1)
{
a = 1;
assert(a == 1);
}
}}
}
// Issue 18439:
@nogc:
unittest
{
static assert(is(CustomInt!8 == byte));
static assert(is(CustomInt!16 == short));
static assert(is(CustomInt!32 == int));
static assert(is(CustomInt!64 == long));
static assert(CustomInt!3.sizeof == 1);
static assert(!is(CustomInt!3 == byte));
static assert(CustomInt!15.sizeof == 2);
static assert(!is(CustomInt!15 == short));
static assert(CustomInt!19.sizeof == 3);
static assert(!is(CustomInt!15 == int));
static assert(CustomInt!27.sizeof == 4);
static assert(!is(CustomInt!27 == int));
}
unittest
{
CustomInt!(24, Representation.Unsigned, 0, Endianness.LittleEndian) a = 1;
CustomInt!(24, Representation.Unsigned, 0, Endianness.BigEndian) b = 1;
assert(a._payload == alloc!(1,0,0));
assert(b._payload == alloc!(0,0,1));
assert(a == b);
assert(a == 1);
assert(b == 1);
}
template alloc(T...)
{
static immutable ubyte[T.length] alloc = [T];
}
unittest
{
CustomInt!(23, Representation.Unsigned) a = -1;
assert(a._payload == alloc!(255,255,127));
assert(a == 8388607);
assert(a != -1);
a = 1;
assert(a._payload == alloc!(1,0,0));
assert(a == 1);
a *= 4;
assert(a == 4);
assert(a.max == 8388607);
assert(a.min == 0);
}
unittest
{
CustomInt!(23, Representation.TwosComplement) a = -1;
assert(a._payload == alloc!(255,255,127));
assert(a == -1);
a = 1;
assert(a._payload == alloc!(1,0,0));
assert(a == 1);
a *= 4;
assert(a == 4);
assert(a.max == (1 << 22)-1);
assert(a.min == -(1 << 22));
}
unittest
{
CustomInt!(23, Representation.SignedMagnitude) a = -1;
assert(a._payload == alloc!(1,0,64));
assert(a == -1);
a = 1;
assert(a._payload == alloc!(1,0,0));
assert(a == 1);
a *= 4;
assert(a == 4);
assert(a.max == (1 << 22)-1);
assert(a.min == -(1 << 22)+1);
}
unittest
{
CustomInt!(23, Representation.OnesComplement) a = -1;
assert(a._payload == alloc!(254,255,127));
assert(a == -1);
a = 1;
assert(a._payload == alloc!(1,0,0));
assert(a == 1);
a *= 4;
assert(a == 4);
assert(a.max == (1 << 22)-1);
assert(a.min == -(1 << 22)+1);
}
unittest
{
CustomInt!(23, Representation.OffsetBinary, 50) a = 0;
assert(a._payload == alloc!(50,0,0));
assert(a == 0);
a = -39;
assert(a._payload == alloc!(11,0,0));
assert(a == -39);
a = (1 << 23) - 70;
assert(a._payload == alloc!(236,255,127));
assert(a == (1 << 23) - 70);
assert(a.min == -50);
assert(a.max == (1 << 23) - 51);
}
unittest
{
CustomInt!7 a = 127;
assert(a != -1);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment