-
Notifications
You must be signed in to change notification settings - Fork 5
/
wapc.go
105 lines (83 loc) · 2.75 KB
/
wapc.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package wapc
import (
"reflect"
"unsafe"
)
type (
// Function is the function to register in your waPC module.
Function func(payload []byte) ([]byte, error)
// Functions is a map of function name to `Function`.
Functions map[string]Function
// HostError indicates an error when invoking a host operation.
HostError struct {
message string
}
)
var allFunctions = Functions{}
// RegisterFunctions adds functions by name to the registry.
// This should be invoked in `main()`.
func RegisterFunctions(functions Functions) {
for name, fn := range functions {
allFunctions[name] = fn
}
}
// RegisterFunction adds a single function by name to the registry.
// This should be invoked in `main()`.
func RegisterFunction(name string, fn Function) {
allFunctions[name] = fn
}
//go:export __guest_call
func guestCall(operationSize uint32, payloadSize uint32) bool {
operation := make([]byte, operationSize) // alloc
payload := make([]byte, payloadSize) // alloc
guestRequest(bytesToPointer(operation), bytesToPointer(payload))
if f, ok := allFunctions[string(operation)]; ok {
response, err := f(payload)
if err != nil {
message := err.Error()
guestError(stringToPointer(message), uint32(len(message)))
return false
}
guestResponse(bytesToPointer(response), uint32(len(response)))
return true
}
message := `Could not find function "` + string(operation) + `"`
guestError(stringToPointer(message), uint32(len(message)))
return false
}
// ConsoleLog writes the message the underlying waPC console logger.
func ConsoleLog(msg string) {
consoleLog(stringToPointer(msg), uint32(len(msg)))
}
// HostCall invokes an operation on the host. The host uses `namespace` and `operation`
// to route to the `payload` to the appropriate operation. The host will return
// a response payload if successful.
func HostCall(binding, namespace, operation string, payload []byte) ([]byte, error) {
result := hostCall(
stringToPointer(binding), uint32(len(binding)),
stringToPointer(namespace), uint32(len(namespace)),
stringToPointer(operation), uint32(len(operation)),
bytesToPointer(payload), uint32(len(payload)),
)
if !result {
errorLen := hostErrorLen()
message := make([]byte, errorLen) // alloc
hostError(bytesToPointer(message))
return nil, &HostError{message: string(message)} // alloc
}
responseLen := hostResponseLen()
response := make([]byte, responseLen) // alloc
hostResponse(bytesToPointer(response))
return response, nil
}
//go:inline
func bytesToPointer(s []byte) uintptr {
return (*(*reflect.SliceHeader)(unsafe.Pointer(&s))).Data
}
//go:inline
func stringToPointer(s string) uintptr {
return (*(*reflect.StringHeader)(unsafe.Pointer(&s))).Data
}
func (e *HostError) Error() string {
return "Host error: " + e.message // alloc
}