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  }