1 /// Little Endian Base - see https://en.wikipedia.org/wiki/LEB128
2 module leb128;
3 
4 import std.conv, std.traits, std.outbuffer;
5 
6 /// Decode LEB128 value encoded in `buf`.
7 const(T) fromLEB128(T)(const ubyte[] buf, ref size_t processed) {
8 	static if (isScalarType!T && !isFloatingPoint!T) {
9 		T result;
10 		size_t shift;
11 		ubyte b;
12 		static if (isSigned!T)
13 			immutable size = T.sizeof * 8;
14 		do {
15 			b = buf[processed];
16 			result |= to!T(b & 0x7f) << shift;
17 			shift += 7;
18 			processed++;
19 		} while (b & 0x80);
20 		static if (isSigned!T)
21 			if ((shift < size) && (b & 0x40) != 0)
22 				result |= to!T(~0 << shift);
23 		return result;
24 	} else {
25 		static assert(0, T.stringof ~ " is not an integral type");
26 	}
27 }	// fromLEB()
28 
29 /// Decode LEB128 value encoded in `buf` into array `val`.
30 void arrayFromLEB128(T)(const ubyte[] buf, ref size_t processed, ref T val) {
31 	static if (isArray!T && !isFloatingPoint!(T[0])) {
32 		size_t n = processed;
33 		foreach (ref result; val) {
34 			static if (isSigned!(typeof(val[0])))
35 				immutable size = result.sizeof * 8;
36 			size_t shift;
37 			ubyte b;
38 			do {
39 				b = buf[n];
40 				result |= to!(typeof(result))(b & 0x7f) << shift;
41 				shift += 7;
42 				n++;
43 			} while (b & 0x80);
44 			static if (isSigned!(typeof(val[0])))
45 				if ((shift < size) && (b & 0x40) != 0)
46 					result |= to!(typeof(result))(~0 << shift);
47 		}
48 		processed = n;
49 	} else {
50 		static assert(0, T.stringof ~ " is not an integral array type");
51 	}
52 }	// arrayFromLEB()
53 
54 /// Encode value `_val` of type `T` into given `buf`.
55 void toLEB128(T)(OutBuffer buf, const T _val) {
56 	static if (isScalarType!T && !isFloatingPoint!T) {
57 		bool more = true;
58 		ubyte[32] d;
59 		size_t idx;
60 		static if (isSigned!T)
61 			long val = _val;
62 		else
63 			ulong val = _val;
64 		do {
65 			ubyte b = val & 0x7f;
66 			val >>= 7;
67 			static if (isSigned!T) {
68 				if ((val == 0 && (b & 0x40) == 0) || (val == -1 && (b & 0x40) != 0))
69 					more = false;
70 				else
71 					b |= 0x80;
72 			} else {
73 				if (val != 0)
74 					b |= 0x80;
75 				else
76 					more = false;
77 			}
78 			d[idx] = b;
79 			idx++;
80 		} while (more);
81 		if (idx > 0)
82 			buf.write(d[0..idx]);
83 	} else static if (isArray!T && !isFloatingPoint!(typeof(_val[0]))) {
84 		ubyte[8192] d;
85 		size_t idx;
86 		foreach (v; _val) {
87 			bool more = true;
88 			static if (isSigned!(typeof(v)))
89 				long val = v;
90 			else
91 				ulong val = v;
92 			do {
93 				ubyte b = val & 0x7f;
94 				val >>= 7;
95 				static if (isSigned!(typeof(v))) {
96 					if ((val == 0 && (b & 0x40) == 0) || (val == -1 && (b & 0x40) != 0))
97 						more = false;
98 					else
99 						b |= 0x80;
100 				} else {
101 					if (val != 0)
102 						b |= 0x80;
103 					else
104 						more = false;
105 				}
106 				d[idx] = b;
107 				if (++idx >= d.length) {
108 					buf.write(d);
109 					idx = 0;
110 				}
111 			} while (more);
112 		}
113 		if (idx > 0)
114 			buf.write(d[0..idx]);
115 	} else {
116 		static assert(0, T.stringof ~ " is not an integral type");
117 	}
118 }	// toLEB128()