RosettaCodeData/Task/Variable-length-quantity/D/variable-length-quantity.d

86 lines
2.2 KiB
D

import std.stdio, std.string, std.file, std.algorithm;
/// Variable length quantity (unsigned long, max 63-bit).
struct VLQ {
ulong value;
// This allows VLQ to work like an ulong.
alias value this;
uint extract(in ubyte[] v) pure
in {
assert(v.length > 0);
} body {
immutable limit = min(v.length - 1, 8);
ulong t = 0;
size_t idx = 0;
while ((idx < limit) && ((v[idx] & 0x80) > 0))
t = (t << 7) | (0x7f & v[idx++]);
if (idx > limit)
throw new Exception(
"Too large for ulong or invalid format.");
else
value = (t << 7) | v[idx];
return idx + 1;
}
VLQ from(in ubyte[] v) pure {
extract(v);
return this;
}
@property ubyte[] toVLQ() const pure {
ubyte[] v = [0x7f & value];
for (ulong k = value >>> 7; k > 0; k >>>= 7)
v ~= (k & 0x7f) | 0x80;
if (v.length > 9)
throw new Exception("Too large value.");
v.reverse();
return v;
}
static ulong[] split(in ubyte[] b) pure {
ulong[] res;
VLQ v;
for (size_t i = 0; i < b.length; ) {
i += v.extract(b[i .. $]);
res ~= v.value;
}
return res;
}
string toString() const pure /*nothrow*/ {
return format("(%(%02X:%))", this.toVLQ);
}
}
void main() { // VLQ demo code.
VLQ a = VLQ(0x7f),
b = VLQ(0x4000),
c;
writefln("a:%8x = %s\nb:%8x = %s\nc:%8x = %s",
a.value, a, b.value, b, c.value, c);
// Some operations.
c = (a + 1) * b;
a = c - 1;
b = VLQ().from(a.toVLQ);
a <<= 1;
// Convert ulong to octet sequence.
writefln("\na:%8x = %s\nb:%8x = %s\nc:%8x = %s",
a.value, a, b.value, b, c.value, c);
// Write them to a binary file.
std.file.write("vlqtest.bin", a.toVLQ ~ b.toVLQ ~ c.toVLQ);
// Read them back.
const buf = cast(ubyte[])std.file.read("vlqtest.bin");
writefln("\nFile length: %d bytes.", buf.length);
// Convert octet sequence to ulongs.
foreach (immutable i, immutable v; VLQ.split(buf))
writefln("%d:%8x = %s", i + 1, v, VLQ(v));
}