api: adds Encode and Decode functions for int32 and uint32 (#871)
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
This commit is contained in:
37
api/wasm.go
37
api/wasm.go
@@ -53,7 +53,7 @@ func ExternTypeName(et ExternType) string {
|
||||
//
|
||||
// The following describes how to convert between Wasm and Golang types:
|
||||
//
|
||||
// - ValueTypeI32 - uint64(uint32,int32)
|
||||
// - ValueTypeI32 - EncodeU32 DecodeU32 for uint32 / EncodeI32 DecodeI32 for int32
|
||||
// - ValueTypeI64 - uint64(int64)
|
||||
// - ValueTypeF32 - EncodeF32 DecodeF32 from float32
|
||||
// - ValueTypeF64 - EncodeF64 DecodeF64 from float64
|
||||
@@ -300,6 +300,9 @@ type Function interface {
|
||||
// Call is not goroutine-safe, therefore it is recommended to create
|
||||
// another Function if you want to invoke the same function concurrently.
|
||||
// On the other hand, sequential invocations of Call is allowed.
|
||||
//
|
||||
// To safely encode/decode params/results expressed as uint64, users are encouraged to
|
||||
// use api.EncodeXXX or DecodeXXX functions. See the docs on api.ValueType.
|
||||
Call(ctx context.Context, params ...uint64) ([]uint64, error)
|
||||
}
|
||||
|
||||
@@ -315,10 +318,10 @@ type Function interface {
|
||||
// Here's a typical way to read three parameters and write back one.
|
||||
//
|
||||
// // read parameters off the stack in index order
|
||||
// argv, argvBuf := uint32(stack[0]), uint32(stack[1])
|
||||
// argv, argvBuf := api.DecodeU32(stack[0]), api.DecodeU32(stack[1])
|
||||
//
|
||||
// // write results back to the stack in index order
|
||||
// stack[0] = uint64(ErrnoSuccess)
|
||||
// stack[0] = api.EncodeU32(ErrnoSuccess)
|
||||
//
|
||||
// This function can be non-deterministic or cause side effects. It also
|
||||
// has special properties not defined in the WebAssembly Core specification.
|
||||
@@ -329,23 +332,26 @@ type Function interface {
|
||||
// use reflection or code generators instead. These approaches are more
|
||||
// idiomatic as they can map go types to ValueType. This type is exposed for
|
||||
// those willing to trade usability and safety for performance.
|
||||
//
|
||||
// To safely decode/encode values from/to the uint64 stack, users are encouraged to use
|
||||
// api.EncodeXXX or api.DecodeXXX functions. See the docs on api.ValueType.
|
||||
type GoModuleFunction interface {
|
||||
Call(ctx context.Context, mod Module, stack []uint64)
|
||||
}
|
||||
|
||||
// GoModuleFunc is a convenience for defining an inlined function.
|
||||
//
|
||||
// For example, the following returns a uint32 value read from parameter zero:
|
||||
// For example, the following returns an uint32 value read from parameter zero:
|
||||
//
|
||||
// api.GoModuleFunc(func(ctx context.Context, mod api.Module, stack []uint64) {
|
||||
// offset := uint32(params[0]) // read the parameter from the stack
|
||||
// offset := api.DecodeU32(params[0]) // read the parameter from the stack
|
||||
//
|
||||
// ret, ok := mod.Memory().ReadUint32Le(ctx, offset)
|
||||
// if !ok {
|
||||
// panic("out of memory")
|
||||
// }
|
||||
//
|
||||
// results[0] = uint64(ret) // add the result back to the stack.
|
||||
// results[0] = api.EncodeU32(ret) // add the result back to the stack.
|
||||
// })
|
||||
type GoModuleFunc func(ctx context.Context, mod Module, stack []uint64)
|
||||
|
||||
@@ -368,8 +374,8 @@ type GoFunction interface {
|
||||
// For example, the following returns the sum of two uint32 parameters:
|
||||
//
|
||||
// api.GoFunc(func(ctx context.Context, stack []uint64) {
|
||||
// x, y := uint32(params[0]), uint32(params[1])
|
||||
// results[0] = uint64(x + y)
|
||||
// x, y := api.DecodeU32(params[0]), api.DecodeU32(params[1])
|
||||
// results[0] = api.EncodeU32(x + y)
|
||||
// })
|
||||
type GoFunc func(ctx context.Context, stack []uint64)
|
||||
|
||||
@@ -560,6 +566,21 @@ func EncodeI32(input int32) uint64 {
|
||||
return uint64(uint32(input))
|
||||
}
|
||||
|
||||
// DecodeI32 decodes the input as a ValueTypeI32.
|
||||
func DecodeI32(input uint64) int32 {
|
||||
return int32(input)
|
||||
}
|
||||
|
||||
// EncodeU32 encodes the input as a ValueTypeI32.
|
||||
func EncodeU32(input uint32) uint64 {
|
||||
return uint64(input)
|
||||
}
|
||||
|
||||
// DecodeU32 decodes the input as a ValueTypeI32.
|
||||
func DecodeU32(input uint64) uint32 {
|
||||
return uint32(input)
|
||||
}
|
||||
|
||||
// EncodeI64 encodes the input as a ValueTypeI64.
|
||||
func EncodeI64(input int64) uint64 {
|
||||
return uint64(input)
|
||||
|
||||
@@ -111,7 +111,7 @@ func TestEncodeDecodeF64(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeCastI32(t *testing.T) {
|
||||
func TestEncodeI32(t *testing.T) {
|
||||
for _, v := range []int32{
|
||||
0, 100, -100, 1, -1,
|
||||
math.MaxInt32,
|
||||
@@ -126,7 +126,57 @@ func TestEncodeCastI32(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeCastI64(t *testing.T) {
|
||||
func TestDecodeI32(t *testing.T) {
|
||||
mini32 := math.MinInt32
|
||||
for _, tc := range []struct {
|
||||
in uint64
|
||||
exp int32
|
||||
}{
|
||||
{in: 0, exp: 0},
|
||||
{in: 1 << 60, exp: 0},
|
||||
{in: 1 << 30, exp: 1 << 30},
|
||||
{in: 1<<30 | 1<<60, exp: 1 << 30},
|
||||
{in: uint64(uint32(mini32)) | 1<<59, exp: math.MinInt32},
|
||||
{in: uint64(uint32(math.MaxInt32)) | 1<<50, exp: math.MaxInt32},
|
||||
} {
|
||||
decoded := DecodeI32(tc.in)
|
||||
require.Equal(t, tc.exp, decoded)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeU32(t *testing.T) {
|
||||
for _, v := range []uint32{
|
||||
0, 100, 1, 1 << 31,
|
||||
math.MaxInt32,
|
||||
math.MaxUint32,
|
||||
} {
|
||||
t.Run(fmt.Sprintf("%d", v), func(t *testing.T) {
|
||||
encoded := EncodeU32(v)
|
||||
require.Zero(t, encoded>>32) // Ensures high bits aren't set
|
||||
require.Equal(t, v, uint32(encoded))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeU32(t *testing.T) {
|
||||
mini32 := math.MinInt32
|
||||
for _, tc := range []struct {
|
||||
in uint64
|
||||
exp uint32
|
||||
}{
|
||||
{in: 0, exp: 0},
|
||||
{in: 1 << 60, exp: 0},
|
||||
{in: 1 << 30, exp: 1 << 30},
|
||||
{in: 1<<30 | 1<<60, exp: 1 << 30},
|
||||
{in: uint64(uint32(mini32)) | 1<<59, exp: uint32(mini32)},
|
||||
{in: uint64(uint32(math.MaxInt32)) | 1<<50, exp: math.MaxInt32},
|
||||
} {
|
||||
decoded := DecodeU32(tc.in)
|
||||
require.Equal(t, tc.exp, decoded)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeI64(t *testing.T) {
|
||||
for _, v := range []int64{
|
||||
0, 100, -100, 1, -1,
|
||||
math.MaxInt64,
|
||||
|
||||
Reference in New Issue
Block a user