grand Go SDK update (#1165)

* grand Go SDK update

- Add `outbound-redis` set operations and `execute` function
- Add `key-value` support
- Remove `*_free` calls since they were buggy and unnecessary given that we tell `tinygo` to leak anyway
- Remove `-wasm-abi=generic` option since it's been removed as of `tinygo` 0.27

Signed-off-by: Joel Dice <joel.dice@fermyon.com>

* fix Go CI issues

- Update Go and `tinygo` versions
- Use `interface{}` instead of `any` for broader compatibility
- Make `tinygo-outbound-redis` example sort the `SMEMBERS` result before comparing with the expected value

Signed-off-by: Joel Dice <joel.dice@fermyon.com>

* rename Go SDK key-value directory to key_value

Signed-off-by: Joel Dice <joel.dice@fermyon.com>

---------

Signed-off-by: Joel Dice <joel.dice@fermyon.com>
This commit is contained in:
Joel Dice 2023-02-16 15:40:08 -07:00 committed by GitHub
parent 12596f3e59
commit 49721f2daf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 1224 additions and 61 deletions

View File

@ -63,7 +63,7 @@ inputs:
type: bool
golang-version:
description: 'golang version to setup'
default: '1.17'
default: '1.20'
required: false
type: string
@ -82,10 +82,10 @@ inputs:
description: 'setup tinygo'
required: false
default: 'false'
type: bool
type: bool
tinygo-version:
description: 'tinygo version to setup'
default: 'v0.22.0'
default: 'v0.27.0'
required: false
type: string
@ -150,4 +150,4 @@ runs:
uses: rajatjindal/setup-actions/tinygo@v0.0.1
if: ${{ inputs.tinygo == 'true' }}
with:
version: ${{ inputs.tinygo-version }}
version: ${{ inputs.tinygo-version }}

View File

@ -16,4 +16,4 @@ route = "/..."
[component.config]
message = "I'm a {{object}}"
[component.build]
command = "tinygo build -wasm-abi=generic -target=wasi -gc=leaking -no-debug -o main.wasm main.go"
command = "tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go"

View File

@ -25,7 +25,7 @@ Building this as a WebAssembly module can be done using the `tinygo` compiler:
```shell
$ spin build
Executing the build command for component tinygo-hello: tinygo build -wasm-abi=generic -target=wasi -gc=leaking -no-debug -o main.wasm main.go
Executing the build command for component tinygo-hello: tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go
Successfully ran the build command for the Spin components.
```
@ -44,7 +44,7 @@ allowed_http_hosts = [ "https://some-random-api.ml", "https://postman-echo.com"
[component.trigger]
route = "/hello"
[component.build]
command = "tinygo build -wasm-abi=generic -target=wasi -gc=leaking -no-debug -o main.wasm main.go"
command = "tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go"
```
At this point, we can execute the application with the `spin` CLI:

View File

@ -12,4 +12,4 @@ allowed_http_hosts = ["https://some-random-api.ml", "https://postman-echo.com"]
[component.trigger]
route = "/hello"
[component.build]
command = "tinygo build -wasm-abi=generic -target=wasi -gc=leaking -no-debug -o main.wasm main.go"
command = "tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go"

View File

@ -31,7 +31,7 @@ Building this as a WebAssembly module can be done using the `tinygo` compiler:
```shell
$ spin build
Executing the build command for component tinygo-hello: tinygo build -wasm-abi=generic -target=wasi -gc=leaking -no-debug -o main.wasm main.go
Executing the build command for component tinygo-hello: tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go
Successfully ran the build command for the Spin components.
```
@ -52,7 +52,7 @@ source = "main.wasm"
[component.trigger]
route = "/hello"
[component.build]
command = "tinygo build -wasm-abi=generic -target=wasi -gc=leaking -no-debug -o main.wasm main.go"
command = "tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go"
```
At this point, we can execute the application with the `spin` CLI:

View File

@ -11,4 +11,4 @@ source = "main.wasm"
[component.trigger]
route = "/hello"
[component.build]
command = "tinygo build -wasm-abi=generic -target=wasi -gc=leaking -no-debug -o main.wasm main.go"
command = "tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go"

View File

@ -0,0 +1,14 @@
# Spin component in TinyGo
```shell
$ RUST_LOG=spin=trace spin build --up
```
The application can now receive requests on `http://localhost:3000/test`:
```shell
$ curl -i localhost:3000/test
HTTP/1.1 200 OK
content-length: 67
date: Tue, 29 Nov 2022 07:03:52 GMT
```

View File

@ -0,0 +1,7 @@
module github.com/fermyon/spin/templates/spin-http-tinygo-key-value
go 1.17
require github.com/fermyon/spin/sdk/go v0.0.0
replace github.com/fermyon/spin/sdk/go v0.0.0 => ../../sdk/go/

View File

@ -0,0 +1,74 @@
package main
import (
"net/http"
"reflect"
"fmt"
spin_http "github.com/fermyon/spin/sdk/go/http"
"github.com/fermyon/spin/sdk/go/key_value"
)
func init() {
// handler for the http trigger
spin_http.Handle(func(w http.ResponseWriter, r *http.Request) {
store, err := key_value.Open("default");
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer key_value.Close(store)
if err := key_value.Set(store, "foo", []byte("bar")); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
{
expected := []byte("bar")
if value, err := key_value.Get(store, "foo"); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
} else if !reflect.DeepEqual(value, expected) {
http.Error(
w,
fmt.Sprintf("expected %v, got %v", expected, value),
http.StatusInternalServerError,
)
return
}
}
{
expected := []string{"foo"}
if value, err := key_value.GetKeys(store); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
} else if !reflect.DeepEqual(value, expected) {
http.Error(
w,
fmt.Sprintf("expected %v, got %v", expected, value),
http.StatusInternalServerError,
)
return
}
}
if err := key_value.Delete(store, "foo"); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if exists, err := key_value.Exists(store, "foo"); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
} else if exists {
http.Error(w, "key was not deleted as expected", http.StatusInternalServerError)
return
}
})
}
func main() {}

View File

@ -0,0 +1,14 @@
spin_version = "1"
authors = ["Fermyon Engineering <engineering@fermyon.com>"]
name = "tinygo-key-value-example"
trigger = { type = "http", base = "/" }
version = "0.1.0"
[[component]]
id = "key-value"
source = "main.wasm"
key_value_stores = ["default"]
[component.trigger]
route = "/test"
[component.build]
command = "tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go"

View File

@ -4,4 +4,6 @@ go 1.17
require github.com/fermyon/spin/sdk/go v0.0.0
require golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb // indirect
replace github.com/fermyon/spin/sdk/go v0.0.0 => ../../sdk/go/

View File

@ -0,0 +1,2 @@
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w=
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=

View File

@ -4,7 +4,10 @@ import (
"net/http"
"os"
"strconv"
"reflect"
"fmt"
"golang.org/x/exp/slices"
spin_http "github.com/fermyon/spin/sdk/go/http"
"github.com/fermyon/spin/sdk/go/redis"
)
@ -39,6 +42,7 @@ func init() {
// get redis payload for `mykey`
if payload, err := redis.Get(addr, "mykey"); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
} else {
w.Write([]byte("mykey value was: "))
w.Write(payload)
@ -48,6 +52,7 @@ func init() {
// incr `spin-go-incr` by 1
if payload, err := redis.Incr(addr, "spin-go-incr"); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
} else {
w.Write([]byte("spin-go-incr value: "))
w.Write([]byte(strconv.FormatInt(payload, 10)))
@ -62,6 +67,83 @@ func init() {
w.Write([]byte(strconv.FormatInt(payload, 10)))
w.Write([]byte("\n"))
}
if _, err := redis.Sadd(addr, "myset", []string{"foo", "bar"}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
{
expected := []string{"bar", "foo"}
payload, err := redis.Smembers(addr, "myset")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
slices.Sort(payload)
if !reflect.DeepEqual(payload, expected) {
http.Error(
w,
fmt.Sprintf(
"unexpected SMEMBERS result: expected %v, got %v",
expected,
payload,
),
http.StatusInternalServerError,
)
return
}
}
if _, err := redis.Srem(addr, "myset", []string{"bar"}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
{
expected := []string{"foo"}
if payload, err := redis.Smembers(addr, "myset"); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
} else if !reflect.DeepEqual(payload, expected) {
http.Error(
w,
fmt.Sprintf(
"unexpected SMEMBERS result: expected %v, got %v",
expected,
payload,
),
http.StatusInternalServerError,
)
return
}
}
message := redis.RedisParameter{Kind: redis.RedisParameterKindBinary, Val: []byte("message")}
hello := redis.RedisParameter{Kind: redis.RedisParameterKindBinary, Val: []byte("hello")}
if _, err := redis.Execute(addr, "set", []redis.RedisParameter{message, hello}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
world := redis.RedisParameter{Kind: redis.RedisParameterKindBinary, Val: []byte(" world")}
if _, err := redis.Execute(addr, "append", []redis.RedisParameter{message, world}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if payload, err := redis.Execute(addr, "get", []redis.RedisParameter{message}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
} else if !reflect.DeepEqual(
payload,
[]redis.RedisResult{redis.RedisResult{
Kind: redis.RedisResultKindBinary,
Val: []byte("hello world"),
}}) {
http.Error(w, "unexpected GET result", http.StatusInternalServerError)
return
}
})
}

View File

@ -11,4 +11,4 @@ environment = { REDIS_ADDRESS = "redis://127.0.0.1:6379", REDIS_CHANNEL = "messa
[component.trigger]
route = "/publish"
[component.build]
command = "tinygo build -wasm-abi=generic -target=wasi -gc=leaking -no-debug -o main.wasm main.go"
command = "tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go"

View File

@ -11,4 +11,4 @@ source = "main.wasm"
[component.trigger]
channel = "messages"
[component.build]
command = "tinygo build -wasm-abi=generic -target=wasi -gc=leaking -no-debug -o main.wasm main.go"
command = "tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go"

View File

@ -3,8 +3,8 @@
# ----------------------------------------------------------------------
.PHONY: test
test: test-integration
tinygo test -wasm-abi=generic -target=wasi -gc=leaking -v ./http
tinygo test -wasm-abi=generic -target=wasi -gc=leaking -v ./redis
tinygo test -target=wasi -gc=leaking -v ./http
tinygo test -target=wasi -gc=leaking -v ./redis
.PHONY: test-integration
test-integration: http/testdata/http-tinygo/main.wasm
@ -12,7 +12,7 @@ test-integration: http/testdata/http-tinygo/main.wasm
http/testdata/http-tinygo/main.wasm: generate
http/testdata/http-tinygo/main.wasm: http/testdata/http-tinygo/main.go
tinygo build -wasm-abi=generic -target=wasi -gc=leaking -no-debug -o http/testdata/http-tinygo/main.wasm http/testdata/http-tinygo/main.go
tinygo build -target=wasi -gc=leaking -no-debug -o http/testdata/http-tinygo/main.wasm http/testdata/http-tinygo/main.go
# ----------------------------------------------------------------------
# Build examples
@ -26,9 +26,10 @@ build-examples: $(EXAMPLES_DIR)/http-tinygo-outbound-http/main.wasm
build-examples: $(EXAMPLES_DIR)/http-tinygo/main.wasm
build-examples: $(EXAMPLES_DIR)/tinygo-outbound-redis/main.wasm
build-examples: $(EXAMPLES_DIR)/tinygo-redis/main.wasm
build-examples: $(EXAMPLES_DIR)/tinygo-key-value/main.wasm
$(EXAMPLES_DIR)/%/main.wasm: $(EXAMPLES_DIR)/%/main.go
tinygo build -wasm-abi=generic -target=wasi -gc=leaking -no-debug -o $@ $<
tinygo build -target=wasi -gc=leaking -no-debug -o $@ $<
# ----------------------------------------------------------------------
# Generate C bindings
@ -38,11 +39,12 @@ GENERATED_OUTBOUND_HTTP = http/wasi-outbound-http.c http/wasi-outbound-http.h
GENERATED_SPIN_HTTP = http/spin-http.c http/spin-http.h
GENERATED_OUTBOUND_REDIS = redis/outbound-redis.c redis/outbound-redis.h
GENERATED_SPIN_REDIS = redis/spin-redis.c redis/spin-redis.h
GENERATED_KEY_VALUE = key_value/key-value.c key_value/key-value.h
.PHONY: generate
generate: $(GENERATED_OUTBOUND_HTTP) $(GENERATED_SPIN_HTTP)
generate: $(GENERATED_OUTBOUND_REDIS) $(GENERATED_SPIN_REDIS)
generate: $(GENERATED_SPIN_CONFIG)
generate: $(GENERATED_SPIN_CONFIG) $(GENERATED_KEY_VALUE)
$(GENERATED_SPIN_CONFIG):
wit-bindgen c --import ../../wit/ephemeral/spin-config.wit --out-dir ./config
@ -59,16 +61,21 @@ $(GENERATED_OUTBOUND_REDIS):
$(GENERATED_SPIN_REDIS):
wit-bindgen c --export ../../wit/ephemeral/spin-redis.wit --out-dir ./redis
$(GENERATED_KEY_VALUE):
wit-bindgen c --import ../../wit/ephemeral/key-value.wit --out-dir ./key_value
# ----------------------------------------------------------------------
# Cleanup
# ----------------------------------------------------------------------
.PHONY: clean
clean:
rm -rf $(GENERATED_SPIN_CONFIG)
rm -f $(GENERATED_SPIN_CONFIG)
rm -f $(GENERATED_OUTBOUND_HTTP) $(GENERATED_SPIN_HTTP)
rm -f $(GENERATED_OUTBOUND_REDIS) $(GENERATED_SPIN_REDIS)
rm -f $(GENERATED_KEY_VALUE)
rm -f http/testdata/http-tinygo/main.wasm
rm -f $(EXAMPLES_DIR)/http-tinygo/main.wasm
rm -f $(EXAMPLES_DIR)/http-tinygo-outbound-http/main.wasm
rm -f $(EXAMPLES_DIR)/tinygo-outbound-redis/main.wasm
rm -f $(EXAMPLES_DIR)/tinygo-redis/main.wasm
rm -f $(EXAMPLES_DIR)/tinygo-key-value/main.wasm

View File

@ -15,8 +15,6 @@ import (
//export spin_http_handle_http_request
func handle_http_request(req *C.spin_http_request_t, res *C.spin_http_response_t) {
defer C.spin_http_request_free(req)
var body []byte
if req.body.is_some {
body = C.GoBytes(unsafe.Pointer(req.body.val.ptr), C.int(req.body.val.len))

View File

@ -43,10 +43,6 @@ func post(url string, contentType string, body io.Reader) (*http.Response, error
func send(req *http.Request) (*http.Response, error) {
var spinReq C.wasi_outbound_http_request_t
var spinRes C.wasi_outbound_http_response_t
defer func() {
C.wasi_outbound_http_request_free(&spinReq)
C.wasi_outbound_http_response_free(&spinRes)
}()
m, err := method(req.Method)
if err != nil {

View File

@ -37,6 +37,7 @@ extern "C"
#define SPIN_HTTP_HTTP_ERROR_INVALID_URL 2
#define SPIN_HTTP_HTTP_ERROR_REQUEST_ERROR 3
#define SPIN_HTTP_HTTP_ERROR_RUNTIME_ERROR 4
#define SPIN_HTTP_HTTP_ERROR_TOO_MANY_REQUESTS 5
typedef uint16_t spin_http_http_status_t;
typedef uint8_t spin_http_method_t;
#define SPIN_HTTP_METHOD_GET 0

View File

@ -1,3 +1,3 @@
.PHONY: build
build:
tinygo build -wasm-abi=generic -target=wasi -gc=leaking -o main.wasm main.go
tinygo build -target=wasi -gc=leaking -o main.wasm main.go

View File

@ -37,6 +37,7 @@ extern "C"
#define WASI_OUTBOUND_HTTP_HTTP_ERROR_INVALID_URL 2
#define WASI_OUTBOUND_HTTP_HTTP_ERROR_REQUEST_ERROR 3
#define WASI_OUTBOUND_HTTP_HTTP_ERROR_RUNTIME_ERROR 4
#define WASI_OUTBOUND_HTTP_HTTP_ERROR_TOO_MANY_REQUESTS 5
typedef uint16_t wasi_outbound_http_http_status_t;
typedef uint8_t wasi_outbound_http_method_t;
#define WASI_OUTBOUND_HTTP_METHOD_GET 0

View File

@ -61,7 +61,7 @@ func buildTinyGo(t *testing.T, dir string) {
t.Log("building example: ", dir)
cmd := exec.Command("tinygo", "build", "-wasm-abi=generic", "-target=wasi", "-gc=leaking", "-o", "main.wasm", "main.go")
cmd := exec.Command("tinygo", "build", "-target=wasi", "-gc=leaking", "-o", "main.wasm", "main.go")
cmd.Dir = dir
stderr := new(bytes.Buffer)
@ -110,6 +110,7 @@ func TestBuildExamples(t *testing.T) {
"../../examples/http-tinygo-outbound-http",
"../../examples/tinygo-outbound-redis",
"../../examples/tinygo-redis",
"../../examples/tinygo-key-value",
} {
buildTinyGo(t, example)
}

View File

@ -0,0 +1,367 @@
#include <stdlib.h>
#include <key-value.h>
__attribute__((weak, export_name("canonical_abi_realloc")))
void *canonical_abi_realloc(
void *ptr,
size_t orig_size,
size_t align,
size_t new_size
) {
if (new_size == 0)
return (void*) align;
void *ret = realloc(ptr, new_size);
if (!ret)
abort();
return ret;
}
__attribute__((weak, export_name("canonical_abi_free")))
void canonical_abi_free(
void *ptr,
size_t size,
size_t align
) {
if (size == 0)
return;
free(ptr);
}
#include <string.h>
void key_value_string_set(key_value_string_t *ret, const char *s) {
ret->ptr = (char*) s;
ret->len = strlen(s);
}
void key_value_string_dup(key_value_string_t *ret, const char *s) {
ret->len = strlen(s);
ret->ptr = canonical_abi_realloc(NULL, 0, 1, ret->len);
memcpy(ret->ptr, s, ret->len);
}
void key_value_string_free(key_value_string_t *ret) {
canonical_abi_free(ret->ptr, ret->len, 1);
ret->ptr = NULL;
ret->len = 0;
}
void key_value_error_free(key_value_error_t *ptr) {
switch ((int32_t) ptr->tag) {
case 5: {
key_value_string_free(&ptr->val.io);
break;
}
}
}
void key_value_expected_store_error_free(key_value_expected_store_error_t *ptr) {
if (!ptr->is_err) {
} else {
key_value_error_free(&ptr->val.err);
}
}
void key_value_list_u8_free(key_value_list_u8_t *ptr) {
canonical_abi_free(ptr->ptr, ptr->len * 1, 1);
}
void key_value_expected_list_u8_error_free(key_value_expected_list_u8_error_t *ptr) {
if (!ptr->is_err) {
key_value_list_u8_free(&ptr->val.ok);
} else {
key_value_error_free(&ptr->val.err);
}
}
void key_value_expected_unit_error_free(key_value_expected_unit_error_t *ptr) {
if (!ptr->is_err) {
} else {
key_value_error_free(&ptr->val.err);
}
}
void key_value_expected_bool_error_free(key_value_expected_bool_error_t *ptr) {
if (!ptr->is_err) {
} else {
key_value_error_free(&ptr->val.err);
}
}
void key_value_list_string_free(key_value_list_string_t *ptr) {
for (size_t i = 0; i < ptr->len; i++) {
key_value_string_free(&ptr->ptr[i]);
}
canonical_abi_free(ptr->ptr, ptr->len * 8, 4);
}
void key_value_expected_list_string_error_free(key_value_expected_list_string_error_t *ptr) {
if (!ptr->is_err) {
key_value_list_string_free(&ptr->val.ok);
} else {
key_value_error_free(&ptr->val.err);
}
}
__attribute__((aligned(4)))
static uint8_t RET_AREA[16];
__attribute__((import_module("key-value"), import_name("open")))
void __wasm_import_key_value_open(int32_t, int32_t, int32_t);
void key_value_open(key_value_string_t *name, key_value_expected_store_error_t *ret0) {
int32_t ptr = (int32_t) &RET_AREA;
__wasm_import_key_value_open((int32_t) (*name).ptr, (int32_t) (*name).len, ptr);
key_value_expected_store_error_t expected;
switch ((int32_t) (*((uint8_t*) (ptr + 0)))) {
case 0: {
expected.is_err = false;
expected.val.ok = (uint32_t) (*((int32_t*) (ptr + 4)));
break;
}
case 1: {
expected.is_err = true;
key_value_error_t variant;
variant.tag = (int32_t) (*((uint8_t*) (ptr + 4)));
switch ((int32_t) variant.tag) {
case 0: {
break;
}
case 1: {
break;
}
case 2: {
break;
}
case 3: {
break;
}
case 4: {
break;
}
case 5: {
variant.val.io = (key_value_string_t) { (char*)(*((int32_t*) (ptr + 8))), (size_t)(*((int32_t*) (ptr + 12))) };
break;
}
}
expected.val.err = variant;
break;
}
}*ret0 = expected;
}
__attribute__((import_module("key-value"), import_name("get")))
void __wasm_import_key_value_get(int32_t, int32_t, int32_t, int32_t);
void key_value_get(key_value_store_t store, key_value_string_t *key, key_value_expected_list_u8_error_t *ret0) {
int32_t ptr = (int32_t) &RET_AREA;
__wasm_import_key_value_get((int32_t) (store), (int32_t) (*key).ptr, (int32_t) (*key).len, ptr);
key_value_expected_list_u8_error_t expected;
switch ((int32_t) (*((uint8_t*) (ptr + 0)))) {
case 0: {
expected.is_err = false;
expected.val.ok = (key_value_list_u8_t) { (uint8_t*)(*((int32_t*) (ptr + 4))), (size_t)(*((int32_t*) (ptr + 8))) };
break;
}
case 1: {
expected.is_err = true;
key_value_error_t variant;
variant.tag = (int32_t) (*((uint8_t*) (ptr + 4)));
switch ((int32_t) variant.tag) {
case 0: {
break;
}
case 1: {
break;
}
case 2: {
break;
}
case 3: {
break;
}
case 4: {
break;
}
case 5: {
variant.val.io = (key_value_string_t) { (char*)(*((int32_t*) (ptr + 8))), (size_t)(*((int32_t*) (ptr + 12))) };
break;
}
}
expected.val.err = variant;
break;
}
}*ret0 = expected;
}
__attribute__((import_module("key-value"), import_name("set")))
void __wasm_import_key_value_set(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t);
void key_value_set(key_value_store_t store, key_value_string_t *key, key_value_list_u8_t *value, key_value_expected_unit_error_t *ret0) {
int32_t ptr = (int32_t) &RET_AREA;
__wasm_import_key_value_set((int32_t) (store), (int32_t) (*key).ptr, (int32_t) (*key).len, (int32_t) (*value).ptr, (int32_t) (*value).len, ptr);
key_value_expected_unit_error_t expected;
switch ((int32_t) (*((uint8_t*) (ptr + 0)))) {
case 0: {
expected.is_err = false;
break;
}
case 1: {
expected.is_err = true;
key_value_error_t variant;
variant.tag = (int32_t) (*((uint8_t*) (ptr + 4)));
switch ((int32_t) variant.tag) {
case 0: {
break;
}
case 1: {
break;
}
case 2: {
break;
}
case 3: {
break;
}
case 4: {
break;
}
case 5: {
variant.val.io = (key_value_string_t) { (char*)(*((int32_t*) (ptr + 8))), (size_t)(*((int32_t*) (ptr + 12))) };
break;
}
}
expected.val.err = variant;
break;
}
}*ret0 = expected;
}
__attribute__((import_module("key-value"), import_name("delete")))
void __wasm_import_key_value_delete(int32_t, int32_t, int32_t, int32_t);
void key_value_delete(key_value_store_t store, key_value_string_t *key, key_value_expected_unit_error_t *ret0) {
int32_t ptr = (int32_t) &RET_AREA;
__wasm_import_key_value_delete((int32_t) (store), (int32_t) (*key).ptr, (int32_t) (*key).len, ptr);
key_value_expected_unit_error_t expected;
switch ((int32_t) (*((uint8_t*) (ptr + 0)))) {
case 0: {
expected.is_err = false;
break;
}
case 1: {
expected.is_err = true;
key_value_error_t variant;
variant.tag = (int32_t) (*((uint8_t*) (ptr + 4)));
switch ((int32_t) variant.tag) {
case 0: {
break;
}
case 1: {
break;
}
case 2: {
break;
}
case 3: {
break;
}
case 4: {
break;
}
case 5: {
variant.val.io = (key_value_string_t) { (char*)(*((int32_t*) (ptr + 8))), (size_t)(*((int32_t*) (ptr + 12))) };
break;
}
}
expected.val.err = variant;
break;
}
}*ret0 = expected;
}
__attribute__((import_module("key-value"), import_name("exists")))
void __wasm_import_key_value_exists(int32_t, int32_t, int32_t, int32_t);
void key_value_exists(key_value_store_t store, key_value_string_t *key, key_value_expected_bool_error_t *ret0) {
int32_t ptr = (int32_t) &RET_AREA;
__wasm_import_key_value_exists((int32_t) (store), (int32_t) (*key).ptr, (int32_t) (*key).len, ptr);
key_value_expected_bool_error_t expected;
switch ((int32_t) (*((uint8_t*) (ptr + 0)))) {
case 0: {
expected.is_err = false;
expected.val.ok = (int32_t) (*((uint8_t*) (ptr + 4)));
break;
}
case 1: {
expected.is_err = true;
key_value_error_t variant;
variant.tag = (int32_t) (*((uint8_t*) (ptr + 4)));
switch ((int32_t) variant.tag) {
case 0: {
break;
}
case 1: {
break;
}
case 2: {
break;
}
case 3: {
break;
}
case 4: {
break;
}
case 5: {
variant.val.io = (key_value_string_t) { (char*)(*((int32_t*) (ptr + 8))), (size_t)(*((int32_t*) (ptr + 12))) };
break;
}
}
expected.val.err = variant;
break;
}
}*ret0 = expected;
}
__attribute__((import_module("key-value"), import_name("get-keys")))
void __wasm_import_key_value_get_keys(int32_t, int32_t);
void key_value_get_keys(key_value_store_t store, key_value_expected_list_string_error_t *ret0) {
int32_t ptr = (int32_t) &RET_AREA;
__wasm_import_key_value_get_keys((int32_t) (store), ptr);
key_value_expected_list_string_error_t expected;
switch ((int32_t) (*((uint8_t*) (ptr + 0)))) {
case 0: {
expected.is_err = false;
expected.val.ok = (key_value_list_string_t) { (key_value_string_t*)(*((int32_t*) (ptr + 4))), (size_t)(*((int32_t*) (ptr + 8))) };
break;
}
case 1: {
expected.is_err = true;
key_value_error_t variant;
variant.tag = (int32_t) (*((uint8_t*) (ptr + 4)));
switch ((int32_t) variant.tag) {
case 0: {
break;
}
case 1: {
break;
}
case 2: {
break;
}
case 3: {
break;
}
case 4: {
break;
}
case 5: {
variant.val.io = (key_value_string_t) { (char*)(*((int32_t*) (ptr + 8))), (size_t)(*((int32_t*) (ptr + 12))) };
break;
}
}
expected.val.err = variant;
break;
}
}*ret0 = expected;
}
__attribute__((import_module("key-value"), import_name("close")))
void __wasm_import_key_value_close(int32_t);
void key_value_close(key_value_store_t store) {
__wasm_import_key_value_close((int32_t) (store));
}

View File

@ -0,0 +1,134 @@
package key_value
// #include "key-value.h"
import "C"
import (
"errors"
"unsafe"
"fmt"
)
type Store C.key_value_store_t
type ErrorKind C.uint8_t
const (
ErrorKindStoreTableFull = iota
ErrorKindNoSuchStore
ErrorKindAccessDenied
ErrorKindInvalidStore
ErrorKindNoSuchKey
ErrorKindIo
)
type Error struct {
Kind ErrorKind
Val interface{}
}
func Open(name string) (Store, error) {
cname := toCStr(name)
var ret C.key_value_expected_store_error_t
C.key_value_open(&cname, &ret)
if ret.is_err {
return 0xFFFF_FFFF, toErr((*C.key_value_error_t)(unsafe.Pointer(&ret.val)))
} else {
return *(*Store)(unsafe.Pointer(&ret.val)), nil
}
}
func Get(store Store, key string) ([]byte, error) {
ckey := toCStr(key)
var ret C.key_value_expected_list_u8_error_t
C.key_value_get(C.uint32_t(store), &ckey, &ret)
if ret.is_err {
return []byte{}, toErr((*C.key_value_error_t)(unsafe.Pointer(&ret.val)))
} else {
list := (*C.key_value_list_u8_t)(unsafe.Pointer(&ret.val))
return C.GoBytes(unsafe.Pointer(list.ptr), C.int(list.len)), nil
}
}
func Set(store Store, key string, value []byte) error {
ckey := toCStr(key)
cbytes := toCBytes(value)
var ret C.key_value_expected_unit_error_t
C.key_value_set(C.uint32_t(store), &ckey, &cbytes, &ret)
if ret.is_err {
return toErr((*C.key_value_error_t)(unsafe.Pointer(&ret.val)))
} else {
return nil
}
}
func Delete(store Store, key string) error {
ckey := toCStr(key)
var ret C.key_value_expected_unit_error_t
C.key_value_delete(C.uint32_t(store), &ckey, &ret)
if ret.is_err {
return toErr((*C.key_value_error_t)(unsafe.Pointer(&ret.val)))
} else {
return nil
}
}
func Exists(store Store, key string) (bool, error) {
ckey := toCStr(key)
var ret C.key_value_expected_bool_error_t
C.key_value_exists(C.uint32_t(store), &ckey, &ret)
if ret.is_err {
return false, toErr((*C.key_value_error_t)(unsafe.Pointer(&ret.val)))
} else {
return *(*bool)(unsafe.Pointer(&ret.val)), nil
}
}
func GetKeys(store Store) ([]string, error) {
var ret C.key_value_expected_list_string_error_t
C.key_value_get_keys(C.uint32_t(store), &ret)
if ret.is_err {
return []string{}, toErr((*C.key_value_error_t)(unsafe.Pointer(&ret.val)))
} else {
return fromCStrList((*C.key_value_list_string_t)(unsafe.Pointer(&ret.val))), nil
}
}
func Close(store Store) {
C.key_value_close(C.uint32_t(store))
}
func toCBytes(x []byte) C.key_value_list_u8_t {
return C.key_value_list_u8_t{ptr: &x[0], len: C.size_t(len(x))}
}
func toCStr(x string) C.key_value_string_t {
return C.key_value_string_t{ptr: C.CString(x), len: C.size_t(len(x))}
}
func fromCStrList(list *C.key_value_list_string_t) []string {
listLen := int(list.len)
var result []string
slice := unsafe.Slice(list.ptr, listLen)
for i := 0; i < listLen; i++ {
string := slice[i]
result = append(result, C.GoStringN(string.ptr, C.int(string.len)))
}
return result
}
func toErr(error *C.key_value_error_t) error {
switch error.tag {
case ErrorKindStoreTableFull: return errors.New("store table full")
case ErrorKindNoSuchStore: return errors.New("no such store")
case ErrorKindAccessDenied: return errors.New("access denied")
case ErrorKindInvalidStore: return errors.New("invalid store")
case ErrorKindNoSuchKey: return errors.New("no such key")
case ErrorKindIo: {
string := (*C.key_value_string_t)(unsafe.Pointer(&error.val))
return errors.New(fmt.Sprintf("io error: %s", C.GoStringN(string.ptr, C.int(string.len))))
}
default: return errors.New(fmt.Sprintf("unrecognized error: %v", error.tag))
}
}

View File

@ -0,0 +1,92 @@
#ifndef __BINDINGS_KEY_VALUE_H
#define __BINDINGS_KEY_VALUE_H
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdint.h>
#include <stdbool.h>
typedef struct {
char *ptr;
size_t len;
} key_value_string_t;
void key_value_string_set(key_value_string_t *ret, const char *s);
void key_value_string_dup(key_value_string_t *ret, const char *s);
void key_value_string_free(key_value_string_t *ret);
typedef uint32_t key_value_store_t;
typedef struct {
uint8_t tag;
union {
key_value_string_t io;
} val;
} key_value_error_t;
#define KEY_VALUE_ERROR_STORE_TABLE_FULL 0
#define KEY_VALUE_ERROR_NO_SUCH_STORE 1
#define KEY_VALUE_ERROR_ACCESS_DENIED 2
#define KEY_VALUE_ERROR_INVALID_STORE 3
#define KEY_VALUE_ERROR_NO_SUCH_KEY 4
#define KEY_VALUE_ERROR_IO 5
void key_value_error_free(key_value_error_t *ptr);
typedef struct {
bool is_err;
union {
key_value_store_t ok;
key_value_error_t err;
} val;
} key_value_expected_store_error_t;
void key_value_expected_store_error_free(key_value_expected_store_error_t *ptr);
typedef struct {
uint8_t *ptr;
size_t len;
} key_value_list_u8_t;
void key_value_list_u8_free(key_value_list_u8_t *ptr);
typedef struct {
bool is_err;
union {
key_value_list_u8_t ok;
key_value_error_t err;
} val;
} key_value_expected_list_u8_error_t;
void key_value_expected_list_u8_error_free(key_value_expected_list_u8_error_t *ptr);
typedef struct {
bool is_err;
union {
key_value_error_t err;
} val;
} key_value_expected_unit_error_t;
void key_value_expected_unit_error_free(key_value_expected_unit_error_t *ptr);
typedef struct {
bool is_err;
union {
bool ok;
key_value_error_t err;
} val;
} key_value_expected_bool_error_t;
void key_value_expected_bool_error_free(key_value_expected_bool_error_t *ptr);
typedef struct {
key_value_string_t *ptr;
size_t len;
} key_value_list_string_t;
void key_value_list_string_free(key_value_list_string_t *ptr);
typedef struct {
bool is_err;
union {
key_value_list_string_t ok;
key_value_error_t err;
} val;
} key_value_expected_list_string_error_t;
void key_value_expected_list_string_error_free(key_value_expected_list_string_error_t *ptr);
void key_value_open(key_value_string_t *name, key_value_expected_store_error_t *ret0);
void key_value_get(key_value_store_t store, key_value_string_t *key, key_value_expected_list_u8_error_t *ret0);
void key_value_set(key_value_store_t store, key_value_string_t *key, key_value_list_u8_t *value, key_value_expected_unit_error_t *ret0);
void key_value_delete(key_value_store_t store, key_value_string_t *key, key_value_expected_unit_error_t *ret0);
void key_value_exists(key_value_store_t store, key_value_string_t *key, key_value_expected_bool_error_t *ret0);
void key_value_get_keys(key_value_store_t store, key_value_expected_list_string_error_t *ret0);
void key_value_close(key_value_store_t store);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -24,12 +24,6 @@ func publish(addr, channel string, payload []byte) error {
cchannel := redisStr(channel)
cpayload := C.outbound_redis_payload_t{ptr: &payload[0], len: C.size_t(len(payload))}
defer func() {
C.outbound_redis_string_free(&caddr)
C.outbound_redis_string_free(&cchannel)
C.outbound_redis_payload_free(&cpayload)
}()
err := C.outbound_redis_publish(&caddr, &cchannel, &cpayload)
return toErr(err)
}
@ -40,12 +34,6 @@ func get(addr, key string) ([]byte, error) {
var cpayload C.outbound_redis_payload_t
defer func() {
C.outbound_redis_string_free(&caddr)
C.outbound_redis_string_free(&ckey)
C.outbound_redis_payload_free(&cpayload)
}()
err := C.outbound_redis_get(&caddr, &ckey, &cpayload)
payload := C.GoBytes(unsafe.Pointer(cpayload.ptr), C.int(cpayload.len))
return payload, toErr(err)
@ -56,12 +44,6 @@ func set(addr, key string, payload []byte) error {
ckey := redisStr(key)
cpayload := C.outbound_redis_payload_t{ptr: &payload[0], len: C.size_t(len(payload))}
defer func() {
C.outbound_redis_string_free(&caddr)
C.outbound_redis_string_free(&ckey)
C.outbound_redis_payload_free(&cpayload)
}()
err := C.outbound_redis_set(&caddr, &ckey, &cpayload)
return toErr(err)
}
@ -72,11 +54,6 @@ func incr(addr, key string) (int64, error) {
var cpayload C.int64_t
defer func() {
C.outbound_redis_string_free(&caddr)
C.outbound_redis_string_free(&ckey)
}()
err := C.outbound_redis_incr(&caddr, &ckey, &cpayload)
return int64(cpayload), toErr(err)
}
@ -87,15 +64,83 @@ func del(addr string, keys []string) (int64, error) {
var cpayload C.int64_t
defer func() {
C.outbound_redis_string_free(&caddr)
C.outbound_redis_list_string_free(&ckeys)
}()
err := C.outbound_redis_del(&caddr, &ckeys, &cpayload)
return int64(cpayload), toErr(err)
}
func sadd(addr string, key string, values []string) (int64, error) {
caddr := redisStr(addr)
ckey := redisStr(key)
cvalues := redisListStr(values)
var cpayload C.int64_t
err := C.outbound_redis_sadd(&caddr, &ckey, &cvalues, &cpayload)
return int64(cpayload), toErr(err)
}
func smembers(addr string, key string) ([]string, error) {
caddr := redisStr(addr)
ckey := redisStr(key)
var cpayload C.outbound_redis_list_string_t
err := C.outbound_redis_smembers(&caddr, &ckey, &cpayload)
return fromRedisListStr(&cpayload), toErr(err)
}
func srem(addr string, key string, values []string) (int64, error) {
caddr := redisStr(addr)
ckey := redisStr(key)
cvalues := redisListStr(values)
var cpayload C.int64_t
err := C.outbound_redis_srem(&caddr, &ckey, &cvalues, &cpayload)
return int64(cpayload), toErr(err)
}
type RedisParameterKind C.uint8_t
const (
RedisParameterKindInt64 = iota
RedisParameterKindBinary
)
type RedisParameter struct {
Kind RedisParameterKind
Val interface{}
}
type RedisResultKind C.uint8_t
const (
RedisResultKindNil = iota
RedisResultKindStatus
RedisResultKindInt64
RedisResultKindBinary
)
type RedisResult struct {
Kind RedisResultKind
Val interface{}
}
func execute(addr string, command string, arguments []RedisParameter) ([]RedisResult, error) {
caddr := redisStr(addr)
ccommand := redisStr(command)
carguments := redisListParameter(arguments)
var cpayload C.outbound_redis_list_redis_result_t
err := C.outbound_redis_execute(&caddr, &ccommand, &carguments, &cpayload)
return fromRedisListResult(&cpayload), toErr(err)
}
func redisStr(x string) C.outbound_redis_string_t {
return C.outbound_redis_string_t{ptr: C.CString(x), len: C.size_t(len(x))}
}
func redisListStr(xs []string) C.outbound_redis_list_string_t {
var cxs []C.outbound_redis_string_t
@ -105,8 +150,69 @@ func redisListStr(xs []string) C.outbound_redis_list_string_t {
return C.outbound_redis_list_string_t{ptr: &cxs[0], len: C.size_t(len(cxs))}
}
func redisStr(x string) C.outbound_redis_string_t {
return C.outbound_redis_string_t{ptr: C.CString(x), len: C.size_t(len(x))}
func fromRedisListStr(list *C.outbound_redis_list_string_t) []string {
listLen := int(list.len)
var result []string
slice := unsafe.Slice(list.ptr, listLen)
for i := 0; i < listLen; i++ {
string := slice[i]
result = append(result, C.GoStringN(string.ptr, C.int(string.len)))
}
return result
}
func redisParameter(x RedisParameter) C.outbound_redis_redis_parameter_t {
var val C._Ctype_union___9
switch x.Kind {
case RedisParameterKindInt64: *(*C.int64_t)(unsafe.Pointer(&val)) = x.Val.(int64)
case RedisParameterKindBinary: {
value := x.Val.([]byte)
payload := C.outbound_redis_payload_t{ptr: &value[0], len: C.size_t(len(value))}
*(*C.outbound_redis_payload_t)(unsafe.Pointer(&val)) = payload
}
}
return C.outbound_redis_redis_parameter_t{tag: C.uint8_t(x.Kind), val: val}
}
func redisListParameter(xs []RedisParameter) C.outbound_redis_list_redis_parameter_t {
var cxs []C.outbound_redis_redis_parameter_t
for i := 0; i < len(xs); i++ {
cxs = append(cxs, redisParameter(xs[i]))
}
return C.outbound_redis_list_redis_parameter_t{ptr: &cxs[0], len: C.size_t(len(cxs))}
}
func fromRedisResult(result *C.outbound_redis_redis_result_t) RedisResult {
var val interface{}
switch result.tag {
case 0: val = nil
case 1: {
string := (*C.outbound_redis_string_t)(unsafe.Pointer(&result.val))
val = C.GoStringN(string.ptr, C.int(string.len))
}
case 2: val = int64(*(*C.int64_t)(unsafe.Pointer(&result.val)))
case 3: {
payload := (*C.outbound_redis_payload_t)(unsafe.Pointer(&result.val))
val = C.GoBytes(unsafe.Pointer(payload.ptr), C.int(payload.len))
}
}
return RedisResult{Kind: RedisResultKind(result.tag), Val: val}
}
func fromRedisListResult(list *C.outbound_redis_list_redis_result_t) []RedisResult {
listLen := int(list.len)
var result []RedisResult
slice := unsafe.Slice(list.ptr, listLen)
for i := 0; i < listLen; i++ {
result = append(result, fromRedisResult(&slice[i]))
}
return result
}
func toErr(code C.uint8_t) error {

View File

@ -47,6 +47,26 @@ void outbound_redis_string_free(outbound_redis_string_t *ret) {
void outbound_redis_payload_free(outbound_redis_payload_t *ptr) {
canonical_abi_free(ptr->ptr, ptr->len * 1, 1);
}
void outbound_redis_redis_parameter_free(outbound_redis_redis_parameter_t *ptr) {
switch ((int32_t) ptr->tag) {
case 1: {
outbound_redis_payload_free(&ptr->val.binary);
break;
}
}
}
void outbound_redis_redis_result_free(outbound_redis_redis_result_t *ptr) {
switch ((int32_t) ptr->tag) {
case 1: {
outbound_redis_string_free(&ptr->val.status);
break;
}
case 3: {
outbound_redis_payload_free(&ptr->val.binary);
break;
}
}
}
typedef struct {
bool is_err;
union {
@ -73,6 +93,32 @@ void outbound_redis_list_string_free(outbound_redis_list_string_t *ptr) {
}
canonical_abi_free(ptr->ptr, ptr->len * 8, 4);
}
typedef struct {
bool is_err;
union {
outbound_redis_list_string_t ok;
outbound_redis_error_t err;
} val;
} outbound_redis_expected_list_string_error_t;
void outbound_redis_list_redis_parameter_free(outbound_redis_list_redis_parameter_t *ptr) {
for (size_t i = 0; i < ptr->len; i++) {
outbound_redis_redis_parameter_free(&ptr->ptr[i]);
}
canonical_abi_free(ptr->ptr, ptr->len * 16, 8);
}
void outbound_redis_list_redis_result_free(outbound_redis_list_redis_result_t *ptr) {
for (size_t i = 0; i < ptr->len; i++) {
outbound_redis_redis_result_free(&ptr->ptr[i]);
}
canonical_abi_free(ptr->ptr, ptr->len * 16, 8);
}
typedef struct {
bool is_err;
union {
outbound_redis_list_redis_result_t ok;
outbound_redis_error_t err;
} val;
} outbound_redis_expected_list_redis_result_error_t;
__attribute__((aligned(8)))
static uint8_t RET_AREA[16];
@ -184,3 +230,91 @@ outbound_redis_error_t outbound_redis_del(outbound_redis_string_t *address, outb
}*ret0 = expected.val.ok;
return expected.is_err ? expected.val.err : -1;
}
__attribute__((import_module("outbound-redis"), import_name("sadd")))
void __wasm_import_outbound_redis_sadd(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t);
outbound_redis_error_t outbound_redis_sadd(outbound_redis_string_t *address, outbound_redis_string_t *key, outbound_redis_list_string_t *values, int64_t *ret0) {
int32_t ptr = (int32_t) &RET_AREA;
__wasm_import_outbound_redis_sadd((int32_t) (*address).ptr, (int32_t) (*address).len, (int32_t) (*key).ptr, (int32_t) (*key).len, (int32_t) (*values).ptr, (int32_t) (*values).len, ptr);
outbound_redis_expected_s64_error_t expected;
switch ((int32_t) (*((uint8_t*) (ptr + 0)))) {
case 0: {
expected.is_err = false;
expected.val.ok = *((int64_t*) (ptr + 8));
break;
}
case 1: {
expected.is_err = true;
expected.val.err = (int32_t) (*((uint8_t*) (ptr + 8)));
break;
}
}*ret0 = expected.val.ok;
return expected.is_err ? expected.val.err : -1;
}
__attribute__((import_module("outbound-redis"), import_name("smembers")))
void __wasm_import_outbound_redis_smembers(int32_t, int32_t, int32_t, int32_t, int32_t);
outbound_redis_error_t outbound_redis_smembers(outbound_redis_string_t *address, outbound_redis_string_t *key, outbound_redis_list_string_t *ret0) {
int32_t ptr = (int32_t) &RET_AREA;
__wasm_import_outbound_redis_smembers((int32_t) (*address).ptr, (int32_t) (*address).len, (int32_t) (*key).ptr, (int32_t) (*key).len, ptr);
outbound_redis_expected_list_string_error_t expected;
switch ((int32_t) (*((uint8_t*) (ptr + 0)))) {
case 0: {
expected.is_err = false;
expected.val.ok = (outbound_redis_list_string_t) { (outbound_redis_string_t*)(*((int32_t*) (ptr + 4))), (size_t)(*((int32_t*) (ptr + 8))) };
break;
}
case 1: {
expected.is_err = true;
expected.val.err = (int32_t) (*((uint8_t*) (ptr + 4)));
break;
}
}*ret0 = expected.val.ok;
return expected.is_err ? expected.val.err : -1;
}
__attribute__((import_module("outbound-redis"), import_name("srem")))
void __wasm_import_outbound_redis_srem(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t);
outbound_redis_error_t outbound_redis_srem(outbound_redis_string_t *address, outbound_redis_string_t *key, outbound_redis_list_string_t *values, int64_t *ret0) {
int32_t ptr = (int32_t) &RET_AREA;
__wasm_import_outbound_redis_srem((int32_t) (*address).ptr, (int32_t) (*address).len, (int32_t) (*key).ptr, (int32_t) (*key).len, (int32_t) (*values).ptr, (int32_t) (*values).len, ptr);
outbound_redis_expected_s64_error_t expected;
switch ((int32_t) (*((uint8_t*) (ptr + 0)))) {
case 0: {
expected.is_err = false;
expected.val.ok = *((int64_t*) (ptr + 8));
break;
}
case 1: {
expected.is_err = true;
expected.val.err = (int32_t) (*((uint8_t*) (ptr + 8)));
break;
}
}*ret0 = expected.val.ok;
return expected.is_err ? expected.val.err : -1;
}
__attribute__((import_module("outbound-redis"), import_name("execute")))
void __wasm_import_outbound_redis_execute(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t);
outbound_redis_error_t outbound_redis_execute(outbound_redis_string_t *address, outbound_redis_string_t *command, outbound_redis_list_redis_parameter_t *arguments, outbound_redis_list_redis_result_t *ret0) {
int32_t ptr = (int32_t) &RET_AREA;
__wasm_import_outbound_redis_execute((int32_t) (*address).ptr, (int32_t) (*address).len, (int32_t) (*command).ptr, (int32_t) (*command).len, (int32_t) (*arguments).ptr, (int32_t) (*arguments).len, ptr);
outbound_redis_expected_list_redis_result_error_t expected;
switch ((int32_t) (*((uint8_t*) (ptr + 0)))) {
case 0: {
expected.is_err = false;
expected.val.ok = (outbound_redis_list_redis_result_t) { (outbound_redis_redis_result_t*)(*((int32_t*) (ptr + 4))), (size_t)(*((int32_t*) (ptr + 8))) };
break;
}
case 1: {
expected.is_err = true;
expected.val.err = (int32_t) (*((uint8_t*) (ptr + 4)));
break;
}
}*ret0 = expected.val.ok;
return expected.is_err ? expected.val.err : -1;
}

View File

@ -24,16 +24,53 @@ extern "C"
size_t len;
} outbound_redis_payload_t;
void outbound_redis_payload_free(outbound_redis_payload_t *ptr);
typedef struct {
uint8_t tag;
union {
int64_t int64;
outbound_redis_payload_t binary;
} val;
} outbound_redis_redis_parameter_t;
#define OUTBOUND_REDIS_REDIS_PARAMETER_INT64 0
#define OUTBOUND_REDIS_REDIS_PARAMETER_BINARY 1
void outbound_redis_redis_parameter_free(outbound_redis_redis_parameter_t *ptr);
typedef struct {
uint8_t tag;
union {
outbound_redis_string_t status;
int64_t int64;
outbound_redis_payload_t binary;
} val;
} outbound_redis_redis_result_t;
#define OUTBOUND_REDIS_REDIS_RESULT_NIL 0
#define OUTBOUND_REDIS_REDIS_RESULT_STATUS 1
#define OUTBOUND_REDIS_REDIS_RESULT_INT64 2
#define OUTBOUND_REDIS_REDIS_RESULT_BINARY 3
void outbound_redis_redis_result_free(outbound_redis_redis_result_t *ptr);
typedef struct {
outbound_redis_string_t *ptr;
size_t len;
} outbound_redis_list_string_t;
void outbound_redis_list_string_free(outbound_redis_list_string_t *ptr);
typedef struct {
outbound_redis_redis_parameter_t *ptr;
size_t len;
} outbound_redis_list_redis_parameter_t;
void outbound_redis_list_redis_parameter_free(outbound_redis_list_redis_parameter_t *ptr);
typedef struct {
outbound_redis_redis_result_t *ptr;
size_t len;
} outbound_redis_list_redis_result_t;
void outbound_redis_list_redis_result_free(outbound_redis_list_redis_result_t *ptr);
outbound_redis_error_t outbound_redis_publish(outbound_redis_string_t *address, outbound_redis_string_t *channel, outbound_redis_payload_t *payload);
outbound_redis_error_t outbound_redis_get(outbound_redis_string_t *address, outbound_redis_string_t *key, outbound_redis_payload_t *ret0);
outbound_redis_error_t outbound_redis_set(outbound_redis_string_t *address, outbound_redis_string_t *key, outbound_redis_payload_t *value);
outbound_redis_error_t outbound_redis_incr(outbound_redis_string_t *address, outbound_redis_string_t *key, int64_t *ret0);
outbound_redis_error_t outbound_redis_del(outbound_redis_string_t *address, outbound_redis_list_string_t *keys, int64_t *ret0);
outbound_redis_error_t outbound_redis_sadd(outbound_redis_string_t *address, outbound_redis_string_t *key, outbound_redis_list_string_t *values, int64_t *ret0);
outbound_redis_error_t outbound_redis_smembers(outbound_redis_string_t *address, outbound_redis_string_t *key, outbound_redis_list_string_t *ret0);
outbound_redis_error_t outbound_redis_srem(outbound_redis_string_t *address, outbound_redis_string_t *key, outbound_redis_list_string_t *values, int64_t *ret0);
outbound_redis_error_t outbound_redis_execute(outbound_redis_string_t *address, outbound_redis_string_t *command, outbound_redis_list_redis_parameter_t *arguments, outbound_redis_list_redis_result_t *ret0);
#ifdef __cplusplus
}
#endif

View File

@ -49,3 +49,27 @@ func Incr(addr, key string) (int64, error) {
func Del(addr string, keys []string) (int64, error) {
return del(addr, keys)
}
// Adds the specified values to the set for the specified key, creating it if it
// does not already exist.
func Sadd(addr string, key string, values []string) (int64, error) {
return sadd(addr, key, values)
}
// Get the elements of the set for the specified key.
func Smembers(addr string, key string) ([]string, error) {
return smembers(addr, key)
}
// Removes the specified elements from the set for the specified key. This has
// no effect if the key does not exist.
func Srem(addr string, key string, values []string) (int64, error) {
return srem(addr, key, values)
}
// Run the specified Redis command with the specified arguments, returning zero
// or more results. This is a general-purpose function which should work with
// any Redis command.
func Execute(addr string, command string, arguments []RedisParameter) ([]RedisResult, error) {
return execute(addr, command, arguments)
}

View File

@ -26,9 +26,47 @@ size_t align
return;
free(ptr);
}
#include <string.h>
void spin_redis_string_set(spin_redis_string_t *ret, const char *s) {
ret->ptr = (char*) s;
ret->len = strlen(s);
}
void spin_redis_string_dup(spin_redis_string_t *ret, const char *s) {
ret->len = strlen(s);
ret->ptr = canonical_abi_realloc(NULL, 0, 1, ret->len);
memcpy(ret->ptr, s, ret->len);
}
void spin_redis_string_free(spin_redis_string_t *ret) {
canonical_abi_free(ret->ptr, ret->len, 1);
ret->ptr = NULL;
ret->len = 0;
}
void spin_redis_payload_free(spin_redis_payload_t *ptr) {
canonical_abi_free(ptr->ptr, ptr->len * 1, 1);
}
void spin_redis_redis_parameter_free(spin_redis_redis_parameter_t *ptr) {
switch ((int32_t) ptr->tag) {
case 1: {
spin_redis_payload_free(&ptr->val.binary);
break;
}
}
}
void spin_redis_redis_result_free(spin_redis_redis_result_t *ptr) {
switch ((int32_t) ptr->tag) {
case 1: {
spin_redis_string_free(&ptr->val.status);
break;
}
case 3: {
spin_redis_payload_free(&ptr->val.binary);
break;
}
}
}
typedef struct {
bool is_err;
union {

View File

@ -7,6 +7,15 @@ extern "C"
#include <stdint.h>
#include <stdbool.h>
typedef struct {
char *ptr;
size_t len;
} spin_redis_string_t;
void spin_redis_string_set(spin_redis_string_t *ret, const char *s);
void spin_redis_string_dup(spin_redis_string_t *ret, const char *s);
void spin_redis_string_free(spin_redis_string_t *ret);
typedef uint8_t spin_redis_error_t;
#define SPIN_REDIS_ERROR_SUCCESS 0
#define SPIN_REDIS_ERROR_ERROR 1
@ -15,6 +24,29 @@ extern "C"
size_t len;
} spin_redis_payload_t;
void spin_redis_payload_free(spin_redis_payload_t *ptr);
typedef struct {
uint8_t tag;
union {
int64_t int64;
spin_redis_payload_t binary;
} val;
} spin_redis_redis_parameter_t;
#define SPIN_REDIS_REDIS_PARAMETER_INT64 0
#define SPIN_REDIS_REDIS_PARAMETER_BINARY 1
void spin_redis_redis_parameter_free(spin_redis_redis_parameter_t *ptr);
typedef struct {
uint8_t tag;
union {
spin_redis_string_t status;
int64_t int64;
spin_redis_payload_t binary;
} val;
} spin_redis_redis_result_t;
#define SPIN_REDIS_REDIS_RESULT_NIL 0
#define SPIN_REDIS_REDIS_RESULT_STATUS 1
#define SPIN_REDIS_REDIS_RESULT_INT64 2
#define SPIN_REDIS_REDIS_RESULT_BINARY 3
void spin_redis_redis_result_free(spin_redis_redis_result_t *ptr);
spin_redis_error_t spin_redis_handle_redis_message(spin_redis_payload_t *message);
#ifdef __cplusplus
}

View File

@ -12,4 +12,4 @@ allowed_http_hosts = []
[component.trigger]
route = "{{http-path}}"
[component.build]
command = "tinygo build -wasm-abi=generic -target=wasi -gc=leaking -no-debug -o main.wasm main.go"
command = "tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go"

View File

@ -5,5 +5,5 @@ allowed_http_hosts = []
[component.trigger]
route = "{{http-path}}"
[component.build]
command = "tinygo build -wasm-abi=generic -target=wasi -gc=leaking -no-debug -o main.wasm main.go"
command = "tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go"
workdir = "{{ output-path }}"

View File

@ -12,4 +12,4 @@ allowed_http_hosts = []
[component.trigger]
channel = "{{redis-channel}}"
[component.build]
command = "tinygo build -wasm-abi=generic -target=wasi -gc=leaking -no-debug -o main.wasm main.go"
command = "tinygo build -target=wasi -gc=leaking -no-debug -o main.wasm main.go"

View File

@ -1,3 +1,3 @@
.PHONY: build
build:
tinygo build -wasm-abi=generic -target=wasi -gc=leaking -o main.wasm main.go
tinygo build -target=wasi -gc=leaking -o main.wasm main.go