Compare commits

..

No commits in common. "master" and "v1.0.0" have entirely different histories.

17 changed files with 650 additions and 2145 deletions

View File

@ -1,60 +0,0 @@
package meta
import "bytes"
type ChangedFields struct {
fieldNames map[string]struct{}
fieldsKey *bytes.Buffer
}
func NewChangedFields(fieldCount int) ChangedFields {
keyBuffer := bytes.NewBuffer(make([]byte, 0, fieldCount*2))
cf := ChangedFields{
fieldNames: make(map[string]struct{}, fieldCount),
fieldsKey: keyBuffer,
}
return cf
}
func (cf *ChangedFields) Reset() {
if cf.fieldNames == nil {
cf.fieldNames = make(map[string]struct{})
} else {
for k := range cf.fieldNames {
delete(cf.fieldNames, k)
}
}
}
func (cf ChangedFields) Changed(field string) bool {
_, ok := cf.fieldNames[field]
return ok
}
func (cf *ChangedFields) SetChanged(fields ...string) {
if cf.fieldNames == nil {
cf.fieldNames = make(map[string]struct{})
}
for _, field := range fields {
if _, exists := cf.fieldNames[field]; exists {
continue
}
cf.fieldNames[field] = struct{}{}
cf.fieldsKey.WriteString(field)
}
}
func (cf ChangedFields) Empty() bool {
return len(cf.fieldNames) == 0
}
func (cf ChangedFields) IsNil() bool {
return cf.fieldNames == nil
}
func (cf ChangedFields) GetFieldsKey() string {
return cf.fieldsKey.String()
}

View File

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

View File

@ -2,49 +2,49 @@ package meta
const ( const (
FieldsMaskCapacity = 4 FieldsMaskCapacity = 4
FieldsMaskPartBitSize = 64 FieldsMaskItemBitSize = 64
) )
func MakeFieldsMaskFromInt64(v int64) FieldsMask { func MakeFieldsMaskFromInt64(v int64) FieldsMask {
var mask FieldsMask var mask FieldsMask
mask.SetPartFromInt64(0, v) mask.SetItemFromInt64(0, v)
return mask return mask
} }
type FieldsMaskPart uint64 type FieldsMaskItem uint64
func (part FieldsMaskPart) FieldIsDirty(index uint64) bool { func (fmi FieldsMaskItem) FieldIsDirty(index uint64) bool {
return (1<<index)&part != 0 return (1<<index)&fmi != 0
} }
type FieldsMask struct { type FieldsMask struct {
parts [FieldsMaskCapacity]FieldsMaskPart masks [FieldsMaskCapacity]FieldsMaskItem
} }
func (fm *FieldsMask) SetPartFromUint64(index int, value uint64) { func (fm *FieldsMask) SetItemFromUint64(index int, value uint64) {
fm.parts[index] = FieldsMaskPart(value) fm.masks[index] = FieldsMaskItem(value)
} }
func (fm *FieldsMask) SetPartFromInt64(index int, value int64) { func (fm *FieldsMask) SetItemFromInt64(index int, value int64) {
fm.parts[index] = FieldsMaskPart(value) fm.masks[index] = FieldsMaskItem(value)
} }
func (fm FieldsMask) FieldChanged(index uint64) bool { func (fm FieldsMask) FieldChanged(index uint64) bool {
partIndex := fm.partIndex(index) itemIndex := fm.itemIndex(index)
fieldIndex := fm.fieldIndex(index) maskIndex := fm.maskIndex(index)
part := fm.parts[partIndex] mask := fm.masks[itemIndex]
return part.FieldIsDirty(fieldIndex) return mask.FieldIsDirty(maskIndex)
} }
func (fm *FieldsMask) SetFieldChanged(index uint64) { func (fm *FieldsMask) SetFieldChanged(index uint64) {
partIndex := fm.partIndex(index) itemIndex := fm.itemIndex(index)
fieldIndex := fm.fieldIndex(index) maskIndex := fm.maskIndex(index)
fm.parts[partIndex] |= (1 << fieldIndex) fm.masks[itemIndex] |= (1 << maskIndex)
} }
func (fm FieldsMask) IsFilled() bool { func (fm FieldsMask) IsFilled() bool {
for _, mask := range fm.parts { for _, mask := range fm.masks {
if mask > 0 { if mask > 0 {
return true return true
} }
@ -52,22 +52,10 @@ func (fm FieldsMask) IsFilled() bool {
return false return false
} }
func (fm *FieldsMask) Reset() { func (fm FieldsMask) itemIndex(index uint64) uint64 {
for i := range fm.parts { return index / FieldsMaskItemBitSize
fm.parts[i] = 0
}
} }
func (fm *FieldsMask) SetChangedAll() { func (fm FieldsMask) maskIndex(index uint64) uint64 {
for i := range fm.parts { return index % FieldsMaskItemBitSize
fm.parts[i] = 1<<64 - 1
}
}
func (fm FieldsMask) partIndex(index uint64) uint64 {
return index / FieldsMaskPartBitSize
}
func (fm FieldsMask) fieldIndex(index uint64) uint64 {
return index % FieldsMaskPartBitSize
} }

View File

@ -3,7 +3,7 @@ package meta_test
import ( import (
"testing" "testing"
"git.bit5.ru/backend/meta/v5" "git.bit5.ru/backend/meta"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -19,7 +19,7 @@ func TestFieldsMask(t *testing.T) {
}) })
t.Run("filled value", func(t *testing.T) { t.Run("filled value", func(t *testing.T) {
var mask meta.FieldsMask var mask meta.FieldsMask
mask.SetPartFromInt64(0, 1) mask.SetItemFromInt64(0, 1)
actualIsFilled := mask.IsFilled() actualIsFilled := mask.IsFilled()
require.True(t, actualIsFilled) require.True(t, actualIsFilled)
@ -38,7 +38,7 @@ func TestFieldsMask(t *testing.T) {
var mask meta.FieldsMask var mask meta.FieldsMask
var fieldIndex uint64 = 4 var fieldIndex uint64 = 4
mask.SetPartFromInt64(0, 16) mask.SetItemFromInt64(0, 16)
fieldChanged := mask.FieldChanged(fieldIndex) fieldChanged := mask.FieldChanged(fieldIndex)
require.True(t, fieldChanged) require.True(t, fieldChanged)
@ -47,7 +47,7 @@ func TestFieldsMask(t *testing.T) {
var mask meta.FieldsMask var mask meta.FieldsMask
var fieldIndex uint64 = 68 var fieldIndex uint64 = 68
mask.SetPartFromInt64(1, 16) mask.SetItemFromInt64(1, 16)
fieldChanged := mask.FieldChanged(fieldIndex) fieldChanged := mask.FieldChanged(fieldIndex)
require.True(t, fieldChanged) require.True(t, fieldChanged)
@ -56,7 +56,7 @@ func TestFieldsMask(t *testing.T) {
var mask meta.FieldsMask var mask meta.FieldsMask
var fieldIndex uint64 = 131 var fieldIndex uint64 = 131
mask.SetPartFromInt64(2, 8) mask.SetItemFromInt64(2, 8)
fieldChanged := mask.FieldChanged(fieldIndex) fieldChanged := mask.FieldChanged(fieldIndex)
require.True(t, fieldChanged) require.True(t, fieldChanged)
@ -65,7 +65,7 @@ func TestFieldsMask(t *testing.T) {
var mask meta.FieldsMask var mask meta.FieldsMask
var fieldIndex uint64 = 194 var fieldIndex uint64 = 194
mask.SetPartFromInt64(3, 4) mask.SetItemFromInt64(3, 4)
fieldChanged := mask.FieldChanged(fieldIndex) fieldChanged := mask.FieldChanged(fieldIndex)
require.True(t, fieldChanged) require.True(t, fieldChanged)
@ -76,7 +76,7 @@ func TestFieldsMask(t *testing.T) {
func TestFieldsMaskItem(t *testing.T) { func TestFieldsMaskItem(t *testing.T) {
t.Run("FieldIsDirty", func(t *testing.T) { t.Run("FieldIsDirty", func(t *testing.T) {
cases := []struct { cases := []struct {
maskItem meta.FieldsMaskPart maskItem meta.FieldsMaskItem
expectedDirtyIndexes []uint64 expectedDirtyIndexes []uint64
}{ }{
{ {
@ -102,8 +102,8 @@ func TestFieldsMaskItem(t *testing.T) {
} }
for i, c := range cases { for i, c := range cases {
actualDirtyIndexes := make([]uint64, 0, meta.FieldsMaskPartBitSize) actualDirtyIndexes := make([]uint64, 0, meta.FieldsMaskItemBitSize)
for j := uint64(0); j < meta.FieldsMaskPartBitSize; j++ { for j := uint64(0); j < meta.FieldsMaskItemBitSize; j++ {
if c.maskItem.FieldIsDirty(j) { if c.maskItem.FieldIsDirty(j) {
actualDirtyIndexes = append(actualDirtyIndexes, j) actualDirtyIndexes = append(actualDirtyIndexes, j)
} }

8
go.mod
View File

@ -1,16 +1,16 @@
module git.bit5.ru/backend/meta/v5 module git.bit5.ru/backend/meta
go 1.20 go 1.18
require ( require (
git.bit5.ru/backend/msgpack v1.0.0
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.7.3 github.com/stretchr/testify v1.7.3
github.com/vmihailenco/msgpack/v5 v5.3.5
) )
require ( require (
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/vmihailenco/bufio v0.0.0-20140618134113-fe7b595919de // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

17
go.sum
View File

@ -1,3 +1,5 @@
git.bit5.ru/backend/msgpack v1.0.0 h1:D7sFCFjSN1ADUaESjrRVIWY9TGVATq5i08eKn0ep6ZE=
git.bit5.ru/backend/msgpack v1.0.0/go.mod h1:Syf8E+3pr9z3TropB/eN4PJUekRg5ZD/0sHydHH17r0=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -7,16 +9,21 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.3 h1:dAm0YRdRQlWojc3CrCRgPBzG5f941d0zvAKu7qY4e+I= github.com/stretchr/testify v1.7.3 h1:dAm0YRdRQlWojc3CrCRgPBzG5f941d0zvAKu7qY4e+I=
github.com/stretchr/testify v1.7.3/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.7.3/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
github.com/vmihailenco/bufio v0.0.0-20140618134113-fe7b595919de h1:U+I4zEVstMdfNES/2UO8iqkIf214SDMRhdaFTE3A5rA=
github.com/vmihailenco/bufio v0.0.0-20140618134113-fe7b595919de/go.mod h1:ghSGoeEoFFkXNguSget72dMA0+OLq3AGZiqRohVojxI=
gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e h1:wGA78yza6bu/mWcc4QfBuIEHEtc06xdiU0X8sY36yUU=
gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e/go.mod h1:xsQCaysVCudhrYTfzYWe577fCe7Ceci+6qjO2Rdc0Z4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54=
launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM=

View File

@ -1,101 +1,99 @@
package meta package meta
type Reader interface { type Reader interface {
ReadInt8(v *int8, field string) error ReadI8(v *int8, field string) error
ReadInt16(v *int16, field string) error ReadU8(v *uint8, field string) error
ReadInt32(v *int32, field string) error ReadI16(v *int16, field string) error
ReadInt64(v *int64, field string) error ReadU16(v *uint16, field string) error
ReadI32(v *int32, field string) error
ReadUint8(v *uint8, field string) error ReadU32(v *uint32, field string) error
ReadUint16(v *uint16, field string) error ReadI64(v *int64, field string) error
ReadUint32(v *uint32, field string) error ReadU64(v *uint64, field string) error
ReadUint64(v *uint64, field string) error
ReadBool(v *bool, field string) error ReadBool(v *bool, field string) error
ReadFloat(v *float32, field string) error
ReadFloat32(v *float32, field string) error ReadDouble(v *float64, field string) error
ReadFloat64(v *float64, field string) error
ReadString(v *string, field string) error ReadString(v *string, field string) error
ReadBytes(v *[]byte, field string) error ReadBlob(v *[]byte, field string) error
BeginContainer(field string) error BeginContainer(field string) error
EndContainer() error EndContainer() error
GetContainerSize() (int, error)
BeginCollection(field string) error
EndCollection() error
ContainerSize() (int, error)
IsContainerAssoc() (bool, error)
Skip() error Skip() error
TryReadMask() (bool, FieldsMask, error) TryReadMask() (bool, FieldsMask, error)
} }
type Writer interface { type Writer interface {
WriteInt8(v int8, field string) error WriteI8(v int8, field string) error
WriteInt16(v int16, field string) error WriteU8(v uint8, field string) error
WriteInt32(v int32, field string) error WriteI16(v int16, field string) error
WriteInt64(v int64, field string) error WriteU16(v uint16, field string) error
WriteI32(v int32, field string) error
WriteUint8(v uint8, field string) error WriteU32(v uint32, field string) error
WriteUint16(v uint16, field string) error WriteU64(v uint64, field string) error
WriteUint32(v uint32, field string) error WriteI64(v int64, field string) error
WriteUint64(v uint64, field string) error
WriteBool(v bool, field string) error WriteBool(v bool, field string) error
WriteFloat(v float32, field string) error
WriteFloat32(v float32, field string) error WriteDouble(v float64, field string) error
WriteFloat64(v float64, field string) error
WriteString(v string, field string) error WriteString(v string, field string) error
WriteBytes(v []byte, field string) error WriteBlob(v []byte, field string) error
BeginContainer(field string)
BeginCollection(length int, field string) error
EndCollection() error
BeginContainer(length int, field string) error
EndContainer() error EndContainer() error
GetData() ([]byte, error)
} }
type Class interface { type ClassFieldsProps map[string]map[string]string
ClassId() uint32
ClassName() string type IClassProps interface {
CLASS_ID() uint32
CLASS_NAME() string
CLASS_PROPS() *map[string]string
CLASS_FIELDS() []string
CLASS_FIELDS_PROPS() *ClassFieldsProps
} }
type Readable interface { type MetaFactory func(classId uint32) (IMetaStruct, error)
type IMetaStruct interface {
IClassProps
Read(reader Reader) error
Write(writer Writer) error
ReadFields(reader Reader) error
WriteFields(writer Writer) error
Reset() Reset()
Read(Reader) error
ReadFields(Reader) error
} }
type Writable interface { type IMetaDataItem interface {
Write(Writer) error IClassProps
WriteFields(Writer) error GetDbTableName() string
FieldsCount() int GetDbFields() []string
GetOwnerFieldName() string
GetIdFieldName() string
GetIdValue() uint64
Import(interface{})
Export([]interface{})
NewInstance() IMetaDataItem
} }
type Struct interface { type IRemovedIds interface {
Class GetList(classId uint32) []uint64
Readable Add(classId uint32, id uint64)
Writable HasList(classId uint32) bool
} }
type StructFactory func(classId uint32) (Readable, error) type IBitmasked interface {
type Bitmasked interface {
FieldChanged(index uint64) bool
SetFieldChanged(index uint64) SetFieldChanged(index uint64)
HasChangedFields() bool HasValue(index uint64) bool
FieldsMask() FieldsMask IsMaskFilled() bool
GetMask() FieldsMask
} }
type WritableClass interface { type RPCFactory func(classId uint32) (IRPC, error)
Class
Writable
}
type ReadableWritable interface { type IRPC interface {
Readable GetCode() int32
Writable GetName() string
GetRequest() IMetaStruct
GetResponse() IMetaStruct
SetError(int32, string)
GetError() (int32, string)
Execute(interface{}) error
} }

85
meta.go
View File

@ -1,53 +1,64 @@
package meta package meta
const classField = "$id" func Read(reader Reader, createById MetaFactory) (v IMetaStruct, err error) {
return ReadStructGeneric(reader, createById, "")
}
func ReadGeneric(r Reader, createFunc StructFactory, field string) (Readable, error) { func Write(writer Writer, m IMetaStruct) error {
if err := r.BeginContainer(field); err != nil { return WriteStructGeneric(writer, m, "")
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 func ReadStructGeneric(reader Reader, createById MetaFactory, field string) (v IMetaStruct, err error) {
if err = reader.BeginContainer(field); err != nil {
return
} }
var classId uint32 var classId uint32
if err := r.ReadUint32(&classId, classField); err != nil { if err = reader.ReadU32(&classId, ""); err != nil {
return nil, err return
} }
if v, err = createById(classId); err != nil {
return
}
if err = v.ReadFields(reader); err != nil {
return
}
err = reader.EndContainer()
return
}
s, err := createFunc(classId) func WriteStructGeneric(writer Writer, m IMetaStruct, field string) error {
writer.BeginContainer(field)
if err := writer.WriteU32(m.CLASS_ID(), ""); err != nil {
return err
}
if err := m.WriteFields(writer); err != nil {
return err
}
return writer.EndContainer()
}
func ReadStruct(reader Reader, m IMetaStruct, field string) error {
err := reader.BeginContainer(field)
if err != nil { if err != nil {
return nil, err return err
} }
err = m.ReadFields(reader)
if err := s.ReadFields(r); err != nil { if err != nil {
return nil, err return err
} }
return reader.EndContainer()
if err := r.EndContainer(); err != nil {
return nil, err
}
return s, nil
} }
func WriteGeneric(w Writer, s WritableClass, field string) error { func WriteStruct(writer Writer, m IMetaStruct, field string) error {
if err := w.BeginContainer(s.FieldsCount()+1, field); err != nil { writer.BeginContainer(field)
err := m.WriteFields(writer)
if err != nil {
return err return err
} }
if err := w.WriteUint32(s.ClassId(), classField); err != nil { return writer.EndContainer()
return err }
}
if err := s.WriteFields(w); err != nil { func IsRPCFailed(rpc IRPC) bool {
return err code, _ := rpc.GetError()
} return code != 0
return w.EndContainer()
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -1,306 +1,412 @@
package meta package meta
import ( import (
"io"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/vmihailenco/msgpack/v5"
"github.com/vmihailenco/msgpack/v5/msgpcode" "git.bit5.ru/backend/msgpack"
) )
var ( type msgpReader struct {
FieldNotFound = errors.New("field not found") stack []*msgpReadState
NoOpenContainer = errors.New("there is no open container") current *msgpReadState
)
type msgpackReader struct {
dec *msgpack.Decoder
stack []readContainer
curr readContainer
} }
type readContainer struct { type msgpReadState struct {
length int data []interface{}
idx int
} }
func NewMsgpackReader(r io.Reader) Reader { func NewMsgpackReader(bytes []byte) (Reader, error) {
return &msgpackReader{ arr := make([]interface{}, 1)
dec: msgpack.NewDecoder(r), err := msgpack.Unmarshal(bytes, &arr[0])
stack: make([]readContainer, 0, 2),
curr: readContainer{},
}
}
func (rd *msgpackReader) ReadInt8(v *int8, targetField string) error {
return decodeInt8(rd.dec, v)
}
func (rd *msgpackReader) ReadInt16(v *int16, targetField string) error {
return decodeInt16(rd.dec, v)
}
func (rd *msgpackReader) ReadInt32(v *int32, targetField string) error {
return decodeInt32(rd.dec, v)
}
func (rd *msgpackReader) ReadInt64(v *int64, targetField string) error {
return decodeInt64(rd.dec, v)
}
func (rd *msgpackReader) ReadUint8(v *uint8, targetField string) error {
return decodeUint8(rd.dec, v)
}
func (rd *msgpackReader) ReadUint16(v *uint16, targetField string) error {
return decodeUint16(rd.dec, v)
}
func (rd *msgpackReader) ReadUint32(v *uint32, targetField string) error {
return decodeUint32(rd.dec, v)
}
func (rd *msgpackReader) ReadUint64(v *uint64, targetField string) error {
return decodeUint64(rd.dec, v)
}
func (rd *msgpackReader) ReadBool(v *bool, targetField string) error {
return decodeBool(rd.dec, v)
}
func (rd *msgpackReader) ReadFloat32(v *float32, targetField string) error {
return decodeFloat32(rd.dec, v)
}
func (rd *msgpackReader) ReadFloat64(v *float64, targetField string) error {
return decodeFloat64(rd.dec, v)
}
func (rd *msgpackReader) ReadString(v *string, targetField string) error {
return decodeString(rd.dec, v)
}
func (rd *msgpackReader) ReadBytes(v *[]byte, targetField string) error {
return decodeBytes(rd.dec, v)
}
func (rd *msgpackReader) BeginContainer(targetField string) error {
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 != nil {
if err == io.EOF { return nil, errors.WithStack(err)
return false, FieldsMask{}, nil
}
return false, FieldsMask{}, errors.WithStack(err)
} }
var mask FieldsMask rd := &msgpReader{}
for i := 0; i < maskLen; i++ { rd.stack = make([]*msgpReadState, 0, 2)
maskPart, err := rd.dec.DecodeUint64() rd.current = &msgpReadState{arr, 0}
if err != nil { rd.stack = append(rd.stack, rd.current)
return false, FieldsMask{}, errors.WithStack(err)
}
mask.SetPartFromUint64(i, maskPart)
}
return true, mask, nil return rd, nil
} }
func (rd *msgpackReader) beginContainer(field string) error { func (state *msgpReadState) clear() {
code, err := rd.dec.PeekCode() state.data = nil
if err != nil { }
return errors.WithStack(err)
}
switch { func (state *msgpReadState) read(field string) (v interface{}, err error) {
case code == msgpcode.Nil: if len(state.data) <= state.idx {
if err := rd.dec.DecodeNil(); err != nil { err = errors.Errorf("No more data for read, field:%s", field)
return errors.WithStack(err) return
}
rd.stack = append(rd.stack, rd.curr)
rd.curr = readContainer{
length: 0,
} }
v = state.data[state.idx]
state.idx++
return
}
case msgpcode.IsFixedArray(code), code == msgpcode.Array16, code == msgpcode.Array32: func (state *msgpReadState) backCursor() error {
l, err := rd.dec.DecodeArrayLen() 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 { if err != nil {
return errors.WithStack(err) return err
} }
rd.stack = append(rd.stack, rd.curr) switch t := value.(type) {
rd.curr = readContainer{ case float64:
length: l, *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: default:
return errors.Errorf("field `%s` is not an array", field) return errors.Errorf("Can't convert to string %s (%T)", field, t)
} }
return nil return nil
} }
func (rd *msgpackReader) endContainer() error { func (rd *msgpReader) ReadBlob(v *[]byte, field string) error {
if len(rd.stack) == 0 { value, err := rd.current.read(field)
return NoOpenContainer
}
rd.curr = rd.stack[len(rd.stack)-1]
rd.stack = rd.stack[:len(rd.stack)-1]
return nil
}
func decodeUint8(dec *msgpack.Decoder, v *uint8) error {
tmp, err := dec.DecodeUint8()
if err != nil { if err != nil {
return errors.WithStack(err) return err
}
switch t := value.(type) {
case []byte:
*v = t
default:
return errors.Errorf("Can't convert to []byte %s (%T)", field, t)
} }
*v = tmp
return nil return nil
} }
func decodeUint16(dec *msgpack.Decoder, v *uint16) error { func (rd *msgpReader) BeginContainer(field string) error {
tmp, err := dec.DecodeUint16() v, err := rd.current.read(field)
if err != nil { if err != nil {
return errors.WithStack(err)
}
*v = tmp
return nil
}
func decodeUint32(dec *msgpack.Decoder, v *uint32) error {
tmp, err := dec.DecodeUint32()
if err != nil {
return errors.WithStack(err)
}
*v = tmp
return nil
}
func decodeUint64(dec *msgpack.Decoder, v *uint64) error {
tmp, err := dec.DecodeUint64()
if err != nil {
return errors.WithStack(err)
}
*v = tmp
return nil
}
func decodeInt8(dec *msgpack.Decoder, v *int8) error {
tmp, err := dec.DecodeInt8()
if err != nil {
return errors.WithStack(err)
}
*v = tmp
return nil
}
func decodeInt16(dec *msgpack.Decoder, v *int16) error {
tmp, err := dec.DecodeInt16()
if err != nil {
return errors.WithStack(err)
}
*v = tmp
return nil
}
func decodeInt32(dec *msgpack.Decoder, v *int32) error {
tmp, err := dec.DecodeInt32()
if err != nil {
return errors.WithStack(err)
}
*v = tmp
return nil
}
func decodeInt64(dec *msgpack.Decoder, v *int64) error {
tmp, err := dec.DecodeInt64()
if err != nil {
return errors.WithStack(err)
}
*v = tmp
return nil
}
func decodeBool(dec *msgpack.Decoder, v *bool) error {
tmp, err := dec.DecodeBool()
if err != nil {
return errors.WithStack(err)
}
*v = tmp
return nil
}
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 return err
} }
*v = float32(tmp) if data, ok := v.([]interface{}); ok {
cur := &msgpReadState{}
cur.data = data
rd.stack = append(rd.stack, cur)
rd.current = cur
return nil return nil
} else {
return errors.Errorf("Next value isn't array but:%T, field:%s", v, field)
} }
}
tmp, err := dec.DecodeFloat32() func (rd *msgpReader) EndContainer() error {
if err != nil { rd.current.clear()
return errors.WithStack(err) 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
} }
*v = tmp
return nil return nil
} }
func decodeFloat64(dec *msgpack.Decoder, v *float64) error { func (rd *msgpReader) GetContainerSize() (int, error) {
tmp, err := dec.DecodeFloat64() return rd.current.size()
if err != nil {
return errors.WithStack(err)
}
*v = tmp
return nil
} }
func decodeString(dec *msgpack.Decoder, v *string) error { func (rd *msgpReader) Skip() error {
tmp, err := dec.DecodeString() return rd.current.skip()
if err != nil {
return errors.WithStack(err)
}
*v = tmp
return nil
} }
func decodeBytes(dec *msgpack.Decoder, v *[]byte) error { func (rd *msgpReader) TryReadMask() (bool, FieldsMask, error) {
tmp, err := dec.DecodeBytes() value, err := rd.current.read("mask")
if err != nil { if err != nil {
return errors.WithStack(err) return false, FieldsMask{}, err
} }
*v = tmp
return nil 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
} }

View File

@ -1,124 +0,0 @@
package meta_test
import (
"bytes"
"encoding/hex"
"testing"
"git.bit5.ru/backend/meta/v5"
"github.com/stretchr/testify/require"
)
func TestMsgpackReader(t *testing.T) {
t.Run("reading parent", func(t *testing.T) {
// ["blabla",[1],[2,4,6],[[10],[1024]],null]
src := "95A6626C61626C6191019302040692910A91CD0400C0"
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", func(t *testing.T) {
// ["blabla",[1],[2,4,6],[[10],[1024]],null,"qwerty"]
src := "96A6626C61626C6191019302040692910A91CD0400C0A6717765727479"
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.NewMsgpackReader(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 map", func(t *testing.T) {
// {"f1":"blabla","f3":[2,4,6],"f2":{"field":1},"f4":[{"field":10},{"field":1024}]}
src := "84a26631a6626c61626c61a2663393020406a2663281a56669656c6401a266349281a56669656c640a81a56669656c64cd0400"
expected := TestParent{}
data, err := hex.DecodeString(src)
require.NoError(t, err)
rdr := meta.NewMsgpackReader(bytes.NewReader(data))
var actual TestParent
readErr := actual.Read(rdr)
require.ErrorContains(t, readErr, "field `` is not an array")
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}]]
src := "94a6626c61626c6181a56669656c6401930204069281a56669656c640a81a56669656c64cd0400"
expected := TestParent{
Field1: "blabla",
Field3: []int8{},
Field4: []TestFoo{},
}
data, err := hex.DecodeString(src)
require.NoError(t, err)
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
readErr := actual.Read(rdr)
require.ErrorContains(t, readErr, "field `` is not an array")
require.EqualValues(t, expected, actual)
})
}

View File

@ -4,93 +4,126 @@ import (
"io" "io"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/vmihailenco/msgpack/v5"
"git.bit5.ru/backend/msgpack"
) )
type msgpackWriter struct { type msgpWriter struct {
enc *msgpack.Encoder stack []*msgpWriteContainer
containers []struct{} current *msgpWriteContainer
} }
func NewMsgpackWriter(w io.Writer) Writer { type msgpWriteContainer struct {
return &msgpackWriter{ arr []interface{}
enc: msgpack.NewEncoder(w), }
containers: make([]struct{}, 0, 1),
func NewMsgpackWriter() Writer {
wr := &msgpWriter{}
wr.stack = make([]*msgpWriteContainer, 0, 1)
wr.current = newMsgpWriteContainer()
wr.stack = append(wr.stack, wr.current)
return wr
}
func newMsgpWriteContainer() *msgpWriteContainer {
return &msgpWriteContainer{make([]interface{}, 0, 1)}
}
func (state *msgpWriteContainer) add(v interface{}) error {
state.arr = append(state.arr, v)
return nil
}
func (wr *msgpWriter) WriteI8(v int8, field string) error {
return wr.current.add(v)
}
func (wr *msgpWriter) WriteU8(v uint8, field string) error {
return wr.current.add(v)
}
func (wr *msgpWriter) WriteI16(v int16, field string) error {
return wr.current.add(v)
}
func (wr *msgpWriter) WriteU16(v uint16, field string) error {
return wr.current.add(v)
}
func (wr *msgpWriter) WriteI32(v int32, field string) error {
return wr.current.add(v)
}
func (wr *msgpWriter) WriteU32(v uint32, field string) error {
return wr.current.add(v)
}
func (wr *msgpWriter) WriteI64(v int64, field string) error {
return wr.current.add(v)
}
func (wr *msgpWriter) WriteU64(v uint64, field string) error {
return wr.current.add(v)
}
func (wr *msgpWriter) WriteBool(v bool, field string) error {
return wr.current.add(v)
}
func (wr *msgpWriter) WriteFloat(v float32, field string) error {
return wr.current.add(v)
}
func (wr *msgpWriter) WriteDouble(v float64, field string) error {
return wr.current.add(v)
}
func (wr *msgpWriter) WriteString(v string, field string) error {
return wr.current.add(v)
}
func (wr *msgpWriter) WriteBlob(v []byte, field string) error {
return wr.current.add(v)
}
func (wr *msgpWriter) BeginContainer(field string) {
wr.current = newMsgpWriteContainer()
wr.stack = append(wr.stack, wr.current)
}
func (wr *msgpWriter) EndContainer() error {
if len(wr.stack) <= 1 {
return errors.New("No open container")
} }
last := wr.current
wr.stack[len(wr.stack)-1] = nil
wr.stack = wr.stack[0 : len(wr.stack)-1]
wr.current = wr.stack[len(wr.stack)-1]
bytes, err := msgpack.Marshal(last.arr)
if err != nil {
return err
}
//NOTE: using custom msgpack encoder
wr.current.add(&msgpCustomBytes{bytes})
return nil
} }
func (wr *msgpackWriter) WriteInt8(v int8, field string) error { func (wr *msgpWriter) GetData() ([]byte, error) {
return errors.WithStack(wr.enc.EncodeInt(int64(v))) if len(wr.stack) != 1 {
return nil, errors.New("Stack isn't empty")
}
if len(wr.current.arr) != 1 {
return nil, errors.New("Arr size isn't valid")
}
return msgpack.Marshal(wr.current.arr[0])
} }
func (wr *msgpackWriter) WriteInt16(v int16, field string) error { type msgpCustomBytes struct {
return errors.WithStack(wr.enc.EncodeInt(int64(v))) v []byte
} }
func (wr *msgpackWriter) WriteInt32(v int32, field string) error { func (msgp *msgpCustomBytes) EncodeMsgpack(writer io.Writer) error {
return errors.WithStack(wr.enc.EncodeInt(int64(v))) _, err := writer.Write(msgp.v)
}
func (wr *msgpackWriter) WriteInt64(v int64, field string) error {
return errors.WithStack(wr.enc.EncodeInt(v))
}
func (wr *msgpackWriter) WriteUint8(v uint8, field string) error {
return errors.WithStack(wr.enc.EncodeUint(uint64(v)))
}
func (wr *msgpackWriter) WriteUint16(v uint16, field string) error {
return errors.WithStack(wr.enc.EncodeUint(uint64(v)))
}
func (wr *msgpackWriter) WriteUint32(v uint32, field string) error {
return errors.WithStack(wr.enc.EncodeUint(uint64(v)))
}
func (wr *msgpackWriter) WriteUint64(v uint64, field string) error {
return errors.WithStack(wr.enc.EncodeUint(uint64(v)))
}
func (wr *msgpackWriter) WriteBool(v bool, field string) error {
return errors.WithStack(wr.enc.EncodeBool(v))
}
func (wr *msgpackWriter) WriteFloat32(v float32, field string) error {
return errors.WithStack(wr.enc.EncodeFloat32(v))
}
func (wr *msgpackWriter) WriteFloat64(v float64, field string) error {
return errors.WithStack(wr.enc.EncodeFloat64(v))
}
func (wr *msgpackWriter) WriteString(v string, field string) error {
return errors.WithStack(wr.enc.EncodeString(v))
}
func (wr *msgpackWriter) WriteBytes(v []byte, field string) error {
return errors.WithStack(wr.enc.EncodeBytes(v))
}
func (wr *msgpackWriter) BeginContainer(length int, field string) error {
return wr.BeginCollection(length, field)
}
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 {
return errors.WithStack(err) return errors.WithStack(err)
}
wr.containers = append(wr.containers, struct{}{})
return nil
}
func (wr *msgpackWriter) EndCollection() error {
if len(wr.containers) == 0 {
return errors.New("there is no open containers")
}
wr.containers = wr.containers[:len(wr.containers)-1]
return nil
} }

View File

@ -1,63 +0,0 @@
package meta_test
import (
"bytes"
"encoding/hex"
"testing"
"git.bit5.ru/backend/meta/v5"
"github.com/stretchr/testify/require"
)
func TestMsgpackWriter(t *testing.T) {
t.Run("write struct", func(t *testing.T) {
var buf bytes.Buffer
wr := meta.NewMsgpackWriter(&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 := "94a6626c61626c6191019302040692910a91cd0400"
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.NewMsgpackWriter(&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 := "95a6626c61626c6191019302040692910a91cd0400a6717765727479"
actual := hex.EncodeToString(buf.Bytes())
require.EqualValues(t, expected, actual)
})
}

View File

@ -1,375 +0,0 @@
package meta_test
import (
"git.bit5.ru/backend/meta/v5"
"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 {
Field1 string `json:"f1" msgpack:"f1"`
Field2 TestFoo `json:"f2" msgpack:"f2"`
Field3 []int8 `json:"f3" msgpack:"f3"`
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() {
s.Field1 = ""
s.Field2.Reset()
if s.Field3 == nil {
s.Field3 = make([]int8, 0)
} else {
s.Field3 = s.Field3[:0]
}
if s.Field4 == nil {
s.Field4 = make([]TestFoo, 0)
} else {
s.Field4 = s.Field4[:0]
}
s.Field5 = nil
}
func (s *TestParent) Read(reader meta.Reader) error {
if err := reader.BeginContainer(""); err != nil {
return err
}
if err := s.ReadFields(reader); err != nil {
return err
}
return reader.EndContainer()
}
func (s *TestParent) ReadFields(reader meta.Reader) error {
s.Reset()
contSize, err := reader.ContainerSize()
if err != nil {
return err
}
if contSize < 5 {
contSize = 5
}
if contSize <= 0 {
return nil
}
contSize--
if err := reader.ReadString(&s.Field1, "f1"); err != nil {
return err
}
if contSize <= 0 {
return nil
}
contSize--
if err := reader.BeginContainer("f2"); err != nil {
return err
}
if err := s.Field2.ReadFields(reader); err != nil {
return err
}
if err := reader.EndContainer(); err != nil {
return err
}
if contSize <= 0 {
return nil
}
contSize--
if err := reader.BeginCollection("f3"); err != nil {
return err
}
field3Size, err := reader.ContainerSize()
if err != nil {
return err
}
for ; field3Size > 0; field3Size-- {
var tmpField3 int8
if err := reader.ReadInt8(&tmpField3, ""); err != nil {
return err
}
s.Field3 = append(s.Field3, tmpField3)
}
if err := reader.EndCollection(); err != nil {
return err
}
if contSize <= 0 {
return nil
}
contSize--
if err := reader.BeginCollection("f4"); err != nil {
return err
}
field4Size, err := reader.ContainerSize()
if err != nil {
return err
}
for ; field4Size > 0; field4Size-- {
var tmpField4 TestFoo
if err := reader.BeginContainer(""); err != nil {
return err
}
if err := tmpField4.ReadFields(reader); err != nil {
return err
}
if err := reader.EndContainer(); err != nil {
return err
}
s.Field4 = append(s.Field4, tmpField4)
}
if err := reader.EndCollection(); err != nil {
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
}
func (s *TestParent) Write(writer meta.Writer) error {
if err := writer.BeginContainer(4, ""); err != nil {
return err
}
if err := s.WriteFields(writer); err != nil {
return err
}
return writer.EndContainer()
}
func (s *TestParent) WriteFields(writer meta.Writer) error {
if err := writer.WriteString(s.Field1, "f1"); err != nil {
return err
}
if err := writer.BeginContainer(1, "f2"); err != nil {
return err
}
if err := s.Field2.WriteFields(writer); err != nil {
return err
}
if err := writer.EndContainer(); err != nil {
return err
}
if err := writer.BeginCollection(len(s.Field3), "f3"); err != nil {
return err
}
for _, v := range s.Field3 {
if err := writer.WriteInt8(v, ""); err != nil {
return err
}
}
if err := writer.EndCollection(); err != nil {
return err
}
if err := writer.BeginCollection(len(s.Field4), "f4"); err != nil {
return err
}
for _, v := range s.Field4 {
if err := writer.BeginContainer(1, ""); err != nil {
return err
}
if err := v.WriteFields(writer); err != nil {
return err
}
if err := writer.EndContainer(); err != nil {
return err
}
}
if err := writer.EndCollection(); err != nil {
return err
}
return nil
}
func (s *TestParent) FieldsCount() int {
return 5
}
type TestChild struct {
TestParent
Field string `json:"f" msgpack:"f"`
}
func (s *TestChild) Reset() {
s.TestParent.Reset()
s.Field = ""
}
func (s *TestChild) Read(reader meta.Reader) error {
if err := reader.BeginContainer(""); err != nil {
return err
}
if err := s.ReadFields(reader); err != nil {
return err
}
return reader.EndContainer()
}
func (s *TestChild) ReadFields(reader meta.Reader) error {
s.Reset()
contSize, err := reader.ContainerSize()
if err != nil {
return err
}
if contSize < 1 {
contSize = 1
}
if err := s.TestParent.ReadFields(reader); err != nil {
return err
}
if contSize <= 0 {
return nil
}
contSize--
if err := reader.ReadString(&s.Field, "f"); err != nil {
return err
}
return nil
}
func (s *TestChild) Write(writer meta.Writer) error {
if err := writer.BeginContainer(5, ""); err != nil {
return err
}
if err := s.WriteFields(writer); err != nil {
return err
}
return writer.EndContainer()
}
func (s *TestChild) WriteFields(writer meta.Writer) error {
if err := s.TestParent.WriteFields(writer); err != nil {
return err
}
if err := writer.WriteString(s.Field, "f"); err != nil {
return err
}
return nil
}
type TestFoo struct {
Field int64 `json:"field" msgpack:"field"`
}
func (s *TestFoo) Reset() {
s.Field = 0
}
func (s *TestFoo) Read(reader meta.Reader) error {
if err := reader.BeginContainer(""); err != nil {
return err
}
if err := s.ReadFields(reader); err != nil {
return err
}
return reader.EndContainer()
}
func (s *TestFoo) ReadFields(reader meta.Reader) error {
s.Reset()
contSize, err := reader.ContainerSize()
if err != nil {
return err
}
if contSize < 1 {
contSize = 1
}
if contSize <= 0 {
return nil
}
contSize--
if err := reader.ReadInt64(&s.Field, "field"); err != nil {
return err
}
return nil
}
func (s *TestFoo) Write(writer meta.Writer) error {
if err := writer.BeginContainer(1, ""); err != nil {
return err
}
if err := s.WriteFields(writer); err != nil {
return err
}
return writer.EndContainer()
}
func (s *TestFoo) WriteFields(writer meta.Writer) error {
if err := writer.WriteInt64(s.Field, "field"); err != nil {
return err
}
return nil
}