Merge pull request #2141 from nspcc-dev/decoding-speedup
Decoding speedup
This commit is contained in:
commit
581132727b
4 changed files with 47 additions and 34 deletions
|
@ -130,7 +130,12 @@ func (t *Transaction) GetAttributes(typ AttrType) []Attribute {
|
||||||
|
|
||||||
// decodeHashableFields decodes the fields that are used for signing the
|
// decodeHashableFields decodes the fields that are used for signing the
|
||||||
// transaction, which are all fields except the scripts.
|
// transaction, which are all fields except the scripts.
|
||||||
func (t *Transaction) decodeHashableFields(br *io.BinReader) {
|
func (t *Transaction) decodeHashableFields(br *io.BinReader, buf []byte) {
|
||||||
|
var start, end int
|
||||||
|
|
||||||
|
if buf != nil {
|
||||||
|
start = len(buf) - br.Len()
|
||||||
|
}
|
||||||
t.Version = uint8(br.ReadB())
|
t.Version = uint8(br.ReadB())
|
||||||
t.Nonce = br.ReadU32LE()
|
t.Nonce = br.ReadU32LE()
|
||||||
t.SystemFee = int64(br.ReadU64LE())
|
t.SystemFee = int64(br.ReadU64LE())
|
||||||
|
@ -164,10 +169,14 @@ func (t *Transaction) decodeHashableFields(br *io.BinReader) {
|
||||||
if br.Err == nil {
|
if br.Err == nil {
|
||||||
br.Err = t.isValid()
|
br.Err = t.isValid()
|
||||||
}
|
}
|
||||||
|
if buf != nil {
|
||||||
|
end = len(buf) - br.Len()
|
||||||
|
t.hash = hash.Sha256(buf[start:end])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transaction) decodeBinaryNoSize(br *io.BinReader) {
|
func (t *Transaction) decodeBinaryNoSize(br *io.BinReader, buf []byte) {
|
||||||
t.decodeHashableFields(br)
|
t.decodeHashableFields(br, buf)
|
||||||
if br.Err != nil {
|
if br.Err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -186,14 +195,14 @@ func (t *Transaction) decodeBinaryNoSize(br *io.BinReader) {
|
||||||
|
|
||||||
// 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.
|
||||||
if br.Err == nil {
|
if br.Err == nil && buf == nil {
|
||||||
br.Err = t.createHash()
|
br.Err = t.createHash()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements Serializable interface.
|
// DecodeBinary implements Serializable interface.
|
||||||
func (t *Transaction) DecodeBinary(br *io.BinReader) {
|
func (t *Transaction) DecodeBinary(br *io.BinReader) {
|
||||||
t.decodeBinaryNoSize(br)
|
t.decodeBinaryNoSize(br, nil)
|
||||||
|
|
||||||
if br.Err == nil {
|
if br.Err == nil {
|
||||||
_ = t.Size()
|
_ = t.Size()
|
||||||
|
@ -258,18 +267,15 @@ func (t *Transaction) createHash() error {
|
||||||
// DecodeHashableFields decodes a part of transaction which should be hashed.
|
// DecodeHashableFields decodes a part of transaction which should be hashed.
|
||||||
func (t *Transaction) DecodeHashableFields(buf []byte) error {
|
func (t *Transaction) DecodeHashableFields(buf []byte) error {
|
||||||
r := io.NewBinReaderFromBuf(buf)
|
r := io.NewBinReaderFromBuf(buf)
|
||||||
t.decodeHashableFields(r)
|
t.decodeHashableFields(r, buf)
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
return r.Err
|
return r.Err
|
||||||
}
|
}
|
||||||
// Ensure all the data was read.
|
// Ensure all the data was read.
|
||||||
_ = r.ReadB()
|
if r.Len() != 0 {
|
||||||
if r.Err == nil {
|
|
||||||
return errors.New("additional data after the signed part")
|
return errors.New("additional data after the signed part")
|
||||||
}
|
}
|
||||||
t.Scripts = make([]Witness, 0)
|
t.Scripts = make([]Witness, 0)
|
||||||
|
|
||||||
t.hash = hash.Sha256(buf)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,12 +293,11 @@ func (t *Transaction) Bytes() []byte {
|
||||||
func NewTransactionFromBytes(b []byte) (*Transaction, error) {
|
func NewTransactionFromBytes(b []byte) (*Transaction, error) {
|
||||||
tx := &Transaction{}
|
tx := &Transaction{}
|
||||||
r := io.NewBinReaderFromBuf(b)
|
r := io.NewBinReaderFromBuf(b)
|
||||||
tx.decodeBinaryNoSize(r)
|
tx.decodeBinaryNoSize(r, b)
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
return nil, r.Err
|
return nil, r.Err
|
||||||
}
|
}
|
||||||
_ = r.ReadB()
|
if r.Len() != 0 {
|
||||||
if r.Err == nil {
|
|
||||||
return nil, errors.New("additional data after the transaction")
|
return nil, errors.New("additional data after the transaction")
|
||||||
}
|
}
|
||||||
tx.size = len(b)
|
tx.size = len(b)
|
||||||
|
|
|
@ -97,6 +97,11 @@ func TestNewTransactionFromBytes(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, tx, tx1)
|
require.Equal(t, tx, tx1)
|
||||||
|
|
||||||
|
tx2 := new(Transaction)
|
||||||
|
err = testserdes.DecodeBinary(data, tx2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tx1, tx2)
|
||||||
|
|
||||||
data = append(data, 42)
|
data = append(data, 42)
|
||||||
_, err = NewTransactionFromBytes(data)
|
_, err = NewTransactionFromBytes(data)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
|
|
@ -16,20 +16,13 @@ const MaxArraySize = 0x1000000
|
||||||
// Used to simplify error handling when reading into a struct with many fields.
|
// Used to simplify error handling when reading into a struct with many fields.
|
||||||
type BinReader struct {
|
type BinReader struct {
|
||||||
r io.Reader
|
r io.Reader
|
||||||
u64 []byte
|
uv [8]byte
|
||||||
u32 []byte
|
|
||||||
u16 []byte
|
|
||||||
u8 []byte
|
|
||||||
Err error
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBinReaderFromIO makes a BinReader from io.Reader.
|
// NewBinReaderFromIO makes a BinReader from io.Reader.
|
||||||
func NewBinReaderFromIO(ior io.Reader) *BinReader {
|
func NewBinReaderFromIO(ior io.Reader) *BinReader {
|
||||||
u64 := make([]byte, 8)
|
return &BinReader{r: ior}
|
||||||
u32 := u64[:4]
|
|
||||||
u16 := u64[:2]
|
|
||||||
u8 := u64[:1]
|
|
||||||
return &BinReader{r: ior, u64: u64, u32: u32, u16: u16, u8: u8}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBinReaderFromBuf makes a BinReader from byte buffer.
|
// NewBinReaderFromBuf makes a BinReader from byte buffer.
|
||||||
|
@ -38,54 +31,65 @@ func NewBinReaderFromBuf(b []byte) *BinReader {
|
||||||
return NewBinReaderFromIO(r)
|
return NewBinReaderFromIO(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Len returns the number of bytes of the unread portion of the buffer if
|
||||||
|
// reading from bytes.Reader or -1 otherwise.
|
||||||
|
func (r *BinReader) Len() int {
|
||||||
|
var res = -1
|
||||||
|
byteReader, ok := r.r.(*bytes.Reader)
|
||||||
|
if ok {
|
||||||
|
res = byteReader.Len()
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
// ReadU64LE reads a little-endian encoded uint64 value from the underlying
|
// ReadU64LE reads a little-endian encoded uint64 value from the underlying
|
||||||
// io.Reader. On read failures it returns zero.
|
// io.Reader. On read failures it returns zero.
|
||||||
func (r *BinReader) ReadU64LE() uint64 {
|
func (r *BinReader) ReadU64LE() uint64 {
|
||||||
r.ReadBytes(r.u64)
|
r.ReadBytes(r.uv[:8])
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return binary.LittleEndian.Uint64(r.u64)
|
return binary.LittleEndian.Uint64(r.uv[:8])
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadU32LE reads a little-endian encoded uint32 value from the underlying
|
// ReadU32LE reads a little-endian encoded uint32 value from the underlying
|
||||||
// io.Reader. On read failures it returns zero.
|
// io.Reader. On read failures it returns zero.
|
||||||
func (r *BinReader) ReadU32LE() uint32 {
|
func (r *BinReader) ReadU32LE() uint32 {
|
||||||
r.ReadBytes(r.u32)
|
r.ReadBytes(r.uv[:4])
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return binary.LittleEndian.Uint32(r.u32)
|
return binary.LittleEndian.Uint32(r.uv[:4])
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadU16LE reads a little-endian encoded uint16 value from the underlying
|
// ReadU16LE reads a little-endian encoded uint16 value from the underlying
|
||||||
// io.Reader. On read failures it returns zero.
|
// io.Reader. On read failures it returns zero.
|
||||||
func (r *BinReader) ReadU16LE() uint16 {
|
func (r *BinReader) ReadU16LE() uint16 {
|
||||||
r.ReadBytes(r.u16)
|
r.ReadBytes(r.uv[:2])
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return binary.LittleEndian.Uint16(r.u16)
|
return binary.LittleEndian.Uint16(r.uv[:2])
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadU16BE reads a big-endian encoded uint16 value from the underlying
|
// ReadU16BE reads a big-endian encoded uint16 value from the underlying
|
||||||
// io.Reader. On read failures it returns zero.
|
// io.Reader. On read failures it returns zero.
|
||||||
func (r *BinReader) ReadU16BE() uint16 {
|
func (r *BinReader) ReadU16BE() uint16 {
|
||||||
r.ReadBytes(r.u16)
|
r.ReadBytes(r.uv[:2])
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return binary.BigEndian.Uint16(r.u16)
|
return binary.BigEndian.Uint16(r.uv[:2])
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadB reads a byte from the underlying io.Reader. On read failures it
|
// ReadB reads a byte from the underlying io.Reader. On read failures it
|
||||||
// returns zero.
|
// returns zero.
|
||||||
func (r *BinReader) ReadB() byte {
|
func (r *BinReader) ReadB() byte {
|
||||||
r.ReadBytes(r.u8)
|
r.ReadBytes(r.uv[:1])
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return r.u8[0]
|
return r.uv[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadBool reads a boolean value encoded in a zero/non-zero byte from the
|
// ReadBool reads a boolean value encoded in a zero/non-zero byte from the
|
||||||
|
|
|
@ -29,8 +29,7 @@ func NewP2PNotaryRequestFromBytes(b []byte) (*P2PNotaryRequest, error) {
|
||||||
if br.Err != nil {
|
if br.Err != nil {
|
||||||
return nil, br.Err
|
return nil, br.Err
|
||||||
}
|
}
|
||||||
_ = br.ReadB()
|
if br.Len() != 0 {
|
||||||
if br.Err == nil {
|
|
||||||
return nil, errors.New("additional data after the payload")
|
return nil, errors.New("additional data after the payload")
|
||||||
}
|
}
|
||||||
return req, nil
|
return req, nil
|
||||||
|
|
Loading…
Reference in a new issue