github.com/MetalBlockchain/metalgo@v1.11.9/utils/wrappers/packing.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package wrappers 5 6 import ( 7 "encoding/binary" 8 "errors" 9 "math" 10 ) 11 12 const ( 13 MaxStringLen = math.MaxUint16 14 15 // ByteLen is the number of bytes per byte... 16 ByteLen = 1 17 // ShortLen is the number of bytes per short 18 ShortLen = 2 19 // IntLen is the number of bytes per int 20 IntLen = 4 21 // LongLen is the number of bytes per long 22 LongLen = 8 23 // BoolLen is the number of bytes per bool 24 BoolLen = 1 25 ) 26 27 func StringLen(str string) int { 28 // note: there is a max length for string ([MaxStringLen]) 29 // we defer to PackString checking whether str is within limits 30 return ShortLen + len(str) 31 } 32 33 var ( 34 ErrInsufficientLength = errors.New("packer has insufficient length for input") 35 errNegativeOffset = errors.New("negative offset") 36 errInvalidInput = errors.New("input does not match expected format") 37 errBadBool = errors.New("unexpected value when unpacking bool") 38 errOversized = errors.New("size is larger than limit") 39 ) 40 41 // Packer packs and unpacks a byte array from/to standard values 42 type Packer struct { 43 Errs 44 45 // The largest allowed size of expanding the byte array 46 MaxSize int 47 // The current byte array 48 Bytes []byte 49 // The offset that is being written to in the byte array 50 Offset int 51 } 52 53 // PackByte append a byte to the byte array 54 func (p *Packer) PackByte(val byte) { 55 p.expand(ByteLen) 56 if p.Errored() { 57 return 58 } 59 60 p.Bytes[p.Offset] = val 61 p.Offset++ 62 } 63 64 // UnpackByte unpack a byte from the byte array 65 func (p *Packer) UnpackByte() byte { 66 p.checkSpace(ByteLen) 67 if p.Errored() { 68 return 0 69 } 70 71 val := p.Bytes[p.Offset] 72 p.Offset += ByteLen 73 return val 74 } 75 76 // PackShort append a short to the byte array 77 func (p *Packer) PackShort(val uint16) { 78 p.expand(ShortLen) 79 if p.Errored() { 80 return 81 } 82 83 binary.BigEndian.PutUint16(p.Bytes[p.Offset:], val) 84 p.Offset += ShortLen 85 } 86 87 // UnpackShort unpack a short from the byte array 88 func (p *Packer) UnpackShort() uint16 { 89 p.checkSpace(ShortLen) 90 if p.Errored() { 91 return 0 92 } 93 94 val := binary.BigEndian.Uint16(p.Bytes[p.Offset:]) 95 p.Offset += ShortLen 96 return val 97 } 98 99 // PackInt append an int to the byte array 100 func (p *Packer) PackInt(val uint32) { 101 p.expand(IntLen) 102 if p.Errored() { 103 return 104 } 105 106 binary.BigEndian.PutUint32(p.Bytes[p.Offset:], val) 107 p.Offset += IntLen 108 } 109 110 // UnpackInt unpack an int from the byte array 111 func (p *Packer) UnpackInt() uint32 { 112 p.checkSpace(IntLen) 113 if p.Errored() { 114 return 0 115 } 116 117 val := binary.BigEndian.Uint32(p.Bytes[p.Offset:]) 118 p.Offset += IntLen 119 return val 120 } 121 122 // PackLong append a long to the byte array 123 func (p *Packer) PackLong(val uint64) { 124 p.expand(LongLen) 125 if p.Errored() { 126 return 127 } 128 129 binary.BigEndian.PutUint64(p.Bytes[p.Offset:], val) 130 p.Offset += LongLen 131 } 132 133 // UnpackLong unpack a long from the byte array 134 func (p *Packer) UnpackLong() uint64 { 135 p.checkSpace(LongLen) 136 if p.Errored() { 137 return 0 138 } 139 140 val := binary.BigEndian.Uint64(p.Bytes[p.Offset:]) 141 p.Offset += LongLen 142 return val 143 } 144 145 // PackBool packs a bool into the byte array 146 func (p *Packer) PackBool(b bool) { 147 if b { 148 p.PackByte(1) 149 } else { 150 p.PackByte(0) 151 } 152 } 153 154 // UnpackBool unpacks a bool from the byte array 155 func (p *Packer) UnpackBool() bool { 156 b := p.UnpackByte() 157 switch b { 158 case 0: 159 return false 160 case 1: 161 return true 162 default: 163 p.Add(errBadBool) 164 return false 165 } 166 } 167 168 // PackFixedBytes append a byte slice, with no length descriptor to the byte 169 // array 170 func (p *Packer) PackFixedBytes(bytes []byte) { 171 p.expand(len(bytes)) 172 if p.Errored() { 173 return 174 } 175 176 copy(p.Bytes[p.Offset:], bytes) 177 p.Offset += len(bytes) 178 } 179 180 // UnpackFixedBytes unpack a byte slice, with no length descriptor from the byte 181 // array 182 func (p *Packer) UnpackFixedBytes(size int) []byte { 183 p.checkSpace(size) 184 if p.Errored() { 185 return nil 186 } 187 188 bytes := p.Bytes[p.Offset : p.Offset+size] 189 p.Offset += size 190 return bytes 191 } 192 193 // PackBytes append a byte slice to the byte array 194 func (p *Packer) PackBytes(bytes []byte) { 195 p.PackInt(uint32(len(bytes))) 196 p.PackFixedBytes(bytes) 197 } 198 199 // UnpackBytes unpack a byte slice from the byte array 200 func (p *Packer) UnpackBytes() []byte { 201 size := p.UnpackInt() 202 return p.UnpackFixedBytes(int(size)) 203 } 204 205 // UnpackLimitedBytes unpacks a byte slice. If the size of the slice is greater 206 // than [limit], adds [errOversized] to the packer and returns nil. 207 func (p *Packer) UnpackLimitedBytes(limit uint32) []byte { 208 size := p.UnpackInt() 209 if size > limit { 210 p.Add(errOversized) 211 return nil 212 } 213 return p.UnpackFixedBytes(int(size)) 214 } 215 216 // PackStr append a string to the byte array 217 func (p *Packer) PackStr(str string) { 218 strSize := len(str) 219 if strSize > MaxStringLen { 220 p.Add(errInvalidInput) 221 return 222 } 223 p.PackShort(uint16(strSize)) 224 p.PackFixedBytes([]byte(str)) 225 } 226 227 // UnpackStr unpacks a string from the byte array 228 func (p *Packer) UnpackStr() string { 229 strSize := p.UnpackShort() 230 return string(p.UnpackFixedBytes(int(strSize))) 231 } 232 233 // UnpackLimitedStr unpacks a string. If the size of the string is greater than 234 // [limit], adds [errOversized] to the packer and returns the empty string. 235 func (p *Packer) UnpackLimitedStr(limit uint16) string { 236 strSize := p.UnpackShort() 237 if strSize > limit { 238 p.Add(errOversized) 239 return "" 240 } 241 return string(p.UnpackFixedBytes(int(strSize))) 242 } 243 244 // checkSpace requires that there is at least [bytes] of write space left in the 245 // byte array. If this is not true, an error is added to the packer 246 func (p *Packer) checkSpace(bytes int) { 247 switch { 248 case p.Offset < 0: 249 p.Add(errNegativeOffset) 250 case bytes < 0: 251 p.Add(errInvalidInput) 252 case len(p.Bytes)-p.Offset < bytes: 253 p.Add(ErrInsufficientLength) 254 } 255 } 256 257 // expand ensures that there is [bytes] bytes left of space in the byte slice. 258 // If this is not allowed due to the maximum size, an error is added to the packer 259 // In order to understand this code, its important to understand the difference 260 // between a slice's length and its capacity. 261 func (p *Packer) expand(bytes int) { 262 neededSize := bytes + p.Offset // Need byte slice's length to be at least [neededSize] 263 switch { 264 case neededSize <= len(p.Bytes): // Byte slice has sufficient length already 265 return 266 case neededSize > p.MaxSize: // Lengthening the byte slice would cause it to grow too large 267 p.Err = ErrInsufficientLength 268 return 269 case neededSize <= cap(p.Bytes): // Byte slice has sufficient capacity to lengthen it without mem alloc 270 p.Bytes = p.Bytes[:neededSize] 271 return 272 default: // Add capacity/length to byte slice 273 p.Bytes = append(p.Bytes[:cap(p.Bytes)], make([]byte, neededSize-cap(p.Bytes))...) 274 } 275 }