io: refactor BinReader.ReadArray()

Make it accept arbitrary slice pointer.
This commit is contained in:
Evgenii Stratonikov 2019-11-14 10:50:03 +03:00
parent 2f838df585
commit b16e56a47b
9 changed files with 86 additions and 34 deletions

View file

@ -40,7 +40,7 @@ const uint256size = 32
// DecodeBinary implements io.Serializable interface. // DecodeBinary implements io.Serializable interface.
func (m *recoveryMessage) DecodeBinary(r *io.BinReader) { func (m *recoveryMessage) DecodeBinary(r *io.BinReader) {
m.ChangeViewPayloads = r.ReadArray(changeViewCompact{}).([]*changeViewCompact) r.ReadArray(&m.ChangeViewPayloads)
var hasReq bool var hasReq bool
r.ReadLE(&hasReq) r.ReadLE(&hasReq)
@ -61,8 +61,8 @@ func (m *recoveryMessage) DecodeBinary(r *io.BinReader) {
} }
} }
m.PreparationPayloads = r.ReadArray(preparationCompact{}).([]*preparationCompact) r.ReadArray(&m.PreparationPayloads)
m.CommitPayloads = r.ReadArray(commitCompact{}).([]*commitCompact) r.ReadArray(&m.CommitPayloads)
} }
// EncodeBinary implements io.Serializable interface. // EncodeBinary implements io.Serializable interface.

View file

@ -94,7 +94,7 @@ func (s *AccountState) DecodeBinary(br *io.BinReader) {
br.ReadLE(&s.Version) br.ReadLE(&s.Version)
br.ReadLE(&s.ScriptHash) br.ReadLE(&s.ScriptHash)
br.ReadLE(&s.IsFrozen) br.ReadLE(&s.IsFrozen)
s.Votes = br.ReadArray(keys.PublicKey{}).([]*keys.PublicKey) br.ReadArray(&s.Votes)
s.Balances = make(map[util.Uint256]util.Fixed8) s.Balances = make(map[util.Uint256]util.Fixed8)
lenBalances := br.ReadVarUint() lenBalances := br.ReadVarUint()

View file

@ -128,7 +128,7 @@ func (b *Block) Trim() ([]byte, error) {
// Serializable interface. // Serializable interface.
func (b *Block) DecodeBinary(br *io.BinReader) { func (b *Block) DecodeBinary(br *io.BinReader) {
b.BlockBase.DecodeBinary(br) b.BlockBase.DecodeBinary(br)
b.Transactions = br.ReadArray(transaction.Transaction{}).([]*transaction.Transaction) br.ReadArray(&b.Transactions)
} }
// EncodeBinary encodes the block to the given BinWriter, implementing // EncodeBinary encodes the block to the given BinWriter, implementing

View file

@ -11,7 +11,7 @@ type ClaimTX struct {
// DecodeBinary implements Serializable interface. // DecodeBinary implements Serializable interface.
func (tx *ClaimTX) DecodeBinary(br *io.BinReader) { func (tx *ClaimTX) DecodeBinary(br *io.BinReader) {
tx.Claims = br.ReadArray(Input{}).([]*Input) br.ReadArray(&tx.Claims)
} }
// EncodeBinary implements Serializable interface. // EncodeBinary implements Serializable interface.

View file

@ -11,7 +11,7 @@ type StateTX struct {
// DecodeBinary implements Serializable interface. // DecodeBinary implements Serializable interface.
func (tx *StateTX) DecodeBinary(r *io.BinReader) { func (tx *StateTX) DecodeBinary(r *io.BinReader) {
tx.Descriptors = r.ReadArray(StateDescriptor{}).([]*StateDescriptor) r.ReadArray(&tx.Descriptors)
} }
// EncodeBinary implements Serializable interface. // EncodeBinary implements Serializable interface.

View file

@ -95,10 +95,10 @@ func (t *Transaction) DecodeBinary(br *io.BinReader) {
br.ReadLE(&t.Version) br.ReadLE(&t.Version)
t.decodeData(br) t.decodeData(br)
t.Attributes = br.ReadArray(Attribute{}).([]*Attribute) br.ReadArray(&t.Attributes)
t.Inputs = br.ReadArray(Input{}).([]*Input) br.ReadArray(&t.Inputs)
t.Outputs = br.ReadArray(Output{}).([]*Output) br.ReadArray(&t.Outputs)
t.Scripts = br.ReadArray(Witness{}).([]*Witness) br.ReadArray(&t.Scripts)
// Create the hash of the transaction at decode, so we dont need // Create the hash of the transaction at decode, so we dont need
// to do it anymore. // to do it anymore.

View file

@ -34,29 +34,50 @@ func (r *BinReader) ReadLE(v interface{}) {
r.Err = binary.Read(r.r, binary.LittleEndian, v) r.Err = binary.Read(r.r, binary.LittleEndian, v)
} }
// ReadArray reads a slice or an array of pointer to t from r and returns. // ReadArray reads array into value which must be
func (r *BinReader) ReadArray(t interface{}) interface{} { // a pointer to a slice.
elemType := reflect.ValueOf(t).Type() func (r *BinReader) ReadArray(t interface{}) {
method, ok := reflect.PtrTo(elemType).MethodByName("DecodeBinary") value := reflect.ValueOf(t)
if !ok || !isDecodeBinaryMethod(method) { if value.Kind() != reflect.Ptr || value.Elem().Kind() != reflect.Slice {
panic(elemType.String() + " does not have DecodeBinary(*io.BinReader)") panic(value.Type().String() + " is not a pointer to a slice")
}
sliceType := value.Elem().Type()
elemType := sliceType.Elem()
isPtr := elemType.Kind() == reflect.Ptr
if isPtr {
checkHasDecodeBinary(elemType)
} else {
checkHasDecodeBinary(reflect.PtrTo(elemType))
} }
sliceType := reflect.SliceOf(reflect.PtrTo(elemType))
if r.Err != nil { if r.Err != nil {
return reflect.Zero(sliceType).Interface() return
} }
l := int(r.ReadVarUint()) l := int(r.ReadVarUint())
arr := reflect.MakeSlice(sliceType, l, l) arr := reflect.MakeSlice(sliceType, l, l)
for i := 0; i < l; i++ { for i := 0; i < l; i++ {
elem := arr.Index(i) var elem reflect.Value
if isPtr {
elem = reflect.New(elemType.Elem())
arr.Index(i).Set(elem)
} else {
elem = arr.Index(i).Addr()
}
method := elem.MethodByName("DecodeBinary") method := elem.MethodByName("DecodeBinary")
elem.Set(reflect.New(elemType))
method.Call([]reflect.Value{reflect.ValueOf(r)}) method.Call([]reflect.Value{reflect.ValueOf(r)})
} }
return arr.Interface() value.Elem().Set(arr)
}
func checkHasDecodeBinary(v reflect.Type) {
method, ok := v.MethodByName("DecodeBinary")
if !ok || !isDecodeBinaryMethod(method) {
panic(v.String() + " does not have DecodeBinary(*io.BinReader)")
}
} }
func isDecodeBinaryMethod(method reflect.Method) bool { func isDecodeBinaryMethod(method reflect.Method) bool {

View file

@ -253,22 +253,53 @@ func TestBinWriter_WriteArray(t *testing.T) {
func TestBinReader_ReadArray(t *testing.T) { func TestBinReader_ReadArray(t *testing.T) {
data := []byte{3, 0, 0, 1, 0, 2, 0} data := []byte{3, 0, 0, 1, 0, 2, 0}
r := NewBinReaderFromBuf(data)
result := r.ReadArray(testSerializable(0))
elems := []testSerializable{0, 1, 2} elems := []testSerializable{0, 1, 2}
require.Equal(t, []*testSerializable{&elems[0], &elems[1], &elems[2]}, result)
r := NewBinReaderFromBuf(data)
arrPtr := []*testSerializable{}
r.ReadArray(&arrPtr)
require.Equal(t, []*testSerializable{&elems[0], &elems[1], &elems[2]}, arrPtr)
r = NewBinReaderFromBuf(data) r = NewBinReaderFromBuf(data)
r.Err = errors.New("error") arrVal := []testSerializable{}
result = r.ReadArray(testSerializable(0)) r.ReadArray(&arrVal)
require.Error(t, r.Err)
require.Equal(t, ([]*testSerializable)(nil), result)
r = NewBinReaderFromBuf([]byte{0})
result = r.ReadArray(testSerializable(0))
require.NoError(t, r.Err) require.NoError(t, r.Err)
require.Equal(t, []*testSerializable{}, result) require.Equal(t, elems, arrVal)
r = NewBinReaderFromBuf([]byte{0}) r = NewBinReaderFromBuf([]byte{0})
r.ReadArray(&arrVal)
require.NoError(t, r.Err)
require.Equal(t, []testSerializable{}, arrVal)
r = NewBinReaderFromBuf([]byte{0})
r.Err = errors.New("error")
arrVal = ([]testSerializable)(nil)
r.ReadArray(&arrVal)
require.Error(t, r.Err)
require.Equal(t, ([]testSerializable)(nil), arrVal)
r = NewBinReaderFromBuf([]byte{0})
r.Err = errors.New("error")
arrPtr = ([]*testSerializable)(nil)
r.ReadArray(&arrVal)
require.Error(t, r.Err)
require.Equal(t, ([]*testSerializable)(nil), arrPtr)
r = NewBinReaderFromBuf([]byte{0})
arrVal = []testSerializable{1, 2}
r.ReadArray(&arrVal)
require.NoError(t, r.Err)
require.Equal(t, []testSerializable{}, arrVal)
r = NewBinReaderFromBuf([]byte{0})
r.Err = errors.New("error")
require.Panics(t, func() { r.ReadArray(&[]*int{}) })
r = NewBinReaderFromBuf([]byte{0})
r.Err = errors.New("error")
require.Panics(t, func() { r.ReadArray(&[]int{}) })
r = NewBinReaderFromBuf([]byte{0})
r.Err = errors.New("error")
require.Panics(t, func() { r.ReadArray(0) }) require.Panics(t, func() { r.ReadArray(0) })
} }

View file

@ -67,7 +67,7 @@ func NewAddressList(n int) *AddressList {
// DecodeBinary implements Serializable interface. // DecodeBinary implements Serializable interface.
func (p *AddressList) DecodeBinary(br *io.BinReader) { func (p *AddressList) DecodeBinary(br *io.BinReader) {
p.Addrs = br.ReadArray(AddressAndTime{}).([]*AddressAndTime) br.ReadArray(&p.Addrs)
} }
// EncodeBinary implements Serializable interface. // EncodeBinary implements Serializable interface.