1 module xbuffer.varint; 2 3 import std.traits : isIntegral, isSigned, isUnsigned, Unsigned; 4 5 import xbuffer.buffer : Buffer; 6 7 enum isVar(T) = is(T == Var!V, V); 8 9 // debug 10 import std.stdio : writeln; 11 12 unittest { 13 14 static assert(isVar!varshort); 15 static assert(!isVar!short); 16 17 } 18 19 struct Var(T) if(isIntegral!T && T.sizeof > 1) { 20 21 alias Base = T; 22 23 static if(isSigned!T) private alias U = Unsigned!T; 24 else private enum size_t limit = T.sizeof * 8 / 7 + 1; 25 26 @disable this(); 27 28 static void encode(Buffer buffer, T value) pure nothrow @safe @nogc { 29 static if(isUnsigned!T) { 30 while(value > 0x7F) { 31 buffer.write!ubyte((value & 0x7F) | 0x80); 32 value >>>= 7; 33 } 34 buffer.write!ubyte(value & 0x7F); 35 } else { 36 static if(T.sizeof < int.sizeof) Var!U.encode(buffer, cast(U)(value >= 0 ? value << 1 : (-cast(int)value << 1) - 1)); 37 else Var!U.encode(buffer, value >= 0 ? value << 1 : (-value << 1) - 1); 38 } 39 } 40 41 static T decode(bool consume)(Buffer buffer) pure @safe { 42 size_t count = 0; 43 static if(!consume) scope(success) buffer.back(count); 44 return decodeImpl(buffer, count); 45 } 46 47 static T decodeImpl(Buffer buffer, ref size_t count) pure @safe { 48 static if(isUnsigned!T) { 49 scope(failure) buffer.back(count); 50 T ret; 51 ubyte next; 52 do { 53 next = buffer.read!ubyte(); 54 ret |= T(next & 0x7F) << (count++ * 7); 55 } while(next > 0x7F && count < limit); 56 return ret; 57 } else { 58 U ret = Var!U.decodeImpl(buffer, count); 59 if(ret & 1) return ((ret >> 1) + 1) * -1; 60 else return ret >> 1; 61 } 62 } 63 64 } 65 66 alias varshort = Var!short; 67 68 alias varushort = Var!ushort; 69 70 alias varint = Var!int; 71 72 alias varuint = Var!uint; 73 74 alias varlong = Var!long; 75 76 alias varulong = Var!ulong; 77 78 unittest { 79 80 Buffer buffer = new Buffer(16); 81 82 varint.encode(buffer, 0); 83 assert(buffer.data!ubyte == [0]); 84 85 buffer.reset(); 86 varshort.encode(buffer, -1); 87 varint.encode(buffer, 1); 88 varint.encode(buffer, -2); 89 assert(buffer.data!ubyte == [1, 2, 3]); 90 91 buffer.reset(); 92 varint.encode(buffer, 2147483647); 93 varint.encode(buffer, -2147483648); 94 assert(buffer.data!ubyte == [254, 255, 255, 255, 15, 255, 255, 255, 255, 15]); 95 96 assert(varint.decode!true(buffer) == 2147483647); 97 assert(varint.decode!true(buffer) == -2147483648); 98 99 buffer.data = cast(ubyte[])[1, 2, 3]; 100 assert(varint.decode!false(buffer) == -1); 101 assert(varint.decode!true(buffer) == -1); 102 assert(varint.decode!true(buffer) == 1); 103 assert(varint.decode!true(buffer) == -2); 104 105 varuint.encode(buffer, 1); 106 varuint.encode(buffer, 2); 107 varuint.encode(buffer, uint.max); 108 assert(buffer.data!ubyte == [1, 2, 255, 255, 255, 255, 15]); 109 assert(varushort.decode!true(buffer) == 1); 110 assert(varuint.decode!true(buffer) == 2); 111 assert(varulong.decode!true(buffer) == uint.max); 112 113 // limit 114 115 buffer.data = cast(ubyte[])[255, 255, 255, 255, 255, 255]; 116 varuint.decode!true(buffer); 117 assert(buffer.data!ubyte == [255]); 118 119 // exception 120 121 import xbuffer.buffer : BufferOverflowException; 122 123 buffer.data = cast(ubyte[])[255, 255, 255]; 124 try { 125 varuint.decode!true(buffer); assert(0); 126 } catch(BufferOverflowException) { 127 assert(buffer.data!ubyte == [255, 255, 255]); 128 varushort.decode!true(buffer); 129 assert(buffer.data.length == 0); 130 } 131 132 }