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:
|
// 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)
|
// - ValueTypeI64 - uint64(int64)
|
||||||
// - ValueTypeF32 - EncodeF32 DecodeF32 from float32
|
// - ValueTypeF32 - EncodeF32 DecodeF32 from float32
|
||||||
// - ValueTypeF64 - EncodeF64 DecodeF64 from float64
|
// - ValueTypeF64 - EncodeF64 DecodeF64 from float64
|
||||||
@@ -300,6 +300,9 @@ type Function interface {
|
|||||||
// Call is not goroutine-safe, therefore it is recommended to create
|
// Call is not goroutine-safe, therefore it is recommended to create
|
||||||
// another Function if you want to invoke the same function concurrently.
|
// another Function if you want to invoke the same function concurrently.
|
||||||
// On the other hand, sequential invocations of Call is allowed.
|
// 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)
|
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.
|
// Here's a typical way to read three parameters and write back one.
|
||||||
//
|
//
|
||||||
// // read parameters off the stack in index order
|
// // 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
|
// // 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
|
// This function can be non-deterministic or cause side effects. It also
|
||||||
// has special properties not defined in the WebAssembly Core specification.
|
// 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
|
// use reflection or code generators instead. These approaches are more
|
||||||
// idiomatic as they can map go types to ValueType. This type is exposed for
|
// idiomatic as they can map go types to ValueType. This type is exposed for
|
||||||
// those willing to trade usability and safety for performance.
|
// 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 {
|
type GoModuleFunction interface {
|
||||||
Call(ctx context.Context, mod Module, stack []uint64)
|
Call(ctx context.Context, mod Module, stack []uint64)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GoModuleFunc is a convenience for defining an inlined function.
|
// 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) {
|
// 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)
|
// ret, ok := mod.Memory().ReadUint32Le(ctx, offset)
|
||||||
// if !ok {
|
// if !ok {
|
||||||
// panic("out of memory")
|
// 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)
|
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:
|
// For example, the following returns the sum of two uint32 parameters:
|
||||||
//
|
//
|
||||||
// api.GoFunc(func(ctx context.Context, stack []uint64) {
|
// api.GoFunc(func(ctx context.Context, stack []uint64) {
|
||||||
// x, y := uint32(params[0]), uint32(params[1])
|
// x, y := api.DecodeU32(params[0]), api.DecodeU32(params[1])
|
||||||
// results[0] = uint64(x + y)
|
// results[0] = api.EncodeU32(x + y)
|
||||||
// })
|
// })
|
||||||
type GoFunc func(ctx context.Context, stack []uint64)
|
type GoFunc func(ctx context.Context, stack []uint64)
|
||||||
|
|
||||||
@@ -560,6 +566,21 @@ func EncodeI32(input int32) uint64 {
|
|||||||
return uint64(uint32(input))
|
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.
|
// EncodeI64 encodes the input as a ValueTypeI64.
|
||||||
func EncodeI64(input int64) uint64 {
|
func EncodeI64(input int64) uint64 {
|
||||||
return uint64(input)
|
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{
|
for _, v := range []int32{
|
||||||
0, 100, -100, 1, -1,
|
0, 100, -100, 1, -1,
|
||||||
math.MaxInt32,
|
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{
|
for _, v := range []int64{
|
||||||
0, 100, -100, 1, -1,
|
0, 100, -100, 1, -1,
|
||||||
math.MaxInt64,
|
math.MaxInt64,
|
||||||
|
|||||||
Reference in New Issue
Block a user