github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/io/binaryReader.go (about)

     1  package io
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"io"
     8  	"reflect"
     9  )
    10  
    11  // MaxArraySize is the maximum size of an array which can be decoded.
    12  // It is taken from https://github.com/neo-project/neo/blob/master/neo/IO/Helper.cs#L130
    13  const MaxArraySize = 0x1000000
    14  
    15  // BinReader is a convenient wrapper around an io.Reader and err object.
    16  // Used to simplify error handling when reading into a struct with many fields.
    17  type BinReader struct {
    18  	r   io.Reader
    19  	uv  [8]byte
    20  	Err error
    21  }
    22  
    23  // NewBinReaderFromIO makes a BinReader from io.Reader.
    24  func NewBinReaderFromIO(ior io.Reader) *BinReader {
    25  	return &BinReader{r: ior}
    26  }
    27  
    28  // NewBinReaderFromBuf makes a BinReader from a byte buffer.
    29  func NewBinReaderFromBuf(b []byte) *BinReader {
    30  	r := bytes.NewReader(b)
    31  	return NewBinReaderFromIO(r)
    32  }
    33  
    34  // Len returns the number of bytes of the unread portion of the buffer if
    35  // reading from bytes.Reader or -1 otherwise.
    36  func (r *BinReader) Len() int {
    37  	var res = -1
    38  	byteReader, ok := r.r.(*bytes.Reader)
    39  	if ok {
    40  		res = byteReader.Len()
    41  	}
    42  	return res
    43  }
    44  
    45  // ReadU64LE reads a little-endian encoded uint64 value from the underlying
    46  // io.Reader. On read failures it returns zero.
    47  func (r *BinReader) ReadU64LE() uint64 {
    48  	r.ReadBytes(r.uv[:8])
    49  	if r.Err != nil {
    50  		return 0
    51  	}
    52  	return binary.LittleEndian.Uint64(r.uv[:8])
    53  }
    54  
    55  // ReadU32LE reads a little-endian encoded uint32 value from the underlying
    56  // io.Reader. On read failures it returns zero.
    57  func (r *BinReader) ReadU32LE() uint32 {
    58  	r.ReadBytes(r.uv[:4])
    59  	if r.Err != nil {
    60  		return 0
    61  	}
    62  	return binary.LittleEndian.Uint32(r.uv[:4])
    63  }
    64  
    65  // ReadU16LE reads a little-endian encoded uint16 value from the underlying
    66  // io.Reader. On read failures it returns zero.
    67  func (r *BinReader) ReadU16LE() uint16 {
    68  	r.ReadBytes(r.uv[:2])
    69  	if r.Err != nil {
    70  		return 0
    71  	}
    72  	return binary.LittleEndian.Uint16(r.uv[:2])
    73  }
    74  
    75  // ReadU16BE reads a big-endian encoded uint16 value from the underlying
    76  // io.Reader. On read failures it returns zero.
    77  func (r *BinReader) ReadU16BE() uint16 {
    78  	r.ReadBytes(r.uv[:2])
    79  	if r.Err != nil {
    80  		return 0
    81  	}
    82  	return binary.BigEndian.Uint16(r.uv[:2])
    83  }
    84  
    85  // ReadB reads a byte from the underlying io.Reader. On read failures it
    86  // returns zero.
    87  func (r *BinReader) ReadB() byte {
    88  	r.ReadBytes(r.uv[:1])
    89  	if r.Err != nil {
    90  		return 0
    91  	}
    92  	return r.uv[0]
    93  }
    94  
    95  // ReadBool reads a boolean value encoded in a zero/non-zero byte from the
    96  // underlying io.Reader. On read failures it returns false.
    97  func (r *BinReader) ReadBool() bool {
    98  	return r.ReadB() != 0
    99  }
   100  
   101  // ReadArray reads an array into a value which must be
   102  // a pointer to a slice.
   103  func (r *BinReader) ReadArray(t any, maxSize ...int) {
   104  	value := reflect.ValueOf(t)
   105  	if value.Kind() != reflect.Ptr || value.Elem().Kind() != reflect.Slice {
   106  		panic(value.Type().String() + " is not a pointer to a slice")
   107  	}
   108  
   109  	if r.Err != nil {
   110  		return
   111  	}
   112  
   113  	sliceType := value.Elem().Type()
   114  	elemType := sliceType.Elem()
   115  	isPtr := elemType.Kind() == reflect.Ptr
   116  
   117  	ms := MaxArraySize
   118  	if len(maxSize) != 0 {
   119  		ms = maxSize[0]
   120  	}
   121  
   122  	lu := r.ReadVarUint()
   123  	if lu > uint64(ms) {
   124  		r.Err = fmt.Errorf("array is too big (%d)", lu)
   125  		return
   126  	}
   127  
   128  	l := int(lu)
   129  	arr := reflect.MakeSlice(sliceType, l, l)
   130  
   131  	for i := 0; i < l; i++ {
   132  		var elem reflect.Value
   133  		if isPtr {
   134  			elem = reflect.New(elemType.Elem())
   135  			arr.Index(i).Set(elem)
   136  		} else {
   137  			elem = arr.Index(i).Addr()
   138  		}
   139  
   140  		el, ok := elem.Interface().(decodable)
   141  		if !ok {
   142  			panic(elemType.String() + "is not decodable")
   143  		}
   144  
   145  		el.DecodeBinary(r)
   146  	}
   147  
   148  	value.Elem().Set(arr)
   149  }
   150  
   151  // ReadVarUint reads a variable-length-encoded integer from the
   152  // underlying reader.
   153  func (r *BinReader) ReadVarUint() uint64 {
   154  	if r.Err != nil {
   155  		return 0
   156  	}
   157  
   158  	var b = r.ReadB()
   159  
   160  	if b == 0xfd {
   161  		return uint64(r.ReadU16LE())
   162  	}
   163  	if b == 0xfe {
   164  		return uint64(r.ReadU32LE())
   165  	}
   166  	if b == 0xff {
   167  		return r.ReadU64LE()
   168  	}
   169  
   170  	return uint64(b)
   171  }
   172  
   173  // ReadVarBytes reads the next set of bytes from the underlying reader.
   174  // ReadVarUInt() is used to determine how large that slice is.
   175  func (r *BinReader) ReadVarBytes(maxSize ...int) []byte {
   176  	n := r.ReadVarUint()
   177  	ms := MaxArraySize
   178  	if len(maxSize) != 0 {
   179  		ms = maxSize[0]
   180  	}
   181  	if n > uint64(ms) {
   182  		r.Err = fmt.Errorf("byte-slice is too big (%d)", n)
   183  		return nil
   184  	}
   185  	b := make([]byte, n)
   186  	r.ReadBytes(b)
   187  	return b
   188  }
   189  
   190  // ReadBytes copies a fixed-size buffer from the reader to the provided slice.
   191  func (r *BinReader) ReadBytes(buf []byte) {
   192  	if r.Err != nil {
   193  		return
   194  	}
   195  
   196  	_, r.Err = io.ReadFull(r.r, buf)
   197  }
   198  
   199  // ReadString calls ReadVarBytes and casts the results as a string.
   200  func (r *BinReader) ReadString(maxSize ...int) string {
   201  	b := r.ReadVarBytes(maxSize...)
   202  	return string(b)
   203  }