initial commit
This commit is contained in:
commit
e330a36104
|
@ -0,0 +1,61 @@
|
||||||
|
package meta
|
||||||
|
|
||||||
|
const (
|
||||||
|
FieldsMaskCapacity = 4
|
||||||
|
FieldsMaskItemBitSize = 64
|
||||||
|
)
|
||||||
|
|
||||||
|
func MakeFieldsMaskFromInt64(v int64) FieldsMask {
|
||||||
|
var mask FieldsMask
|
||||||
|
mask.SetItemFromInt64(0, v)
|
||||||
|
|
||||||
|
return mask
|
||||||
|
}
|
||||||
|
|
||||||
|
type FieldsMaskItem uint64
|
||||||
|
|
||||||
|
func (fmi FieldsMaskItem) FieldIsDirty(index uint64) bool {
|
||||||
|
return (1<<index)&fmi != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type FieldsMask struct {
|
||||||
|
masks [FieldsMaskCapacity]FieldsMaskItem
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fm *FieldsMask) SetItemFromUint64(index int, value uint64) {
|
||||||
|
fm.masks[index] = FieldsMaskItem(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fm *FieldsMask) SetItemFromInt64(index int, value int64) {
|
||||||
|
fm.masks[index] = FieldsMaskItem(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fm FieldsMask) FieldChanged(index uint64) bool {
|
||||||
|
itemIndex := fm.itemIndex(index)
|
||||||
|
maskIndex := fm.maskIndex(index)
|
||||||
|
mask := fm.masks[itemIndex]
|
||||||
|
return mask.FieldIsDirty(maskIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fm *FieldsMask) SetFieldChanged(index uint64) {
|
||||||
|
itemIndex := fm.itemIndex(index)
|
||||||
|
maskIndex := fm.maskIndex(index)
|
||||||
|
fm.masks[itemIndex] |= (1 << maskIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fm FieldsMask) IsFilled() bool {
|
||||||
|
for _, mask := range fm.masks {
|
||||||
|
if mask > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fm FieldsMask) itemIndex(index uint64) uint64 {
|
||||||
|
return index / FieldsMaskItemBitSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fm FieldsMask) maskIndex(index uint64) uint64 {
|
||||||
|
return index % FieldsMaskItemBitSize
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
package meta_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.bit5.ru/backend/meta"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFieldsMask(t *testing.T) {
|
||||||
|
t.Run("IsFilled", func(t *testing.T) {
|
||||||
|
t.Run("default value", func(t *testing.T) {
|
||||||
|
var mask meta.FieldsMask
|
||||||
|
|
||||||
|
actualIsFilled := mask.IsFilled()
|
||||||
|
require.False(t, actualIsFilled)
|
||||||
|
})
|
||||||
|
t.Run("filled value", func(t *testing.T) {
|
||||||
|
var mask meta.FieldsMask
|
||||||
|
mask.SetItemFromInt64(0, 1)
|
||||||
|
|
||||||
|
actualIsFilled := mask.IsFilled()
|
||||||
|
require.True(t, actualIsFilled)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("FieldChanged", func(t *testing.T) {
|
||||||
|
t.Run("field not changed", func(t *testing.T) {
|
||||||
|
var mask meta.FieldsMask
|
||||||
|
var fieldIndex uint64 = 4
|
||||||
|
|
||||||
|
fieldChanged := mask.FieldChanged(fieldIndex)
|
||||||
|
require.False(t, fieldChanged)
|
||||||
|
})
|
||||||
|
t.Run("filled changed #1", func(t *testing.T) {
|
||||||
|
var mask meta.FieldsMask
|
||||||
|
var fieldIndex uint64 = 4
|
||||||
|
|
||||||
|
mask.SetItemFromInt64(0, 16)
|
||||||
|
|
||||||
|
fieldChanged := mask.FieldChanged(fieldIndex)
|
||||||
|
require.True(t, fieldChanged)
|
||||||
|
})
|
||||||
|
t.Run("filled changed #2", func(t *testing.T) {
|
||||||
|
var mask meta.FieldsMask
|
||||||
|
var fieldIndex uint64 = 68
|
||||||
|
|
||||||
|
mask.SetItemFromInt64(1, 16)
|
||||||
|
|
||||||
|
fieldChanged := mask.FieldChanged(fieldIndex)
|
||||||
|
require.True(t, fieldChanged)
|
||||||
|
})
|
||||||
|
t.Run("filled changed #3", func(t *testing.T) {
|
||||||
|
var mask meta.FieldsMask
|
||||||
|
var fieldIndex uint64 = 131
|
||||||
|
|
||||||
|
mask.SetItemFromInt64(2, 8)
|
||||||
|
|
||||||
|
fieldChanged := mask.FieldChanged(fieldIndex)
|
||||||
|
require.True(t, fieldChanged)
|
||||||
|
})
|
||||||
|
t.Run("filled changed #4", func(t *testing.T) {
|
||||||
|
var mask meta.FieldsMask
|
||||||
|
var fieldIndex uint64 = 194
|
||||||
|
|
||||||
|
mask.SetItemFromInt64(3, 4)
|
||||||
|
|
||||||
|
fieldChanged := mask.FieldChanged(fieldIndex)
|
||||||
|
require.True(t, fieldChanged)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFieldsMaskItem(t *testing.T) {
|
||||||
|
t.Run("FieldIsDirty", func(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
maskItem meta.FieldsMaskItem
|
||||||
|
expectedDirtyIndexes []uint64
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
maskItem: 0,
|
||||||
|
expectedDirtyIndexes: []uint64{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
maskItem: 1, // 0b0001
|
||||||
|
expectedDirtyIndexes: []uint64{0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
maskItem: 2, // 0b0010
|
||||||
|
expectedDirtyIndexes: []uint64{1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
maskItem: 10, // 0b1010
|
||||||
|
expectedDirtyIndexes: []uint64{1, 3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
maskItem: 11, // 0b1011
|
||||||
|
expectedDirtyIndexes: []uint64{0, 1, 3},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, c := range cases {
|
||||||
|
actualDirtyIndexes := make([]uint64, 0, meta.FieldsMaskItemBitSize)
|
||||||
|
for j := uint64(0); j < meta.FieldsMaskItemBitSize; j++ {
|
||||||
|
if c.maskItem.FieldIsDirty(j) {
|
||||||
|
actualDirtyIndexes = append(actualDirtyIndexes, j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equalf(t, c.expectedDirtyIndexes, actualDirtyIndexes, "case #%d", i)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
module git.bit5.ru/backend/meta
|
||||||
|
|
||||||
|
go 1.18
|
||||||
|
|
||||||
|
require (
|
||||||
|
git.bit5.ru/backend/msgpack v1.0.0
|
||||||
|
github.com/pkg/errors v0.9.1
|
||||||
|
github.com/stretchr/testify v1.7.3
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/vmihailenco/bufio v0.0.0-20140618134113-fe7b595919de // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
|
@ -0,0 +1,29 @@
|
||||||
|
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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
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/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
|
||||||
|
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||||
|
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||||
|
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/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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
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=
|
|
@ -0,0 +1,99 @@
|
||||||
|
package meta
|
||||||
|
|
||||||
|
type Reader interface {
|
||||||
|
ReadI8(v *int8, field string) error
|
||||||
|
ReadU8(v *uint8, field string) error
|
||||||
|
ReadI16(v *int16, field string) error
|
||||||
|
ReadU16(v *uint16, field string) error
|
||||||
|
ReadI32(v *int32, field string) error
|
||||||
|
ReadU32(v *uint32, field string) error
|
||||||
|
ReadI64(v *int64, field string) error
|
||||||
|
ReadU64(v *uint64, field string) error
|
||||||
|
ReadBool(v *bool, field string) error
|
||||||
|
ReadFloat(v *float32, field string) error
|
||||||
|
ReadDouble(v *float64, field string) error
|
||||||
|
ReadString(v *string, field string) error
|
||||||
|
ReadBlob(v *[]byte, field string) error
|
||||||
|
BeginContainer(field string) error
|
||||||
|
EndContainer() error
|
||||||
|
GetContainerSize() (int, error)
|
||||||
|
Skip() error
|
||||||
|
TryReadMask() (bool, FieldsMask, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Writer interface {
|
||||||
|
WriteI8(v int8, field string) error
|
||||||
|
WriteU8(v uint8, field string) error
|
||||||
|
WriteI16(v int16, field string) error
|
||||||
|
WriteU16(v uint16, field string) error
|
||||||
|
WriteI32(v int32, field string) error
|
||||||
|
WriteU32(v uint32, field string) error
|
||||||
|
WriteU64(v uint64, field string) error
|
||||||
|
WriteI64(v int64, field string) error
|
||||||
|
WriteBool(v bool, field string) error
|
||||||
|
WriteFloat(v float32, field string) error
|
||||||
|
WriteDouble(v float64, field string) error
|
||||||
|
WriteString(v string, field string) error
|
||||||
|
WriteBlob(v []byte, field string) error
|
||||||
|
BeginContainer(field string)
|
||||||
|
EndContainer() error
|
||||||
|
GetData() ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClassFieldsProps map[string]map[string]string
|
||||||
|
|
||||||
|
type IClassProps interface {
|
||||||
|
CLASS_ID() uint32
|
||||||
|
CLASS_NAME() string
|
||||||
|
CLASS_PROPS() *map[string]string
|
||||||
|
CLASS_FIELDS() []string
|
||||||
|
CLASS_FIELDS_PROPS() *ClassFieldsProps
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
type IMetaDataItem interface {
|
||||||
|
IClassProps
|
||||||
|
GetDbTableName() string
|
||||||
|
GetDbFields() []string
|
||||||
|
GetOwnerFieldName() string
|
||||||
|
GetIdFieldName() string
|
||||||
|
GetIdValue() uint64
|
||||||
|
Import(interface{})
|
||||||
|
Export([]interface{})
|
||||||
|
NewInstance() IMetaDataItem
|
||||||
|
}
|
||||||
|
|
||||||
|
type IRemovedIds interface {
|
||||||
|
GetList(classId uint32) []uint64
|
||||||
|
Add(classId uint32, id uint64)
|
||||||
|
HasList(classId uint32) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type IBitmasked interface {
|
||||||
|
SetFieldChanged(index uint64)
|
||||||
|
HasValue(index uint64) bool
|
||||||
|
IsMaskFilled() bool
|
||||||
|
GetMask() FieldsMask
|
||||||
|
}
|
||||||
|
|
||||||
|
type RPCFactory func(classId uint32) (IRPC, error)
|
||||||
|
|
||||||
|
type IRPC interface {
|
||||||
|
GetCode() int32
|
||||||
|
GetName() string
|
||||||
|
GetRequest() IMetaStruct
|
||||||
|
GetResponse() IMetaStruct
|
||||||
|
SetError(int32, string)
|
||||||
|
GetError() (int32, string)
|
||||||
|
Execute(interface{}) error
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package meta
|
||||||
|
|
||||||
|
func Read(reader Reader, createById MetaFactory) (v IMetaStruct, err error) {
|
||||||
|
return ReadStructGeneric(reader, createById, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Write(writer Writer, m IMetaStruct) error {
|
||||||
|
return WriteStructGeneric(writer, m, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadStructGeneric(reader Reader, createById MetaFactory, field string) (v IMetaStruct, err error) {
|
||||||
|
if err = reader.BeginContainer(field); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var classId uint32
|
||||||
|
if err = reader.ReadU32(&classId, ""); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if v, err = createById(classId); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = v.ReadFields(reader); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = reader.EndContainer()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = m.ReadFields(reader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return reader.EndContainer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteStruct(writer Writer, m IMetaStruct, field string) error {
|
||||||
|
writer.BeginContainer(field)
|
||||||
|
err := m.WriteFields(writer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return writer.EndContainer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsRPCFailed(rpc IRPC) bool {
|
||||||
|
code, _ := rpc.GetError()
|
||||||
|
return code != 0
|
||||||
|
}
|
|
@ -0,0 +1,412 @@
|
||||||
|
package meta
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"git.bit5.ru/backend/msgpack"
|
||||||
|
)
|
||||||
|
|
||||||
|
type msgpReader struct {
|
||||||
|
stack []*msgpReadState
|
||||||
|
current *msgpReadState
|
||||||
|
}
|
||||||
|
|
||||||
|
type msgpReadState struct {
|
||||||
|
data []interface{}
|
||||||
|
idx int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMsgpackReader(bytes []byte) (Reader, error) {
|
||||||
|
arr := make([]interface{}, 1)
|
||||||
|
err := msgpack.Unmarshal(bytes, &arr[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rd := &msgpReader{}
|
||||||
|
rd.stack = make([]*msgpReadState, 0, 2)
|
||||||
|
rd.current = &msgpReadState{arr, 0}
|
||||||
|
rd.stack = append(rd.stack, rd.current)
|
||||||
|
|
||||||
|
return rd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *msgpReadState) clear() {
|
||||||
|
state.data = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *msgpReadState) read(field string) (v interface{}, err error) {
|
||||||
|
if len(state.data) <= state.idx {
|
||||||
|
err = errors.Errorf("No more data for read, field:%s", field)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v = state.data[state.idx]
|
||||||
|
state.idx++
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *msgpReadState) backCursor() error {
|
||||||
|
if state.data == nil {
|
||||||
|
return errors.New("No more data for read")
|
||||||
|
}
|
||||||
|
state.idx--
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *msgpReadState) skip() error {
|
||||||
|
if state.data == nil || len(state.data) <= state.idx {
|
||||||
|
return errors.New("No more data for read")
|
||||||
|
}
|
||||||
|
state.idx++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *msgpReadState) size() (int, error) {
|
||||||
|
return len(state.data) - state.idx, nil // relative to current idx?
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpReader) ReadI8(v *int8, field string) (err error) {
|
||||||
|
var c int32
|
||||||
|
err = rd.ReadI32(&c, field)
|
||||||
|
*v = int8(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpReader) ReadU8(v *uint8, field string) (err error) {
|
||||||
|
var c uint32
|
||||||
|
err = rd.ReadU32(&c, field)
|
||||||
|
*v = uint8(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpReader) ReadI16(v *int16, field string) (err error) {
|
||||||
|
var c int32
|
||||||
|
err = rd.ReadI32(&c, field)
|
||||||
|
*v = int16(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpReader) ReadU16(v *uint16, field string) (err error) {
|
||||||
|
var c uint32
|
||||||
|
err = rd.ReadU32(&c, field)
|
||||||
|
*v = uint16(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpReader) ReadI32(v *int32, field string) error {
|
||||||
|
value, err := rd.current.read(field)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch t := value.(type) {
|
||||||
|
case float64:
|
||||||
|
*v = int32(t)
|
||||||
|
case int8:
|
||||||
|
*v = int32(t)
|
||||||
|
case uint8:
|
||||||
|
*v = int32(t)
|
||||||
|
case int16:
|
||||||
|
*v = int32(t)
|
||||||
|
case uint16:
|
||||||
|
*v = int32(t)
|
||||||
|
case int32:
|
||||||
|
*v = int32(t)
|
||||||
|
case uint32:
|
||||||
|
*v = int32(t)
|
||||||
|
case int64:
|
||||||
|
*v = int32(t)
|
||||||
|
case uint64:
|
||||||
|
*v = int32(t)
|
||||||
|
case bool:
|
||||||
|
if t {
|
||||||
|
*v = int32(1)
|
||||||
|
} else {
|
||||||
|
*v = int32(0)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errors.Errorf("Can't convert to int32 %v (%T), field:%s", t, t, field)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpReader) ReadU32(v *uint32, field string) error {
|
||||||
|
value, err := rd.current.read(field)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch t := value.(type) {
|
||||||
|
case float64:
|
||||||
|
*v = uint32(t)
|
||||||
|
case int8:
|
||||||
|
*v = uint32(t)
|
||||||
|
case uint8:
|
||||||
|
*v = uint32(t)
|
||||||
|
case int16:
|
||||||
|
*v = uint32(t)
|
||||||
|
case uint16:
|
||||||
|
*v = uint32(t)
|
||||||
|
case int32:
|
||||||
|
*v = uint32(t)
|
||||||
|
case uint32:
|
||||||
|
*v = uint32(t)
|
||||||
|
case uint64:
|
||||||
|
*v = uint32(t)
|
||||||
|
case int64:
|
||||||
|
*v = uint32(t)
|
||||||
|
case bool:
|
||||||
|
if t {
|
||||||
|
*v = uint32(1)
|
||||||
|
} else {
|
||||||
|
*v = uint32(0)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errors.Errorf("Can't convert to uint32 (%T), field:%s", t, field)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpReader) ReadI64(v *int64, field string) error {
|
||||||
|
value, err := rd.current.read(field)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch t := value.(type) {
|
||||||
|
case float64:
|
||||||
|
*v = int64(t)
|
||||||
|
case int8:
|
||||||
|
*v = int64(t)
|
||||||
|
case uint8:
|
||||||
|
*v = int64(t)
|
||||||
|
case int16:
|
||||||
|
*v = int64(t)
|
||||||
|
case uint16:
|
||||||
|
*v = int64(t)
|
||||||
|
case int32:
|
||||||
|
*v = int64(t)
|
||||||
|
case uint32:
|
||||||
|
*v = int64(t)
|
||||||
|
case uint64:
|
||||||
|
*v = int64(t)
|
||||||
|
case int64:
|
||||||
|
*v = int64(t)
|
||||||
|
default:
|
||||||
|
return errors.Errorf("Can't convert to uint64 (%T), field:%s", t, field)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpReader) ReadU64(v *uint64, field string) error {
|
||||||
|
value, err := rd.current.read(field)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch t := value.(type) {
|
||||||
|
case float64:
|
||||||
|
*v = uint64(t)
|
||||||
|
case int8:
|
||||||
|
*v = uint64(t)
|
||||||
|
case uint8:
|
||||||
|
*v = uint64(t)
|
||||||
|
case int16:
|
||||||
|
*v = uint64(t)
|
||||||
|
case uint16:
|
||||||
|
*v = uint64(t)
|
||||||
|
case int32:
|
||||||
|
*v = uint64(t)
|
||||||
|
case uint32:
|
||||||
|
*v = uint64(t)
|
||||||
|
case uint64:
|
||||||
|
*v = uint64(t)
|
||||||
|
case int64:
|
||||||
|
*v = uint64(t)
|
||||||
|
default:
|
||||||
|
return errors.Errorf("Can't convert to uint64 (%T), field:%s", t, field)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpReader) ReadBool(v *bool, field string) (err error) {
|
||||||
|
value, err := rd.current.read(field)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch t := value.(type) {
|
||||||
|
case bool:
|
||||||
|
*v = bool(t)
|
||||||
|
default:
|
||||||
|
return errors.Errorf("Can't convert to bool (%T), field:%s", t, field)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpReader) ReadFloat(v *float32, field string) error {
|
||||||
|
value, err := rd.current.read(field)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch t := value.(type) {
|
||||||
|
case float64:
|
||||||
|
//NOTE: possible type coersion error!
|
||||||
|
*v = float32(t)
|
||||||
|
case float32:
|
||||||
|
*v = t
|
||||||
|
case int64:
|
||||||
|
*v = float32(t)
|
||||||
|
case uint64:
|
||||||
|
*v = float32(t)
|
||||||
|
default:
|
||||||
|
return errors.Errorf("Can't convert to float32 (%T), field:%s", t, field)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpReader) ReadDouble(v *float64, field string) error {
|
||||||
|
value, err := rd.current.read(field)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch t := value.(type) {
|
||||||
|
case float64:
|
||||||
|
*v = t
|
||||||
|
case float32:
|
||||||
|
*v = float64(t)
|
||||||
|
case int64:
|
||||||
|
*v = float64(t)
|
||||||
|
case uint64:
|
||||||
|
*v = float64(t)
|
||||||
|
default:
|
||||||
|
return errors.Errorf("Can't convert to float64 (%T), field:%s", t, field)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpReader) ReadString(v *string, field string) error {
|
||||||
|
value, err := rd.current.read(field)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch t := value.(type) {
|
||||||
|
case string:
|
||||||
|
*v = t
|
||||||
|
|
||||||
|
default:
|
||||||
|
return errors.Errorf("Can't convert to string %s (%T)", field, t)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpReader) ReadBlob(v *[]byte, field string) error {
|
||||||
|
value, err := rd.current.read(field)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch t := value.(type) {
|
||||||
|
case []byte:
|
||||||
|
*v = t
|
||||||
|
|
||||||
|
default:
|
||||||
|
return errors.Errorf("Can't convert to []byte %s (%T)", field, t)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpReader) BeginContainer(field string) error {
|
||||||
|
v, err := rd.current.read(field)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if data, ok := v.([]interface{}); ok {
|
||||||
|
cur := &msgpReadState{}
|
||||||
|
cur.data = data
|
||||||
|
rd.stack = append(rd.stack, cur)
|
||||||
|
rd.current = cur
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return errors.Errorf("Next value isn't array but:%T, field:%s", v, field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpReader) EndContainer() error {
|
||||||
|
rd.current.clear()
|
||||||
|
rd.stack[len(rd.stack)-1] = nil
|
||||||
|
rd.stack = rd.stack[0 : len(rd.stack)-1]
|
||||||
|
if len(rd.stack) > 0 {
|
||||||
|
rd.current = rd.stack[len(rd.stack)-1]
|
||||||
|
} else {
|
||||||
|
rd.current = nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpReader) GetContainerSize() (int, error) {
|
||||||
|
return rd.current.size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpReader) Skip() error {
|
||||||
|
return rd.current.skip()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpReader) TryReadMask() (bool, FieldsMask, error) {
|
||||||
|
value, err := rd.current.read("mask")
|
||||||
|
if err != nil {
|
||||||
|
return false, FieldsMask{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if value == nil { //mask detected
|
||||||
|
newMask, err := rd.ReadNewMask()
|
||||||
|
if err == nil {
|
||||||
|
return true, newMask, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rd.current.backCursor(); err != nil {
|
||||||
|
return false, FieldsMask{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
oldMask, err := rd.ReadOldMask()
|
||||||
|
if err != nil {
|
||||||
|
return false, FieldsMask{}, err
|
||||||
|
}
|
||||||
|
return true, oldMask, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rd.current.backCursor(); err != nil {
|
||||||
|
return false, FieldsMask{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, FieldsMask{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpReader) ReadNewMask() (FieldsMask, error) {
|
||||||
|
if err := rd.BeginContainer("new_mask"); err != nil {
|
||||||
|
return FieldsMask{}, err
|
||||||
|
}
|
||||||
|
maskSize, err := rd.GetContainerSize()
|
||||||
|
if err != nil {
|
||||||
|
return FieldsMask{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var mask FieldsMask
|
||||||
|
for i := 0; i < maskSize; i++ {
|
||||||
|
var maskItem uint64
|
||||||
|
if err := rd.ReadU64(&maskItem, "mask_item"); err != nil {
|
||||||
|
return FieldsMask{}, err
|
||||||
|
}
|
||||||
|
mask.SetItemFromUint64(i, maskItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rd.EndContainer(); err != nil {
|
||||||
|
return FieldsMask{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return mask, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *msgpReader) ReadOldMask() (FieldsMask, error) {
|
||||||
|
var mask int64
|
||||||
|
if err := rd.ReadI64(&mask, "mask"); err != nil {
|
||||||
|
return FieldsMask{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return MakeFieldsMaskFromInt64(mask), nil
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
package meta
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"git.bit5.ru/backend/msgpack"
|
||||||
|
)
|
||||||
|
|
||||||
|
type msgpWriter struct {
|
||||||
|
stack []*msgpWriteContainer
|
||||||
|
current *msgpWriteContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
type msgpWriteContainer struct {
|
||||||
|
arr []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 *msgpWriter) GetData() ([]byte, error) {
|
||||||
|
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])
|
||||||
|
}
|
||||||
|
|
||||||
|
type msgpCustomBytes struct {
|
||||||
|
v []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msgp *msgpCustomBytes) EncodeMsgpack(writer io.Writer) error {
|
||||||
|
_, err := writer.Write(msgp.v)
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
Loading…
Reference in New Issue