Skip to content

Commit 1b6e89c

Browse files
author
Jagger De Leo
committed
Add updated TinyGo SDK
1 parent 0fc6901 commit 1b6e89c

File tree

19 files changed

+510
-0
lines changed

19 files changed

+510
-0
lines changed

go.mod

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/suborbital/sdk
2+
3+
go 1.19

tinygo/errors/errors.go

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package errors
2+
3+
import (
4+
"errors"
5+
)
6+
7+
// RunErr adds a status code for use in FFI calls to return_result()
8+
type RunErr struct {
9+
error
10+
Code int
11+
}
12+
13+
// New creates a new RunErr
14+
func New(message string, code int) RunErr {
15+
return RunErr{errors.New(message), code}
16+
}
17+
18+
// WithCode creates a new RunErr from an existing error and a status code
19+
func WithCode(err error, code int) RunErr {
20+
return RunErr{err, code}
21+
}

tinygo/http/http.go

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package http
2+
3+
import (
4+
"github.com/suborbital/sdk/tinygo/internal/ffi"
5+
6+
"github.com/suborbital/sdk/tinygo/http/method"
7+
)
8+
9+
func GET(url string, headers map[string]string) ([]byte, error) {
10+
return do(method.GET, url, nil, headers)
11+
}
12+
13+
func HEAD(url string, headers map[string]string) ([]byte, error) {
14+
return do(method.HEAD, url, nil, headers)
15+
}
16+
17+
func OPTIONS(url string, headers map[string]string) ([]byte, error) {
18+
return do(method.OPTIONS, url, nil, headers)
19+
}
20+
21+
func POST(url string, body []byte, headers map[string]string) ([]byte, error) {
22+
return do(method.POST, url, body, headers)
23+
}
24+
25+
func PUT(url string, body []byte, headers map[string]string) ([]byte, error) {
26+
return do(method.PUT, url, body, headers)
27+
}
28+
29+
func PATCH(url string, body []byte, headers map[string]string) ([]byte, error) {
30+
return do(method.PATCH, url, body, headers)
31+
}
32+
33+
func DELETE(url string, headers map[string]string) ([]byte, error) {
34+
return do(method.DELETE, url, nil, headers)
35+
}
36+
37+
// Remark: The URL gets encoded with headers added on the end, seperated by ::
38+
// eg. https://google.com/somepage::authorization:bearer qdouwrnvgoquwnrg::anotherheader:nicetomeetyou
39+
func do(method method.MethodType, url string, body []byte, headers map[string]string) ([]byte, error) {
40+
urlStr := url
41+
42+
if headers != nil {
43+
headerStr := renderHeaderString(headers)
44+
if headerStr != "" {
45+
urlStr += "::" + headerStr
46+
}
47+
}
48+
49+
return ffi.DoHTTPRequest(int32(method), urlStr, body, headers)
50+
}
51+
52+
func renderHeaderString(headers map[string]string) string {
53+
out := ""
54+
55+
for key, value := range headers {
56+
out += key + ":" + value
57+
out += "::"
58+
}
59+
60+
return out[:len(out)-2]
61+
}

tinygo/http/method/method.go

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package method
2+
3+
type MethodType int32
4+
5+
const (
6+
GET MethodType = iota
7+
HEAD
8+
OPTIONS
9+
POST
10+
PUT
11+
PATCH
12+
DELETE
13+
)

tinygo/internal/ffi/error.go

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package ffi
2+
3+
import "errors"
4+
5+
type HostErr error
6+
7+
func NewHostError(message string) HostErr {
8+
return errors.New(message).(HostErr)
9+
}

tinygo/internal/ffi/ffi.go

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package ffi
2+
3+
// #include <plugin.h>
4+
import "C"
5+
6+
func result(size int32) ([]byte, HostErr) {
7+
allocSize := size
8+
9+
if size < 0 {
10+
if size == -1 {
11+
return nil, NewHostError("unknown error returned from host")
12+
}
13+
14+
allocSize = -size
15+
}
16+
17+
result := make([]byte, allocSize)
18+
resultPtr, _ := unsafeSlicePointer(result)
19+
20+
if code := C.get_ffi_result(resultPtr, Ident()); code != 0 {
21+
return nil, NewHostError("unknown error returned from host")
22+
}
23+
24+
if size < 0 {
25+
return nil, NewHostError(string(result))
26+
}
27+
28+
return result, nil
29+
}
30+
31+
func addVar(name, value string) {
32+
namePtr, nameSize := unsafeSlicePointer([]byte(name))
33+
valuePtr, valueSize := unsafeSlicePointer([]byte(value))
34+
35+
C.add_ffi_var(namePtr, nameSize, valuePtr, valueSize, Ident())
36+
}

tinygo/internal/ffi/globals.go

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package ffi
2+
3+
import (
4+
"github.com/suborbital/sdk/tinygo/plugin"
5+
)
6+
7+
var plugin_ plugin.Plugin
8+
var ident_ int32
9+
10+
func Ident() int32 {
11+
return ident_
12+
}
13+
14+
func Use(plugin plugin.Plugin) {
15+
plugin_ = plugin
16+
}

tinygo/internal/ffi/http.go

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package ffi
2+
3+
// #include <plugin.h>
4+
import "C"
5+
6+
func DoHTTPRequest(method int32, urlStr string, body []byte, headers map[string]string) ([]byte, error) {
7+
urlPtr, urlSize := unsafeSlicePointer([]byte(urlStr))
8+
bodyPtr, bodySize := unsafeSlicePointer(body)
9+
10+
size := C.fetch_url(method, urlPtr, urlSize, bodyPtr, bodySize, Ident())
11+
12+
return result(size)
13+
}

tinygo/internal/ffi/log.go

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package ffi
2+
3+
// #include <plugin.h>
4+
import "C"
5+
6+
func LogAtLevel(message string, level int32) {
7+
msgPtr, size := unsafeSlicePointer([]byte(message))
8+
9+
C.log_msg(msgPtr, size, level, Ident())
10+
}

tinygo/internal/ffi/plugin.h

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <stdint.h>
2+
3+
int32_t get_ffi_result(void* ptr, int32_t ident);
4+
int32_t add_ffi_var(void* name_ptr, int32_t name_size, void* var_ptr, int32_t val_size, int32_t ident);
5+
6+
void return_result(void* rawdata, int32_t size, int32_t ident);
7+
void return_error(int32_t code, void* rawdata, int32_t size, int32_t ident);
8+
9+
void log_msg(void *ptr, int32_t size, int32_t level, int32_t ident);
10+
11+
int32_t request_get_field(int32_t field_type, void* key_ptr, int32_t key_size, int32_t ident);
12+
int32_t request_set_field(int32_t field_type, void *key_ptr, int32_t key_size, void *value_ptr, int32_t value_size, int32_t ident);
13+
14+
int32_t fetch_url(int32_t method, void *url_ptr, int32_t url_size, void *body_ptr, int32_t body_size, int32_t ident);
15+
16+
void resp_set_header(void *key_ptr, int32_t key_size, void *value_ptr, int32_t value_size, int32_t ident);

tinygo/internal/ffi/req.go

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package ffi
2+
3+
// #include <plugin.h>
4+
import "C"
5+
6+
func ReqGetField(fieldType int32, key string) []byte {
7+
ptr, size := unsafeSlicePointer([]byte(key))
8+
9+
res, err := result(C.request_get_field(fieldType, ptr, size, Ident()))
10+
if err != nil {
11+
return []byte{}
12+
}
13+
return res
14+
}
15+
16+
func ReqSetField(fieldType int32, key string, value string) ([]byte, error) {
17+
keyPtr, keySize := unsafeSlicePointer([]byte(key))
18+
valPtr, valSize := unsafeSlicePointer([]byte(value))
19+
20+
return result(C.request_set_field(fieldType, keyPtr, keySize, valPtr, valSize, Ident()))
21+
}

tinygo/internal/ffi/resp.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package ffi
2+
3+
// #include <plugin.h>
4+
import "C"
5+
6+
func RespSetHeader(key, value string) {
7+
keyPtr, keySize := unsafeSlicePointer([]byte(key))
8+
valPtr, valSize := unsafeSlicePointer([]byte(value))
9+
10+
C.resp_set_header(keyPtr, keySize, valPtr, valSize, Ident())
11+
}

tinygo/internal/ffi/run.go

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package ffi
2+
3+
// #include <plugin.h>
4+
import "C"
5+
import (
6+
"reflect"
7+
"unsafe"
8+
9+
"github.com/suborbital/sdk/tinygo/errors"
10+
)
11+
12+
//export run_e
13+
func run_e(rawdata uintptr, size int32, ident int32) {
14+
var input []byte
15+
ident_ = ident
16+
17+
inputHeader := (*reflect.SliceHeader)(unsafe.Pointer(&input))
18+
inputHeader.Data = rawdata
19+
inputHeader.Len = uintptr(size)
20+
inputHeader.Cap = uintptr(size)
21+
22+
result, err := plugin_.Run(input)
23+
24+
if err != nil {
25+
returnError(err, ident)
26+
return
27+
}
28+
29+
resPtr, resLen := unsafeSlicePointer(result)
30+
31+
C.return_result(resPtr, resLen, ident)
32+
}
33+
34+
func returnError(err error, ident int32) {
35+
code := int32(500)
36+
37+
if err == nil {
38+
C.return_error(code, unsafe.Pointer(uintptr(0)), 0, ident)
39+
return
40+
}
41+
42+
switch e := err.(type) {
43+
case errors.RunErr:
44+
code = int32(e.Code)
45+
}
46+
47+
errPtr, errLen := unsafeSlicePointer([]byte(err.Error()))
48+
49+
C.return_error(code, errPtr, errLen, ident)
50+
}

tinygo/internal/ffi/unsafe.go

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package ffi
2+
3+
import (
4+
"reflect"
5+
"runtime"
6+
"unsafe"
7+
)
8+
9+
//export allocate
10+
func allocate(size int32) uintptr {
11+
arr := make([]byte, size)
12+
13+
header := (*reflect.SliceHeader)(unsafe.Pointer(&arr))
14+
15+
runtime.KeepAlive(arr)
16+
17+
return uintptr(header.Data)
18+
}
19+
20+
//export deallocate
21+
func deallocate(pointer uintptr, size int32) {
22+
var arr []byte
23+
24+
header := (*reflect.SliceHeader)(unsafe.Pointer(&arr))
25+
header.Data = pointer
26+
header.Len = uintptr(size) // Interestingly, the types of .Len and .Cap here
27+
header.Cap = uintptr(size) // differ from standard Go, where they are both int
28+
29+
arr = nil // I think this is sufficient to mark the slice for garbage collection
30+
}
31+
32+
func unsafeSlicePointer(slice []byte) (unsafe.Pointer, int32) {
33+
header := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
34+
35+
return unsafe.Pointer(header.Data), int32(len(slice))
36+
}
37+
38+
func unsafeSlice(size int) ([]byte, uintptr) {
39+
slice := make([]byte, size)
40+
header := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
41+
42+
runtime.KeepAlive(slice)
43+
ptr := unsafe.Pointer(header.Data)
44+
45+
return slice, uintptr(ptr)
46+
}

tinygo/log/log.go

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package log
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/suborbital/sdk/tinygo/internal/ffi"
7+
)
8+
9+
type LogLevel int32
10+
11+
func logAtLevel(message string, level LogLevel) {
12+
ffi.LogAtLevel(message, int32(level))
13+
}
14+
15+
const (
16+
LogLevelError LogLevel = iota + 1
17+
LogLevelWarn
18+
LogLevelInfo
19+
LogLevelDebug
20+
)
21+
22+
func Debug(message string) {
23+
logAtLevel(message, LogLevelDebug)
24+
}
25+
26+
func Info(message string) {
27+
logAtLevel(message, LogLevelInfo)
28+
}
29+
30+
func Warn(message string) {
31+
logAtLevel(message, LogLevelWarn)
32+
}
33+
34+
func Error(message string) {
35+
logAtLevel(message, LogLevelError)
36+
}
37+
38+
func Debugf(format string, args ...interface{}) {
39+
Debug(fmt.Sprintf(format, args...))
40+
}
41+
42+
func Infof(format string, args ...interface{}) {
43+
Info(fmt.Sprintf(format, args...))
44+
}
45+
46+
func Warnf(format string, args ...interface{}) {
47+
Warn(fmt.Sprintf(format, args...))
48+
}
49+
50+
func Errorf(format string, args ...interface{}) {
51+
Error(fmt.Sprintf(format, args...))
52+
}

tinygo/plugin/types.go

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package plugin
2+
3+
// The Plugin interface is all that needs to be implemented by an E2 Core plugin.
4+
type Plugin interface {
5+
Run(input []byte) ([]byte, error)
6+
}

0 commit comments

Comments
 (0)