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