Compare commits
No commits in common. "master" and "v2.0.2" have entirely different histories.
|
@ -1,18 +1,12 @@
|
||||||
package meta
|
package meta
|
||||||
|
|
||||||
import "bytes"
|
|
||||||
|
|
||||||
type ChangedFields struct {
|
type ChangedFields struct {
|
||||||
fieldNames map[string]struct{}
|
fieldNames map[string]struct{}
|
||||||
fieldsKey *bytes.Buffer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewChangedFields(fieldCount int) ChangedFields {
|
func NewChangedFields(fieldCount int) ChangedFields {
|
||||||
keyBuffer := bytes.NewBuffer(make([]byte, 0, fieldCount*2))
|
|
||||||
|
|
||||||
cf := ChangedFields{
|
cf := ChangedFields{
|
||||||
fieldNames: make(map[string]struct{}, fieldCount),
|
fieldNames: make(map[string]struct{}, fieldCount),
|
||||||
fieldsKey: keyBuffer,
|
|
||||||
}
|
}
|
||||||
return cf
|
return cf
|
||||||
}
|
}
|
||||||
|
@ -38,12 +32,7 @@ func (cf *ChangedFields) SetChanged(fields ...string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, field := range fields {
|
for _, field := range fields {
|
||||||
if _, exists := cf.fieldNames[field]; exists {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
cf.fieldNames[field] = struct{}{}
|
cf.fieldNames[field] = struct{}{}
|
||||||
cf.fieldsKey.WriteString(field)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +43,3 @@ func (cf ChangedFields) Empty() bool {
|
||||||
func (cf ChangedFields) IsNil() bool {
|
func (cf ChangedFields) IsNil() bool {
|
||||||
return cf.fieldNames == nil
|
return cf.fieldNames == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cf ChangedFields) GetFieldsKey() string {
|
|
||||||
return cf.fieldsKey.String()
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
package meta
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestChangedFields(t *testing.T) {
|
|
||||||
t.Run("#SetChanged", func(t *testing.T) {
|
|
||||||
changedFields := NewChangedFields(3)
|
|
||||||
|
|
||||||
expectedFieldsMap := map[string]struct{}{
|
|
||||||
"a": {},
|
|
||||||
"b": {},
|
|
||||||
"c": {},
|
|
||||||
}
|
|
||||||
|
|
||||||
changedFields.SetChanged("a", "b", "c", "a")
|
|
||||||
|
|
||||||
assert.Equal(t, expectedFieldsMap, changedFields.fieldNames)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("#GetFieldsKey", func(t *testing.T) {
|
|
||||||
changedFields := NewChangedFields(3)
|
|
||||||
changedFields.SetChanged("b", "c", "a")
|
|
||||||
expectedKey := "bca"
|
|
||||||
|
|
||||||
actualKey := changedFields.GetFieldsKey()
|
|
||||||
|
|
||||||
assert.Equal(t, expectedKey, actualKey)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@ package meta_test
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.bit5.ru/backend/meta/v5"
|
"git.bit5.ru/backend/meta/v2"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -1,4 +1,4 @@
|
||||||
module git.bit5.ru/backend/meta/v5
|
module git.bit5.ru/backend/meta/v2
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
|
|
20
interface.go
20
interface.go
|
@ -22,9 +22,6 @@ type Reader interface {
|
||||||
BeginContainer(field string) error
|
BeginContainer(field string) error
|
||||||
EndContainer() error
|
EndContainer() error
|
||||||
|
|
||||||
BeginCollection(field string) error
|
|
||||||
EndCollection() error
|
|
||||||
|
|
||||||
ContainerSize() (int, error)
|
ContainerSize() (int, error)
|
||||||
IsContainerAssoc() (bool, error)
|
IsContainerAssoc() (bool, error)
|
||||||
|
|
||||||
|
@ -51,10 +48,8 @@ type Writer interface {
|
||||||
WriteString(v string, field string) error
|
WriteString(v string, field string) error
|
||||||
WriteBytes(v []byte, field string) error
|
WriteBytes(v []byte, field string) error
|
||||||
|
|
||||||
BeginCollection(length int, field string) error
|
|
||||||
EndCollection() error
|
|
||||||
|
|
||||||
BeginContainer(length int, field string) error
|
BeginContainer(length int, field string) error
|
||||||
|
BeginAssocContainer(length int, field string) error
|
||||||
EndContainer() error
|
EndContainer() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +67,6 @@ type Readable interface {
|
||||||
type Writable interface {
|
type Writable interface {
|
||||||
Write(Writer) error
|
Write(Writer) error
|
||||||
WriteFields(Writer) error
|
WriteFields(Writer) error
|
||||||
FieldsCount() int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Struct interface {
|
type Struct interface {
|
||||||
|
@ -81,21 +75,9 @@ type Struct interface {
|
||||||
Writable
|
Writable
|
||||||
}
|
}
|
||||||
|
|
||||||
type StructFactory func(classId uint32) (Readable, error)
|
|
||||||
|
|
||||||
type Bitmasked interface {
|
type Bitmasked interface {
|
||||||
FieldChanged(index uint64) bool
|
FieldChanged(index uint64) bool
|
||||||
SetFieldChanged(index uint64)
|
SetFieldChanged(index uint64)
|
||||||
HasChangedFields() bool
|
HasChangedFields() bool
|
||||||
FieldsMask() FieldsMask
|
FieldsMask() FieldsMask
|
||||||
}
|
}
|
||||||
|
|
||||||
type WritableClass interface {
|
|
||||||
Class
|
|
||||||
Writable
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReadableWritable interface {
|
|
||||||
Readable
|
|
||||||
Writable
|
|
||||||
}
|
|
||||||
|
|
53
meta.go
53
meta.go
|
@ -1,53 +0,0 @@
|
||||||
package meta
|
|
||||||
|
|
||||||
const classField = "$id"
|
|
||||||
|
|
||||||
func ReadGeneric(r Reader, createFunc StructFactory, field string) (Readable, error) {
|
|
||||||
if err := r.BeginContainer(field); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
size, err := r.ContainerSize()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if size == 0 {
|
|
||||||
if err := r.EndContainer(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var classId uint32
|
|
||||||
if err := r.ReadUint32(&classId, classField); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
s, err := createFunc(classId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.ReadFields(r); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := r.EndContainer(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteGeneric(w Writer, s WritableClass, field string) error {
|
|
||||||
if err := w.BeginContainer(s.FieldsCount()+1, field); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := w.WriteUint32(s.ClassId(), classField); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := s.WriteFields(w); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return w.EndContainer()
|
|
||||||
}
|
|
|
@ -1,629 +0,0 @@
|
||||||
package meta
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/vmihailenco/msgpack/v5"
|
|
||||||
"github.com/vmihailenco/msgpack/v5/msgpcode"
|
|
||||||
)
|
|
||||||
|
|
||||||
type msgpackAssocReader struct {
|
|
||||||
dec *msgpack.Decoder
|
|
||||||
stack []assocReadContainer
|
|
||||||
curr assocReadContainer
|
|
||||||
}
|
|
||||||
|
|
||||||
type assocReadContainer struct {
|
|
||||||
started bool
|
|
||||||
length int
|
|
||||||
assoc bool
|
|
||||||
values map[string]msgpack.RawMessage
|
|
||||||
reader io.Reader
|
|
||||||
readCnt int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMsgpackAssocReader(r io.Reader) Reader {
|
|
||||||
return &msgpackAssocReader{
|
|
||||||
dec: msgpack.NewDecoder(r),
|
|
||||||
stack: make([]assocReadContainer, 0, 2),
|
|
||||||
curr: assocReadContainer{
|
|
||||||
reader: r,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) readField() (string, error) {
|
|
||||||
field, err := rd.dec.DecodeString()
|
|
||||||
if err != nil {
|
|
||||||
return "", errors.WithStack(err)
|
|
||||||
}
|
|
||||||
return field, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) ReadInt8(v *int8, targetField string) error {
|
|
||||||
if !rd.curr.started || !rd.curr.assoc {
|
|
||||||
return decodeInt8(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b, ok := rd.curr.values[targetField]; ok {
|
|
||||||
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
|
||||||
return decodeInt8(dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
|
||||||
field, err := rd.readField()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if field == targetField {
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
return decodeInt8(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, err := rd.dec.DecodeRaw()
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
rd.curr.values[field] = raw
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return FieldNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) ReadInt16(v *int16, targetField string) error {
|
|
||||||
if !rd.curr.started || !rd.curr.assoc {
|
|
||||||
return decodeInt16(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b, ok := rd.curr.values[targetField]; ok {
|
|
||||||
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
|
||||||
return decodeInt16(dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
|
||||||
field, err := rd.readField()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if field == targetField {
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
return decodeInt16(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, err := rd.dec.DecodeRaw()
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
rd.curr.values[field] = raw
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return FieldNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) ReadInt32(v *int32, targetField string) error {
|
|
||||||
if !rd.curr.started || !rd.curr.assoc {
|
|
||||||
return decodeInt32(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b, ok := rd.curr.values[targetField]; ok {
|
|
||||||
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
|
||||||
return decodeInt32(dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
|
||||||
field, err := rd.readField()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if field == targetField {
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
return decodeInt32(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, err := rd.dec.DecodeRaw()
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
rd.curr.values[field] = raw
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return FieldNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) ReadInt64(v *int64, targetField string) error {
|
|
||||||
if !rd.curr.started || !rd.curr.assoc {
|
|
||||||
return decodeInt64(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b, ok := rd.curr.values[targetField]; ok {
|
|
||||||
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
|
||||||
return decodeInt64(dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
|
||||||
field, err := rd.readField()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if field == targetField {
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
return decodeInt64(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, err := rd.dec.DecodeRaw()
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
rd.curr.values[field] = raw
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return FieldNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) ReadUint8(v *uint8, targetField string) error {
|
|
||||||
if !rd.curr.started || !rd.curr.assoc {
|
|
||||||
return decodeUint8(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b, ok := rd.curr.values[targetField]; ok {
|
|
||||||
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
|
||||||
return decodeUint8(dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
|
||||||
field, err := rd.readField()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if field == targetField {
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
return decodeUint8(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, err := rd.dec.DecodeRaw()
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
rd.curr.values[field] = raw
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return FieldNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) ReadUint16(v *uint16, targetField string) error {
|
|
||||||
if !rd.curr.started || !rd.curr.assoc {
|
|
||||||
return decodeUint16(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b, ok := rd.curr.values[targetField]; ok {
|
|
||||||
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
|
||||||
return decodeUint16(dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
|
||||||
field, err := rd.readField()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if field == targetField {
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
return decodeUint16(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, err := rd.dec.DecodeRaw()
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
rd.curr.values[field] = raw
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return FieldNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) ReadUint32(v *uint32, targetField string) error {
|
|
||||||
if !rd.curr.started || !rd.curr.assoc {
|
|
||||||
return decodeUint32(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b, ok := rd.curr.values[targetField]; ok {
|
|
||||||
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
|
||||||
return decodeUint32(dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
|
||||||
field, err := rd.readField()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if field == targetField {
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
return decodeUint32(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, err := rd.dec.DecodeRaw()
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
rd.curr.values[field] = raw
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return FieldNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) ReadUint64(v *uint64, targetField string) error {
|
|
||||||
if !rd.curr.started || !rd.curr.assoc {
|
|
||||||
return decodeUint64(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b, ok := rd.curr.values[targetField]; ok {
|
|
||||||
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
|
||||||
return decodeUint64(dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
|
||||||
field, err := rd.readField()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if field == targetField {
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
return decodeUint64(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, err := rd.dec.DecodeRaw()
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
rd.curr.values[field] = raw
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return FieldNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) ReadBool(v *bool, targetField string) error {
|
|
||||||
if !rd.curr.started || !rd.curr.assoc {
|
|
||||||
return decodeBool(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b, ok := rd.curr.values[targetField]; ok {
|
|
||||||
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
|
||||||
return decodeBool(dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
|
||||||
field, err := rd.readField()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if field == targetField {
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
return decodeBool(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, err := rd.dec.DecodeRaw()
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
rd.curr.values[field] = raw
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return FieldNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) ReadFloat32(v *float32, targetField string) error {
|
|
||||||
if !rd.curr.started || !rd.curr.assoc {
|
|
||||||
return decodeFloat32(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b, ok := rd.curr.values[targetField]; ok {
|
|
||||||
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
|
||||||
return decodeFloat32(dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
|
||||||
field, err := rd.readField()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if field == targetField {
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
return decodeFloat32(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, err := rd.dec.DecodeRaw()
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
rd.curr.values[field] = raw
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return FieldNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) ReadFloat64(v *float64, targetField string) error {
|
|
||||||
if !rd.curr.started || !rd.curr.assoc {
|
|
||||||
return decodeFloat64(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b, ok := rd.curr.values[targetField]; ok {
|
|
||||||
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
|
||||||
return decodeFloat64(dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
|
||||||
field, err := rd.readField()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if field == targetField {
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
return decodeFloat64(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, err := rd.dec.DecodeRaw()
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
rd.curr.values[field] = raw
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return FieldNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) ReadString(v *string, targetField string) error {
|
|
||||||
if !rd.curr.started || !rd.curr.assoc {
|
|
||||||
return decodeString(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b, ok := rd.curr.values[targetField]; ok {
|
|
||||||
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
|
||||||
return decodeString(dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
|
||||||
field, err := rd.readField()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if field == targetField {
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
return decodeString(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, err := rd.dec.DecodeRaw()
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
rd.curr.values[field] = raw
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return FieldNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) ReadBytes(v *[]byte, targetField string) error {
|
|
||||||
if !rd.curr.started || !rd.curr.assoc {
|
|
||||||
return decodeBytes(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b, ok := rd.curr.values[targetField]; ok {
|
|
||||||
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
|
||||||
return decodeBytes(dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
|
||||||
field, err := rd.readField()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if field == targetField {
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
return decodeBytes(rd.dec, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, err := rd.dec.DecodeRaw()
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
rd.curr.values[field] = raw
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return FieldNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) BeginContainer(targetField string) error {
|
|
||||||
if !rd.curr.started || !rd.curr.assoc {
|
|
||||||
return rd.beginContainer(targetField)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b, ok := rd.curr.values[targetField]; ok {
|
|
||||||
rd.dec.Reset(bytes.NewReader(b))
|
|
||||||
return rd.beginContainer(targetField)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
|
||||||
field, err := rd.readField()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if field == targetField {
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
return rd.beginContainer(targetField)
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, err := rd.dec.DecodeRaw()
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
rd.curr.values[field] = raw
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return FieldNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) EndContainer() error {
|
|
||||||
return rd.endContainer()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) BeginCollection(targetField string) error {
|
|
||||||
if !rd.curr.started || !rd.curr.assoc {
|
|
||||||
return rd.beginCollection(targetField)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b, ok := rd.curr.values[targetField]; ok {
|
|
||||||
rd.dec.Reset(bytes.NewReader(b))
|
|
||||||
return rd.beginCollection(targetField)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
|
||||||
field, err := rd.readField()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if field == targetField {
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
return rd.beginCollection(targetField)
|
|
||||||
}
|
|
||||||
|
|
||||||
raw, err := rd.dec.DecodeRaw()
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
rd.curr.values[field] = raw
|
|
||||||
rd.curr.readCnt = i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return FieldNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) EndCollection() error {
|
|
||||||
return rd.endContainer()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) ContainerSize() (int, error) {
|
|
||||||
return rd.curr.length, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) IsContainerAssoc() (bool, error) {
|
|
||||||
return rd.curr.assoc, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) Skip() error {
|
|
||||||
return errors.WithStack(rd.dec.Skip())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) TryReadMask() (bool, FieldsMask, error) {
|
|
||||||
if rd.curr.assoc {
|
|
||||||
return false, FieldsMask{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
maskLen, err := rd.dec.DecodeArrayLen()
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
return false, FieldsMask{}, nil
|
|
||||||
}
|
|
||||||
return false, FieldsMask{}, errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var mask FieldsMask
|
|
||||||
for i := 0; i < maskLen; i++ {
|
|
||||||
maskPart, err := rd.dec.DecodeUint64()
|
|
||||||
if err != nil {
|
|
||||||
return false, FieldsMask{}, errors.WithStack(err)
|
|
||||||
}
|
|
||||||
mask.SetPartFromUint64(i, maskPart)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, mask, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) beginContainer(field string) error {
|
|
||||||
code, err := rd.dec.PeekCode()
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case code == msgpcode.Nil:
|
|
||||||
if err := rd.dec.DecodeNil(); err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
rd.stack = append(rd.stack, rd.curr)
|
|
||||||
rd.curr = assocReadContainer{
|
|
||||||
started: true,
|
|
||||||
length: 0,
|
|
||||||
assoc: true,
|
|
||||||
values: make(map[string]msgpack.RawMessage),
|
|
||||||
reader: rd.dec.Buffered(),
|
|
||||||
}
|
|
||||||
|
|
||||||
case msgpcode.IsFixedMap(code), code == msgpcode.Map16, code == msgpcode.Map32:
|
|
||||||
l, err := rd.dec.DecodeMapLen()
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
rd.stack = append(rd.stack, rd.curr)
|
|
||||||
rd.curr = assocReadContainer{
|
|
||||||
started: true,
|
|
||||||
length: l,
|
|
||||||
assoc: true,
|
|
||||||
values: make(map[string]msgpack.RawMessage, l),
|
|
||||||
reader: rd.dec.Buffered(),
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return errors.Errorf("field `%s` is not a map", field)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) beginCollection(field string) error {
|
|
||||||
code, err := rd.dec.PeekCode()
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case msgpcode.IsFixedArray(code), code == msgpcode.Array16, code == msgpcode.Array32:
|
|
||||||
l, err := rd.dec.DecodeArrayLen()
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
rd.stack = append(rd.stack, rd.curr)
|
|
||||||
rd.curr = assocReadContainer{
|
|
||||||
started: true,
|
|
||||||
length: l,
|
|
||||||
assoc: false,
|
|
||||||
reader: rd.dec.Buffered(),
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return errors.Errorf("field `%s` is not an array", field)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackAssocReader) endContainer() error {
|
|
||||||
if len(rd.stack) == 0 {
|
|
||||||
return NoOpenContainer
|
|
||||||
}
|
|
||||||
rd.curr = rd.stack[len(rd.stack)-1]
|
|
||||||
rd.stack = rd.stack[:len(rd.stack)-1]
|
|
||||||
rd.dec.Reset(rd.curr.reader)
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
package meta_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/hex"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"git.bit5.ru/backend/meta/v5"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAssocMsgpackReader(t *testing.T) {
|
|
||||||
t.Run("reading parent", func(t *testing.T) {
|
|
||||||
// {"f1":"blabla","f3":[2,4,6],"f2":{"field":1},"f4":[{"field":10},{"field":1024}],"f5":null}
|
|
||||||
src := "85A26631A6626C61626C61A2663393020406A2663281A56669656C6401A266349281A56669656C640A81A56669656C64CD0400A26635C0"
|
|
||||||
|
|
||||||
expected := TestParent{
|
|
||||||
Field1: "blabla",
|
|
||||||
Field2: TestFoo{
|
|
||||||
Field: 1,
|
|
||||||
},
|
|
||||||
Field3: []int8{2, 4, 6},
|
|
||||||
Field4: []TestFoo{
|
|
||||||
{Field: 10},
|
|
||||||
{Field: 1024},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := hex.DecodeString(src)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
rdr := meta.NewMsgpackAssocReader(bytes.NewReader(data))
|
|
||||||
|
|
||||||
var actual TestParent
|
|
||||||
readErr := actual.Read(rdr)
|
|
||||||
require.NoError(t, readErr)
|
|
||||||
require.EqualValues(t, expected, actual)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("reading child", func(t *testing.T) {
|
|
||||||
// {"f":"qwerty","f1":"blabla","f3":[2,4,6],"f2":{"field":1},"f4":[{"field":10},{"field":1024}]}
|
|
||||||
src := "85A166A6717765727479A26631A6626C61626C61A2663393020406A2663281A56669656C6401A266349281A56669656C640A81A56669656C64CD0400"
|
|
||||||
|
|
||||||
expected := TestChild{
|
|
||||||
Field: "qwerty",
|
|
||||||
TestParent: TestParent{
|
|
||||||
Field1: "blabla",
|
|
||||||
Field2: TestFoo{
|
|
||||||
Field: 1,
|
|
||||||
},
|
|
||||||
Field3: []int8{2, 4, 6},
|
|
||||||
Field4: []TestFoo{
|
|
||||||
{Field: 10},
|
|
||||||
{Field: 1024},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := hex.DecodeString(src)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
rdr := meta.NewMsgpackAssocReader(bytes.NewReader(data))
|
|
||||||
|
|
||||||
var actual TestChild
|
|
||||||
readErr := actual.Read(rdr)
|
|
||||||
require.NoError(t, readErr)
|
|
||||||
require.EqualValues(t, expected, actual)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("fail reading parent as array", func(t *testing.T) {
|
|
||||||
// ["blabla",[1],[2,4,6],[[10],[1024]],null]
|
|
||||||
src := "95A6626C61626C6191019302040692910A91CD0400C0"
|
|
||||||
|
|
||||||
expected := TestParent{}
|
|
||||||
|
|
||||||
data, err := hex.DecodeString(src)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
rdr := meta.NewMsgpackAssocReader(bytes.NewReader(data))
|
|
||||||
|
|
||||||
var actual TestParent
|
|
||||||
readErr := actual.Read(rdr)
|
|
||||||
require.ErrorContains(t, readErr, "field `` is not a map")
|
|
||||||
require.EqualValues(t, expected, actual)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("fail reading parent as array with maps", func(t *testing.T) {
|
|
||||||
// ["blabla",{"field":1},[2,4,6],[{"field":10},{"field":1024}],null]
|
|
||||||
src := "95A6626C61626C6181A56669656C6401930204069281A56669656C640A81A56669656C64CD0400C0"
|
|
||||||
|
|
||||||
expected := TestParent{}
|
|
||||||
|
|
||||||
data, err := hex.DecodeString(src)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
rdr := meta.NewMsgpackAssocReader(bytes.NewReader(data))
|
|
||||||
|
|
||||||
var actual TestParent
|
|
||||||
readErr := actual.Read(rdr)
|
|
||||||
require.ErrorContains(t, readErr, "field `` is not a map")
|
|
||||||
require.EqualValues(t, expected, actual)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("fail reading child as array", func(t *testing.T) {
|
|
||||||
// ["blabla",[1],[2,4,6],[[10],[1024]],null,"qwerty"]
|
|
||||||
src := "96A6626C61626C6191019302040692910A91CD0400C0A6717765727479"
|
|
||||||
|
|
||||||
expected := TestChild{}
|
|
||||||
|
|
||||||
data, err := hex.DecodeString(src)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
rdr := meta.NewMsgpackAssocReader(bytes.NewReader(data))
|
|
||||||
|
|
||||||
var actual TestChild
|
|
||||||
readErr := actual.Read(rdr)
|
|
||||||
require.ErrorContains(t, readErr, "field `` is not a map")
|
|
||||||
require.EqualValues(t, expected, actual)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,171 +0,0 @@
|
||||||
package meta
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/vmihailenco/msgpack/v5"
|
|
||||||
)
|
|
||||||
|
|
||||||
type msgpackAssocWriter struct {
|
|
||||||
enc *msgpack.Encoder
|
|
||||||
containers []writeContainer
|
|
||||||
}
|
|
||||||
|
|
||||||
type writeContainer struct {
|
|
||||||
length int
|
|
||||||
assoc bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMsgpackAssocWriter(w io.Writer) Writer {
|
|
||||||
return &msgpackAssocWriter{
|
|
||||||
enc: msgpack.NewEncoder(w),
|
|
||||||
containers: make([]writeContainer, 0, 1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wr *msgpackAssocWriter) currentContainer() writeContainer {
|
|
||||||
if wr == nil || len(wr.containers) == 0 {
|
|
||||||
return writeContainer{}
|
|
||||||
}
|
|
||||||
return wr.containers[len(wr.containers)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wr *msgpackAssocWriter) writeFieldName(field string) error {
|
|
||||||
if !wr.currentContainer().assoc {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.WithStack(wr.enc.EncodeString(field))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wr *msgpackAssocWriter) WriteInt8(v int8, field string) error {
|
|
||||||
if err := wr.writeFieldName(field); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return errors.WithStack(wr.enc.EncodeInt(int64(v)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wr *msgpackAssocWriter) WriteInt16(v int16, field string) error {
|
|
||||||
if err := wr.writeFieldName(field); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return errors.WithStack(wr.enc.EncodeInt(int64(v)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wr *msgpackAssocWriter) WriteInt32(v int32, field string) error {
|
|
||||||
if err := wr.writeFieldName(field); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return errors.WithStack(wr.enc.EncodeInt(int64(v)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wr *msgpackAssocWriter) WriteInt64(v int64, field string) error {
|
|
||||||
if err := wr.writeFieldName(field); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return errors.WithStack(wr.enc.EncodeInt(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wr *msgpackAssocWriter) WriteUint8(v uint8, field string) error {
|
|
||||||
if err := wr.writeFieldName(field); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return errors.WithStack(wr.enc.EncodeUint(uint64(v)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wr *msgpackAssocWriter) WriteUint16(v uint16, field string) error {
|
|
||||||
if err := wr.writeFieldName(field); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return errors.WithStack(wr.enc.EncodeUint(uint64(v)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wr *msgpackAssocWriter) WriteUint32(v uint32, field string) error {
|
|
||||||
if err := wr.writeFieldName(field); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return errors.WithStack(wr.enc.EncodeUint(uint64(v)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wr *msgpackAssocWriter) WriteUint64(v uint64, field string) error {
|
|
||||||
if err := wr.writeFieldName(field); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return errors.WithStack(wr.enc.EncodeUint(uint64(v)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wr *msgpackAssocWriter) WriteBool(v bool, field string) error {
|
|
||||||
if err := wr.writeFieldName(field); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return errors.WithStack(wr.enc.EncodeBool(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wr *msgpackAssocWriter) WriteFloat32(v float32, field string) error {
|
|
||||||
if err := wr.writeFieldName(field); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return errors.WithStack(wr.enc.EncodeFloat32(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wr *msgpackAssocWriter) WriteFloat64(v float64, field string) error {
|
|
||||||
if err := wr.writeFieldName(field); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return errors.WithStack(wr.enc.EncodeFloat64(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wr *msgpackAssocWriter) WriteString(v string, field string) error {
|
|
||||||
if err := wr.writeFieldName(field); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return errors.WithStack(wr.enc.EncodeString(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wr *msgpackAssocWriter) WriteBytes(v []byte, field string) error {
|
|
||||||
if err := wr.writeFieldName(field); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return errors.WithStack(wr.enc.EncodeBytes(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wr *msgpackAssocWriter) BeginContainer(length int, field string) error {
|
|
||||||
if err := wr.writeFieldName(field); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := wr.enc.EncodeMapLen(length); err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
wr.containers = append(wr.containers, writeContainer{
|
|
||||||
length: length,
|
|
||||||
assoc: true,
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wr *msgpackAssocWriter) EndContainer() error {
|
|
||||||
if len(wr.containers) == 0 {
|
|
||||||
return errors.New("there is no open containers")
|
|
||||||
}
|
|
||||||
wr.containers = wr.containers[:len(wr.containers)-1]
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wr *msgpackAssocWriter) BeginCollection(length int, field string) error {
|
|
||||||
if err := wr.writeFieldName(field); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := wr.enc.EncodeArrayLen(length); err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
wr.containers = append(wr.containers, writeContainer{
|
|
||||||
length: length,
|
|
||||||
assoc: false,
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wr *msgpackAssocWriter) EndCollection() error {
|
|
||||||
return wr.EndContainer()
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
package meta_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/hex"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"git.bit5.ru/backend/meta/v5"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMsgpackAssocWriter(t *testing.T) {
|
|
||||||
t.Run("write struct", func(t *testing.T) {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
wr := meta.NewMsgpackAssocWriter(&buf)
|
|
||||||
|
|
||||||
s := TestParent{
|
|
||||||
Field1: "blabla",
|
|
||||||
Field2: TestFoo{
|
|
||||||
Field: 1,
|
|
||||||
},
|
|
||||||
Field3: []int8{2, 4, 6},
|
|
||||||
Field4: []TestFoo{
|
|
||||||
{Field: 10},
|
|
||||||
{Field: 1024},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := s.Write(wr)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
expected := "84a26631a6626c61626c61a2663281a56669656c6401a2663393020406a266349281a56669656c640a81a56669656c64cd0400"
|
|
||||||
actual := hex.EncodeToString(buf.Bytes())
|
|
||||||
require.EqualValues(t, expected, actual)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("write child struct", func(t *testing.T) {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
wr := meta.NewMsgpackAssocWriter(&buf)
|
|
||||||
|
|
||||||
s := TestChild{
|
|
||||||
Field: "qwerty",
|
|
||||||
TestParent: TestParent{
|
|
||||||
Field1: "blabla",
|
|
||||||
Field2: TestFoo{
|
|
||||||
Field: 1,
|
|
||||||
},
|
|
||||||
Field3: []int8{2, 4, 6},
|
|
||||||
Field4: []TestFoo{
|
|
||||||
{Field: 10},
|
|
||||||
{Field: 1024},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := s.Write(wr)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
expected := "85a26631a6626c61626c61a2663281a56669656c6401a2663393020406a266349281a56669656c640a81a56669656c64cd0400a166a6717765727479"
|
|
||||||
actual := hex.EncodeToString(buf.Bytes())
|
|
||||||
require.EqualValues(t, expected, actual)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,6 +1,7 @@
|
||||||
package meta
|
package meta
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -8,11 +9,6 @@ import (
|
||||||
"github.com/vmihailenco/msgpack/v5/msgpcode"
|
"github.com/vmihailenco/msgpack/v5/msgpcode"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
FieldNotFound = errors.New("field not found")
|
|
||||||
NoOpenContainer = errors.New("there is no open container")
|
|
||||||
)
|
|
||||||
|
|
||||||
type msgpackReader struct {
|
type msgpackReader struct {
|
||||||
dec *msgpack.Decoder
|
dec *msgpack.Decoder
|
||||||
stack []readContainer
|
stack []readContainer
|
||||||
|
@ -20,116 +16,450 @@ type msgpackReader struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type readContainer struct {
|
type readContainer struct {
|
||||||
length int
|
started bool
|
||||||
|
length int
|
||||||
|
assoc bool
|
||||||
|
values map[string]msgpack.RawMessage
|
||||||
|
reader io.Reader
|
||||||
|
readCnt int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMsgpackReader(r io.Reader) Reader {
|
func NewMsgpackReader(r io.Reader) Reader {
|
||||||
return &msgpackReader{
|
return &msgpackReader{
|
||||||
dec: msgpack.NewDecoder(r),
|
dec: msgpack.NewDecoder(r),
|
||||||
stack: make([]readContainer, 0, 2),
|
stack: make([]readContainer, 0, 2),
|
||||||
curr: readContainer{},
|
curr: readContainer{
|
||||||
|
reader: r,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rd *msgpackReader) readField() (string, error) {
|
||||||
|
field, err := rd.dec.DecodeString()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.WithStack(err)
|
||||||
|
}
|
||||||
|
return field, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (rd *msgpackReader) ReadInt8(v *int8, targetField string) error {
|
func (rd *msgpackReader) ReadInt8(v *int8, targetField string) error {
|
||||||
return decodeInt8(rd.dec, v)
|
if !rd.curr.started || !rd.curr.assoc {
|
||||||
|
return decodeInt8(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b, ok := rd.curr.values[targetField]; ok {
|
||||||
|
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
||||||
|
return decodeInt8(dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
||||||
|
field, err := rd.readField()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if field == targetField {
|
||||||
|
rd.curr.readCnt = i + 1
|
||||||
|
return decodeInt8(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := rd.dec.DecodeRaw()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
rd.curr.values[field] = raw
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Errorf("field `%s` not found", targetField)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rd *msgpackReader) ReadInt16(v *int16, targetField string) error {
|
func (rd *msgpackReader) ReadInt16(v *int16, targetField string) error {
|
||||||
return decodeInt16(rd.dec, v)
|
if !rd.curr.started || !rd.curr.assoc {
|
||||||
|
return decodeInt16(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b, ok := rd.curr.values[targetField]; ok {
|
||||||
|
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
||||||
|
return decodeInt16(dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
||||||
|
field, err := rd.readField()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if field == targetField {
|
||||||
|
rd.curr.readCnt = i + 1
|
||||||
|
return decodeInt16(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := rd.dec.DecodeRaw()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
rd.curr.values[field] = raw
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Errorf("field `%s` not found", targetField)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rd *msgpackReader) ReadInt32(v *int32, targetField string) error {
|
func (rd *msgpackReader) ReadInt32(v *int32, targetField string) error {
|
||||||
return decodeInt32(rd.dec, v)
|
if !rd.curr.started || !rd.curr.assoc {
|
||||||
|
return decodeInt32(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b, ok := rd.curr.values[targetField]; ok {
|
||||||
|
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
||||||
|
return decodeInt32(dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
||||||
|
field, err := rd.readField()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if field == targetField {
|
||||||
|
rd.curr.readCnt = i + 1
|
||||||
|
return decodeInt32(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := rd.dec.DecodeRaw()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
rd.curr.values[field] = raw
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Errorf("field `%s` not found", targetField)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rd *msgpackReader) ReadInt64(v *int64, targetField string) error {
|
func (rd *msgpackReader) ReadInt64(v *int64, targetField string) error {
|
||||||
return decodeInt64(rd.dec, v)
|
if !rd.curr.started || !rd.curr.assoc {
|
||||||
|
return decodeInt64(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b, ok := rd.curr.values[targetField]; ok {
|
||||||
|
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
||||||
|
return decodeInt64(dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
||||||
|
field, err := rd.readField()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if field == targetField {
|
||||||
|
rd.curr.readCnt = i + 1
|
||||||
|
return decodeInt64(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := rd.dec.DecodeRaw()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
rd.curr.values[field] = raw
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Errorf("field `%s` not found", targetField)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rd *msgpackReader) ReadUint8(v *uint8, targetField string) error {
|
func (rd *msgpackReader) ReadUint8(v *uint8, targetField string) error {
|
||||||
return decodeUint8(rd.dec, v)
|
if !rd.curr.started || !rd.curr.assoc {
|
||||||
|
return decodeUint8(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b, ok := rd.curr.values[targetField]; ok {
|
||||||
|
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
||||||
|
return decodeUint8(dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
||||||
|
field, err := rd.readField()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if field == targetField {
|
||||||
|
rd.curr.readCnt = i + 1
|
||||||
|
return decodeUint8(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := rd.dec.DecodeRaw()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
rd.curr.values[field] = raw
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Errorf("field `%s` not found", targetField)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rd *msgpackReader) ReadUint16(v *uint16, targetField string) error {
|
func (rd *msgpackReader) ReadUint16(v *uint16, targetField string) error {
|
||||||
return decodeUint16(rd.dec, v)
|
if !rd.curr.started || !rd.curr.assoc {
|
||||||
|
return decodeUint16(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b, ok := rd.curr.values[targetField]; ok {
|
||||||
|
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
||||||
|
return decodeUint16(dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
||||||
|
field, err := rd.readField()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if field == targetField {
|
||||||
|
rd.curr.readCnt = i + 1
|
||||||
|
return decodeUint16(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := rd.dec.DecodeRaw()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
rd.curr.values[field] = raw
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Errorf("field `%s` not found", targetField)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rd *msgpackReader) ReadUint32(v *uint32, targetField string) error {
|
func (rd *msgpackReader) ReadUint32(v *uint32, targetField string) error {
|
||||||
return decodeUint32(rd.dec, v)
|
if !rd.curr.started || !rd.curr.assoc {
|
||||||
|
return decodeUint32(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b, ok := rd.curr.values[targetField]; ok {
|
||||||
|
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
||||||
|
return decodeUint32(dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
||||||
|
field, err := rd.readField()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if field == targetField {
|
||||||
|
rd.curr.readCnt = i + 1
|
||||||
|
return decodeUint32(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := rd.dec.DecodeRaw()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
rd.curr.values[field] = raw
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Errorf("field `%s` not found", targetField)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rd *msgpackReader) ReadUint64(v *uint64, targetField string) error {
|
func (rd *msgpackReader) ReadUint64(v *uint64, targetField string) error {
|
||||||
return decodeUint64(rd.dec, v)
|
if !rd.curr.started || !rd.curr.assoc {
|
||||||
|
return decodeUint64(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b, ok := rd.curr.values[targetField]; ok {
|
||||||
|
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
||||||
|
return decodeUint64(dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
||||||
|
field, err := rd.readField()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if field == targetField {
|
||||||
|
rd.curr.readCnt = i + 1
|
||||||
|
return decodeUint64(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := rd.dec.DecodeRaw()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
rd.curr.values[field] = raw
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Errorf("field `%s` not found", targetField)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rd *msgpackReader) ReadBool(v *bool, targetField string) error {
|
func (rd *msgpackReader) ReadBool(v *bool, targetField string) error {
|
||||||
return decodeBool(rd.dec, v)
|
if !rd.curr.started || !rd.curr.assoc {
|
||||||
|
return decodeBool(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b, ok := rd.curr.values[targetField]; ok {
|
||||||
|
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
||||||
|
return decodeBool(dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
||||||
|
field, err := rd.readField()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if field == targetField {
|
||||||
|
rd.curr.readCnt = i + 1
|
||||||
|
return decodeBool(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := rd.dec.DecodeRaw()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
rd.curr.values[field] = raw
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Errorf("field `%s` not found", targetField)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rd *msgpackReader) ReadFloat32(v *float32, targetField string) error {
|
func (rd *msgpackReader) ReadFloat32(v *float32, targetField string) error {
|
||||||
return decodeFloat32(rd.dec, v)
|
if !rd.curr.started || !rd.curr.assoc {
|
||||||
|
return decodeFloat32(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b, ok := rd.curr.values[targetField]; ok {
|
||||||
|
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
||||||
|
return decodeFloat32(dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
||||||
|
field, err := rd.readField()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if field == targetField {
|
||||||
|
rd.curr.readCnt = i + 1
|
||||||
|
return decodeFloat32(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := rd.dec.DecodeRaw()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
rd.curr.values[field] = raw
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Errorf("field `%s` not found", targetField)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rd *msgpackReader) ReadFloat64(v *float64, targetField string) error {
|
func (rd *msgpackReader) ReadFloat64(v *float64, targetField string) error {
|
||||||
return decodeFloat64(rd.dec, v)
|
if !rd.curr.started || !rd.curr.assoc {
|
||||||
|
return decodeFloat64(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b, ok := rd.curr.values[targetField]; ok {
|
||||||
|
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
||||||
|
return decodeFloat64(dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
||||||
|
field, err := rd.readField()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if field == targetField {
|
||||||
|
rd.curr.readCnt = i + 1
|
||||||
|
return decodeFloat64(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := rd.dec.DecodeRaw()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
rd.curr.values[field] = raw
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Errorf("field `%s` not found", targetField)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rd *msgpackReader) ReadString(v *string, targetField string) error {
|
func (rd *msgpackReader) ReadString(v *string, targetField string) error {
|
||||||
return decodeString(rd.dec, v)
|
if !rd.curr.started || !rd.curr.assoc {
|
||||||
|
return decodeString(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b, ok := rd.curr.values[targetField]; ok {
|
||||||
|
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
||||||
|
return decodeString(dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
||||||
|
field, err := rd.readField()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if field == targetField {
|
||||||
|
rd.curr.readCnt = i + 1
|
||||||
|
return decodeString(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := rd.dec.DecodeRaw()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
rd.curr.values[field] = raw
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Errorf("field `%s` not found", targetField)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rd *msgpackReader) ReadBytes(v *[]byte, targetField string) error {
|
func (rd *msgpackReader) ReadBytes(v *[]byte, targetField string) error {
|
||||||
return decodeBytes(rd.dec, v)
|
if !rd.curr.started || !rd.curr.assoc {
|
||||||
|
return decodeBytes(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b, ok := rd.curr.values[targetField]; ok {
|
||||||
|
dec := msgpack.NewDecoder(bytes.NewReader(b))
|
||||||
|
return decodeBytes(dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
||||||
|
field, err := rd.readField()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if field == targetField {
|
||||||
|
rd.curr.readCnt = i + 1
|
||||||
|
return decodeBytes(rd.dec, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := rd.dec.DecodeRaw()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
rd.curr.values[field] = raw
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Errorf("field `%s` not found", targetField)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rd *msgpackReader) BeginContainer(targetField string) error {
|
func (rd *msgpackReader) BeginContainer(targetField string) error {
|
||||||
return rd.beginContainer(targetField)
|
if !rd.curr.started || !rd.curr.assoc {
|
||||||
}
|
return rd.beginContainer(targetField)
|
||||||
|
|
||||||
func (rd *msgpackReader) EndContainer() error {
|
|
||||||
return rd.endContainer()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackReader) BeginCollection(targetField string) error {
|
|
||||||
return rd.beginContainer(targetField)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackReader) EndCollection() error {
|
|
||||||
return rd.endContainer()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackReader) ContainerSize() (int, error) {
|
|
||||||
return rd.curr.length, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackReader) IsContainerAssoc() (bool, error) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackReader) Skip() error {
|
|
||||||
return errors.WithStack(rd.dec.Skip())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rd *msgpackReader) TryReadMask() (bool, FieldsMask, error) {
|
|
||||||
maskLen, err := rd.dec.DecodeArrayLen()
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
return false, FieldsMask{}, nil
|
|
||||||
}
|
|
||||||
return false, FieldsMask{}, errors.WithStack(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var mask FieldsMask
|
if b, ok := rd.curr.values[targetField]; ok {
|
||||||
for i := 0; i < maskLen; i++ {
|
rd.dec.Reset(bytes.NewReader(b))
|
||||||
maskPart, err := rd.dec.DecodeUint64()
|
return rd.beginContainer(targetField)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := rd.curr.readCnt; i < rd.curr.length; i++ {
|
||||||
|
field, err := rd.readField()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, FieldsMask{}, errors.WithStack(err)
|
return err
|
||||||
}
|
}
|
||||||
mask.SetPartFromUint64(i, maskPart)
|
if field == targetField {
|
||||||
|
rd.curr.readCnt = i + 1
|
||||||
|
return rd.beginContainer(targetField)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := rd.dec.DecodeRaw()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
rd.curr.values[field] = raw
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, mask, nil
|
return errors.Errorf("field `%s` not found", targetField)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rd *msgpackReader) beginContainer(field string) error {
|
func (rd *msgpackReader) beginContainer(field string) error {
|
||||||
|
@ -139,13 +469,18 @@ func (rd *msgpackReader) beginContainer(field string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case code == msgpcode.Nil:
|
case msgpcode.IsFixedMap(code), code == msgpcode.Map16, code == msgpcode.Map32:
|
||||||
if err := rd.dec.DecodeNil(); err != nil {
|
l, err := rd.dec.DecodeMapLen()
|
||||||
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
rd.stack = append(rd.stack, rd.curr)
|
rd.stack = append(rd.stack, rd.curr)
|
||||||
rd.curr = readContainer{
|
rd.curr = readContainer{
|
||||||
length: 0,
|
started: true,
|
||||||
|
length: l,
|
||||||
|
assoc: true,
|
||||||
|
values: make(map[string]msgpack.RawMessage, l),
|
||||||
|
reader: rd.dec.Buffered(),
|
||||||
}
|
}
|
||||||
|
|
||||||
case msgpcode.IsFixedArray(code), code == msgpcode.Array16, code == msgpcode.Array32:
|
case msgpcode.IsFixedArray(code), code == msgpcode.Array16, code == msgpcode.Array32:
|
||||||
|
@ -155,24 +490,71 @@ func (rd *msgpackReader) beginContainer(field string) error {
|
||||||
}
|
}
|
||||||
rd.stack = append(rd.stack, rd.curr)
|
rd.stack = append(rd.stack, rd.curr)
|
||||||
rd.curr = readContainer{
|
rd.curr = readContainer{
|
||||||
length: l,
|
started: true,
|
||||||
|
length: l,
|
||||||
|
assoc: false,
|
||||||
|
reader: rd.dec.Buffered(),
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return errors.Errorf("field `%s` is not an array", field)
|
return errors.Errorf("there is no container for field `%s`", field)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rd *msgpackReader) endContainer() error {
|
func (rd *msgpackReader) EndContainer() error {
|
||||||
if len(rd.stack) == 0 {
|
if len(rd.stack) == 0 {
|
||||||
return NoOpenContainer
|
return errors.New("there is no open containers")
|
||||||
}
|
}
|
||||||
rd.curr = rd.stack[len(rd.stack)-1]
|
rd.curr = rd.stack[len(rd.stack)-1]
|
||||||
rd.stack = rd.stack[:len(rd.stack)-1]
|
rd.stack = rd.stack[:len(rd.stack)-1]
|
||||||
|
rd.dec.Reset(rd.curr.reader)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rd *msgpackReader) ContainerSize() (int, error) {
|
||||||
|
return rd.curr.length, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpackReader) IsContainerAssoc() (bool, error) {
|
||||||
|
return rd.curr.assoc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpackReader) Skip() error {
|
||||||
|
return errors.WithStack(rd.dec.Skip())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpackReader) TryReadMask() (bool, FieldsMask, error) {
|
||||||
|
code, err := rd.dec.PeekCode()
|
||||||
|
if err != nil {
|
||||||
|
return false, FieldsMask{}, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if code != msgpcode.Nil {
|
||||||
|
return false, FieldsMask{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rd.dec.Skip(); err != nil {
|
||||||
|
return false, FieldsMask{}, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var mask FieldsMask
|
||||||
|
maskLen, err := rd.dec.DecodeArrayLen()
|
||||||
|
if err != nil {
|
||||||
|
return false, FieldsMask{}, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < maskLen; i++ {
|
||||||
|
maskPart, err := rd.dec.DecodeUint64()
|
||||||
|
if err != nil {
|
||||||
|
return false, FieldsMask{}, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
mask.SetPartFromUint64(i, maskPart)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, mask, nil
|
||||||
|
}
|
||||||
|
|
||||||
func decodeUint8(dec *msgpack.Decoder, v *uint8) error {
|
func decodeUint8(dec *msgpack.Decoder, v *uint8) error {
|
||||||
tmp, err := dec.DecodeUint8()
|
tmp, err := dec.DecodeUint8()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -255,21 +637,6 @@ func decodeBool(dec *msgpack.Decoder, v *bool) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeFloat32(dec *msgpack.Decoder, v *float32) error {
|
func decodeFloat32(dec *msgpack.Decoder, v *float32) error {
|
||||||
code, err := dec.PeekCode()
|
|
||||||
if err != nil {
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if code == msgpcode.Double {
|
|
||||||
var tmp float64
|
|
||||||
if err := decodeFloat64(dec, &tmp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*v = float32(tmp)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp, err := dec.DecodeFloat32()
|
tmp, err := dec.DecodeFloat32()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
|
|
|
@ -5,14 +5,14 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.bit5.ru/backend/meta/v5"
|
"git.bit5.ru/backend/meta/v2"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMsgpackReader(t *testing.T) {
|
func TestMsgpackReader(t *testing.T) {
|
||||||
t.Run("reading parent", func(t *testing.T) {
|
t.Run("reading struct as map", func(t *testing.T) {
|
||||||
// ["blabla",[1],[2,4,6],[[10],[1024]],null]
|
// {"f1":"blabla","f3":[2,4,6],"f2":{"field":1},"f4":[{"field":10},{"field":1024}]}
|
||||||
src := "95A6626C61626C6191019302040692910A91CD0400C0"
|
src := "84a26631a6626c61626c61a2663393020406a2663281a56669656c6401a266349281a56669656c640a81a56669656c64cd0400"
|
||||||
|
|
||||||
expected := TestParent{
|
expected := TestParent{
|
||||||
Field1: "blabla",
|
Field1: "blabla",
|
||||||
|
@ -37,9 +37,63 @@ func TestMsgpackReader(t *testing.T) {
|
||||||
require.EqualValues(t, expected, actual)
|
require.EqualValues(t, expected, actual)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("reading child", func(t *testing.T) {
|
t.Run("reading struct as array with maps", func(t *testing.T) {
|
||||||
// ["blabla",[1],[2,4,6],[[10],[1024]],null,"qwerty"]
|
// ["blabla",{"field":1},[2,4,6],[{"field":10},{"field":1024}]]
|
||||||
src := "96A6626C61626C6191019302040692910A91CD0400C0A6717765727479"
|
src := "94a6626c61626c6181a56669656c6401930204069281a56669656c640a81a56669656c64cd0400"
|
||||||
|
|
||||||
|
expected := TestParent{
|
||||||
|
Field1: "blabla",
|
||||||
|
Field2: TestFoo{
|
||||||
|
Field: 1,
|
||||||
|
},
|
||||||
|
Field3: []int8{2, 4, 6},
|
||||||
|
Field4: []TestFoo{
|
||||||
|
{Field: 10},
|
||||||
|
{Field: 1024},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := hex.DecodeString(src)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
rdr := meta.NewMsgpackReader(bytes.NewReader(data))
|
||||||
|
|
||||||
|
var actual TestParent
|
||||||
|
readErr := actual.Read(rdr)
|
||||||
|
require.NoError(t, readErr)
|
||||||
|
require.EqualValues(t, expected, actual)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("reading struct only as array", func(t *testing.T) {
|
||||||
|
// ["blabla",[1],[2,4,6],[[10],[1024]]]
|
||||||
|
src := "94a6626c61626c6191019302040692910a91cd0400"
|
||||||
|
|
||||||
|
expected := TestParent{
|
||||||
|
Field1: "blabla",
|
||||||
|
Field2: TestFoo{
|
||||||
|
Field: 1,
|
||||||
|
},
|
||||||
|
Field3: []int8{2, 4, 6},
|
||||||
|
Field4: []TestFoo{
|
||||||
|
{Field: 10},
|
||||||
|
{Field: 1024},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := hex.DecodeString(src)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
rdr := meta.NewMsgpackReader(bytes.NewReader(data))
|
||||||
|
|
||||||
|
var actual TestParent
|
||||||
|
readErr := actual.Read(rdr)
|
||||||
|
require.NoError(t, readErr)
|
||||||
|
require.EqualValues(t, expected, actual)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("reading child struct as map", func(t *testing.T) {
|
||||||
|
// {"f":"qwerty","f1":"blabla","f3":[2,4,6],"f2":{"field":1},"f4":[{"field":10},{"field":1024}]}
|
||||||
|
src := "85a166a6717765727479a26631a6626c61626c61a2663393020406a2663281a56669656c6401a266349281a56669656c640a81a56669656c64cd0400"
|
||||||
|
|
||||||
expected := TestChild{
|
expected := TestChild{
|
||||||
Field: "qwerty",
|
Field: "qwerty",
|
||||||
|
@ -67,31 +121,23 @@ func TestMsgpackReader(t *testing.T) {
|
||||||
require.EqualValues(t, expected, actual)
|
require.EqualValues(t, expected, actual)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("fail reading parent as map", func(t *testing.T) {
|
t.Run("reading child struct as array", func(t *testing.T) {
|
||||||
// {"f1":"blabla","f3":[2,4,6],"f2":{"field":1},"f4":[{"field":10},{"field":1024}]}
|
// ["blabla",[1],[2,4,6],[[10],[1024]],"qwerty"]
|
||||||
src := "84a26631a6626c61626c61a2663393020406a2663281a56669656c6401a266349281a56669656c640a81a56669656c64cd0400"
|
src := "95a6626c61626c6191019302040692910a91cd0400a6717765727479"
|
||||||
|
|
||||||
expected := TestParent{}
|
expected := TestChild{
|
||||||
|
Field: "qwerty",
|
||||||
data, err := hex.DecodeString(src)
|
TestParent: TestParent{
|
||||||
require.NoError(t, err)
|
Field1: "blabla",
|
||||||
|
Field2: TestFoo{
|
||||||
rdr := meta.NewMsgpackReader(bytes.NewReader(data))
|
Field: 1,
|
||||||
|
},
|
||||||
var actual TestParent
|
Field3: []int8{2, 4, 6},
|
||||||
readErr := actual.Read(rdr)
|
Field4: []TestFoo{
|
||||||
require.ErrorContains(t, readErr, "field `` is not an array")
|
{Field: 10},
|
||||||
require.EqualValues(t, expected, actual)
|
{Field: 1024},
|
||||||
})
|
},
|
||||||
|
},
|
||||||
t.Run("fail reading parent as array with maps", func(t *testing.T) {
|
|
||||||
// ["blabla",{"field":1},[2,4,6],[{"field":10},{"field":1024}]]
|
|
||||||
src := "94a6626c61626c6181a56669656c6401930204069281a56669656c640a81a56669656c64cd0400"
|
|
||||||
|
|
||||||
expected := TestParent{
|
|
||||||
Field1: "blabla",
|
|
||||||
Field3: []int8{},
|
|
||||||
Field4: []TestFoo{},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := hex.DecodeString(src)
|
data, err := hex.DecodeString(src)
|
||||||
|
@ -99,26 +145,9 @@ func TestMsgpackReader(t *testing.T) {
|
||||||
|
|
||||||
rdr := meta.NewMsgpackReader(bytes.NewReader(data))
|
rdr := meta.NewMsgpackReader(bytes.NewReader(data))
|
||||||
|
|
||||||
var actual TestParent
|
|
||||||
readErr := actual.Read(rdr)
|
|
||||||
require.ErrorContains(t, readErr, "field `f2` is not an array")
|
|
||||||
require.EqualValues(t, expected, actual)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("fail reading child struct as map", func(t *testing.T) {
|
|
||||||
// {"f":"qwerty","f1":"blabla","f3":[2,4,6],"f2":{"field":1},"f4":[{"field":10},{"field":1024}]}
|
|
||||||
src := "85a166a6717765727479a26631a6626c61626c61a2663393020406a2663281a56669656c6401a266349281a56669656c640a81a56669656c64cd0400"
|
|
||||||
|
|
||||||
expected := TestChild{}
|
|
||||||
|
|
||||||
data, err := hex.DecodeString(src)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
rdr := meta.NewMsgpackReader(bytes.NewReader(data))
|
|
||||||
|
|
||||||
var actual TestChild
|
var actual TestChild
|
||||||
readErr := actual.Read(rdr)
|
readErr := actual.Read(rdr)
|
||||||
require.ErrorContains(t, readErr, "field `` is not an array")
|
require.NoError(t, readErr)
|
||||||
require.EqualValues(t, expected, actual)
|
require.EqualValues(t, expected, actual)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,85 +9,156 @@ import (
|
||||||
|
|
||||||
type msgpackWriter struct {
|
type msgpackWriter struct {
|
||||||
enc *msgpack.Encoder
|
enc *msgpack.Encoder
|
||||||
containers []struct{}
|
containers []writeContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
type writeContainer struct {
|
||||||
|
length int
|
||||||
|
assoc bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMsgpackWriter(w io.Writer) Writer {
|
func NewMsgpackWriter(w io.Writer) Writer {
|
||||||
return &msgpackWriter{
|
return &msgpackWriter{
|
||||||
enc: msgpack.NewEncoder(w),
|
enc: msgpack.NewEncoder(w),
|
||||||
containers: make([]struct{}, 0, 1),
|
containers: make([]writeContainer, 0, 1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (wr *msgpackWriter) currentContainer() writeContainer {
|
||||||
|
if wr == nil || len(wr.containers) == 0 {
|
||||||
|
return writeContainer{}
|
||||||
|
}
|
||||||
|
return wr.containers[len(wr.containers)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *msgpackWriter) writeFieldName(field string) error {
|
||||||
|
if !wr.currentContainer().assoc {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.WithStack(wr.enc.EncodeString(field))
|
||||||
|
}
|
||||||
|
|
||||||
func (wr *msgpackWriter) WriteInt8(v int8, field string) error {
|
func (wr *msgpackWriter) WriteInt8(v int8, field string) error {
|
||||||
|
if err := wr.writeFieldName(field); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return errors.WithStack(wr.enc.EncodeInt(int64(v)))
|
return errors.WithStack(wr.enc.EncodeInt(int64(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wr *msgpackWriter) WriteInt16(v int16, field string) error {
|
func (wr *msgpackWriter) WriteInt16(v int16, field string) error {
|
||||||
|
if err := wr.writeFieldName(field); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return errors.WithStack(wr.enc.EncodeInt(int64(v)))
|
return errors.WithStack(wr.enc.EncodeInt(int64(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wr *msgpackWriter) WriteInt32(v int32, field string) error {
|
func (wr *msgpackWriter) WriteInt32(v int32, field string) error {
|
||||||
|
if err := wr.writeFieldName(field); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return errors.WithStack(wr.enc.EncodeInt(int64(v)))
|
return errors.WithStack(wr.enc.EncodeInt(int64(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wr *msgpackWriter) WriteInt64(v int64, field string) error {
|
func (wr *msgpackWriter) WriteInt64(v int64, field string) error {
|
||||||
|
if err := wr.writeFieldName(field); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return errors.WithStack(wr.enc.EncodeInt(v))
|
return errors.WithStack(wr.enc.EncodeInt(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wr *msgpackWriter) WriteUint8(v uint8, field string) error {
|
func (wr *msgpackWriter) WriteUint8(v uint8, field string) error {
|
||||||
|
if err := wr.writeFieldName(field); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return errors.WithStack(wr.enc.EncodeUint(uint64(v)))
|
return errors.WithStack(wr.enc.EncodeUint(uint64(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wr *msgpackWriter) WriteUint16(v uint16, field string) error {
|
func (wr *msgpackWriter) WriteUint16(v uint16, field string) error {
|
||||||
|
if err := wr.writeFieldName(field); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return errors.WithStack(wr.enc.EncodeUint(uint64(v)))
|
return errors.WithStack(wr.enc.EncodeUint(uint64(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wr *msgpackWriter) WriteUint32(v uint32, field string) error {
|
func (wr *msgpackWriter) WriteUint32(v uint32, field string) error {
|
||||||
|
if err := wr.writeFieldName(field); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return errors.WithStack(wr.enc.EncodeUint(uint64(v)))
|
return errors.WithStack(wr.enc.EncodeUint(uint64(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wr *msgpackWriter) WriteUint64(v uint64, field string) error {
|
func (wr *msgpackWriter) WriteUint64(v uint64, field string) error {
|
||||||
|
if err := wr.writeFieldName(field); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return errors.WithStack(wr.enc.EncodeUint(uint64(v)))
|
return errors.WithStack(wr.enc.EncodeUint(uint64(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wr *msgpackWriter) WriteBool(v bool, field string) error {
|
func (wr *msgpackWriter) WriteBool(v bool, field string) error {
|
||||||
|
if err := wr.writeFieldName(field); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return errors.WithStack(wr.enc.EncodeBool(v))
|
return errors.WithStack(wr.enc.EncodeBool(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wr *msgpackWriter) WriteFloat32(v float32, field string) error {
|
func (wr *msgpackWriter) WriteFloat32(v float32, field string) error {
|
||||||
|
if err := wr.writeFieldName(field); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return errors.WithStack(wr.enc.EncodeFloat32(v))
|
return errors.WithStack(wr.enc.EncodeFloat32(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wr *msgpackWriter) WriteFloat64(v float64, field string) error {
|
func (wr *msgpackWriter) WriteFloat64(v float64, field string) error {
|
||||||
|
if err := wr.writeFieldName(field); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return errors.WithStack(wr.enc.EncodeFloat64(v))
|
return errors.WithStack(wr.enc.EncodeFloat64(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wr *msgpackWriter) WriteString(v string, field string) error {
|
func (wr *msgpackWriter) WriteString(v string, field string) error {
|
||||||
|
if err := wr.writeFieldName(field); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return errors.WithStack(wr.enc.EncodeString(v))
|
return errors.WithStack(wr.enc.EncodeString(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wr *msgpackWriter) WriteBytes(v []byte, field string) error {
|
func (wr *msgpackWriter) WriteBytes(v []byte, field string) error {
|
||||||
|
if err := wr.writeFieldName(field); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return errors.WithStack(wr.enc.EncodeBytes(v))
|
return errors.WithStack(wr.enc.EncodeBytes(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wr *msgpackWriter) BeginContainer(length int, field string) error {
|
func (wr *msgpackWriter) BeginContainer(length int, field string) error {
|
||||||
return wr.BeginCollection(length, field)
|
if err := wr.writeFieldName(field); err != nil {
|
||||||
}
|
return err
|
||||||
|
}
|
||||||
func (wr *msgpackWriter) EndContainer() error {
|
|
||||||
return wr.EndCollection()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wr *msgpackWriter) BeginCollection(length int, field string) error {
|
|
||||||
if err := wr.enc.EncodeArrayLen(length); err != nil {
|
if err := wr.enc.EncodeArrayLen(length); err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
wr.containers = append(wr.containers, struct{}{})
|
wr.containers = append(wr.containers, writeContainer{
|
||||||
|
length: length,
|
||||||
|
assoc: false,
|
||||||
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wr *msgpackWriter) EndCollection() error {
|
func (wr *msgpackWriter) BeginAssocContainer(length int, field string) error {
|
||||||
|
if err := wr.writeFieldName(field); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := wr.enc.EncodeMapLen(length); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
wr.containers = append(wr.containers, writeContainer{
|
||||||
|
length: length,
|
||||||
|
assoc: true,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wr *msgpackWriter) EndContainer() error {
|
||||||
if len(wr.containers) == 0 {
|
if len(wr.containers) == 0 {
|
||||||
return errors.New("there is no open containers")
|
return errors.New("there is no open containers")
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.bit5.ru/backend/meta/v5"
|
"git.bit5.ru/backend/meta/v2"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ func TestMsgpackWriter(t *testing.T) {
|
||||||
err := s.Write(wr)
|
err := s.Write(wr)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
expected := "94a6626c61626c6191019302040692910a91cd0400"
|
expected := "84a26631a6626c61626c61a2663281a56669656c6401a2663393020406a266349281a56669656c640a81a56669656c64cd0400"
|
||||||
actual := hex.EncodeToString(buf.Bytes())
|
actual := hex.EncodeToString(buf.Bytes())
|
||||||
require.EqualValues(t, expected, actual)
|
require.EqualValues(t, expected, actual)
|
||||||
})
|
})
|
||||||
|
@ -56,7 +56,7 @@ func TestMsgpackWriter(t *testing.T) {
|
||||||
err := s.Write(wr)
|
err := s.Write(wr)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
expected := "95a6626c61626c6191019302040692910a91cd0400a6717765727479"
|
expected := "85a26631a6626c61626c61a2663281a56669656c6401a2663393020406a266349281a56669656c640a81a56669656c64cd0400a166a6717765727479"
|
||||||
actual := hex.EncodeToString(buf.Bytes())
|
actual := hex.EncodeToString(buf.Bytes())
|
||||||
require.EqualValues(t, expected, actual)
|
require.EqualValues(t, expected, actual)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,40 +1,14 @@
|
||||||
package meta_test
|
package meta_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.bit5.ru/backend/meta/v5"
|
"git.bit5.ru/backend/meta/v2"
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateTestParent(id uint32) (meta.Readable, error) {
|
|
||||||
if id == 111 {
|
|
||||||
return &TestParent{}, nil
|
|
||||||
}
|
|
||||||
return nil, errors.Errorf("wrong id")
|
|
||||||
}
|
|
||||||
|
|
||||||
type TestParent struct {
|
type TestParent struct {
|
||||||
Field1 string `json:"f1" msgpack:"f1"`
|
Field1 string `json:"f1" msgpack:"f1"`
|
||||||
Field2 TestFoo `json:"f2" msgpack:"f2"`
|
Field2 TestFoo `json:"f2" msgpack:"f2"`
|
||||||
Field3 []int8 `json:"f3" msgpack:"f3"`
|
Field3 []int8 `json:"f3" msgpack:"f3"`
|
||||||
Field4 []TestFoo `json:"f4" msgpack:"f4"`
|
Field4 []TestFoo `json:"f4" msgpack:"f4"`
|
||||||
Field5 ITestParent `json:"f5" msgpack:"f5"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ITestParent interface {
|
|
||||||
meta.Struct
|
|
||||||
PtrTestParent() *TestParent
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *TestParent) PtrTestParent() *TestParent {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *TestParent) ClassId() uint32 {
|
|
||||||
return 111
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *TestParent) ClassName() string {
|
|
||||||
return "TestParent"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TestParent) Reset() {
|
func (s *TestParent) Reset() {
|
||||||
|
@ -52,8 +26,6 @@ func (s *TestParent) Reset() {
|
||||||
s.Field4 = s.Field4[:0]
|
s.Field4 = s.Field4[:0]
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Field5 = nil
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TestParent) Read(reader meta.Reader) error {
|
func (s *TestParent) Read(reader meta.Reader) error {
|
||||||
|
@ -74,8 +46,8 @@ func (s *TestParent) ReadFields(reader meta.Reader) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if contSize < 5 {
|
if contSize < 4 {
|
||||||
contSize = 5
|
contSize = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
if contSize <= 0 {
|
if contSize <= 0 {
|
||||||
|
@ -107,7 +79,7 @@ func (s *TestParent) ReadFields(reader meta.Reader) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
contSize--
|
contSize--
|
||||||
if err := reader.BeginCollection("f3"); err != nil {
|
if err := reader.BeginContainer("f3"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
field3Size, err := reader.ContainerSize()
|
field3Size, err := reader.ContainerSize()
|
||||||
|
@ -122,7 +94,7 @@ func (s *TestParent) ReadFields(reader meta.Reader) error {
|
||||||
|
|
||||||
s.Field3 = append(s.Field3, tmpField3)
|
s.Field3 = append(s.Field3, tmpField3)
|
||||||
}
|
}
|
||||||
if err := reader.EndCollection(); err != nil {
|
if err := reader.EndContainer(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +103,7 @@ func (s *TestParent) ReadFields(reader meta.Reader) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
contSize--
|
contSize--
|
||||||
if err := reader.BeginCollection("f4"); err != nil {
|
if err := reader.BeginContainer("f4"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
field4Size, err := reader.ContainerSize()
|
field4Size, err := reader.ContainerSize()
|
||||||
|
@ -152,30 +124,15 @@ func (s *TestParent) ReadFields(reader meta.Reader) error {
|
||||||
|
|
||||||
s.Field4 = append(s.Field4, tmpField4)
|
s.Field4 = append(s.Field4, tmpField4)
|
||||||
}
|
}
|
||||||
if err := reader.EndCollection(); err != nil {
|
if err := reader.EndContainer(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if contSize <= 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
contSize--
|
|
||||||
if v, err := meta.ReadGeneric(reader, CreateTestParent, "f5"); err != nil {
|
|
||||||
if err != meta.FieldNotFound {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if v != nil {
|
|
||||||
s.Field5 = v.(ITestParent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TestParent) Write(writer meta.Writer) error {
|
func (s *TestParent) Write(writer meta.Writer) error {
|
||||||
if err := writer.BeginContainer(4, ""); err != nil {
|
if err := writer.BeginAssocContainer(4, ""); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := s.WriteFields(writer); err != nil {
|
if err := s.WriteFields(writer); err != nil {
|
||||||
|
@ -190,7 +147,7 @@ func (s *TestParent) WriteFields(writer meta.Writer) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := writer.BeginContainer(1, "f2"); err != nil {
|
if err := writer.BeginAssocContainer(1, "f2"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := s.Field2.WriteFields(writer); err != nil {
|
if err := s.Field2.WriteFields(writer); err != nil {
|
||||||
|
@ -200,23 +157,24 @@ func (s *TestParent) WriteFields(writer meta.Writer) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := writer.BeginCollection(len(s.Field3), "f3"); err != nil {
|
if err := writer.BeginContainer(len(s.Field3), "f3"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, v := range s.Field3 {
|
for _, v := range s.Field3 {
|
||||||
if err := writer.WriteInt8(v, ""); err != nil {
|
if err := writer.WriteInt8(v, ""); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if err := writer.EndCollection(); err != nil {
|
if err := writer.EndContainer(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := writer.BeginCollection(len(s.Field4), "f4"); err != nil {
|
if err := writer.BeginContainer(len(s.Field4), "f4"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, v := range s.Field4 {
|
for _, v := range s.Field4 {
|
||||||
if err := writer.BeginContainer(1, ""); err != nil {
|
if err := writer.BeginAssocContainer(1, ""); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := v.WriteFields(writer); err != nil {
|
if err := v.WriteFields(writer); err != nil {
|
||||||
|
@ -227,17 +185,13 @@ func (s *TestParent) WriteFields(writer meta.Writer) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if err := writer.EndCollection(); err != nil {
|
if err := writer.EndContainer(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TestParent) FieldsCount() int {
|
|
||||||
return 5
|
|
||||||
}
|
|
||||||
|
|
||||||
type TestChild struct {
|
type TestChild struct {
|
||||||
TestParent
|
TestParent
|
||||||
|
|
||||||
|
@ -290,7 +244,7 @@ func (s *TestChild) ReadFields(reader meta.Reader) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TestChild) Write(writer meta.Writer) error {
|
func (s *TestChild) Write(writer meta.Writer) error {
|
||||||
if err := writer.BeginContainer(5, ""); err != nil {
|
if err := writer.BeginAssocContainer(5, ""); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := s.WriteFields(writer); err != nil {
|
if err := s.WriteFields(writer); err != nil {
|
||||||
|
@ -356,7 +310,7 @@ func (s *TestFoo) ReadFields(reader meta.Reader) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TestFoo) Write(writer meta.Writer) error {
|
func (s *TestFoo) Write(writer meta.Writer) error {
|
||||||
if err := writer.BeginContainer(1, ""); err != nil {
|
if err := writer.BeginAssocContainer(1, ""); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := s.WriteFields(writer); err != nil {
|
if err := s.WriteFields(writer); err != nil {
|
||||||
|
|
Loading…
Reference in New Issue