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