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 }