ssob/marshal.go

485 lines
12 KiB
Go

package ssob
import (
"encoding/binary"
"fmt"
"io"
"math"
"reflect"
)
func MarshalString(in string) (ret []byte) {
l := int32(len(in))
b := MarshalInt32(l)
ret = make([]byte, int(l)+len(b))
copy(ret, b)
copy(ret[4:], []byte(in))
return ret
}
func MarshalBool(in bool) (ret []byte) {
if in {
return []byte{byte(0)}
}
return []byte{byte(1)}
}
func MarshalInt8(in int8) (ret []byte) {
return []byte{byte(in)}
}
func MarshalUint8(in uint8) (ret []byte) {
return []byte{byte(in)}
}
func MarshalInt16(in int16) (ret []byte) {
out := make([]byte, 2)
binary.BigEndian.PutUint16(out, uint16(in))
return out
}
func MarshalUint16(in uint16) (ret []byte) {
out := make([]byte, 2)
binary.BigEndian.PutUint16(out, in)
return out
}
func MarshalInt32(in int32) (ret []byte) {
out := make([]byte, 4)
binary.BigEndian.PutUint32(out, uint32(in))
return out
}
func MarshalUint32(in uint32) (ret []byte) {
out := make([]byte, 4)
binary.BigEndian.PutUint32(out, in)
return out
}
func MarshalFloat32(in float32) (ret []byte) {
out := make([]byte, 4)
binary.BigEndian.PutUint32(out, math.Float32bits(in))
return out
}
func MarshalInt64(in int64) (ret []byte) {
out := make([]byte, 8)
binary.BigEndian.PutUint64(out, uint64(in))
return out
}
func MarshalUint64(in uint64) (ret []byte) {
out := make([]byte, 8)
binary.BigEndian.PutUint64(out, in)
return out
}
func MarshalFloat64(in float64) (ret []byte) {
out := make([]byte, 8)
binary.BigEndian.PutUint64(out, math.Float64bits(in))
return out
}
type MarshalFunc func(e interface{}) (ret []byte, err error)
var encoderCache map[string]MarshalFunc
func init() {
encoderCache = make(map[string]MarshalFunc)
RegisterEncoder("bool", func(e interface{}) (ret []byte, err error) {
if i, ok := e.(bool); ok {
return MarshalBool(i), nil
}
return nil, fmt.Errorf("ssob: Incompatible type - expected bool: %w", ErrTypeMismatch)
})
RegisterEncoder("int8", func(e interface{}) (ret []byte, err error) {
if i, ok := e.(int8); ok {
return MarshalInt8(i), nil
}
return nil, fmt.Errorf("ssob: Incompatible type - expected int8: %w", ErrTypeMismatch)
})
RegisterEncoder("uint8", func(e interface{}) (ret []byte, err error) {
if i, ok := e.(uint8); ok {
return MarshalUint8(i), nil
}
return nil, fmt.Errorf("ssob: Incompatible type - expected uint8: %w", ErrTypeMismatch)
})
RegisterEncoder("int16", func(e interface{}) (ret []byte, err error) {
if i, ok := e.(int16); ok {
return MarshalInt16(i), nil
}
return nil, fmt.Errorf("ssob: Incompatible type - expected int16: %w", ErrTypeMismatch)
})
RegisterEncoder("uint16", func(e interface{}) (ret []byte, err error) {
if i, ok := e.(uint16); ok {
return MarshalUint16(i), nil
}
return nil, fmt.Errorf("ssob: Incompatible type - expected uint16: %w", ErrTypeMismatch)
})
RegisterEncoder("int32", func(e interface{}) (ret []byte, err error) {
if i, ok := e.(int32); ok {
return MarshalInt32(i), nil
}
return nil, fmt.Errorf("ssob: Incompatible type - expected int32: %w", ErrTypeMismatch)
})
RegisterEncoder("uint32", func(e interface{}) (ret []byte, err error) {
if i, ok := e.(uint32); ok {
return MarshalUint32(i), nil
}
return nil, fmt.Errorf("ssob: Incompatible type - expected uint32: %w", ErrTypeMismatch)
})
RegisterEncoder("float32", func(e interface{}) (ret []byte, err error) {
if i, ok := e.(float32); ok {
return MarshalFloat32(i), nil
}
return nil, fmt.Errorf("ssob: Incompatible type - expected float32: %w", ErrTypeMismatch)
})
RegisterEncoder("int64", func(e interface{}) (ret []byte, err error) {
if i, ok := e.(int64); ok {
return MarshalInt64(i), nil
}
return nil, fmt.Errorf("ssob: Incompatible type - expected int64: %w", ErrTypeMismatch)
})
RegisterEncoder("uint64", func(e interface{}) (ret []byte, err error) {
if i, ok := e.(uint64); ok {
return MarshalUint64(i), nil
}
return nil, fmt.Errorf("ssob: Incompatible type - expected uint64: %w", ErrTypeMismatch)
})
RegisterEncoder("float64", func(e interface{}) (ret []byte, err error) {
if i, ok := e.(float64); ok {
return MarshalFloat64(i), nil
}
return nil, fmt.Errorf("ssob: Incompatible type - expected float64: %w", ErrTypeMismatch)
})
RegisterEncoder("string", func(e interface{}) (ret []byte, err error) {
if i, ok := e.(string); ok {
return MarshalString(i), nil
}
return nil, fmt.Errorf("ssob: Incompatible type - expected string: %w", ErrTypeMismatch)
})
}
func encRegister(e interface{}) (err error) {
t := reflect.TypeOf(e)
if t == nil {
return fmt.Errorf("ssob: nil type: %w", ErrTypeInvalid)
}
switch t.Kind() {
case reflect.Invalid:
return fmt.Errorf("ssob: invalid type: %w", ErrTypeInvalid)
case reflect.Array:
f := func(e interface{}) (ret []byte, err error) {
v := reflect.ValueOf(e)
l := v.Len()
slen := MarshalInt32(int32(l))
blen := 4
bs := make([][]byte, l)
for i := 0; i < l; i++ {
bs[i], err = marshal(v.Index(i).Interface())
if err != nil {
return nil, err
}
blen += len(bs[i])
}
ret = make([]byte, blen)
i := copy(ret, slen)
for c := 0; c < l; c++ {
i += copy(ret[i:], bs[c])
}
return ret, nil
}
encoderCache[string(t.Kind())] = f
case reflect.Slice:
f := func(e interface{}) (ret []byte, err error) {
v := reflect.ValueOf(e)
l := v.Len()
slen := MarshalInt32(int32(l))
blen := 4
bs := make([][]byte, l)
for i := 0; i < l; i++ {
bs[i], err = marshal(v.Index(i).Interface())
if err != nil {
return nil, err
}
blen += len(bs[i])
}
ret = make([]byte, blen)
i := copy(ret, slen)
for c := 0; c < l; c++ {
i += copy(ret[i:], bs[c])
}
return ret, nil
}
encoderCache[string(t.Kind())] = f
case reflect.Map:
f := func(e interface{}) (ret []byte, err error) {
v := reflect.ValueOf(e)
l := v.Len()
ret = MarshalInt32(int32(l))
for _, ek := range v.MapKeys() {
ee := v.MapIndex(ek)
b, err := marshal(ek.Interface())
if err != nil {
return nil, err
}
ret = append(ret, b...)
b, err = marshal(ee.Interface())
if err != nil {
return nil, err
}
ret = append(ret, b...)
}
return ret, nil
}
encoderCache[string(t.Kind())] = f
case reflect.Struct:
zt := reflect.New(t)
n := zt.Elem().NumField()
mfields := []int{}
for i := 0; i < n; i++ {
if zt.Elem().Field(i).CanSet() {
mfields = append(mfields, i)
}
}
if len(mfields) == 0 {
return fmt.Errorf("ssob: No exported fields for %s: %w", string(t.Name()), ErrParseFailed)
}
f := func(e interface{}) (ret []byte, err error) {
v := reflect.ValueOf(e)
bs := make([][]byte, n)
blen := 0
for _, i := range mfields {
bs[i], err = marshal(v.Field(i).Interface())
if err != nil {
return nil, err
}
blen += len(bs[i])
}
ret = make([]byte, blen)
i := 0
for c := 0; c < n; c++ {
i += copy(ret[i:], bs[c])
}
return ret, nil
}
encoderCache[t.Name()] = f
case reflect.Bool:
f := func(e interface{}) (ret []byte, err error) {
v := reflect.ValueOf(e)
return marshalBaseType(bool(v.Bool()))
}
encoderCache[t.Name()] = f
case reflect.Uint8:
f := func(e interface{}) (ret []byte, err error) {
v := reflect.ValueOf(e)
return marshalBaseType(uint8(v.Uint()))
}
encoderCache[t.Name()] = f
case reflect.Uint16:
f := func(e interface{}) (ret []byte, err error) {
v := reflect.ValueOf(e)
return marshalBaseType(uint16(v.Uint()))
}
encoderCache[t.Name()] = f
case reflect.Uint32:
f := func(e interface{}) (ret []byte, err error) {
v := reflect.ValueOf(e)
return marshalBaseType(uint32(v.Uint()))
}
encoderCache[t.Name()] = f
case reflect.Uint64:
f := func(e interface{}) (ret []byte, err error) {
v := reflect.ValueOf(e)
return marshalBaseType(uint64(v.Uint()))
}
encoderCache[t.Name()] = f
case reflect.Int8:
f := func(e interface{}) (ret []byte, err error) {
v := reflect.ValueOf(e)
return marshalBaseType(int8(v.Uint()))
}
encoderCache[t.Name()] = f
case reflect.Int16:
f := func(e interface{}) (ret []byte, err error) {
v := reflect.ValueOf(e)
return marshalBaseType(int16(v.Uint()))
}
encoderCache[t.Name()] = f
case reflect.Int32:
f := func(e interface{}) (ret []byte, err error) {
v := reflect.ValueOf(e)
return marshalBaseType(int32(v.Uint()))
}
encoderCache[t.Name()] = f
case reflect.Int64:
f := func(e interface{}) (ret []byte, err error) {
v := reflect.ValueOf(e)
return marshalBaseType(int64(v.Uint()))
}
encoderCache[t.Name()] = f
case reflect.String:
f := func(e interface{}) (ret []byte, err error) {
v := reflect.ValueOf(e)
return marshalBaseType(string(v.String()))
}
encoderCache[t.Name()] = f
default:
return fmt.Errorf("ssob: %s: %w", string(t.Name()), ErrTypeUnknown)
}
return nil
}
func marshalBaseType(e interface{}) (ret []byte, err error) {
switch t := e.(type) {
case bool:
return encoderCache["bool"](t)
case int8:
return encoderCache["int8"](t)
case uint8:
return encoderCache["uint8"](t)
case int16:
return encoderCache["int16"](t)
case uint16:
return encoderCache["uint16"](t)
case int32:
return encoderCache["int32"](t)
case uint32:
return encoderCache["uint32"](t)
case int64:
return encoderCache["int64"](t)
case uint64:
return encoderCache["uint64"](t)
case float32:
return encoderCache["float32"](t)
case float64:
return encoderCache["float64"](t)
case string:
return encoderCache["string"](t)
default:
return nil, fmt.Errorf("ssob: No base type: %w", ErrTypeUnknown)
}
}
func marshal(e interface{}) (ret []byte, err error) {
ret, err = marshalBaseType(e)
if err == nil {
return ret, err
}
var key string
t := reflect.TypeOf(e)
v := reflect.ValueOf(e)
switch t.Kind() {
case reflect.Ptr:
if v.IsNil() {
return nil, fmt.Errorf("ssob: Cannot marshal nil pointer: %w", ErrValueInvalid)
}
p := reflect.Indirect(v)
return marshal(p.Interface())
case reflect.Bool, reflect.Int8, reflect.Int16, reflect.Int32,
reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32,
reflect.Uint64, reflect.String, reflect.Struct:
key = t.Name()
default:
key = string(t.Kind())
}
if f, ok := encoderCache[key]; ok {
return f(e)
} else {
err = encRegister(e)
if err != nil {
return nil, err
}
}
return marshal(e)
}
func RegisterEncoder(name string, f MarshalFunc) {
encoderCache[name] = f
}
func baseTypeMarshaller(e interface{}, w io.Writer) (err error) {
var sb []byte
switch e.(type) {
case bool:
sb = make([]byte, ST_ID_SIZE+1)
binary.BigEndian.PutUint32(sb, ST_BOOL)
if e.(bool) == true {
sb[ST_ID_SIZE] = 0
} else {
sb[ST_ID_SIZE] = 1
}
case uint8:
sb = make([]byte, ST_ID_SIZE+1)
binary.BigEndian.PutUint32(sb, ST_UINT8)
sb[ST_ID_SIZE] = byte(e.(uint8))
case uint16:
sb = make([]byte, ST_ID_SIZE+2)
binary.BigEndian.PutUint32(sb, ST_UINT16)
binary.BigEndian.PutUint16(sb[ST_ID_SIZE:], e.(uint16))
case uint32:
sb = make([]byte, ST_ID_SIZE+4)
binary.BigEndian.PutUint32(sb, ST_UINT32)
binary.BigEndian.PutUint32(sb[ST_ID_SIZE:], e.(uint32))
case uint64:
sb = make([]byte, ST_ID_SIZE+8)
binary.BigEndian.PutUint32(sb, ST_UINT64)
binary.BigEndian.PutUint64(sb[ST_ID_SIZE:], e.(uint64))
case int8:
sb = make([]byte, ST_ID_SIZE+1)
binary.BigEndian.PutUint32(sb[ST_ID_SIZE:], ST_INT8)
sb[ST_ID_SIZE] = byte(e.(int8))
case int16:
sb = make([]byte, ST_ID_SIZE+2)
binary.BigEndian.PutUint32(sb, ST_INT16)
binary.BigEndian.PutUint16(sb[ST_ID_SIZE:], uint16(e.(int16)))
case int32:
sb = make([]byte, ST_ID_SIZE+4)
binary.BigEndian.PutUint32(sb, ST_INT32)
binary.BigEndian.PutUint32(sb[ST_ID_SIZE:], uint32(e.(int32)))
case int64:
sb = make([]byte, ST_ID_SIZE+8)
binary.BigEndian.PutUint32(sb, ST_INT64)
binary.BigEndian.PutUint64(sb[ST_ID_SIZE:], uint64(e.(int64)))
case float32:
sb = make([]byte, ST_ID_SIZE+4)
binary.BigEndian.PutUint32(sb, ST_FLOAT32)
binary.BigEndian.PutUint32(sb[ST_ID_SIZE:], math.Float32bits(e.(float32)))
case float64:
sb = make([]byte, ST_ID_SIZE+8)
binary.BigEndian.PutUint32(sb, ST_FLOAT64)
binary.BigEndian.PutUint64(sb[ST_ID_SIZE:], math.Float64bits(e.(float64)))
case string:
slen := len(e.(string))
sb = make([]byte, ST_ID_SIZE+slen+4)
binary.BigEndian.PutUint32(sb, ST_STRING)
binary.BigEndian.PutUint32(sb[ST_ID_SIZE:], uint32(slen))
copy(sb[ST_ID_SIZE+4:], []byte(e.(string)))
default:
return fmt.Errorf("ssob: Unknown type: %s: %w", reflect.TypeOf(e), ErrValueInvalid)
}
return binary.Write(w, binary.BigEndian, sb)
}