github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/io/binaryWriter.go (about) 1 package io 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "io" 7 "reflect" 8 ) 9 10 // BinWriter is a convenient wrapper around an io.Writer and err object. 11 // Used to simplify error handling when writing into an io.Writer 12 // from a struct with many fields. 13 type BinWriter struct { 14 w io.Writer 15 Err error 16 uv [9]byte 17 } 18 19 // NewBinWriterFromIO makes a BinWriter from io.Writer. 20 func NewBinWriterFromIO(iow io.Writer) *BinWriter { 21 return &BinWriter{w: iow} 22 } 23 24 // WriteU64LE writes a uint64 value into the underlying io.Writer in 25 // little-endian format. 26 func (w *BinWriter) WriteU64LE(u64 uint64) { 27 binary.LittleEndian.PutUint64(w.uv[:8], u64) 28 w.WriteBytes(w.uv[:8]) 29 } 30 31 // WriteU32LE writes a uint32 value into the underlying io.Writer in 32 // little-endian format. 33 func (w *BinWriter) WriteU32LE(u32 uint32) { 34 binary.LittleEndian.PutUint32(w.uv[:4], u32) 35 w.WriteBytes(w.uv[:4]) 36 } 37 38 // WriteU16LE writes a uint16 value into the underlying io.Writer in 39 // little-endian format. 40 func (w *BinWriter) WriteU16LE(u16 uint16) { 41 binary.LittleEndian.PutUint16(w.uv[:2], u16) 42 w.WriteBytes(w.uv[:2]) 43 } 44 45 // WriteU16BE writes a uint16 value into the underlying io.Writer in 46 // big-endian format. 47 func (w *BinWriter) WriteU16BE(u16 uint16) { 48 binary.BigEndian.PutUint16(w.uv[:2], u16) 49 w.WriteBytes(w.uv[:2]) 50 } 51 52 // WriteB writes a byte into the underlying io.Writer. 53 func (w *BinWriter) WriteB(u8 byte) { 54 w.uv[0] = u8 55 w.WriteBytes(w.uv[:1]) 56 } 57 58 // WriteBool writes a boolean value into the underlying io.Writer encoded as 59 // a byte with values of 0 or 1. 60 func (w *BinWriter) WriteBool(b bool) { 61 var i byte 62 if b { 63 i = 1 64 } 65 w.WriteB(i) 66 } 67 68 // WriteArray writes a slice or an array arr into w. Note that nil slices and 69 // empty slices are gonna be treated the same resulting in an equal zero-length 70 // array encoded. 71 func (w *BinWriter) WriteArray(arr any) { 72 switch val := reflect.ValueOf(arr); val.Kind() { 73 case reflect.Slice, reflect.Array: 74 if w.Err != nil { 75 return 76 } 77 78 typ := val.Type().Elem() 79 80 w.WriteVarUint(uint64(val.Len())) 81 for i := 0; i < val.Len(); i++ { 82 el, ok := val.Index(i).Interface().(encodable) 83 if !ok { 84 el, ok = val.Index(i).Addr().Interface().(encodable) 85 if !ok { 86 panic(typ.String() + " is not encodable") 87 } 88 } 89 90 el.EncodeBinary(w) 91 } 92 default: 93 panic("not an array") 94 } 95 } 96 97 // WriteVarUint writes a uint64 into the underlying writer using variable-length encoding. 98 func (w *BinWriter) WriteVarUint(val uint64) { 99 if w.Err != nil { 100 return 101 } 102 103 n := PutVarUint(w.uv[:], val) 104 w.WriteBytes(w.uv[:n]) 105 } 106 107 // PutVarUint puts a val in the varint form to the pre-allocated buffer. 108 func PutVarUint(data []byte, val uint64) int { 109 _ = data[8] 110 if val < 0xfd { 111 data[0] = byte(val) 112 return 1 113 } 114 if val < 0xFFFF { 115 data[0] = byte(0xfd) 116 binary.LittleEndian.PutUint16(data[1:], uint16(val)) 117 return 3 118 } 119 if val < 0xFFFFFFFF { 120 data[0] = byte(0xfe) 121 binary.LittleEndian.PutUint32(data[1:], uint32(val)) 122 return 5 123 } 124 125 data[0] = byte(0xff) 126 binary.LittleEndian.PutUint64(data[1:], val) 127 return 9 128 } 129 130 // WriteBytes writes a variable byte into the underlying io.Writer without prefix. 131 func (w *BinWriter) WriteBytes(b []byte) { 132 if w.Err != nil { 133 return 134 } 135 _, w.Err = w.w.Write(b) 136 } 137 138 // WriteVarBytes writes a variable length byte array into the underlying io.Writer. 139 func (w *BinWriter) WriteVarBytes(b []byte) { 140 w.WriteVarUint(uint64(len(b))) 141 w.WriteBytes(b) 142 } 143 144 // WriteString writes a variable length string into the underlying io.Writer. 145 func (w *BinWriter) WriteString(s string) { 146 w.WriteVarUint(uint64(len(s))) 147 if w.Err != nil { 148 return 149 } 150 _, w.Err = io.WriteString(w.w, s) 151 } 152 153 // Grow tries to increase the underlying buffer capacity so that at least n bytes 154 // can be written without reallocation. If the writer is not a buffer, this is a no-op. 155 func (w *BinWriter) Grow(n int) { 156 if b, ok := w.w.(*bytes.Buffer); ok { 157 b.Grow(n) 158 } 159 }