1 module varint; 2 import utils; 3 4 // Copyright Stefan Koch 2015 - 2018. 5 // Distributed under the Boost Software License, Version 1.0. 6 // (See accompanying file LICENSE.md or copy at 7 // http://www.boost.org/LICENSE_1_0.txt) 8 9 enum unrolled = true; 10 11 struct VarInt 12 { 13 pure nothrow @safe @nogc: 14 this(BigEndian!long value) @trusted 15 { 16 //FIXME the constructor does not work for value bigger then uint.max; 17 auto len = lengthInVarInt(value); 18 long beValue = value.asBigEndian; 19 20 auto _len = len; 21 while (_len--) 22 { 23 _storage.ptr[_len] = (beValue & 0x7f) | 0x80; 24 beValue >>= 7; 25 } 26 _storage.ptr[len - 1] &= 0x7f; 27 28 byteArray = _storage[0 .. len]; 29 } 30 31 const ubyte[] byteArray; 32 ubyte[9] _storage; 33 alias toBeLong this; 34 35 //TODO FIXME toBeLong does not correctly convert negative Numbers 36 37 @property BigEndian!long toBeLong() @trusted 38 { 39 long tmp; 40 static if (unrolled) 41 { 42 uint v3 = 0; 43 44 if (byteArray.ptr[0] & 0x80) 45 { 46 v3 = 1; 47 if (byteArray.ptr[1] & 0x80) 48 { 49 v3 = 2; 50 if (byteArray.ptr[2] & 0x80) 51 { 52 v3 = 3; 53 if (byteArray.ptr[3] & 0x80) 54 { 55 v3 = 4; 56 if (byteArray.ptr[4] & 0x80) 57 { 58 v3 = 5; 59 if (byteArray.ptr[5] & 0x80) 60 { 61 v3 = 6; 62 if (byteArray.ptr[6] & 0x80) 63 { 64 v3 = 7; 65 if (byteArray.ptr[7] & 0x80) 66 v3 = 8; 67 } 68 } 69 } 70 } 71 } 72 } 73 } 74 else 75 { 76 BigEndian!long result; 77 78 version (LittleEndian) 79 { 80 result.asNative = (cast(long) byteArray.ptr[0] << 56); 81 } 82 else 83 { 84 result.asNative = (cast(long) byteArray.ptr[0]); 85 } 86 return result; 87 } 88 89 } 90 else 91 { 92 uint length = length(); 93 } 94 95 BigEndian!long result; 96 97 // The hottest loop in the whole program! 98 //TODO There must be a way to speed it up! 99 static if (unrolled) 100 { 101 switch (v3) 102 { 103 case 8: 104 tmp |= ((cast(long) byteArray.ptr[8]) << 7 * (v3 - 8)); 105 goto case 7; 106 case 7: 107 tmp |= ((cast(long) byteArray.ptr[7] & 0x7FUL) << 7 * (v3 - 7)); 108 goto case 6; 109 case 6: 110 tmp |= ((cast(long) byteArray.ptr[6] & 0x7FUL) << 7 * (v3 - 6)); 111 goto case 5; 112 case 5: 113 tmp |= ((cast(long) byteArray.ptr[5] & 0x7FUL) << 7 * (v3 - 5)); 114 goto case 4; 115 case 4: 116 tmp |= ((cast(long) byteArray.ptr[4] & 0x7FUL) << 7 * (v3 - 4)); 117 goto case 3; 118 case 3: 119 tmp |= ((cast(long) byteArray.ptr[3] & 0x7FUL) << 7 * (v3 - 3)); 120 goto case 2; 121 case 2: 122 tmp |= ((cast(long) byteArray.ptr[2] & 0x7FUL) << 7 * (v3 - 2)); 123 goto case 1; 124 case 1: 125 tmp |= ((cast(long) byteArray.ptr[1] & 0x7FUL) << 7 * (v3 - 1)); 126 tmp |= ((cast(long) byteArray.ptr[0] & 0x7FUL) << 7 * (v3 - 0)); 127 break; 128 default: 129 assert(0); 130 131 } 132 } 133 else 134 135 { 136 foreach (idx; 0 .. length) 137 { 138 ubyte val = byteArray[idx]; 139 long maskedVal = (cast(long) val & 0x7fUL); // mask 8th bit 140 long shiftBy = (length - idx - 1UL) * 7UL; 141 if (idx < 8) 142 { 143 tmp |= (maskedVal << shiftBy); 144 } 145 else 146 { 147 tmp |= (cast(long) val << 63UL); 148 } 149 } 150 } 151 //this evokes swapIfNeeded 152 result = tmp; 153 154 return result; 155 } 156 157 this(const ubyte[] _arr) 158 { 159 this.byteArray = _arr; 160 } 161 162 static int lengthInVarInt(BigEndian!long value) 163 { 164 if (value > 1L << 56 || value < 0) 165 { 166 return 9; 167 } 168 else if (value < 1 << 7) 169 { 170 return 1; 171 } 172 else if (value < 1 << 14) 173 { 174 return 2; 175 } 176 else if (value < 1 << 21) 177 { 178 return 3; 179 } 180 else if (value < 1 << 28) 181 { 182 return 4; 183 } 184 else if (value < 1L << 35) 185 { 186 return 5; 187 } 188 else if (value < 1L << 42) 189 { 190 return 6; 191 } 192 else if (value < 1L << 49) 193 { 194 return 7; 195 } 196 else if (value < 1L << 56) 197 { 198 return 8; 199 } 200 assert(0, "We should never get here"); 201 } 202 203 @property uint length() 204 { 205 return _length(byteArray); 206 } 207 208 static uint _length(const ubyte[] arr) 209 { 210 211 foreach (idx; 0 .. 9) 212 { 213 if (arr[idx] & (1 << 7)) 214 { 215 continue; 216 } 217 else 218 { 219 return idx + 1; 220 } 221 assert(0, "we should never get here"); 222 } 223 return 9; 224 } 225 226 static assert(_length((cast(ubyte[])[0x6d, 0x00])) == 1); 227 static assert(_length((cast(ubyte[])[0x7f, 0x00])) == 1); 228 static assert(_length((cast(ubyte[])[0x82, 0x12])) == 2); 229 static assert(_length((cast(ubyte[])[0xfe, 0xfe, 0x00])) == 3); 230 static assert(VarInt((cast(ubyte[])[0x81, 0x01])).toBeLong == 129); 231 static assert(VarInt((cast(ubyte[])[0x81, 0x00])).toBeLong == 0x0080); 232 static assert(VarInt((cast(ubyte[])[0x82, 0x00])).toBeLong == 0x0100); // should be 0x0100 233 static assert(_length((cast(ubyte[])[0x82, 0x80, 0x00])) == 3); 234 static assert(VarInt((cast(ubyte[])[0x84, 0x60, 0x00])).toBeLong == 608); 235 //FIXME make this work! 236 // static assert(VarInt(cast(ubyte[])[0xFF, 0xFF, 0xFF, 0XFF, 0xFF, 0XFF, 0xFF, 0xFF, 0xEA]).toBeLong == -22); 237 static assert(VarInt(bigEndian!long(265)).toBeLong == 265); 238 static assert(VarInt(bigEndian!long(6421)).toBeLong == 6421); 239 static assert(VarInt(bigEndian!long(22)).toBeLong == 22); 240 static assert(VarInt.lengthInVarInt(BigEndian!long(-22)) == 9); 241 static assert(VarInt(bigEndian!long(uint.max)).toBeLong == uint.max); 242 static assert(VarInt(cast(ubyte[])[0xFF, 0xFF, 0xFF, 0XFF, 0xFF, 0XFF, 243 0xFF, 0xFF, 0xEA]) == VarInt(bigEndian(-22L))); 244 //static assert (VarInt().lengthInVarInt(608) == 2); 245 static assert(VarInt((cast(ubyte[])[0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 246 0x89])).toBeLong != 0); 247 }