github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/leb128/leb128.go (about) 1 package leb128 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 ) 8 9 const ( 10 maxVarintLen32 = 5 11 maxVarintLen33 = maxVarintLen32 12 maxVarintLen64 = 10 13 14 int33Mask int64 = 1 << 7 15 int33Mask2 = ^int33Mask 16 int33Mask3 = 1 << 6 17 int33Mask4 = 8589934591 // 2^33-1 18 int33Mask5 = 1 << 32 19 int33Mask6 = int33Mask4 + 1 // 2^33 20 21 int64Mask3 = 1 << 6 22 int64Mask4 = ^0 23 ) 24 25 var ( 26 errOverflow32 = errors.New("overflows a 32-bit integer") 27 errOverflow33 = errors.New("overflows a 33-bit integer") 28 errOverflow64 = errors.New("overflows a 64-bit integer") 29 ) 30 31 // EncodeInt32 encodes the signed value into a buffer in LEB128 format 32 // 33 // See https://en.wikipedia.org/wiki/LEB128#Encode_signed_integer 34 func EncodeInt32(value int32) []byte { 35 return EncodeInt64(int64(value)) 36 } 37 38 // EncodeInt64 encodes the signed value into a buffer in LEB128 format 39 // 40 // See https://en.wikipedia.org/wiki/LEB128#Encode_signed_integer 41 func EncodeInt64(value int64) (buf []byte) { 42 for { 43 // Take 7 remaining low-order bits from the value into b. 44 b := uint8(value & 0x7f) 45 // Extract the sign bit. 46 s := uint8(value & 0x40) 47 value >>= 7 48 49 // The encoding unsigned numbers is simpler as it only needs to check if the value is non-zero to tell if there 50 // are more bits to encode. Signed is a little more complicated as you have to double-check the sign bit. 51 // If either case, set the high-order bit to tell the reader there are more bytes in this int. 52 if (value != -1 || s == 0) && (value != 0 || s != 0) { 53 b |= 0x80 54 } 55 56 // Append b into the buffer 57 buf = append(buf, b) 58 if b&0x80 == 0 { 59 break 60 } 61 } 62 return buf 63 } 64 65 // EncodeUint32 encodes the value into a buffer in LEB128 format 66 // 67 // See https://en.wikipedia.org/wiki/LEB128#Encode_unsigned_integer 68 func EncodeUint32(value uint32) []byte { 69 return EncodeUint64(uint64(value)) 70 } 71 72 // EncodeUint64 encodes the value into a buffer in LEB128 format 73 // 74 // See https://en.wikipedia.org/wiki/LEB128#Encode_unsigned_integer 75 func EncodeUint64(value uint64) (buf []byte) { 76 // This is effectively a do/while loop where we take 7 bits of the value and encode them until it is zero. 77 for { 78 // Take 7 remaining low-order bits from the value into b. 79 b := uint8(value & 0x7f) 80 value = value >> 7 81 82 // If there are remaining bits, the value won't be zero: Set the high- 83 // order bit to tell the reader there are more bytes in this uint. 84 if value != 0 { 85 b |= 0x80 86 } 87 88 // Append b into the buffer 89 buf = append(buf, b) 90 if b&0x80 == 0 { 91 return buf 92 } 93 } 94 } 95 96 type nextByte func(i int) (byte, error) 97 98 func DecodeUint32(r io.ByteReader) (ret uint32, bytesRead uint64, err error) { 99 return decodeUint32(func(_ int) (byte, error) { return r.ReadByte() }) 100 } 101 102 func LoadUint32(buf []byte) (ret uint32, bytesRead uint64, err error) { 103 return decodeUint32(func(i int) (byte, error) { 104 if i >= len(buf) { 105 return 0, io.EOF 106 } 107 return buf[i], nil 108 }) 109 } 110 111 func decodeUint32(next nextByte) (ret uint32, bytesRead uint64, err error) { 112 // Derived from https://github.com/golang/go/blob/go1.20/src/encoding/binary/varint.go 113 // with the modification on the overflow handling tailored for 32-bits. 114 var s uint32 115 for i := 0; i < maxVarintLen32; i++ { 116 b, err := next(i) 117 if err != nil { 118 return 0, 0, err 119 } 120 if b < 0x80 { 121 // Unused bits must be all zero. 122 if i == maxVarintLen32-1 && (b&0xf0) > 0 { 123 return 0, 0, errOverflow32 124 } 125 return ret | uint32(b)<<s, uint64(i) + 1, nil 126 } 127 ret |= (uint32(b) & 0x7f) << s 128 s += 7 129 } 130 return 0, 0, errOverflow32 131 } 132 133 func LoadUint64(buf []byte) (ret uint64, bytesRead uint64, err error) { 134 bufLen := len(buf) 135 if bufLen == 0 { 136 return 0, 0, io.EOF 137 } 138 139 // Derived from https://github.com/golang/go/blob/go1.20/src/encoding/binary/varint.go 140 var s uint64 141 for i := 0; i < maxVarintLen64; i++ { 142 if i >= bufLen { 143 return 0, 0, io.EOF 144 } 145 b := buf[i] 146 if b < 0x80 { 147 // Unused bits (non first bit) must all be zero. 148 if i == maxVarintLen64-1 && b > 1 { 149 return 0, 0, errOverflow64 150 } 151 return ret | uint64(b)<<s, uint64(i) + 1, nil 152 } 153 ret |= (uint64(b) & 0x7f) << s 154 s += 7 155 } 156 return 0, 0, errOverflow64 157 } 158 159 func DecodeInt32(r io.ByteReader) (ret int32, bytesRead uint64, err error) { 160 return decodeInt32(func(_ int) (byte, error) { return r.ReadByte() }) 161 } 162 163 func LoadInt32(buf []byte) (ret int32, bytesRead uint64, err error) { 164 return decodeInt32(func(i int) (byte, error) { 165 if i >= len(buf) { 166 return 0, io.EOF 167 } 168 return buf[i], nil 169 }) 170 } 171 172 func decodeInt32(next nextByte) (ret int32, bytesRead uint64, err error) { 173 var shift int 174 var b byte 175 for { 176 b, err = next(int(bytesRead)) 177 if err != nil { 178 return 0, 0, fmt.Errorf("readByte failed: %w", err) 179 } 180 ret |= (int32(b) & 0x7f) << shift 181 shift += 7 182 bytesRead++ 183 if b&0x80 == 0 { 184 if shift < 32 && (b&0x40) != 0 { 185 ret |= ^0 << shift 186 } 187 // Over flow checks. 188 // fixme: can be optimized. 189 if bytesRead > maxVarintLen32 { 190 return 0, 0, errOverflow32 191 } else if unused := b & 0b00110000; bytesRead == maxVarintLen32 && ret < 0 && unused != 0b00110000 { 192 return 0, 0, errOverflow32 193 } else if bytesRead == maxVarintLen32 && ret >= 0 && unused != 0x00 { 194 return 0, 0, errOverflow32 195 } 196 return 197 } 198 } 199 } 200 201 // DecodeInt33AsInt64 is a special cased decoder for wasm.BlockType which is encoded as a positive signed integer, yet 202 // still needs to fit the 32-bit range of allowed indices. Hence, this is 33, not 32-bit! 203 // 204 // See https://webassembly.github.io/spec/core/binary/instructions.html#control-instructions 205 func DecodeInt33AsInt64(r io.ByteReader) (ret int64, bytesRead uint64, err error) { 206 var shift int 207 var b int64 208 var rb byte 209 for shift < 35 { 210 rb, err = r.ReadByte() 211 if err != nil { 212 return 0, 0, fmt.Errorf("readByte failed: %w", err) 213 } 214 b = int64(rb) 215 ret |= (b & int33Mask2) << shift 216 shift += 7 217 bytesRead++ 218 if b&int33Mask == 0 { 219 break 220 } 221 } 222 223 // fixme: can be optimized 224 if shift < 33 && (b&int33Mask3) == int33Mask3 { 225 ret |= int33Mask4 << shift 226 } 227 ret = ret & int33Mask4 228 229 // if 33rd bit == 1, we translate it as a corresponding signed-33bit minus value 230 if ret&int33Mask5 > 0 { 231 ret = ret - int33Mask6 232 } 233 // Over flow checks. 234 // fixme: can be optimized. 235 if bytesRead > maxVarintLen33 { 236 return 0, 0, errOverflow33 237 } else if unused := b & 0b00100000; bytesRead == maxVarintLen33 && ret < 0 && unused != 0b00100000 { 238 return 0, 0, errOverflow33 239 } else if bytesRead == maxVarintLen33 && ret >= 0 && unused != 0x00 { 240 return 0, 0, errOverflow33 241 } 242 return ret, bytesRead, nil 243 } 244 245 func DecodeInt64(r io.ByteReader) (ret int64, bytesRead uint64, err error) { 246 return decodeInt64(func(_ int) (byte, error) { return r.ReadByte() }) 247 } 248 249 func LoadInt64(buf []byte) (ret int64, bytesRead uint64, err error) { 250 return decodeInt64(func(i int) (byte, error) { 251 if i >= len(buf) { 252 return 0, io.EOF 253 } 254 return buf[i], nil 255 }) 256 } 257 258 func decodeInt64(next nextByte) (ret int64, bytesRead uint64, err error) { 259 var shift int 260 var b byte 261 for { 262 b, err = next(int(bytesRead)) 263 if err != nil { 264 return 0, 0, fmt.Errorf("readByte failed: %w", err) 265 } 266 ret |= (int64(b) & 0x7f) << shift 267 shift += 7 268 bytesRead++ 269 if b&0x80 == 0 { 270 if shift < 64 && (b&int64Mask3) == int64Mask3 { 271 ret |= int64Mask4 << shift 272 } 273 // Over flow checks. 274 // fixme: can be optimized. 275 if bytesRead > maxVarintLen64 { 276 return 0, 0, errOverflow64 277 } else if unused := b & 0b00111110; bytesRead == maxVarintLen64 && ret < 0 && unused != 0b00111110 { 278 return 0, 0, errOverflow64 279 } else if bytesRead == maxVarintLen64 && ret >= 0 && unused != 0x00 { 280 return 0, 0, errOverflow64 281 } 282 return 283 } 284 } 285 }