phobos/std/bitmanip.d
jmdavis 8167a7aedb Fix test breakage due to bug# 6354.
http://d.puremagic.com/issues/show_bug.cgi?id=6354

The test in question should be re-enabled once 6354 has been fixed.
2011-07-31 19:10:18 -07:00

1741 lines
42 KiB
D

// Written in the D programming language.
/**
Bit-level manipulation facilities.
Macros:
WIKI = StdBitarray
Copyright: Copyright Digital Mars 2007 - 2011.
License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
Authors: $(WEB digitalmars.com, Walter Bright),
$(WEB erdani.org, Andrei Alexandrescu)
Source: $(PHOBOSSRC std/_bitmanip.d)
*/
/*
Copyright Digital Mars 2007 - 2011.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt)
*/
module std.bitmanip;
//debug = bitarray; // uncomment to turn on debugging printf's
import core.bitop;
import std.traits;
private string myToStringx(ulong n)
{ enum s = "0123456789";
if (n < 10)
return s[cast(size_t)n..cast(size_t)n+1];
else
return myToStringx(n / 10) ~ myToStringx(n % 10);
}
private string myToString(ulong n)
{
return myToStringx(n) ~ (n > uint.max ? "UL" : "U");
}
private template createAccessors(
string store, T, string name, size_t len, size_t offset)
{
static if (!name.length)
{
// No need to create any accessor
enum result = "";
}
else static if (len == 0)
{
// Fields of length 0 are always zero
enum result = "enum "~T.stringof~" "~name~" = 0;\n";
}
else
{
static if (len + offset <= uint.sizeof * 8)
alias uint MasksType;
else
alias ulong MasksType;
enum MasksType
maskAllElse = ((1uL << len) - 1u) << offset,
signBitCheck = 1uL << (len - 1),
extendSign = ~((cast(MasksType)1u << len) - 1);
static if (T.min < 0)
{
enum long minVal = -(1uL << (len - 1));
enum ulong maxVal = (1uL << (len - 1)) - 1;
}
else
{
enum ulong minVal = 0;
enum ulong maxVal = (1uL << len) - 1;
}
static if (is(T == bool))
{
static assert(len == 1);
enum result =
// getter
"bool " ~ name ~ "() const { return "
~"("~store~" & "~myToString(maskAllElse)~") != 0;}\n"
// setter
~"void " ~ name ~ "(bool v){"
~"if (v) "~store~" |= "~myToString(maskAllElse)~";"
~"else "~store~" &= ~"~myToString(maskAllElse)~";}\n";
}
else
{
// getter
enum result = T.stringof~" "~name~"() const { auto result = "
"("~store~" & "
~ myToString(maskAllElse) ~ ") >>"
~ myToString(offset) ~ ";"
~ (T.min < 0
? "if (result >= " ~ myToString(signBitCheck)
~ ") result |= " ~ myToString(extendSign) ~ ";"
: "")
~ " return cast("~T.stringof~") result;}\n"
// setter
~"void "~name~"("~T.stringof~" v){ "
~"assert(v >= "~name~"_min); "
~"assert(v <= "~name~"_max); "
~store~" = cast(typeof("~store~"))"
" (("~store~" & ~"~myToString(maskAllElse)~")"
" | ((cast(typeof("~store~")) v << "~myToString(offset)~")"
" & "~myToString(maskAllElse)~"));}\n"
// constants
~"enum "~T.stringof~" "~name~"_min = cast("~T.stringof~")"
~myToString(minVal)~"; "
~" enum "~T.stringof~" "~name~"_max = cast("~T.stringof~")"
~myToString(maxVal)~"; ";
}
}
}
private template createStoreName(Ts...)
{
static if (Ts.length < 2)
enum createStoreName = "";
else
enum createStoreName = Ts[1] ~ createStoreName!(Ts[3 .. $]);
}
private template createFields(string store, size_t offset, Ts...)
{
static if (!Ts.length)
{
static if (offset == ubyte.sizeof * 8)
alias ubyte StoreType;
else static if (offset == ushort.sizeof * 8)
alias ushort StoreType;
else static if (offset == uint.sizeof * 8)
alias uint StoreType;
else static if (offset == ulong.sizeof * 8)
alias ulong StoreType;
else
{
static assert(false, "Field widths must sum to 8, 16, 32, or 64");
alias ulong StoreType; // just to avoid another error msg
}
enum result = "private " ~ StoreType.stringof ~ " " ~ store ~ ";";
}
else
{
enum result
= createAccessors!(store, Ts[0], Ts[1], Ts[2], offset).result
~ createFields!(store, offset + Ts[2], Ts[3 .. $]).result;
}
}
/**
Allows creating bit fields inside $(D_PARAM struct)s and $(D_PARAM
class)es.
Example:
----
struct A
{
int a;
mixin(bitfields!(
uint, "x", 2,
int, "y", 3,
uint, "z", 2,
bool, "flag", 1));
}
A obj;
obj.x = 2;
obj.z = obj.x;
----
The example above creates a bitfield pack of eight bits, which fit in
one $(D_PARAM ubyte). The bitfields are allocated starting from the
least significant bit, i.e. x occupies the two least significant bits
of the bitfields storage.
The sum of all bit lengths in one $(D_PARAM bitfield) instantiation
must be exactly 8, 16, 32, or 64. If padding is needed, just allocate
one bitfield with an empty name.
Example:
----
struct A
{
mixin(bitfields!(
bool, "flag1", 1,
bool, "flag2", 1,
uint, "", 6));
}
----
The type of a bit field can be any integral type or enumerated
type. The most efficient type to store in bitfields is $(D_PARAM
bool), followed by unsigned types, followed by signed types.
*/
template bitfields(T...)
{
enum { bitfields = createFields!(createStoreName!(T), 0, T).result }
}
/**
Allows manipulating the fraction, exponent, and sign parts of a
$(D_PARAM float) separately. The definition is:
----
struct FloatRep
{
union
{
float value;
mixin(bitfields!(
uint, "fraction", 23,
ubyte, "exponent", 8,
bool, "sign", 1));
}
enum uint bias = 127, fractionBits = 23, exponentBits = 8, signBits = 1;
}
----
*/
struct FloatRep
{
union
{
float value;
mixin(bitfields!(
uint, "fraction", 23,
ubyte, "exponent", 8,
bool, "sign", 1));
}
enum uint bias = 127, fractionBits = 23, exponentBits = 8, signBits = 1;
}
/**
Allows manipulating the fraction, exponent, and sign parts of a
$(D_PARAM double) separately. The definition is:
----
struct DoubleRep
{
union
{
double value;
mixin(bitfields!(
ulong, "fraction", 52,
ushort, "exponent", 11,
bool, "sign", 1));
}
enum uint bias = 1023, signBits = 1, fractionBits = 52, exponentBits = 11;
}
----
*/
struct DoubleRep
{
union
{
double value;
mixin(bitfields!(
ulong, "fraction", 52,
ushort, "exponent", 11,
bool, "sign", 1));
}
enum uint bias = 1023, signBits = 1, fractionBits = 52, exponentBits = 11;
}
unittest
{
// test reading
DoubleRep x;
x.value = 1.0;
assert(x.fraction == 0 && x.exponent == 1023 && !x.sign);
x.value = -0.5;
assert(x.fraction == 0 && x.exponent == 1022 && x.sign);
x.value = 0.5;
assert(x.fraction == 0 && x.exponent == 1022 && !x.sign);
// test writing
x.fraction = 1125899906842624;
x.exponent = 1025;
x.sign = true;
assert(x.value == -5.0);
// test enums
enum ABC { A, B, C };
struct EnumTest
{
mixin(bitfields!(
ABC, "x", 2,
bool, "y", 1,
ubyte, "z", 5));
}
}
/**
* An array of bits.
*/
struct BitArray
{
size_t len;
size_t* ptr;
version(X86)
enum bitsPerSizeT = 32;
else version(X86_64)
enum bitsPerSizeT = 64;
else
static assert(false, "unknown platform");
const size_t dim()
{
return (len + (bitsPerSizeT-1)) / bitsPerSizeT;
}
@property
{
const size_t length()
{
return len;
}
void length(size_t newlen)
{
if (newlen != len)
{
size_t olddim = dim();
size_t newdim = (newlen + (bitsPerSizeT-1)) / bitsPerSizeT;
if (newdim != olddim)
{
// Create a fake array so we can use D's realloc machinery
auto b = ptr[0 .. olddim];
b.length = newdim; // realloc
ptr = b.ptr;
if (newdim & (bitsPerSizeT-1))
{ // Set any pad bits to 0
ptr[newdim - 1] &= ~(~0 << (newdim & (bitsPerSizeT-1)));
}
}
len = newlen;
}
}
}
/**********************************************
* Support for [$(I index)] operation for BitArray.
*/
bool opIndex(size_t i) const
in
{
assert(i < len);
}
body
{
// Andrei: review for @@@64-bit@@@
return cast(bool) bt(ptr, i);
}
unittest
{
void Fun(const BitArray arr)
{
auto x = arr[0];
assert(x == 1);
}
BitArray a;
a.length = 3;
a[0] = 1;
Fun(a);
}
/** ditto */
bool opIndexAssign(bool b, size_t i)
in
{
assert(i < len);
}
body
{
if (b)
bts(ptr, i);
else
btr(ptr, i);
return b;
}
/**********************************************
* Support for array.dup property for BitArray.
*/
BitArray dup()
{
BitArray ba;
auto b = ptr[0 .. dim].dup;
ba.len = len;
ba.ptr = b.ptr;
return ba;
}
unittest
{
BitArray a;
BitArray b;
int i;
debug(bitarray) printf("BitArray.dup.unittest\n");
a.length = 3;
a[0] = 1; a[1] = 0; a[2] = 1;
b = a.dup;
assert(b.length == 3);
for (i = 0; i < 3; i++)
{ debug(bitarray) printf("b[%d] = %d\n", i, b[i]);
assert(b[i] == (((i ^ 1) & 1) ? true : false));
}
}
/**********************************************
* Support for foreach loops for BitArray.
*/
int opApply(scope int delegate(ref bool) dg)
{
int result;
for (size_t i = 0; i < len; i++)
{ bool b = opIndex(i);
result = dg(b);
this[i] = b;
if (result)
break;
}
return result;
}
/** ditto */
int opApply(scope int delegate(ref size_t, ref bool) dg)
{
int result;
for (size_t i = 0; i < len; i++)
{ bool b = opIndex(i);
result = dg(i, b);
this[i] = b;
if (result)
break;
}
return result;
}
unittest
{
debug(bitarray) printf("BitArray.opApply unittest\n");
static bool[] ba = [1,0,1];
BitArray a; a.init(ba);
int i;
foreach (b;a)
{
switch (i)
{ case 0: assert(b == true); break;
case 1: assert(b == false); break;
case 2: assert(b == true); break;
default: assert(0);
}
i++;
}
foreach (j,b;a)
{
switch (j)
{ case 0: assert(b == true); break;
case 1: assert(b == false); break;
case 2: assert(b == true); break;
default: assert(0);
}
}
}
/**********************************************
* Support for array.reverse property for BitArray.
*/
BitArray reverse()
out (result)
{
assert(result == this);
}
body
{
if (len >= 2)
{
bool t;
size_t lo, hi;
lo = 0;
hi = len - 1;
for (; lo < hi; lo++, hi--)
{
t = this[lo];
this[lo] = this[hi];
this[hi] = t;
}
}
return this;
}
unittest
{
debug(bitarray) printf("BitArray.reverse.unittest\n");
BitArray b;
static bool[5] data = [1,0,1,1,0];
int i;
b.init(data);
b.reverse;
for (i = 0; i < data.length; i++)
{
assert(b[i] == data[4 - i]);
}
}
/**********************************************
* Support for array.sort property for BitArray.
*/
BitArray sort()
out (result)
{
assert(result == this);
}
body
{
if (len >= 2)
{
size_t lo, hi;
lo = 0;
hi = len - 1;
while (1)
{
while (1)
{
if (lo >= hi)
goto Ldone;
if (this[lo] == true)
break;
lo++;
}
while (1)
{
if (lo >= hi)
goto Ldone;
if (this[hi] == false)
break;
hi--;
}
this[lo] = false;
this[hi] = true;
lo++;
hi--;
}
Ldone:
;
}
return this;
}
unittest
{
debug(bitarray) printf("BitArray.sort.unittest\n");
__gshared size_t x = 0b1100011000;
__gshared BitArray ba = { 10, &x };
ba.sort;
for (size_t i = 0; i < 6; i++)
assert(ba[i] == false);
for (size_t i = 6; i < 10; i++)
assert(ba[i] == true);
}
/***************************************
* Support for operators == and != for bit arrays.
*/
const bool opEquals(const ref BitArray a2)
{ int i;
if (this.length != a2.length)
return 0; // not equal
byte *p1 = cast(byte*)this.ptr;
byte *p2 = cast(byte*)a2.ptr;
auto n = this.length / 8;
for (i = 0; i < n; i++)
{
if (p1[i] != p2[i])
return 0; // not equal
}
ubyte mask;
n = this.length & 7;
mask = cast(ubyte)((1 << n) - 1);
//printf("i = %d, n = %d, mask = %x, %x, %x\n", i, n, mask, p1[i], p2[i]);
return (mask == 0) || (p1[i] & mask) == (p2[i] & mask);
}
unittest
{
debug(bitarray) printf("BitArray.opEquals unittest\n");
static bool[] ba = [1,0,1,0,1];
static bool[] bb = [1,0,1];
static bool[] bc = [1,0,1,0,1,0,1];
static bool[] bd = [1,0,1,1,1];
static bool[] be = [1,0,1,0,1];
BitArray a; a.init(ba);
BitArray b; b.init(bb);
BitArray c; c.init(bc);
BitArray d; d.init(bd);
BitArray e; e.init(be);
assert(a != b);
assert(a != c);
assert(a != d);
assert(a == e);
}
/***************************************
* Implement comparison operators.
*/
int opCmp(BitArray a2)
{
uint i;
auto len = this.length;
if (a2.length < len)
len = a2.length;
ubyte* p1 = cast(ubyte*)this.ptr;
ubyte* p2 = cast(ubyte*)a2.ptr;
auto n = len / 8;
for (i = 0; i < n; i++)
{
if (p1[i] != p2[i])
break; // not equal
}
for (uint j = i * 8; j < len; j++)
{ ubyte mask = cast(ubyte)(1 << j);
int c;
c = cast(int)(p1[i] & mask) - cast(int)(p2[i] & mask);
if (c)
return c;
}
return cast(int)this.len - cast(int)a2.length;
}
unittest
{
debug(bitarray) printf("BitArray.opCmp unittest\n");
static bool[] ba = [1,0,1,0,1];
static bool[] bb = [1,0,1];
static bool[] bc = [1,0,1,0,1,0,1];
static bool[] bd = [1,0,1,1,1];
static bool[] be = [1,0,1,0,1];
BitArray a; a.init(ba);
BitArray b; b.init(bb);
BitArray c; c.init(bc);
BitArray d; d.init(bd);
BitArray e; e.init(be);
assert(a > b);
assert(a >= b);
assert(a < c);
assert(a <= c);
assert(a < d);
assert(a <= d);
assert(a == e);
assert(a <= e);
assert(a >= e);
}
/***************************************
* Set BitArray to contents of ba[]
*/
void init(bool[] ba)
{
length = ba.length;
foreach (i, b; ba)
{
this[i] = b;
}
}
/***************************************
* Map BitArray onto v[], with numbits being the number of bits
* in the array. Does not copy the data.
*
* This is the inverse of opCast.
*/
void init(void[] v, size_t numbits)
in
{
assert(numbits <= v.length * 8);
assert((v.length & 3) == 0);
}
body
{
ptr = cast(size_t*)v.ptr;
len = numbits;
}
unittest
{
debug(bitarray) printf("BitArray.init unittest\n");
static bool[] ba = [1,0,1,0,1];
BitArray a; a.init(ba);
BitArray b;
void[] v;
v = cast(void[])a;
b.init(v, a.length);
assert(b[0] == 1);
assert(b[1] == 0);
assert(b[2] == 1);
assert(b[3] == 0);
assert(b[4] == 1);
a[0] = 0;
assert(b[0] == 0);
assert(a == b);
}
/***************************************
* Convert to void[].
*/
void[] opCast()
{
return cast(void[])ptr[0 .. dim];
}
unittest
{
debug(bitarray) printf("BitArray.opCast unittest\n");
static bool[] ba = [1,0,1,0,1];
BitArray a; a.init(ba);
void[] v = cast(void[])a;
assert(v.length == a.dim * size_t.sizeof);
}
/***************************************
* Support for unary operator ~ for bit arrays.
*/
BitArray opCom()
{
auto dim = this.dim();
BitArray result;
result.length = len;
for (size_t i = 0; i < dim; i++)
result.ptr[i] = ~this.ptr[i];
if (len & (bitsPerSizeT-1))
result.ptr[dim - 1] &= ~(~0 << (len & (bitsPerSizeT-1)));
return result;
}
unittest
{
debug(bitarray) printf("BitArray.opCom unittest\n");
static bool[] ba = [1,0,1,0,1];
BitArray a; a.init(ba);
BitArray b = ~a;
assert(b[0] == 0);
assert(b[1] == 1);
assert(b[2] == 0);
assert(b[3] == 1);
assert(b[4] == 0);
}
/***************************************
* Support for binary operator & for bit arrays.
*/
BitArray opAnd(BitArray e2)
in
{
assert(len == e2.length);
}
body
{
auto dim = this.dim();
BitArray result;
result.length = len;
for (size_t i = 0; i < dim; i++)
result.ptr[i] = this.ptr[i] & e2.ptr[i];
return result;
}
unittest
{
debug(bitarray) printf("BitArray.opAnd unittest\n");
static bool[] ba = [1,0,1,0,1];
static bool[] bb = [1,0,1,1,0];
BitArray a; a.init(ba);
BitArray b; b.init(bb);
BitArray c = a & b;
assert(c[0] == 1);
assert(c[1] == 0);
assert(c[2] == 1);
assert(c[3] == 0);
assert(c[4] == 0);
}
/***************************************
* Support for binary operator | for bit arrays.
*/
BitArray opOr(BitArray e2)
in
{
assert(len == e2.length);
}
body
{
auto dim = this.dim();
BitArray result;
result.length = len;
for (size_t i = 0; i < dim; i++)
result.ptr[i] = this.ptr[i] | e2.ptr[i];
return result;
}
unittest
{
debug(bitarray) printf("BitArray.opOr unittest\n");
static bool[] ba = [1,0,1,0,1];
static bool[] bb = [1,0,1,1,0];
BitArray a; a.init(ba);
BitArray b; b.init(bb);
BitArray c = a | b;
assert(c[0] == 1);
assert(c[1] == 0);
assert(c[2] == 1);
assert(c[3] == 1);
assert(c[4] == 1);
}
/***************************************
* Support for binary operator ^ for bit arrays.
*/
BitArray opXor(BitArray e2)
in
{
assert(len == e2.length);
}
body
{
auto dim = this.dim();
BitArray result;
result.length = len;
for (size_t i = 0; i < dim; i++)
result.ptr[i] = this.ptr[i] ^ e2.ptr[i];
return result;
}
unittest
{
debug(bitarray) printf("BitArray.opXor unittest\n");
static bool[] ba = [1,0,1,0,1];
static bool[] bb = [1,0,1,1,0];
BitArray a; a.init(ba);
BitArray b; b.init(bb);
BitArray c = a ^ b;
assert(c[0] == 0);
assert(c[1] == 0);
assert(c[2] == 0);
assert(c[3] == 1);
assert(c[4] == 1);
}
/***************************************
* Support for binary operator - for bit arrays.
*
* $(I a - b) for BitArrays means the same thing as $(I a &amp; ~b).
*/
BitArray opSub(BitArray e2)
in
{
assert(len == e2.length);
}
body
{
auto dim = this.dim();
BitArray result;
result.length = len;
for (size_t i = 0; i < dim; i++)
result.ptr[i] = this.ptr[i] & ~e2.ptr[i];
return result;
}
unittest
{
debug(bitarray) printf("BitArray.opSub unittest\n");
static bool[] ba = [1,0,1,0,1];
static bool[] bb = [1,0,1,1,0];
BitArray a; a.init(ba);
BitArray b; b.init(bb);
BitArray c = a - b;
assert(c[0] == 0);
assert(c[1] == 0);
assert(c[2] == 0);
assert(c[3] == 0);
assert(c[4] == 1);
}
/***************************************
* Support for operator &= bit arrays.
*/
BitArray opAndAssign(BitArray e2)
in
{
assert(len == e2.length);
}
body
{
auto dim = this.dim();
for (size_t i = 0; i < dim; i++)
ptr[i] &= e2.ptr[i];
return this;
}
unittest
{
debug(bitarray) printf("BitArray.opAndAssign unittest\n");
static bool[] ba = [1,0,1,0,1];
static bool[] bb = [1,0,1,1,0];
BitArray a; a.init(ba);
BitArray b; b.init(bb);
a &= b;
assert(a[0] == 1);
assert(a[1] == 0);
assert(a[2] == 1);
assert(a[3] == 0);
assert(a[4] == 0);
}
/***************************************
* Support for operator |= for bit arrays.
*/
BitArray opOrAssign(BitArray e2)
in
{
assert(len == e2.length);
}
body
{
auto dim = this.dim();
for (size_t i = 0; i < dim; i++)
ptr[i] |= e2.ptr[i];
return this;
}
unittest
{
debug(bitarray) printf("BitArray.opOrAssign unittest\n");
static bool[] ba = [1,0,1,0,1];
static bool[] bb = [1,0,1,1,0];
BitArray a; a.init(ba);
BitArray b; b.init(bb);
a |= b;
assert(a[0] == 1);
assert(a[1] == 0);
assert(a[2] == 1);
assert(a[3] == 1);
assert(a[4] == 1);
}
/***************************************
* Support for operator ^= for bit arrays.
*/
BitArray opXorAssign(BitArray e2)
in
{
assert(len == e2.length);
}
body
{
auto dim = this.dim();
for (size_t i = 0; i < dim; i++)
ptr[i] ^= e2.ptr[i];
return this;
}
unittest
{
debug(bitarray) printf("BitArray.opXorAssign unittest\n");
static bool[] ba = [1,0,1,0,1];
static bool[] bb = [1,0,1,1,0];
BitArray a; a.init(ba);
BitArray b; b.init(bb);
a ^= b;
assert(a[0] == 0);
assert(a[1] == 0);
assert(a[2] == 0);
assert(a[3] == 1);
assert(a[4] == 1);
}
/***************************************
* Support for operator -= for bit arrays.
*
* $(I a -= b) for BitArrays means the same thing as $(I a &amp;= ~b).
*/
BitArray opSubAssign(BitArray e2)
in
{
assert(len == e2.length);
}
body
{
auto dim = this.dim();
for (size_t i = 0; i < dim; i++)
ptr[i] &= ~e2.ptr[i];
return this;
}
unittest
{
debug(bitarray) printf("BitArray.opSubAssign unittest\n");
static bool[] ba = [1,0,1,0,1];
static bool[] bb = [1,0,1,1,0];
BitArray a; a.init(ba);
BitArray b; b.init(bb);
a -= b;
assert(a[0] == 0);
assert(a[1] == 0);
assert(a[2] == 0);
assert(a[3] == 0);
assert(a[4] == 1);
}
/***************************************
* Support for operator ~= for bit arrays.
*/
BitArray opCatAssign(bool b)
{
length = len + 1;
this[len - 1] = b;
return this;
}
unittest
{
debug(bitarray) printf("BitArray.opCatAssign unittest\n");
static bool[] ba = [1,0,1,0,1];
BitArray a; a.init(ba);
BitArray b;
b = (a ~= true);
assert(a[0] == 1);
assert(a[1] == 0);
assert(a[2] == 1);
assert(a[3] == 0);
assert(a[4] == 1);
assert(a[5] == 1);
assert(b == a);
}
/***************************************
* ditto
*/
BitArray opCatAssign(BitArray b)
{
auto istart = len;
length = len + b.length;
for (auto i = istart; i < len; i++)
this[i] = b[i - istart];
return this;
}
unittest
{
debug(bitarray) printf("BitArray.opCatAssign unittest\n");
static bool[] ba = [1,0];
static bool[] bb = [0,1,0];
BitArray a; a.init(ba);
BitArray b; b.init(bb);
BitArray c;
c = (a ~= b);
assert(a.length == 5);
assert(a[0] == 1);
assert(a[1] == 0);
assert(a[2] == 0);
assert(a[3] == 1);
assert(a[4] == 0);
assert(c == a);
}
/***************************************
* Support for binary operator ~ for bit arrays.
*/
BitArray opCat(bool b)
{
BitArray r;
r = this.dup;
r.length = len + 1;
r[len] = b;
return r;
}
/** ditto */
BitArray opCat_r(bool b)
{
BitArray r;
r.length = len + 1;
r[0] = b;
for (size_t i = 0; i < len; i++)
r[1 + i] = this[i];
return r;
}
/** ditto */
BitArray opCat(BitArray b)
{
BitArray r;
r = this.dup();
r ~= b;
return r;
}
unittest
{
debug(bitarray) printf("BitArray.opCat unittest\n");
static bool[] ba = [1,0];
static bool[] bb = [0,1,0];
BitArray a; a.init(ba);
BitArray b; b.init(bb);
BitArray c;
c = (a ~ b);
assert(c.length == 5);
assert(c[0] == 1);
assert(c[1] == 0);
assert(c[2] == 0);
assert(c[3] == 1);
assert(c[4] == 0);
c = (a ~ true);
assert(c.length == 3);
assert(c[0] == 1);
assert(c[1] == 0);
assert(c[2] == 1);
c = (false ~ a);
assert(c.length == 3);
assert(c[0] == 0);
assert(c[1] == 1);
assert(c[2] == 0);
}
}
/++
Swaps the endianness of the given integral value or character.
+/
T swapEndian(T)(T val) @safe pure nothrow
if(isIntegral!T || isSomeChar!T)
{
static if(val.sizeof == 1)
return val;
else static if(isUnsigned!T)
return swapEndianImpl(val);
else static if(isIntegral!T)
return cast(T)swapEndianImpl(cast(Unsigned!T) val);
else static if(is(Unqual!T == wchar))
return cast(T)swapEndian(cast(ushort)val);
else static if(is(Unqual!T == dchar))
return cast(T)swapEndian(cast(uint)val);
else
static assert(0, T.stringof ~ " unsupported by swapEndian.");
}
private ushort swapEndianImpl(ushort val) @safe pure nothrow
{
return ((val & 0xff00U) >> 8) |
((val & 0x00ffU) << 8);
}
private uint swapEndianImpl(uint val) @trusted pure nothrow
{
return bswap(val);
}
private ulong swapEndianImpl(ulong val) @trusted pure nothrow
{
immutable ulong res = bswap(cast(uint)val);
return res << 32 | bswap(cast(uint)(val >> 32));
}
unittest
{
import std.stdio;
import std.typetuple;
foreach(T; TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong, char, wchar, dchar))
{
scope(failure) writefln("Failed type: %s", T.stringof);
T val;
const T cval;
immutable T ival;
assert(swapEndian(swapEndian(val)) == val);
assert(swapEndian(swapEndian(cval)) == cval);
assert(swapEndian(swapEndian(ival)) == ival);
assert(swapEndian(swapEndian(T.min)) == T.min);
assert(swapEndian(swapEndian(T.max)) == T.max);
foreach(i; 2 .. 10)
{
immutable T maxI = cast(T)(T.max / i);
immutable T minI = cast(T)(T.min / i);
assert(swapEndian(swapEndian(maxI)) == maxI);
static if(isSigned!T)
assert(swapEndian(swapEndian(minI)) == minI);
}
static if(isSigned!T)
assert(swapEndian(swapEndian(cast(T)0)) == 0);
// @@@BUG6354@@@
/+
static if(T.sizeof > 1 && isUnsigned!T)
{
T left = 0xffU;
left <<= (T.sizeof - 1) * 8;
T right = 0xffU;
for(size_t i = 1; i < T.sizeof; ++i)
{
assert(swapEndian(left) == right);
assert(swapEndian(right) == left);
left >>= 8;
right <<= 8;
}
}
+/
}
}
private union EndianSwapper(T)
if(isIntegral!T || isSomeChar!T || is(Unqual!T == float) || is(Unqual!T == double))
{
Unqual!T value;
ubyte[T.sizeof] array;
static if(is(Unqual!T == float))
uint intValue;
else static if(is(Unqual!T == double))
ulong intValue;
}
/++
Converts the given value from the native endianness to big endian and
returns it as a $(D ubyte[n]) where $(D n) is the size of the given type.
Returning a $(D ubyte[n]) helps prevent accidentally using a swapped value
as a regular one (and in the case of floating point values, it's necessary,
because the FPU will mess up any swapped floating point values. So, you
can't actually have swapped floating point values as floating point values).
$(D real) is not supported, because its size is implementation-dependent
and therefore could vary from machine to machine (which could make it
unusable if you tried to transfer it to another machine).
Examples:
--------------------
int i = 12345;
ubyte[4] swappedI = nativeToBigEndian(i);
assert(i == bigEndianToNative!int(swappedI));
double d = 123.45;
ubyte[8] swappedD = nativeToBigEndian(d);
assert(d == bigEndianToNative!double(swappedD));
--------------------
+/
auto nativeToBigEndian(T)(T val) @safe pure nothrow
if(isIntegral!T || isSomeChar!T || is(Unqual!T == float) || is(Unqual!T == double))
{
return nativeToBigEndianImpl(val);
}
//Verify Examples
unittest
{
int i = 12345;
ubyte[4] swappedI = nativeToBigEndian(i);
assert(i == bigEndianToNative!int(swappedI));
double d = 123.45;
ubyte[8] swappedD = nativeToBigEndian(d);
assert(d == bigEndianToNative!double(swappedD));
}
private auto nativeToBigEndianImpl(T)(T val) @safe pure nothrow
if(isIntegral!T || isSomeChar!T)
{
EndianSwapper!T es = void;
version(LittleEndian)
es.value = swapEndian(val);
else
es.value = val;
return es.array;
}
private auto nativeToBigEndianImpl(T)(T val) @safe pure nothrow
if(is(Unqual!T == float) || is(Unqual!T == double))
{
version(LittleEndian)
return floatEndianImpl!(T, true)(val);
else
return floatEndianImpl!(T, false)(val);
}
unittest
{
import std.range;
import std.stdio;
import std.typetuple;
foreach(T; TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong,
char, wchar, dchar,
float, double))
{
scope(failure) writefln("Failed type: %s", T.stringof);
T val;
const T cval;
immutable T ival;
//is instead of == because of NaN for floating point values.
assert(bigEndianToNative!T(nativeToBigEndian(val)) is val);
assert(bigEndianToNative!T(nativeToBigEndian(cval)) is cval);
assert(bigEndianToNative!T(nativeToBigEndian(ival)) is ival);
assert(bigEndianToNative!T(nativeToBigEndian(T.min)) == T.min);
assert(bigEndianToNative!T(nativeToBigEndian(T.max)) == T.max);
static if(isSigned!T)
assert(bigEndianToNative!T(nativeToBigEndian(cast(T)0)) == 0);
foreach(i; [2, 4, 6, 7, 9, 11])
{
immutable T maxI = cast(T)(T.max / i);
immutable T minI = cast(T)(T.min / i);
assert(bigEndianToNative!T(nativeToBigEndian(maxI)) == maxI);
static if(T.sizeof > 1)
assert(nativeToBigEndian(maxI) != nativeToLittleEndian(maxI));
else
assert(nativeToBigEndian(maxI) == nativeToLittleEndian(maxI));
static if(isSigned!T)
{
assert(bigEndianToNative!T(nativeToBigEndian(minI)) == minI);
static if(T.sizeof > 1)
assert(nativeToBigEndian(minI) != nativeToLittleEndian(minI));
else
assert(nativeToBigEndian(minI) == nativeToLittleEndian(minI));
}
}
static if(isUnsigned!T || T.sizeof == 1 || is(T == wchar))
assert(nativeToBigEndian(T.max) == nativeToLittleEndian(T.max));
else
assert(nativeToBigEndian(T.max) != nativeToLittleEndian(T.max));
static if(isUnsigned!T || T.sizeof == 1 || isSomeChar!T)
assert(nativeToBigEndian(T.min) == nativeToLittleEndian(T.min));
else
assert(nativeToBigEndian(T.min) != nativeToLittleEndian(T.min));
}
}
/++
Converts the given value from big endian to the native endianness and
returns it. The value is given as a $(D ubyte[n]) where $(D n) is the size
of the target type. You must give the target type as a template argument,
because there are multiple types with the same size and so the type of the
argument is not enough to determine the return type.
Taking a $(D ubyte[n]) helps prevent accidentally using a swapped value
as a regular one (and in the case of floating point values, it's necessary,
because the FPU will mess up any swapped floating point values. So, you
can't actually have swapped floating point values as floating point values).
Examples:
--------------------
ushort i = 12345;
ubyte[2] swappedI = nativeToBigEndian(i);
assert(i == bigEndianToNative!ushort(swappedI));
dchar c = 'D';
ubyte[4] swappedC = nativeToBigEndian(c);
assert(c == bigEndianToNative!dchar(swappedC));
--------------------
+/
T bigEndianToNative(T, size_t n)(ubyte[n] val) @safe pure nothrow
if((isIntegral!T || isSomeChar!T || is(Unqual!T == float) || is(Unqual!T == double)) &&
n == T.sizeof)
{
return bigEndianToNativeImpl!(T, n)(val);
}
//Verify Examples.
unittest
{
ushort i = 12345;
ubyte[2] swappedI = nativeToBigEndian(i);
assert(i == bigEndianToNative!ushort(swappedI));
dchar c = 'D';
ubyte[4] swappedC = nativeToBigEndian(c);
assert(c == bigEndianToNative!dchar(swappedC));
}
private T bigEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow
if((isIntegral!T || isSomeChar!T) &&
n == T.sizeof)
{
EndianSwapper!T es = void;
es.array = val;
version(LittleEndian)
immutable retval = swapEndian(es.value);
else
immutable retval = es.value;
return retval;
}
private T bigEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow
if((is(Unqual!T == float) || is(Unqual!T == double)) &&
n == T.sizeof)
{
version(LittleEndian)
return floatEndianImpl!(n, true)(val);
else
return floatEndianImpl!(n, false)(val);
}
/++
Converts the given value from the native endianness to little endian and
returns it as a $(D ubyte[n]) where $(D n) is the size of the given type.
Returning a $(D ubyte[n]) helps prevent accidentally using a swapped value
as a regular one (and in the case of floating point values, it's necessary,
because the FPU will mess up any swapped floating point values. So, you
can't actually have swapped floating point values as floating point values).
Examples:
--------------------
int i = 12345;
ubyte[4] swappedI = nativeToLittleEndian(i);
assert(i == littleEndianToNative!int(swappedI));
double d = 123.45;
ubyte[8] swappedD = nativeToLittleEndian(d);
assert(d == littleEndianToNative!double(swappedD));
--------------------
+/
auto nativeToLittleEndian(T)(T val) @safe pure nothrow
if(isIntegral!T || isSomeChar!T || is(Unqual!T == float) || is(Unqual!T == double))
{
return nativeToLittleEndianImpl(val);
}
//Verify Examples.
unittest
{
int i = 12345;
ubyte[4] swappedI = nativeToLittleEndian(i);
assert(i == littleEndianToNative!int(swappedI));
double d = 123.45;
ubyte[8] swappedD = nativeToLittleEndian(d);
assert(d == littleEndianToNative!double(swappedD));
}
private auto nativeToLittleEndianImpl(T)(T val) @safe pure nothrow
if(isIntegral!T || isSomeChar!T)
{
EndianSwapper!T es = void;
version(BigEndian)
es.value = swapEndian(val);
else
es.value = val;
return es.array;
}
private auto nativeToLittleEndianImpl(T)(T val) @safe pure nothrow
if(is(Unqual!T == float) || is(Unqual!T == double))
{
version(BigEndian)
return floatEndianImpl!(T, true)(val);
else
return floatEndianImpl!(T, false)(val);
}
unittest
{
import std.stdio;
import std.typetuple;
foreach(T; TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong,
char, wchar, dchar,
float, double))
{
scope(failure) writefln("Failed type: %s", T.stringof);
T val;
const T cval;
immutable T ival;
//is instead of == because of NaN for floating point values.
assert(littleEndianToNative!T(nativeToLittleEndian(val)) is val);
assert(littleEndianToNative!T(nativeToLittleEndian(cval)) is cval);
assert(littleEndianToNative!T(nativeToLittleEndian(ival)) is ival);
assert(littleEndianToNative!T(nativeToLittleEndian(T.min)) == T.min);
assert(littleEndianToNative!T(nativeToLittleEndian(T.max)) == T.max);
static if(isSigned!T)
assert(littleEndianToNative!T(nativeToLittleEndian(cast(T)0)) == 0);
foreach(i; 2 .. 10)
{
immutable T maxI = cast(T)(T.max / i);
immutable T minI = cast(T)(T.min / i);
assert(littleEndianToNative!T(nativeToLittleEndian(maxI)) == maxI);
static if(isSigned!T)
assert(littleEndianToNative!T(nativeToLittleEndian(minI)) == minI);
}
}
}
/++
Converts the given value from little endian to the native endianness and
returns it. The value is given as a $(D ubyte[n]) where $(D n) is the size
of the target type. You must give the target type as a template argument,
because there are multiple types with the same size and so the type of the
argument is not enough to determine the return type.
Taking a $(D ubyte[n]) helps prevent accidentally using a swapped value
as a regular one (and in the case of floating point values, it's necessary,
because the FPU will mess up any swapped floating point values. So, you
can't actually have swapped floating point values as floating point values).
$(D real) is not supported, because its size is implementation-dependent
and therefore could vary from machine to machine (which could make it
unusable if you tried to transfer it to another machine).
Examples:
--------------------
ushort i = 12345;
ubyte[2] swappedI = nativeToLittleEndian(i);
assert(i == littleEndianToNative!ushort(swappedI));
dchar c = 'D';
ubyte[4] swappedC = nativeToLittleEndian(c);
assert(c == littleEndianToNative!dchar(swappedC));
--------------------
+/
T littleEndianToNative(T, size_t n)(ubyte[n] val) @safe pure nothrow
if((isIntegral!T || isSomeChar!T || is(Unqual!T == float) || is(Unqual!T == double)) &&
n == T.sizeof)
{
return littleEndianToNativeImpl!T(val);
}
//Verify Unittest.
unittest
{
ushort i = 12345;
ubyte[2] swappedI = nativeToLittleEndian(i);
assert(i == littleEndianToNative!ushort(swappedI));
dchar c = 'D';
ubyte[4] swappedC = nativeToLittleEndian(c);
assert(c == littleEndianToNative!dchar(swappedC));
}
private T littleEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow
if((isIntegral!T || isSomeChar!T) &&
n == T.sizeof)
{
EndianSwapper!T es = void;
es.array = val;
version(BigEndian)
immutable retval = swapEndian(es.value);
else
immutable retval = es.value;
return retval;
}
private T littleEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow
if(((is(Unqual!T == float) || is(Unqual!T == double)) &&
n == T.sizeof))
{
version(BigEndian)
return floatEndianImpl!(n, true)(val);
else
return floatEndianImpl!(n, false)(val);
}
private auto floatEndianImpl(T, bool swap)(T val) @safe pure nothrow
if(is(Unqual!T == float) || is(Unqual!T == double))
{
EndianSwapper!T es = void;
es.value = val;
static if(swap)
es.intValue = swapEndian(es.intValue);
return es.array;
}
private auto floatEndianImpl(size_t n, bool swap)(ubyte[n] val) @safe pure nothrow
if(n == 4 || n == 8)
{
static if(n == 4) EndianSwapper!float es = void;
else static if(n == 8) EndianSwapper!double es = void;
es.array = val;
static if(swap)
es.intValue = swapEndian(es.intValue);
return es.value;
}