1 /// variable length unsigned integers 2 module sbin.vluint; 3 4 import std.range; 5 import std.traits : isUnsigned; 6 7 /// 8 void dumpVLUInt(R)(ref R r, ulong val) if (isOutputRange!(R, ubyte)) 9 { 10 immutable mask = cast(ubyte)0b0111_1111; 11 immutable flag = cast(ubyte)0b1000_0000; 12 do 13 { 14 put(r, cast(ubyte)((val & mask) + (val >= flag ? flag : 0))); 15 val >>= 7; 16 } 17 while (val > 0); 18 } 19 20 version (unittest) 21 { 22 import std.array : appender; 23 import std.exception : enforce; 24 } 25 26 @safe 27 unittest 28 { 29 auto buf = appender!(ubyte[]); 30 foreach (i; 0 .. 128) 31 { 32 buf.clear(); 33 dumpVLUInt(buf, i); 34 assert (buf.data.length == 1); 35 assert (buf.data[0] == i); 36 } 37 38 foreach (i; 128 .. 256) 39 { 40 buf.clear(); 41 dumpVLUInt(buf, i); 42 assert (buf.data.length == 2); 43 assert (buf.data[0] == (i & 0b0111_1111) + 0b1000_0000); 44 assert (buf.data[1] == 1); 45 } 46 47 void test(ulong val, ubyte[] need, string file=__FILE__, size_t line=__LINE__) 48 { 49 buf.clear(); 50 dumpVLUInt(buf, val); 51 enforce(buf.data == need, "fail", file, line); 52 } 53 54 test(127, [0b0111_1111]); 55 test(128, [0b1000_0000, 1]); 56 test(129, [0b1000_0001, 1]); 57 test(130, [0b1000_0010, 1]); 58 59 test(255, [0b1111_1111, 1]); 60 test(256, [0b1000_0000, 0b10]); 61 test(257, [0b1000_0001, 0b10]); 62 63 test(16_382, [0b1111_1110, 0b0111_1111]); 64 test(16_383, [0b1111_1111, 0b0111_1111]); 65 test(16_384, [0b1000_0000, 0b1000_0000, 1]); 66 test(16_385, [0b1000_0001, 0b1000_0000, 1]); 67 68 enum b = ubyte.max; 69 test(ulong.max-3, [b-3,b,b, b,b,b, b,b,b, 1]); 70 test(ulong.max-2, [b-2,b,b, b,b,b, b,b,b, 1]); 71 test(ulong.max-1, [b-1,b,b, b,b,b, b,b,b, 1]); 72 test(ulong.max, [b,b,b, b,b,b, b,b,b, 1]); 73 } 74 75 /// 76 ulong readVLUInt(R)(auto ref R r) 77 if (is(typeof(r.front()) == ubyte) && is(typeof(r.popFront()))) 78 { 79 uint count; 80 return readVLUInt(r, count); 81 } 82 83 /// 84 ulong readVLUInt(R, T)(auto ref R r, out T count) //if (isInputRange!(R, ubyte)) 85 if (is(typeof(r.front()) == ubyte) && is(typeof(r.popFront())) && is(T : size_t)) 86 { 87 immutable mask = 0b0111_1111uL; 88 immutable flag = 0b1000_0000uL; 89 90 ubyte b = 0; 91 92 ulong ret; 93 94 while (true) 95 { 96 const v = r.front(); r.popFront(); 97 ret += (v & mask) << (b++ * 7); 98 if (!(v & flag)) 99 { 100 count = b; 101 return ret; 102 } 103 if (b > 10) throw new Exception("so many data for one ulong value"); 104 } 105 } 106 107 @safe 108 unittest 109 { 110 foreach (ubyte i; 0 .. 128) 111 { 112 ubyte[] buf = [i]; 113 assert(readVLUInt(buf) == i); 114 } 115 116 @safe 117 void test(ulong val, ubyte[] need, string file=__FILE__, size_t line=__LINE__) 118 { 119 int cnt; 120 auto readed = readVLUInt(need.dup, cnt); 121 //import std.stdio; 122 //stderr.writeln(cnt, " ", need.length); 123 //stderr.writeln(val, " ", readed, " ", val-readed); 124 assert (cnt == need.length); 125 enforce(readed == val, "op", file, line); 126 } 127 128 test(127, [0b0111_1111]); 129 test(128, [0b1000_0000, 1]); 130 test(129, [0b1000_0001, 1]); 131 test(130, [0b1000_0010, 1]); 132 133 test(255, [0b1111_1111, 1]); 134 test(256, [0b1000_0000, 0b10]); 135 test(257, [0b1000_0001, 0b10]); 136 137 test(16_382, [0b1111_1110, 0b0111_1111]); 138 test(16_383, [0b1111_1111, 0b0111_1111]); 139 test(16_384, [0b1000_0000, 0b1000_0000, 1]); 140 test(16_385, [0b1000_0001, 0b1000_0000, 1]); 141 142 enum b = ubyte.max; 143 144 test(ulong.max, [b,b,b, b,b,b, b,b,b, 1]); 145 test(ulong.max-1, [b-1,b,b, b,b,b, b,b,b, 1]); 146 test(ulong.max-2, [b-2,b,b, b,b,b, b,b,b, 1]); 147 } 148 149 @safe 150 unittest 151 { 152 static struct StaticBuffer(size_t N) 153 { 154 ubyte[N] data; 155 size_t c; 156 157 void put(ubyte v) { data[c++] = v; } 158 159 ubyte front() { return data[c]; } 160 void popFront() { c++; } 161 bool empty() { return data.length == c; } 162 } 163 164 StaticBuffer!10 buf; 165 166 import std.range : chain, iota, repeat, take; 167 import std.random : uniform, Random; 168 169 auto rnd = Random(12); 170 171 auto values = chain(iota(ubyte.max), 172 [ushort.max/2, ushort.max-1, ushort.max, ushort.max+1, ushort.max+2, ushort.max * 2], 173 [uint.max/2, uint.max-1, uint.max, uint.max+1, uint.max+2, uint.max * 2], 174 [ulong.max/2, ulong.max-1, ulong.max], 175 uniform(0, ulong.max, rnd).repeat.take(10_000_000) 176 ); 177 178 import std.datetime.stopwatch; 179 180 StopWatch sw1, sw2; 181 182 foreach (i; values) 183 { 184 buf.c = 0; 185 186 sw1.start(); 187 dumpVLUInt(buf, i); 188 sw1.stop(); 189 190 buf.c = 0; 191 192 sw2.start(); 193 const v = readVLUInt(buf); 194 sw2.stop(); 195 assert(i == v); 196 } 197 198 //import std.stdio; 199 //stderr.writeln("dump: ", sw1.peek()); 200 //stderr.writeln("read: ", sw2.peek()); 201 }