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  }