package meta import ( "github.com/pkg/errors" "git.bit5.ru/backend/msgpack" ) type msgpReader struct { stack []*msgpReadState current *msgpReadState } type msgpReadState struct { data []interface{} idx int } func NewMsgpackReader(bytes []byte) (Reader, error) { arr := make([]interface{}, 1) err := msgpack.Unmarshal(bytes, &arr[0]) if err != nil { return nil, errors.WithStack(err) } rd := &msgpReader{} rd.stack = make([]*msgpReadState, 0, 2) rd.current = &msgpReadState{arr, 0} rd.stack = append(rd.stack, rd.current) return rd, nil } func (state *msgpReadState) clear() { state.data = nil } func (state *msgpReadState) read(field string) (v interface{}, err error) { if len(state.data) <= state.idx { err = errors.Errorf("No more data for read, field:%s", field) return } v = state.data[state.idx] state.idx++ return } func (state *msgpReadState) backCursor() error { if state.data == nil { return errors.New("No more data for read") } state.idx-- return nil } func (state *msgpReadState) skip() error { if state.data == nil || len(state.data) <= state.idx { return errors.New("No more data for read") } state.idx++ return nil } func (state *msgpReadState) size() (int, error) { return len(state.data) - state.idx, nil // relative to current idx? } func (rd *msgpReader) ReadI8(v *int8, field string) (err error) { var c int32 err = rd.ReadI32(&c, field) *v = int8(c) return } func (rd *msgpReader) ReadU8(v *uint8, field string) (err error) { var c uint32 err = rd.ReadU32(&c, field) *v = uint8(c) return } func (rd *msgpReader) ReadI16(v *int16, field string) (err error) { var c int32 err = rd.ReadI32(&c, field) *v = int16(c) return } func (rd *msgpReader) ReadU16(v *uint16, field string) (err error) { var c uint32 err = rd.ReadU32(&c, field) *v = uint16(c) return } func (rd *msgpReader) ReadI32(v *int32, field string) error { value, err := rd.current.read(field) if err != nil { return err } switch t := value.(type) { case float64: *v = int32(t) case int8: *v = int32(t) case uint8: *v = int32(t) case int16: *v = int32(t) case uint16: *v = int32(t) case int32: *v = int32(t) case uint32: *v = int32(t) case int64: *v = int32(t) case uint64: *v = int32(t) case bool: if t { *v = int32(1) } else { *v = int32(0) } default: return errors.Errorf("Can't convert to int32 %v (%T), field:%s", t, t, field) } return nil } func (rd *msgpReader) ReadU32(v *uint32, field string) error { value, err := rd.current.read(field) if err != nil { return err } switch t := value.(type) { case float64: *v = uint32(t) case int8: *v = uint32(t) case uint8: *v = uint32(t) case int16: *v = uint32(t) case uint16: *v = uint32(t) case int32: *v = uint32(t) case uint32: *v = uint32(t) case uint64: *v = uint32(t) case int64: *v = uint32(t) case bool: if t { *v = uint32(1) } else { *v = uint32(0) } default: return errors.Errorf("Can't convert to uint32 (%T), field:%s", t, field) } return nil } func (rd *msgpReader) ReadI64(v *int64, field string) error { value, err := rd.current.read(field) if err != nil { return err } switch t := value.(type) { case float64: *v = int64(t) case int8: *v = int64(t) case uint8: *v = int64(t) case int16: *v = int64(t) case uint16: *v = int64(t) case int32: *v = int64(t) case uint32: *v = int64(t) case uint64: *v = int64(t) case int64: *v = int64(t) default: return errors.Errorf("Can't convert to uint64 (%T), field:%s", t, field) } return nil } func (rd *msgpReader) ReadU64(v *uint64, field string) error { value, err := rd.current.read(field) if err != nil { return err } switch t := value.(type) { case float64: *v = uint64(t) case int8: *v = uint64(t) case uint8: *v = uint64(t) case int16: *v = uint64(t) case uint16: *v = uint64(t) case int32: *v = uint64(t) case uint32: *v = uint64(t) case uint64: *v = uint64(t) case int64: *v = uint64(t) default: return errors.Errorf("Can't convert to uint64 (%T), field:%s", t, field) } return nil } func (rd *msgpReader) ReadBool(v *bool, field string) (err error) { value, err := rd.current.read(field) if err != nil { return err } switch t := value.(type) { case bool: *v = bool(t) default: return errors.Errorf("Can't convert to bool (%T), field:%s", t, field) } return nil } func (rd *msgpReader) ReadFloat(v *float32, field string) error { value, err := rd.current.read(field) if err != nil { return err } switch t := value.(type) { case float64: //NOTE: possible type coersion error! *v = float32(t) case float32: *v = t case int64: *v = float32(t) case uint64: *v = float32(t) default: return errors.Errorf("Can't convert to float32 (%T), field:%s", t, field) } return nil } func (rd *msgpReader) ReadDouble(v *float64, field string) error { value, err := rd.current.read(field) if err != nil { return err } switch t := value.(type) { case float64: *v = t case float32: *v = float64(t) case int64: *v = float64(t) case uint64: *v = float64(t) default: return errors.Errorf("Can't convert to float64 (%T), field:%s", t, field) } return nil } func (rd *msgpReader) ReadString(v *string, field string) error { value, err := rd.current.read(field) if err != nil { return err } switch t := value.(type) { case string: *v = t default: return errors.Errorf("Can't convert to string %s (%T)", field, t) } return nil } func (rd *msgpReader) ReadBlob(v *[]byte, field string) error { value, err := rd.current.read(field) if err != nil { return err } switch t := value.(type) { case []byte: *v = t default: return errors.Errorf("Can't convert to []byte %s (%T)", field, t) } return nil } func (rd *msgpReader) BeginContainer(field string) error { v, err := rd.current.read(field) if err != nil { return err } if data, ok := v.([]interface{}); ok { cur := &msgpReadState{} cur.data = data rd.stack = append(rd.stack, cur) rd.current = cur return nil } else { return errors.Errorf("Next value isn't array but:%T, field:%s", v, field) } } func (rd *msgpReader) EndContainer() error { rd.current.clear() rd.stack[len(rd.stack)-1] = nil rd.stack = rd.stack[0 : len(rd.stack)-1] if len(rd.stack) > 0 { rd.current = rd.stack[len(rd.stack)-1] } else { rd.current = nil } return nil } func (rd *msgpReader) GetContainerSize() (int, error) { return rd.current.size() } func (rd *msgpReader) Skip() error { return rd.current.skip() } func (rd *msgpReader) TryReadMask() (bool, FieldsMask, error) { value, err := rd.current.read("mask") if err != nil { return false, FieldsMask{}, err } if value == nil { //mask detected newMask, err := rd.ReadNewMask() if err == nil { return true, newMask, nil } if err := rd.current.backCursor(); err != nil { return false, FieldsMask{}, err } oldMask, err := rd.ReadOldMask() if err != nil { return false, FieldsMask{}, err } return true, oldMask, nil } if err := rd.current.backCursor(); err != nil { return false, FieldsMask{}, err } return false, FieldsMask{}, nil } func (rd *msgpReader) ReadNewMask() (FieldsMask, error) { if err := rd.BeginContainer("new_mask"); err != nil { return FieldsMask{}, err } maskSize, err := rd.GetContainerSize() if err != nil { return FieldsMask{}, err } var mask FieldsMask for i := 0; i < maskSize; i++ { var maskItem uint64 if err := rd.ReadU64(&maskItem, "mask_item"); err != nil { return FieldsMask{}, err } mask.SetItemFromUint64(i, maskItem) } if err := rd.EndContainer(); err != nil { return FieldsMask{}, err } return mask, nil } func (rd *msgpReader) ReadOldMask() (FieldsMask, error) { var mask int64 if err := rd.ReadI64(&mask, "mask"); err != nil { return FieldsMask{}, err } return MakeFieldsMaskFromInt64(mask), nil }