github.com/safing/portbase@v0.19.5/container/container.go (about) 1 package container 2 3 import ( 4 "errors" 5 "io" 6 7 "github.com/safing/portbase/formats/varint" 8 ) 9 10 // Container is []byte sclie on steroids, allowing for quick data appending, prepending and fetching. 11 type Container struct { 12 compartments [][]byte 13 offset int 14 err error 15 } 16 17 // Data Handling 18 19 // NewContainer is DEPRECATED, please use New(), it's the same thing. 20 func NewContainer(data ...[]byte) *Container { 21 return &Container{ 22 compartments: data, 23 } 24 } 25 26 // New creates a new container with an optional initial []byte slice. Data will NOT be copied. 27 func New(data ...[]byte) *Container { 28 return &Container{ 29 compartments: data, 30 } 31 } 32 33 // Prepend prepends data. Data will NOT be copied. 34 func (c *Container) Prepend(data []byte) { 35 if c.offset < 1 { 36 c.renewCompartments() 37 } 38 c.offset-- 39 c.compartments[c.offset] = data 40 } 41 42 // Append appends the given data. Data will NOT be copied. 43 func (c *Container) Append(data []byte) { 44 c.compartments = append(c.compartments, data) 45 } 46 47 // PrependNumber prepends a number (varint encoded). 48 func (c *Container) PrependNumber(n uint64) { 49 c.Prepend(varint.Pack64(n)) 50 } 51 52 // AppendNumber appends a number (varint encoded). 53 func (c *Container) AppendNumber(n uint64) { 54 c.compartments = append(c.compartments, varint.Pack64(n)) 55 } 56 57 // PrependInt prepends an int (varint encoded). 58 func (c *Container) PrependInt(n int) { 59 c.Prepend(varint.Pack64(uint64(n))) 60 } 61 62 // AppendInt appends an int (varint encoded). 63 func (c *Container) AppendInt(n int) { 64 c.compartments = append(c.compartments, varint.Pack64(uint64(n))) 65 } 66 67 // AppendAsBlock appends the length of the data and the data itself. Data will NOT be copied. 68 func (c *Container) AppendAsBlock(data []byte) { 69 c.AppendNumber(uint64(len(data))) 70 c.Append(data) 71 } 72 73 // PrependAsBlock prepends the length of the data and the data itself. Data will NOT be copied. 74 func (c *Container) PrependAsBlock(data []byte) { 75 c.Prepend(data) 76 c.PrependNumber(uint64(len(data))) 77 } 78 79 // AppendContainer appends another Container. Data will NOT be copied. 80 func (c *Container) AppendContainer(data *Container) { 81 c.compartments = append(c.compartments, data.compartments...) 82 } 83 84 // AppendContainerAsBlock appends another Container (length and data). Data will NOT be copied. 85 func (c *Container) AppendContainerAsBlock(data *Container) { 86 c.AppendNumber(uint64(data.Length())) 87 c.compartments = append(c.compartments, data.compartments...) 88 } 89 90 // HoldsData returns true if the Container holds any data. 91 func (c *Container) HoldsData() bool { 92 for i := c.offset; i < len(c.compartments); i++ { 93 if len(c.compartments[i]) > 0 { 94 return true 95 } 96 } 97 return false 98 } 99 100 // Length returns the full length of all bytes held by the container. 101 func (c *Container) Length() (length int) { 102 for i := c.offset; i < len(c.compartments); i++ { 103 length += len(c.compartments[i]) 104 } 105 return 106 } 107 108 // Replace replaces all held data with a new data slice. Data will NOT be copied. 109 func (c *Container) Replace(data []byte) { 110 c.compartments = [][]byte{data} 111 } 112 113 // CompileData concatenates all bytes held by the container and returns it as one single []byte slice. Data will NOT be copied and is NOT consumed. 114 func (c *Container) CompileData() []byte { 115 if len(c.compartments) != 1 { 116 newBuf := make([]byte, c.Length()) 117 copyBuf := newBuf 118 for i := c.offset; i < len(c.compartments); i++ { 119 copy(copyBuf, c.compartments[i]) 120 copyBuf = copyBuf[len(c.compartments[i]):] 121 } 122 c.compartments = [][]byte{newBuf} 123 c.offset = 0 124 } 125 return c.compartments[0] 126 } 127 128 // Get returns the given amount of bytes. Data MAY be copied and IS consumed. 129 func (c *Container) Get(n int) ([]byte, error) { 130 buf := c.Peek(n) 131 if len(buf) < n { 132 return nil, errors.New("container: not enough data to return") 133 } 134 c.skip(len(buf)) 135 return buf, nil 136 } 137 138 // GetAll returns all data. Data MAY be copied and IS consumed. 139 func (c *Container) GetAll() []byte { 140 // TODO: Improve. 141 buf := c.Peek(c.Length()) 142 c.skip(len(buf)) 143 return buf 144 } 145 146 // GetAsContainer returns the given amount of bytes in a new container. Data will NOT be copied and IS consumed. 147 func (c *Container) GetAsContainer(n int) (*Container, error) { 148 newC := c.PeekContainer(n) 149 if newC == nil { 150 return nil, errors.New("container: not enough data to return") 151 } 152 c.skip(n) 153 return newC, nil 154 } 155 156 // GetMax returns as much as possible, but the given amount of bytes at maximum. Data MAY be copied and IS consumed. 157 func (c *Container) GetMax(n int) []byte { 158 buf := c.Peek(n) 159 c.skip(len(buf)) 160 return buf 161 } 162 163 // WriteToSlice copies data to the give slice until it is full, or the container is empty. It returns the bytes written and if the container is now empty. Data IS copied and IS consumed. 164 func (c *Container) WriteToSlice(slice []byte) (n int, containerEmptied bool) { 165 for i := c.offset; i < len(c.compartments); i++ { 166 copy(slice, c.compartments[i]) 167 if len(slice) < len(c.compartments[i]) { 168 // only part was copied 169 n += len(slice) 170 c.compartments[i] = c.compartments[i][len(slice):] 171 c.checkOffset() 172 return n, false 173 } 174 // all was copied 175 n += len(c.compartments[i]) 176 slice = slice[len(c.compartments[i]):] 177 c.compartments[i] = nil 178 c.offset = i + 1 179 } 180 c.checkOffset() 181 return n, true 182 } 183 184 // WriteAllTo writes all the data to the given io.Writer. Data IS NOT copied (but may be by writer) and IS NOT consumed. 185 func (c *Container) WriteAllTo(writer io.Writer) error { 186 for i := c.offset; i < len(c.compartments); i++ { 187 written := 0 188 for written < len(c.compartments[i]) { 189 n, err := writer.Write(c.compartments[i][written:]) 190 if err != nil { 191 return err 192 } 193 written += n 194 } 195 } 196 return nil 197 } 198 199 func (c *Container) clean() { 200 if c.offset > 100 { 201 c.renewCompartments() 202 } 203 } 204 205 func (c *Container) renewCompartments() { 206 baseLength := len(c.compartments) - c.offset + 5 207 newCompartments := make([][]byte, baseLength, baseLength+5) 208 copy(newCompartments[5:], c.compartments[c.offset:]) 209 c.compartments = newCompartments 210 c.offset = 4 211 } 212 213 func (c *Container) carbonCopy() *Container { 214 newC := &Container{ 215 compartments: make([][]byte, len(c.compartments)), 216 offset: c.offset, 217 err: c.err, 218 } 219 copy(newC.compartments, c.compartments) 220 return newC 221 } 222 223 func (c *Container) checkOffset() { 224 if c.offset >= len(c.compartments) { 225 c.offset = len(c.compartments) / 2 226 } 227 } 228 229 // Block Handling 230 231 // PrependLength prepends the current full length of all bytes in the container. 232 func (c *Container) PrependLength() { 233 c.Prepend(varint.Pack64(uint64(c.Length()))) 234 } 235 236 // Peek returns the given amount of bytes. Data MAY be copied and IS NOT consumed. 237 func (c *Container) Peek(n int) []byte { 238 // Check requested length. 239 if n <= 0 { 240 return nil 241 } 242 243 // Check if the first slice holds enough data. 244 if len(c.compartments[c.offset]) >= n { 245 return c.compartments[c.offset][:n] 246 } 247 248 // Start gathering data. 249 slice := make([]byte, n) 250 copySlice := slice 251 n = 0 252 for i := c.offset; i < len(c.compartments); i++ { 253 copy(copySlice, c.compartments[i]) 254 if len(copySlice) <= len(c.compartments[i]) { 255 n += len(copySlice) 256 return slice[:n] 257 } 258 n += len(c.compartments[i]) 259 copySlice = copySlice[len(c.compartments[i]):] 260 } 261 return slice[:n] 262 } 263 264 // PeekContainer returns the given amount of bytes in a new container. Data will NOT be copied and IS NOT consumed. 265 func (c *Container) PeekContainer(n int) (newC *Container) { 266 // Check requested length. 267 if n < 0 { 268 return nil 269 } else if n == 0 { 270 return &Container{} 271 } 272 273 newC = &Container{} 274 for i := c.offset; i < len(c.compartments); i++ { 275 if n >= len(c.compartments[i]) { 276 newC.compartments = append(newC.compartments, c.compartments[i]) 277 n -= len(c.compartments[i]) 278 } else { 279 newC.compartments = append(newC.compartments, c.compartments[i][:n]) 280 n = 0 281 } 282 } 283 if n > 0 { 284 return nil 285 } 286 return newC 287 } 288 289 func (c *Container) skip(n int) { 290 for i := c.offset; i < len(c.compartments); i++ { 291 if len(c.compartments[i]) <= n { 292 n -= len(c.compartments[i]) 293 c.offset = i + 1 294 c.compartments[i] = nil 295 if n == 0 { 296 c.checkOffset() 297 return 298 } 299 } else { 300 c.compartments[i] = c.compartments[i][n:] 301 c.checkOffset() 302 return 303 } 304 } 305 c.checkOffset() 306 } 307 308 // GetNextBlock returns the next block of data defined by a varint. Data MAY be copied and IS consumed. 309 func (c *Container) GetNextBlock() ([]byte, error) { 310 blockSize, err := c.GetNextN64() 311 if err != nil { 312 return nil, err 313 } 314 return c.Get(int(blockSize)) 315 } 316 317 // GetNextBlockAsContainer returns the next block of data as a Container defined by a varint. Data will NOT be copied and IS consumed. 318 func (c *Container) GetNextBlockAsContainer() (*Container, error) { 319 blockSize, err := c.GetNextN64() 320 if err != nil { 321 return nil, err 322 } 323 return c.GetAsContainer(int(blockSize)) 324 } 325 326 // GetNextN8 parses and returns a varint of type uint8. 327 func (c *Container) GetNextN8() (uint8, error) { 328 buf := c.Peek(2) 329 num, n, err := varint.Unpack8(buf) 330 if err != nil { 331 return 0, err 332 } 333 c.skip(n) 334 return num, nil 335 } 336 337 // GetNextN16 parses and returns a varint of type uint16. 338 func (c *Container) GetNextN16() (uint16, error) { 339 buf := c.Peek(3) 340 num, n, err := varint.Unpack16(buf) 341 if err != nil { 342 return 0, err 343 } 344 c.skip(n) 345 return num, nil 346 } 347 348 // GetNextN32 parses and returns a varint of type uint32. 349 func (c *Container) GetNextN32() (uint32, error) { 350 buf := c.Peek(5) 351 num, n, err := varint.Unpack32(buf) 352 if err != nil { 353 return 0, err 354 } 355 c.skip(n) 356 return num, nil 357 } 358 359 // GetNextN64 parses and returns a varint of type uint64. 360 func (c *Container) GetNextN64() (uint64, error) { 361 buf := c.Peek(10) 362 num, n, err := varint.Unpack64(buf) 363 if err != nil { 364 return 0, err 365 } 366 c.skip(n) 367 return num, nil 368 }