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) }