meta/msgpack_reader.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
}