github.com/taubyte/vm-wasm-utils@v1.0.2/binary/function.go (about)

     1  package binary
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  
     7  	wasm "github.com/taubyte/vm-wasm-utils"
     8  	"github.com/taubyte/vm-wasm-utils/leb128"
     9  )
    10  
    11  var nullary = []byte{0x60, 0, 0}
    12  
    13  // encodedOneParam is a cache of wasm.FunctionType values for param length 1 and result length 0
    14  var encodedOneParam = map[wasm.ValueType][]byte{
    15  	wasm.ValueTypeI32: {0x60, 1, wasm.ValueTypeI32, 0},
    16  	wasm.ValueTypeI64: {0x60, 1, wasm.ValueTypeI64, 0},
    17  	wasm.ValueTypeF32: {0x60, 1, wasm.ValueTypeF32, 0},
    18  	wasm.ValueTypeF64: {0x60, 1, wasm.ValueTypeF64, 0},
    19  }
    20  
    21  // encodedOneResult is a cache of wasm.FunctionType values for param length 0 and result length 1
    22  var encodedOneResult = map[wasm.ValueType][]byte{
    23  	wasm.ValueTypeI32: {0x60, 0, 1, wasm.ValueTypeI32},
    24  	wasm.ValueTypeI64: {0x60, 0, 1, wasm.ValueTypeI64},
    25  	wasm.ValueTypeF32: {0x60, 0, 1, wasm.ValueTypeF32},
    26  	wasm.ValueTypeF64: {0x60, 0, 1, wasm.ValueTypeF64},
    27  }
    28  
    29  // encodeFunctionType returns the wasm.FunctionType encoded in WebAssembly 1.0 (20191205) Binary Format.
    30  //
    31  // Note: Function types are encoded by the byte 0x60 followed by the respective vectors of parameter and result types.
    32  // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#function-types%E2%91%A4
    33  func encodeFunctionType(t *wasm.FunctionType) []byte {
    34  	paramCount, resultCount := len(t.Params), len(t.Results)
    35  	if paramCount == 0 && resultCount == 0 {
    36  		return nullary
    37  	}
    38  	if resultCount == 0 {
    39  		if paramCount == 1 {
    40  			if encoded, ok := encodedOneParam[t.Params[0]]; ok {
    41  				return encoded
    42  			}
    43  		}
    44  		return append(append([]byte{0x60}, encodeValTypes(t.Params)...), 0)
    45  	} else if resultCount == 1 {
    46  		if paramCount == 0 {
    47  			if encoded, ok := encodedOneResult[t.Results[0]]; ok {
    48  				return encoded
    49  			}
    50  		}
    51  		return append(append([]byte{0x60}, encodeValTypes(t.Params)...), 1, t.Results[0])
    52  	}
    53  	// Only reached when "multi-value" is enabled because WebAssembly 1.0 (20191205) supports at most 1 result.
    54  	data := append([]byte{0x60}, encodeValTypes(t.Params)...)
    55  	return append(data, encodeValTypes(t.Results)...)
    56  }
    57  
    58  func decodeFunctionType(enabledFeatures wasm.Features, r *bytes.Reader) (*wasm.FunctionType, error) {
    59  	b, err := r.ReadByte()
    60  	if err != nil {
    61  		return nil, fmt.Errorf("read leading byte: %w", err)
    62  	}
    63  
    64  	if b != 0x60 {
    65  		return nil, fmt.Errorf("%w: %#x != 0x60", ErrInvalidByte, b)
    66  	}
    67  
    68  	paramCount, _, err := leb128.DecodeUint32(r)
    69  	if err != nil {
    70  		return nil, fmt.Errorf("could not read parameter count: %w", err)
    71  	}
    72  
    73  	paramTypes, err := decodeValueTypes(r, paramCount)
    74  	if err != nil {
    75  		return nil, fmt.Errorf("could not read parameter types: %w", err)
    76  	}
    77  
    78  	resultCount, _, err := leb128.DecodeUint32(r)
    79  	if err != nil {
    80  		return nil, fmt.Errorf("could not read result count: %w", err)
    81  	}
    82  
    83  	// Guard >1.0 feature multi-value
    84  	if resultCount > 1 {
    85  		if err = enabledFeatures.Require(wasm.FeatureMultiValue); err != nil {
    86  			return nil, fmt.Errorf("multiple result types invalid as %v", err)
    87  		}
    88  	}
    89  
    90  	resultTypes, err := decodeValueTypes(r, resultCount)
    91  	if err != nil {
    92  		return nil, fmt.Errorf("could not read result types: %w", err)
    93  	}
    94  
    95  	ret := &wasm.FunctionType{
    96  		Params:  paramTypes,
    97  		Results: resultTypes,
    98  	}
    99  	return ret, nil
   100  }