github.com/taubyte/vm-wasm-utils@v1.0.2/binary/value.go (about) 1 package binary 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "unicode/utf8" 8 9 wasm "github.com/taubyte/vm-wasm-utils" 10 "github.com/taubyte/vm-wasm-utils/leb128" 11 ) 12 13 var noValType = []byte{0} 14 15 // encodedValTypes is a cache of size prefixed binary encoding of known val types. 16 var encodedValTypes = map[wasm.ValueType][]byte{ 17 wasm.ValueTypeI32: {1, wasm.ValueTypeI32}, 18 wasm.ValueTypeI64: {1, wasm.ValueTypeI64}, 19 wasm.ValueTypeF32: {1, wasm.ValueTypeF32}, 20 wasm.ValueTypeF64: {1, wasm.ValueTypeF64}, 21 wasm.ValueTypeExternref: {1, wasm.ValueTypeExternref}, 22 wasm.ValueTypeFuncref: {1, wasm.ValueTypeFuncref}, 23 wasm.ValueTypeV128: {1, wasm.ValueTypeV128}, 24 } 25 26 // encodeValTypes fast paths binary encoding of common value type lengths 27 func encodeValTypes(vt []wasm.ValueType) []byte { 28 // Special case nullary and parameter lengths of wasi_snapshot_preview1 to avoid excess allocations 29 switch uint32(len(vt)) { 30 case 0: // nullary 31 return noValType 32 case 1: // ex $wasi.fd_close or any result 33 if encoded, ok := encodedValTypes[vt[0]]; ok { 34 return encoded 35 } 36 case 2: // ex $wasi.environ_sizes_get 37 return []byte{2, vt[0], vt[1]} 38 case 4: // ex $wasi.fd_write 39 return []byte{4, vt[0], vt[1], vt[2], vt[3]} 40 case 9: // ex $wasi.fd_write 41 return []byte{9, vt[0], vt[1], vt[2], vt[3], vt[4], vt[5], vt[6], vt[7], vt[8]} 42 } 43 // Slow path others until someone complains with a valid signature 44 count := leb128.EncodeUint32(uint32(len(vt))) 45 return append(count, vt...) 46 } 47 48 func decodeValueTypes(r *bytes.Reader, num uint32) ([]wasm.ValueType, error) { 49 if num == 0 { 50 return nil, nil 51 } 52 ret := make([]wasm.ValueType, num) 53 buf := make([]wasm.ValueType, num) 54 _, err := io.ReadFull(r, buf) 55 if err != nil { 56 return nil, err 57 } 58 59 for i, v := range buf { 60 switch v { 61 case wasm.ValueTypeI32, wasm.ValueTypeF32, wasm.ValueTypeI64, wasm.ValueTypeF64, 62 wasm.ValueTypeExternref, wasm.ValueTypeFuncref, wasm.ValueTypeV128: 63 ret[i] = v 64 default: 65 return nil, fmt.Errorf("invalid value type: %d", v) 66 } 67 } 68 return ret, nil 69 } 70 71 // decodeUTF8 decodes a size prefixed string from the reader, returning it and the count of bytes read. 72 // contextFormat and contextArgs apply an error format when present 73 func decodeUTF8(r *bytes.Reader, contextFormat string, contextArgs ...interface{}) (string, uint32, error) { 74 size, sizeOfSize, err := leb128.DecodeUint32(r) 75 if err != nil { 76 return "", 0, fmt.Errorf("failed to read %s size: %w", fmt.Sprintf(contextFormat, contextArgs...), err) 77 } 78 79 buf := make([]byte, size) 80 if _, err = io.ReadFull(r, buf); err != nil { 81 return "", 0, fmt.Errorf("failed to read %s: %w", fmt.Sprintf(contextFormat, contextArgs...), err) 82 } 83 84 if !utf8.Valid(buf) { 85 return "", 0, fmt.Errorf("%s is not valid UTF-8", fmt.Sprintf(contextFormat, contextArgs...)) 86 } 87 88 return string(buf), size + uint32(sizeOfSize), nil 89 }