413 lines
7.8 KiB
Go
413 lines
7.8 KiB
Go
|
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
|
||
|
}
|