Created
May 21, 2018 15:30
-
-
Save Biotronic/f6668d8ac95b70302015fee93ae9c8c1 to your computer and use it in GitHub Desktop.
A custom int type supporting various signed number representations as well as different endiannesses
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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