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