github.com/taubyte/vm-wasm-utils@v1.0.2/leb128/leb128.go (about) 1 package leb128 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 ) 8 9 const ( 10 maxVarintLen32 = 5 11 maxVarintLen64 = 10 12 ) 13 14 var ( 15 errOverflow32 = errors.New("overflows a 32-bit integer") 16 errOverflow33 = errors.New("overflows a 33-bit integer") 17 errOverflow64 = errors.New("overflows a 64-bit integer") 18 ) 19 20 // encodeCache reduces allocations by returning a constant slice of size one for values that can encode in a single byte 21 var encodeCache = [0x80][1]byte{ 22 {0x00}, {0x01}, {0x02}, {0x03}, {0x04}, {0x05}, {0x06}, {0x07}, {0x08}, {0x09}, {0x0a}, {0x0b}, {0x0c}, {0x0d}, {0x0e}, {0x0f}, 23 {0x10}, {0x11}, {0x12}, {0x13}, {0x14}, {0x15}, {0x16}, {0x17}, {0x18}, {0x19}, {0x1a}, {0x1b}, {0x1c}, {0x1d}, {0x1e}, {0x1f}, 24 {0x20}, {0x21}, {0x22}, {0x23}, {0x24}, {0x25}, {0x26}, {0x27}, {0x28}, {0x29}, {0x2a}, {0x2b}, {0x2c}, {0x2d}, {0x2e}, {0x2f}, 25 {0x30}, {0x31}, {0x32}, {0x33}, {0x34}, {0x35}, {0x36}, {0x37}, {0x38}, {0x39}, {0x3a}, {0x3b}, {0x3c}, {0x3d}, {0x3e}, {0x3f}, 26 {0x40}, {0x41}, {0x42}, {0x43}, {0x44}, {0x45}, {0x46}, {0x47}, {0x48}, {0x49}, {0x4a}, {0x4b}, {0x4c}, {0x4d}, {0x4e}, {0x4f}, 27 {0x50}, {0x51}, {0x52}, {0x53}, {0x54}, {0x55}, {0x56}, {0x57}, {0x58}, {0x59}, {0x5a}, {0x5b}, {0x5c}, {0x5d}, {0x5e}, {0x5f}, 28 {0x60}, {0x61}, {0x62}, {0x63}, {0x64}, {0x65}, {0x66}, {0x67}, {0x68}, {0x69}, {0x6a}, {0x6b}, {0x6c}, {0x6d}, {0x6e}, {0x6f}, 29 {0x70}, {0x71}, {0x72}, {0x73}, {0x74}, {0x75}, {0x76}, {0x77}, {0x78}, {0x79}, {0x7a}, {0x7b}, {0x7c}, {0x7d}, {0x7e}, {0x7f}, 30 } 31 32 // EncodeInt32 encodes the signed value into a buffer in LEB128 format 33 // 34 // See https://en.wikipedia.org/wiki/LEB128#Encode_signed_integer 35 func EncodeInt32(value int32) []byte { 36 return EncodeInt64(int64(value)) 37 } 38 39 // EncodeInt64 encodes the signed value into a buffer in LEB128 format 40 // 41 // See https://en.wikipedia.org/wiki/LEB128#Encode_signed_integer 42 func EncodeInt64(value int64) (buf []byte) { 43 for { 44 // Take 7 remaining low-order bits from the value into b. 45 b := uint8(value & 0x7f) 46 // Extract the sign bit. 47 s := uint8(value & 0x40) 48 value >>= 7 49 50 // The encoding unsigned numbers is simpler as it only needs to check if the value is non-zero to tell if there 51 // are more bits to encode. Signed is a little more complicated as you have to double-check the sign bit. 52 // If either case, set the high-order bit to tell the reader there are more bytes in this int. 53 if (value != -1 || s == 0) && (value != 0 || s != 0) { 54 b |= 0x80 55 } 56 57 // Append b into the buffer 58 buf = append(buf, b) 59 if b&0x80 == 0 { 60 break 61 } 62 } 63 return buf 64 } 65 66 // EncodeUint32 encodes the value into a buffer in LEB128 format 67 // 68 // See https://en.wikipedia.org/wiki/LEB128#Encode_unsigned_integer 69 func EncodeUint32(value uint32) []byte { 70 return EncodeUint64(uint64(value)) 71 } 72 73 // EncodeUint64 encodes the value into a buffer in LEB128 format 74 // 75 // See https://en.wikipedia.org/wiki/LEB128#Encode_unsigned_integer 76 func EncodeUint64(value uint64) (buf []byte) { 77 if value < 0x80 { 78 return encodeCache[value][:] 79 } 80 81 // This is effectively a do/while loop where we take 7 bits of the value and encode them until it is zero. 82 for { 83 // Take 7 remaining low-order bits from the value into b. 84 b := uint8(value & 0x7f) 85 value = value >> 7 86 87 // If there are remaining bits, the value won't be zero: Set the high- 88 // order bit to tell the reader there are more bytes in this uint. 89 if value != 0 { 90 b |= 0x80 91 } 92 93 // Append b into the buffer 94 buf = append(buf, b) 95 if b&0x80 == 0 { 96 return buf 97 } 98 } 99 } 100 101 func DecodeUint32(r *bytes.Reader) (ret uint32, bytesRead uint64, err error) { 102 // Derived from https://github.com/golang/go/blob/aafad20b617ee63d58fcd4f6e0d98fe27760678c/src/encoding/binary/varint.go 103 // with the modification on the overflow handling tailored for 32-bits. 104 var s uint32 105 for i := 0; i < maxVarintLen32; i++ { 106 b, err := r.ReadByte() 107 if err != nil { 108 return 0, 0, err 109 } 110 if b < 0x80 { 111 // Unused bits must be all zero. 112 if i == maxVarintLen32-1 && (b&0xf0) > 0 { 113 return 0, 0, errOverflow32 114 } 115 return ret | uint32(b)<<s, uint64(i) + 1, nil 116 } 117 ret |= (uint32(b) & 0x7f) << s 118 s += 7 119 } 120 return 0, 0, errOverflow32 121 } 122 123 func DecodeUint64(r *bytes.Reader) (ret uint64, bytesRead uint64, err error) { 124 // Derived from https://github.com/golang/go/blob/aafad20b617ee63d58fcd4f6e0d98fe27760678c/src/encoding/binary/varint.go 125 var s uint64 126 for i := 0; i < maxVarintLen64; i++ { 127 b, err := r.ReadByte() 128 if err != nil { 129 return 0, 0, err 130 } 131 if b < 0x80 { 132 // Unused bits (non first bit) must all be zero. 133 if i == maxVarintLen64-1 && b > 1 { 134 return 0, 0, errOverflow64 135 } 136 return ret | uint64(b)<<s, uint64(i) + 1, nil 137 } 138 ret |= (uint64(b) & 0x7f) << s 139 s += 7 140 } 141 return 0, 0, errOverflow64 142 } 143 144 func DecodeInt32(r *bytes.Reader) (ret int32, bytesRead uint64, err error) { 145 var shift int 146 var b byte 147 for { 148 b, err = r.ReadByte() 149 if err != nil { 150 return 0, 0, fmt.Errorf("readByte failed: %w", err) 151 } 152 ret |= (int32(b) & 0x7f) << shift 153 shift += 7 154 bytesRead++ 155 if b&0x80 == 0 { 156 if shift < 32 && (b&0x40) != 0 { 157 ret |= ^0 << shift 158 } 159 // Over flow checks. 160 // fixme: can be optimized. 161 if bytesRead > 5 { 162 return 0, 0, errOverflow32 163 } else if unused := b & 0b00110000; bytesRead == 5 && ret < 0 && unused != 0b00110000 { 164 return 0, 0, errOverflow32 165 } else if bytesRead == 5 && ret >= 0 && unused != 0x00 { 166 return 0, 0, errOverflow32 167 } 168 return 169 } 170 } 171 } 172 173 // DecodeInt33AsInt64 is a special cased decoder for wasm.BlockType which is encoded as a positive signed integer, yet 174 // still needs to fit the 32-bit range of allowed indices. Hence, this is 33, not 32-bit! 175 // 176 // See https://webassembly.github.io/spec/core/binary/instructions.html#control-instructions 177 func DecodeInt33AsInt64(r *bytes.Reader) (ret int64, bytesRead uint64, err error) { 178 const ( 179 int33Mask int64 = 1 << 7 180 int33Mask2 = ^int33Mask 181 int33Mask3 = 1 << 6 182 int33Mask4 = 8589934591 // 2^33-1 183 int33Mask5 = 1 << 32 184 int33Mask6 = int33Mask4 + 1 // 2^33 185 ) 186 var shift int 187 var b int64 188 var rb byte 189 for shift < 35 { 190 rb, err = r.ReadByte() 191 if err != nil { 192 return 0, 0, fmt.Errorf("readByte failed: %w", err) 193 } 194 b = int64(rb) 195 ret |= (b & int33Mask2) << shift 196 shift += 7 197 bytesRead++ 198 if b&int33Mask == 0 { 199 break 200 } 201 } 202 203 // fixme: can be optimized 204 if shift < 33 && (b&int33Mask3) == int33Mask3 { 205 ret |= int33Mask4 << shift 206 } 207 ret = ret & int33Mask4 208 209 // if 33rd bit == 1, we translate it as a corresponding signed-33bit minus value 210 if ret&int33Mask5 > 0 { 211 ret = ret - int33Mask6 212 } 213 // Over flow checks. 214 // fixme: can be optimized. 215 if bytesRead > 5 { 216 return 0, 0, errOverflow33 217 } else if unused := b & 0b00100000; bytesRead == 5 && ret < 0 && unused != 0b00100000 { 218 return 0, 0, errOverflow33 219 } else if bytesRead == 5 && ret >= 0 && unused != 0x00 { 220 return 0, 0, errOverflow33 221 } 222 return ret, bytesRead, nil 223 } 224 225 func DecodeInt64(r *bytes.Reader) (ret int64, bytesRead uint64, err error) { 226 const ( 227 int64Mask3 = 1 << 6 228 int64Mask4 = ^0 229 ) 230 var shift int 231 var b byte 232 for { 233 b, err = r.ReadByte() 234 if err != nil { 235 return 0, 0, fmt.Errorf("readByte failed: %w", err) 236 } 237 ret |= (int64(b) & 0x7f) << shift 238 shift += 7 239 bytesRead++ 240 if b&0x80 == 0 { 241 if shift < 64 && (b&int64Mask3) == int64Mask3 { 242 ret |= int64Mask4 << shift 243 } 244 // Over flow checks. 245 // fixme: can be optimized. 246 if bytesRead > 10 { 247 return 0, 0, errOverflow64 248 } else if unused := b & 0b00111110; bytesRead == 10 && ret < 0 && unused != 0b00111110 { 249 return 0, 0, errOverflow64 250 } else if bytesRead == 10 && ret >= 0 && unused != 0x00 { 251 return 0, 0, errOverflow64 252 } 253 return 254 } 255 } 256 }