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 }