ssob/encoder.go

125 lines
3.1 KiB
Go

package ssob
import (
"encoding/binary"
"errors"
"fmt"
"io"
"reflect"
"sync"
"gitea.olznet.de/OlzNet/slog"
)
type Encoder struct {
mutex sync.Mutex
w io.Writer
}
func NewEncoder(w io.Writer) *Encoder {
enc := new(Encoder)
enc.w = w
return enc
}
func (enc *Encoder) Encode(e interface{}) (err error) {
t := reflect.TypeOf(e)
slog.LOG_DEBUGFLN("Enc: %v", t)
if t == reflect.TypeOf(errors.New("")) || t == reflect.TypeOf((*error)(nil)).Elem() {
if reflect.ValueOf(e).IsNil() {
return enc.Encode(string(""))
} else {
return enc.Encode(e.(error).Error())
}
} else if t == reflect.TypeOf((*error)(nil)) {
if reflect.Indirect(reflect.ValueOf(e)).IsNil() {
return enc.Encode(string(""))
} else {
return enc.Encode((*e.(*error)).Error())
}
}
return enc.EncodeValue(reflect.ValueOf(e))
}
func (enc *Encoder) EncodeValue(value reflect.Value) (err error) {
if value.Kind() == reflect.Invalid {
return fmt.Errorf("ssob: Cannot encode nil: %w", ErrValueInvalid)
}
if value.Kind() == reflect.Ptr && value.IsNil() {
return fmt.Errorf("ssob: Cannot encode nil of type %s: %w", value.Type().String(), ErrValueInvalid)
}
bb, err := marshal(value.Interface())
if err != nil {
return err
}
bl := MarshalInt32(int32(len(bb)))
w := make([]byte, 4+len(bb))
copy(w, bl)
copy(w[4:], bb)
enc.mutex.Lock()
defer enc.mutex.Unlock()
return binary.Write(enc.w, binary.BigEndian, w)
}
func (enc *Encoder) NEncode(e interface{}) (err error) {
RegisterType(e)
for _, t := range cacheTypes.ntypes {
slog.LOG_DEBUGFLN("t: %v", t)
}
var b []byte
if b, err = marshalVal(e); err != nil {
return fmt.Errorf("ssob: Cannot encode value: %w", err)
}
enc.mutex.Lock()
defer enc.mutex.Unlock()
if _, err = enc.w.Write(b); err != nil {
return fmt.Errorf("ssob: Cannot encode value: %w", err)
}
return nil
}
func (enc *Encoder) encode(e interface{}, indirectLevel int) (err error) {
t := reflect.TypeOf(e)
if t.Kind() == reflect.Invalid {
return fmt.Errorf("ssob: Cannot encode nil: %w", ErrValueInvalid)
}
v := reflect.ValueOf(e)
//slog.LOG_DEBUGFLN("ssob: encode t.name: %s", t.Name())
//slog.LOG_DEBUGFLN("ssob: encode t.kind: %s", t.Kind())
slog.LOG_DEBUGFLN("ssob: encode v.type: %s", v.Type())
switch t.Kind() {
case reflect.Ptr:
if v.IsNil() {
return fmt.Errorf("ssob: Cannot encode nil pointer: %w", ErrValueInvalid)
}
p := reflect.Indirect(v)
return enc.encode(p.Interface(), indirectLevel+1)
case reflect.Struct:
return fmt.Errorf("ssob: Type '%s' named '%s' unknown: %w", t.Kind().String(), t.Name(), ErrTypeUnknown)
case reflect.Slice:
return fmt.Errorf("ssob: Type '%s' with types '%s' unknown: %w", t.Kind().String(), t.Elem().Kind().String(), ErrTypeUnknown)
case reflect.Array:
return fmt.Errorf("ssob: Type '%s' with types '%s' unknown: %w", t.Kind().String(), t.Name(), ErrTypeUnknown)
default:
if v, ok := cacheTypes.names[t.Kind().String()]; !ok {
return fmt.Errorf("ssob: Type '%s' unknown: %w", t.Kind().String(), ErrTypeUnknown)
} else {
enc.mutex.Lock()
defer enc.mutex.Unlock()
return cacheTypes.types[v].marshal(e, enc.w)
}
}
}