summaryrefslogtreecommitdiff
path: root/vendor/github.com/tetratelabs/wazero/internal/leb128/leb128.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/tetratelabs/wazero/internal/leb128/leb128.go')
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/leb128/leb128.go285
1 files changed, 285 insertions, 0 deletions
diff --git a/vendor/github.com/tetratelabs/wazero/internal/leb128/leb128.go b/vendor/github.com/tetratelabs/wazero/internal/leb128/leb128.go
new file mode 100644
index 000000000..a31051724
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/leb128/leb128.go
@@ -0,0 +1,285 @@
+package leb128
+
+import (
+ "errors"
+ "fmt"
+ "io"
+)
+
+const (
+ maxVarintLen32 = 5
+ maxVarintLen33 = maxVarintLen32
+ maxVarintLen64 = 10
+
+ int33Mask int64 = 1 << 7
+ int33Mask2 = ^int33Mask
+ int33Mask3 = 1 << 6
+ int33Mask4 = 8589934591 // 2^33-1
+ int33Mask5 = 1 << 32
+ int33Mask6 = int33Mask4 + 1 // 2^33
+
+ int64Mask3 = 1 << 6
+ int64Mask4 = ^0
+)
+
+var (
+ errOverflow32 = errors.New("overflows a 32-bit integer")
+ errOverflow33 = errors.New("overflows a 33-bit integer")
+ errOverflow64 = errors.New("overflows a 64-bit integer")
+)
+
+// EncodeInt32 encodes the signed value into a buffer in LEB128 format
+//
+// See https://en.wikipedia.org/wiki/LEB128#Encode_signed_integer
+func EncodeInt32(value int32) []byte {
+ return EncodeInt64(int64(value))
+}
+
+// EncodeInt64 encodes the signed value into a buffer in LEB128 format
+//
+// See https://en.wikipedia.org/wiki/LEB128#Encode_signed_integer
+func EncodeInt64(value int64) (buf []byte) {
+ for {
+ // Take 7 remaining low-order bits from the value into b.
+ b := uint8(value & 0x7f)
+ // Extract the sign bit.
+ s := uint8(value & 0x40)
+ value >>= 7
+
+ // The encoding unsigned numbers is simpler as it only needs to check if the value is non-zero to tell if there
+ // are more bits to encode. Signed is a little more complicated as you have to double-check the sign bit.
+ // If either case, set the high-order bit to tell the reader there are more bytes in this int.
+ if (value != -1 || s == 0) && (value != 0 || s != 0) {
+ b |= 0x80
+ }
+
+ // Append b into the buffer
+ buf = append(buf, b)
+ if b&0x80 == 0 {
+ break
+ }
+ }
+ return buf
+}
+
+// EncodeUint32 encodes the value into a buffer in LEB128 format
+//
+// See https://en.wikipedia.org/wiki/LEB128#Encode_unsigned_integer
+func EncodeUint32(value uint32) []byte {
+ return EncodeUint64(uint64(value))
+}
+
+// EncodeUint64 encodes the value into a buffer in LEB128 format
+//
+// See https://en.wikipedia.org/wiki/LEB128#Encode_unsigned_integer
+func EncodeUint64(value uint64) (buf []byte) {
+ // This is effectively a do/while loop where we take 7 bits of the value and encode them until it is zero.
+ for {
+ // Take 7 remaining low-order bits from the value into b.
+ b := uint8(value & 0x7f)
+ value = value >> 7
+
+ // If there are remaining bits, the value won't be zero: Set the high-
+ // order bit to tell the reader there are more bytes in this uint.
+ if value != 0 {
+ b |= 0x80
+ }
+
+ // Append b into the buffer
+ buf = append(buf, b)
+ if b&0x80 == 0 {
+ return buf
+ }
+ }
+}
+
+type nextByte func(i int) (byte, error)
+
+func DecodeUint32(r io.ByteReader) (ret uint32, bytesRead uint64, err error) {
+ return decodeUint32(func(_ int) (byte, error) { return r.ReadByte() })
+}
+
+func LoadUint32(buf []byte) (ret uint32, bytesRead uint64, err error) {
+ return decodeUint32(func(i int) (byte, error) {
+ if i >= len(buf) {
+ return 0, io.EOF
+ }
+ return buf[i], nil
+ })
+}
+
+func decodeUint32(next nextByte) (ret uint32, bytesRead uint64, err error) {
+ // Derived from https://github.com/golang/go/blob/go1.20/src/encoding/binary/varint.go
+ // with the modification on the overflow handling tailored for 32-bits.
+ var s uint32
+ for i := 0; i < maxVarintLen32; i++ {
+ b, err := next(i)
+ if err != nil {
+ return 0, 0, err
+ }
+ if b < 0x80 {
+ // Unused bits must be all zero.
+ if i == maxVarintLen32-1 && (b&0xf0) > 0 {
+ return 0, 0, errOverflow32
+ }
+ return ret | uint32(b)<<s, uint64(i) + 1, nil
+ }
+ ret |= (uint32(b) & 0x7f) << s
+ s += 7
+ }
+ return 0, 0, errOverflow32
+}
+
+func LoadUint64(buf []byte) (ret uint64, bytesRead uint64, err error) {
+ bufLen := len(buf)
+ if bufLen == 0 {
+ return 0, 0, io.EOF
+ }
+
+ // Derived from https://github.com/golang/go/blob/go1.20/src/encoding/binary/varint.go
+ var s uint64
+ for i := 0; i < maxVarintLen64; i++ {
+ if i >= bufLen {
+ return 0, 0, io.EOF
+ }
+ b := buf[i]
+ if b < 0x80 {
+ // Unused bits (non first bit) must all be zero.
+ if i == maxVarintLen64-1 && b > 1 {
+ return 0, 0, errOverflow64
+ }
+ return ret | uint64(b)<<s, uint64(i) + 1, nil
+ }
+ ret |= (uint64(b) & 0x7f) << s
+ s += 7
+ }
+ return 0, 0, errOverflow64
+}
+
+func DecodeInt32(r io.ByteReader) (ret int32, bytesRead uint64, err error) {
+ return decodeInt32(func(_ int) (byte, error) { return r.ReadByte() })
+}
+
+func LoadInt32(buf []byte) (ret int32, bytesRead uint64, err error) {
+ return decodeInt32(func(i int) (byte, error) {
+ if i >= len(buf) {
+ return 0, io.EOF
+ }
+ return buf[i], nil
+ })
+}
+
+func decodeInt32(next nextByte) (ret int32, bytesRead uint64, err error) {
+ var shift int
+ var b byte
+ for {
+ b, err = next(int(bytesRead))
+ if err != nil {
+ return 0, 0, fmt.Errorf("readByte failed: %w", err)
+ }
+ ret |= (int32(b) & 0x7f) << shift
+ shift += 7
+ bytesRead++
+ if b&0x80 == 0 {
+ if shift < 32 && (b&0x40) != 0 {
+ ret |= ^0 << shift
+ }
+ // Over flow checks.
+ // fixme: can be optimized.
+ if bytesRead > maxVarintLen32 {
+ return 0, 0, errOverflow32
+ } else if unused := b & 0b00110000; bytesRead == maxVarintLen32 && ret < 0 && unused != 0b00110000 {
+ return 0, 0, errOverflow32
+ } else if bytesRead == maxVarintLen32 && ret >= 0 && unused != 0x00 {
+ return 0, 0, errOverflow32
+ }
+ return
+ }
+ }
+}
+
+// DecodeInt33AsInt64 is a special cased decoder for wasm.BlockType which is encoded as a positive signed integer, yet
+// still needs to fit the 32-bit range of allowed indices. Hence, this is 33, not 32-bit!
+//
+// See https://webassembly.github.io/spec/core/binary/instructions.html#control-instructions
+func DecodeInt33AsInt64(r io.ByteReader) (ret int64, bytesRead uint64, err error) {
+ var shift int
+ var b int64
+ var rb byte
+ for shift < 35 {
+ rb, err = r.ReadByte()
+ if err != nil {
+ return 0, 0, fmt.Errorf("readByte failed: %w", err)
+ }
+ b = int64(rb)
+ ret |= (b & int33Mask2) << shift
+ shift += 7
+ bytesRead++
+ if b&int33Mask == 0 {
+ break
+ }
+ }
+
+ // fixme: can be optimized
+ if shift < 33 && (b&int33Mask3) == int33Mask3 {
+ ret |= int33Mask4 << shift
+ }
+ ret = ret & int33Mask4
+
+ // if 33rd bit == 1, we translate it as a corresponding signed-33bit minus value
+ if ret&int33Mask5 > 0 {
+ ret = ret - int33Mask6
+ }
+ // Over flow checks.
+ // fixme: can be optimized.
+ if bytesRead > maxVarintLen33 {
+ return 0, 0, errOverflow33
+ } else if unused := b & 0b00100000; bytesRead == maxVarintLen33 && ret < 0 && unused != 0b00100000 {
+ return 0, 0, errOverflow33
+ } else if bytesRead == maxVarintLen33 && ret >= 0 && unused != 0x00 {
+ return 0, 0, errOverflow33
+ }
+ return ret, bytesRead, nil
+}
+
+func DecodeInt64(r io.ByteReader) (ret int64, bytesRead uint64, err error) {
+ return decodeInt64(func(_ int) (byte, error) { return r.ReadByte() })
+}
+
+func LoadInt64(buf []byte) (ret int64, bytesRead uint64, err error) {
+ return decodeInt64(func(i int) (byte, error) {
+ if i >= len(buf) {
+ return 0, io.EOF
+ }
+ return buf[i], nil
+ })
+}
+
+func decodeInt64(next nextByte) (ret int64, bytesRead uint64, err error) {
+ var shift int
+ var b byte
+ for {
+ b, err = next(int(bytesRead))
+ if err != nil {
+ return 0, 0, fmt.Errorf("readByte failed: %w", err)
+ }
+ ret |= (int64(b) & 0x7f) << shift
+ shift += 7
+ bytesRead++
+ if b&0x80 == 0 {
+ if shift < 64 && (b&int64Mask3) == int64Mask3 {
+ ret |= int64Mask4 << shift
+ }
+ // Over flow checks.
+ // fixme: can be optimized.
+ if bytesRead > maxVarintLen64 {
+ return 0, 0, errOverflow64
+ } else if unused := b & 0b00111110; bytesRead == maxVarintLen64 && ret < 0 && unused != 0b00111110 {
+ return 0, 0, errOverflow64
+ } else if bytesRead == maxVarintLen64 && ret >= 0 && unused != 0x00 {
+ return 0, 0, errOverflow64
+ }
+ return
+ }
+ }
+}