Skip to content

Commit

Permalink
protobuf de/serialization implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
syke99 committed Sep 16, 2023
1 parent 3b6ac44 commit 26c247f
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 182 deletions.
293 changes: 111 additions & 182 deletions extism.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ import (
"encoding/hex"
"errors"
"fmt"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
"io/ioutil"
"log"
"net/http"
"reflect"
"time"
"unicode/utf8"
"unsafe"

"github.com/tetratelabs/wazero"
Expand Down Expand Up @@ -105,7 +107,6 @@ type Plugin struct {
Config map[string]string
Var map[string][]byte
vars map[string]any
varbuf *bytes.Buffer
AllowedHosts []string
AllowedPaths map[string]string
LastStatusCode int
Expand All @@ -128,220 +129,148 @@ func (p *Plugin) SetLogLevel(level LogLevel) {
p.logLevel = level
}

// SetVar converts value to a slice of bytes, and
// adds that byte slice to the Plugin's variables
func (p *Plugin) SetVar(key string, value any) error {
if p.Var == nil {
p.Var = make(map[string][]byte)
}

if p.vars == nil {
p.vars = make(map[string]any)
}

if p.varbuf == nil {
p.varbuf = bytes.NewBuffer(make([]byte, 0))
}

err := binary.Write(p.varbuf, nativeEndian, value)
if err != nil {
return err
}

p.Var[key] = p.varbuf.Bytes()
p.vars[key] = value

p.varbuf.Reset()

return nil
}

// GetVar gets the variable from the plugin with the given
// key and reads it into data. GetVar
// wraps encoding/binary.Read(), and thus, data must be a
// pointer to a fixed-size value or a slice of fixed-size values.
// If you don't know the needed size to decode the variable
// into, use the appropriate helper method below
func (p *Plugin) GetVar(key string, data any) error {
_, err := p.varbuf.Read(p.Var[key])
if err != nil {
return err
}

p.varbuf.Reset()

err = binary.Read(p.varbuf, nativeEndian, data)
return err
}

// GetVarString returns the variable at key as a string
func (p *Plugin) GetVarString(key string) string {
return p.vars[key].(string)
}

// GetVarBool returns the variable at key as a bool
func (p *Plugin) GetVarBool(key string) bool {
return p.vars[key].(bool)
}

// GetVarRune returns the first rune decoded from variable
// at key. If decoding the variable returns a RuneError, this
// will instead return a nil slice and an error specifying the
// key used that failed to decode
func (p *Plugin) GetVarRune(key string) (rune, error) {
var r rune
func checkValType(index int, value any) (any, error) {
var err error

switch p.vars[key].(type) {
case string:
r, _ = utf8.DecodeRuneInString(p.vars[key].(string))
if r == utf8.RuneError {
err = fmt.Errorf("error decoding rune for key %s", key)
}
case []byte:
r, _ = utf8.DecodeRune(p.vars[key].([]byte))
if r == utf8.RuneError {
err = fmt.Errorf("error decoding rune for key %s", key)
v := reflect.ValueOf(value)

switch reflect.TypeOf(value).Kind() {
case reflect.Int8, reflect.Int16, reflect.Uint,
reflect.Uintptr, reflect.Uint16,
reflect.Pointer, reflect.UnsafePointer,
reflect.Func, reflect.Chan,
reflect.Complex64, reflect.Complex128,
reflect.Invalid:
err = errors.New("cannot set type as variable value")
case reflect.Array, reflect.Slice:
if v.Len() != 0 {
index++
_, iErr := checkValType(index, v.Index(0))
if iErr == nil {
vals := make([]any, v.Len())

for i := 0; i < v.Len(); i++ {
vals[i] = v.Index(i).Interface()
}

value = vals
} else if iErr.Error() == "is byte" {
vals := make([]byte, v.Len())

for i := 0; i < v.Len(); i++ {
vals[i] = v.Index(i).Interface().(byte)
}

value = vals
}
}
case byte:
r, _ = utf8.DecodeRune([]byte{p.vars[key].(byte)})
if r == utf8.RuneError {
err = fmt.Errorf("error decoding rune for key %s", key)
case reflect.Interface:
if index == 0 {
err = errors.New("cannot set type as variable value")
break
}
case rune:
r = p.vars[key].(rune)
}

if err != nil {
return 0, err
}
return nil, nil
case reflect.Uint8:
_, e := bytes.NewReader([]byte{value.(byte)}).ReadByte()

return r, nil
}

// GetVarRuneSlice returns the variable at key completely
// decoded into a slice of runes. If decoding part of
// the variable returns a RuneError, this will instead
// return a nil slice and an error specifying the key used
// that failed to decode
func (p *Plugin) GetVarRuneSlice(key string) ([]rune, error) {
b := make([]byte, 0)
var runes []rune
var err error

switch p.vars[key].(type) {
case string:
v := p.vars[key].(string)
if e == nil && index == 0 {
value = []any{v.Interface()}
break
} else if e != nil && index == 0 {
err = errors.New("cannot set type as variable value")
break
} else {
return nil, errors.New("is byte")
}
case reflect.Map:
keys := v.MapKeys()

runes = make([]rune, len(v))
if keys[0].Kind() != reflect.String {
err = errors.New("map key invalid for setting as a variable")
break
}

for i, char := range v {
runes[i] = char
if v.IsZero() || v.IsNil() {
break
}
case []byte:
b = p.vars[key].([]byte)

done := false
if v.MapIndex(keys[0]).Type().Kind() != reflect.Interface {
val := make(map[string]interface{})

for !done {
if len(b) == 0 {
done = true
continue
for _, key := range keys {
val[key.String()] = v.Interface()
}

r, cut := utf8.DecodeRune(p.vars[key].([]byte))
if r == utf8.RuneError {
err = fmt.Errorf("error decoding rune for key %s", key)
}
value = val
}
case reflect.Struct:
numFields := v.NumField()
valType := reflect.TypeOf(value)

runes = append(runes, r)
val := make(map[string]interface{})

b = b[cut:]
}
case byte:
r, _ := utf8.DecodeRune([]byte{p.vars[key].(byte)})
if r == utf8.RuneError {
err = fmt.Errorf("error decoding rune for key %s", key)
for i := 0; i < numFields; i++ {
field := v.Field(i)

if field.CanInterface() {
val[valType.Field(i).Name] = field.Interface()
}
}
case rune:
runes = []rune{p.vars[key].(rune)}
}

if err != nil {
return nil, err
value = val
}

return runes, nil
return value, err
}

// GetVarByte returns the variable at key as a byte
func (p *Plugin) GetVarByte(key string) byte {
return p.vars[key].(byte)
func newPbMessage(value any) (*structpb.Value, error) {
return structpb.NewValue(value)
}

// GetVarByteSlice returns the variable at key as a []byte
func (p *Plugin) GetVarByteSlice(key string) []byte {
return p.vars[key].([]byte)
}

// GetVarInt returns the variable at key as an int
func (p *Plugin) GetVarInt(key string) int {
return p.vars[key].(int)
}
// SetVar converts value to a slice of bytes, and
// adds that byte slice to the Plugin's variables
func (p *Plugin) SetVar(key string, value any) error {
var err error

// GetVarInt8 returns the variable at key as an int8
func (p *Plugin) GetVarInt8(key string) int8 {
return p.vars[key].(int8)
}
if p.Var == nil {
p.Var = make(map[string][]byte)
}

// GetVarInt16 returns the variable at key as an int16
func (p *Plugin) GetVarInt16(key string) int16 {
return p.vars[key].(int16)
}
if p.vars == nil {
p.vars = make(map[string]any)
}

// GetVarInt32 returns the variable at key as an int32
func (p *Plugin) GetVarInt32(key string) int32 {
return p.vars[key].(int32)
}
value, err = checkValType(0, value)
if err != nil {
return err
}

// GetVarInt64 returns the variable at key as an int64
func (p *Plugin) GetVarInt64(key string) int64 {
return p.vars[key].(int64)
}
var pb *structpb.Value
pb, err = structpb.NewValue(value)

// GetVarUint returns the variable at key as an uint
func (p *Plugin) GetVarUint(key string) uint {
return p.vars[key].(uint)
}
var valBytes []byte
valBytes, err = proto.Marshal(pb.ProtoReflect().Interface())

// GetVarUint8 returns the variable at key as an uint8
func (p *Plugin) GetVarUint8(key string) uint8 {
return p.vars[key].(uint8)
}
p.Var[key] = valBytes
p.vars[key] = value

// GetVarUint16 returns the variable at key as an uint16
func (p *Plugin) GetVarUint16(key string) uint16 {
return p.vars[key].(uint16)
return nil
}

// GetVarUint32 returns the variable at key as an uint32
func (p *Plugin) GetVarUint32(key string) uint32 {
return p.vars[key].(uint32)
}
// GetVar gets the variable from the plugin with the given
// key and reads it into data
func (p *Plugin) GetVar(key string, data any) error {
var pb *structpb.Value
var err error

// GetVarUint64 returns the variable at key as an uint64
func (p *Plugin) GetVarUint64(key string) uint64 {
return p.vars[key].(uint64)
}
valBytes := p.Var[key]

// GetVarFloat32 returns the variable at key as a float32
func (p *Plugin) GetVarFloat32(key string) float32 {
return p.vars[key].(float32)
}
pb, err = structpb.NewValue(data)
if err != nil {
return err
}

// GetVarFloat64 returns the variable at key as a float64
func (p *Plugin) GetVarFloat64(key string) float64 {
return p.vars[key].(float64)
return proto.Unmarshal(valBytes, pb.ProtoReflect().Interface())
}

func (p *Plugin) Log(level LogLevel, message string) {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ require (
github.com/gobwas/glob v0.2.3 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.8.4 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tetratelabs/wazero v1.3.0 h1:nqw7zCldxE06B8zSZAY0ACrR9OH5QCcPwYmYlwtcwtE=
github.com/tetratelabs/wazero v1.3.0/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 comments on commit 26c247f

Please sign in to comment.