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 }