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