safing-portbase/formats/dsd/dsd.go
2019-09-20 22:01:59 +02:00

130 lines
2.9 KiB
Go

package dsd
// dynamic structured data
// check here for some benchmarks: https://github.com/alecthomas/go_serialization_benchmarks
import (
"encoding/json"
"errors"
"fmt"
// "github.com/pkg/bson"
"github.com/safing/portbase/formats/varint"
)
// define types
const (
AUTO = 0
STRING = 83 // S
BYTES = 88 // X
JSON = 74 // J
BSON = 66 // B
GenCode = 71 // G
)
// define errors
var errNoMoreSpace = errors.New("dsd: no more space left after reading dsd type")
var errNotImplemented = errors.New("dsd: this type is not yet implemented")
// Load loads an dsd structured data blob into the given interface.
func Load(data []byte, t interface{}) (interface{}, error) {
if len(data) < 2 {
return nil, errNoMoreSpace
}
format, read, err := varint.Unpack8(data)
if err != nil {
return nil, err
}
if len(data) <= read {
return nil, errNoMoreSpace
}
return LoadAsFormat(data[read:], format, t)
}
// LoadAsFormat loads a data blob into the interface using the specified format.
func LoadAsFormat(data []byte, format uint8, t interface{}) (interface{}, error) {
switch format {
case STRING:
return string(data), nil
case BYTES:
return data, nil
case JSON:
err := json.Unmarshal(data, t)
if err != nil {
return nil, fmt.Errorf("dsd: failed to unpack json data: %s", data)
}
return t, nil
case BSON:
return nil, errNotImplemented
// err := bson.Unmarshal(data[read:], t)
// if err != nil {
// return nil, err
// }
// return t, nil
case GenCode:
genCodeStruct, ok := t.(GenCodeCompatible)
if !ok {
return nil, errors.New("dsd: gencode is not supported by the given data structure")
}
_, err := genCodeStruct.GenCodeUnmarshal(data)
if err != nil {
return nil, fmt.Errorf("dsd: failed to unpack gencode data: %s", err)
}
return t, nil
default:
return nil, fmt.Errorf("dsd: tried to load unknown type %d, data: %v", format, data)
}
}
// Dump stores the interface as a dsd formatted data structure.
func Dump(t interface{}, format uint8) ([]byte, error) {
if format == AUTO {
switch t.(type) {
case string:
format = STRING
case []byte:
format = BYTES
default:
format = JSON
}
}
f := varint.Pack8(format)
var data []byte
var err error
switch format {
case STRING:
data = []byte(t.(string))
case BYTES:
data = t.([]byte)
case JSON:
// TODO: use SetEscapeHTML(false)
data, err = json.Marshal(t)
if err != nil {
return nil, err
}
case BSON:
return nil, errNotImplemented
// data, err = bson.Marshal(t)
// if err != nil {
// return nil, err
// }
case GenCode:
genCodeStruct, ok := t.(GenCodeCompatible)
if !ok {
return nil, errors.New("dsd: gencode is not supported by the given data structure")
}
data, err = genCodeStruct.GenCodeMarshal(nil)
if err != nil {
return nil, fmt.Errorf("dsd: failed to pack gencode struct: %s", err)
}
default:
return nil, fmt.Errorf("dsd: tried to dump unknown type %d", format)
}
r := append(f, data...)
return r, nil
}