github.com/andybalholm/brotli@v1.0.6/ringbuffer.go (about)

     1  package brotli
     2  
     3  /* Copyright 2013 Google Inc. All Rights Reserved.
     4  
     5     Distributed under MIT license.
     6     See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
     7  */
     8  
     9  /* A ringBuffer(window_bits, tail_bits) contains `1 << window_bits' bytes of
    10     data in a circular manner: writing a byte writes it to:
    11       `position() % (1 << window_bits)'.
    12     For convenience, the ringBuffer array contains another copy of the
    13     first `1 << tail_bits' bytes:
    14       buffer_[i] == buffer_[i + (1 << window_bits)], if i < (1 << tail_bits),
    15     and another copy of the last two bytes:
    16       buffer_[-1] == buffer_[(1 << window_bits) - 1] and
    17       buffer_[-2] == buffer_[(1 << window_bits) - 2]. */
    18  type ringBuffer struct {
    19  	size_       uint32
    20  	mask_       uint32
    21  	tail_size_  uint32
    22  	total_size_ uint32
    23  	cur_size_   uint32
    24  	pos_        uint32
    25  	data_       []byte
    26  	buffer_     []byte
    27  }
    28  
    29  func ringBufferInit(rb *ringBuffer) {
    30  	rb.pos_ = 0
    31  }
    32  
    33  func ringBufferSetup(params *encoderParams, rb *ringBuffer) {
    34  	var window_bits int = computeRbBits(params)
    35  	var tail_bits int = params.lgblock
    36  	*(*uint32)(&rb.size_) = 1 << uint(window_bits)
    37  	*(*uint32)(&rb.mask_) = (1 << uint(window_bits)) - 1
    38  	*(*uint32)(&rb.tail_size_) = 1 << uint(tail_bits)
    39  	*(*uint32)(&rb.total_size_) = rb.size_ + rb.tail_size_
    40  }
    41  
    42  const kSlackForEightByteHashingEverywhere uint = 7
    43  
    44  /* Allocates or re-allocates data_ to the given length + plus some slack
    45     region before and after. Fills the slack regions with zeros. */
    46  func ringBufferInitBuffer(buflen uint32, rb *ringBuffer) {
    47  	var new_data []byte
    48  	var i uint
    49  	size := 2 + int(buflen) + int(kSlackForEightByteHashingEverywhere)
    50  	if cap(rb.data_) < size {
    51  		new_data = make([]byte, size)
    52  	} else {
    53  		new_data = rb.data_[:size]
    54  	}
    55  	if rb.data_ != nil {
    56  		copy(new_data, rb.data_[:2+rb.cur_size_+uint32(kSlackForEightByteHashingEverywhere)])
    57  	}
    58  
    59  	rb.data_ = new_data
    60  	rb.cur_size_ = buflen
    61  	rb.buffer_ = rb.data_[2:]
    62  	rb.data_[1] = 0
    63  	rb.data_[0] = rb.data_[1]
    64  	for i = 0; i < kSlackForEightByteHashingEverywhere; i++ {
    65  		rb.buffer_[rb.cur_size_+uint32(i)] = 0
    66  	}
    67  }
    68  
    69  func ringBufferWriteTail(bytes []byte, n uint, rb *ringBuffer) {
    70  	var masked_pos uint = uint(rb.pos_ & rb.mask_)
    71  	if uint32(masked_pos) < rb.tail_size_ {
    72  		/* Just fill the tail buffer with the beginning data. */
    73  		var p uint = uint(rb.size_ + uint32(masked_pos))
    74  		copy(rb.buffer_[p:], bytes[:brotli_min_size_t(n, uint(rb.tail_size_-uint32(masked_pos)))])
    75  	}
    76  }
    77  
    78  /* Push bytes into the ring buffer. */
    79  func ringBufferWrite(bytes []byte, n uint, rb *ringBuffer) {
    80  	if rb.pos_ == 0 && uint32(n) < rb.tail_size_ {
    81  		/* Special case for the first write: to process the first block, we don't
    82  		   need to allocate the whole ring-buffer and we don't need the tail
    83  		   either. However, we do this memory usage optimization only if the
    84  		   first write is less than the tail size, which is also the input block
    85  		   size, otherwise it is likely that other blocks will follow and we
    86  		   will need to reallocate to the full size anyway. */
    87  		rb.pos_ = uint32(n)
    88  
    89  		ringBufferInitBuffer(rb.pos_, rb)
    90  		copy(rb.buffer_, bytes[:n])
    91  		return
    92  	}
    93  
    94  	if rb.cur_size_ < rb.total_size_ {
    95  		/* Lazily allocate the full buffer. */
    96  		ringBufferInitBuffer(rb.total_size_, rb)
    97  
    98  		/* Initialize the last two bytes to zero, so that we don't have to worry
    99  		   later when we copy the last two bytes to the first two positions. */
   100  		rb.buffer_[rb.size_-2] = 0
   101  
   102  		rb.buffer_[rb.size_-1] = 0
   103  	}
   104  	{
   105  		var masked_pos uint = uint(rb.pos_ & rb.mask_)
   106  
   107  		/* The length of the writes is limited so that we do not need to worry
   108  		   about a write */
   109  		ringBufferWriteTail(bytes, n, rb)
   110  
   111  		if uint32(masked_pos+n) <= rb.size_ {
   112  			/* A single write fits. */
   113  			copy(rb.buffer_[masked_pos:], bytes[:n])
   114  		} else {
   115  			/* Split into two writes.
   116  			   Copy into the end of the buffer, including the tail buffer. */
   117  			copy(rb.buffer_[masked_pos:], bytes[:brotli_min_size_t(n, uint(rb.total_size_-uint32(masked_pos)))])
   118  
   119  			/* Copy into the beginning of the buffer */
   120  			copy(rb.buffer_, bytes[rb.size_-uint32(masked_pos):][:uint32(n)-(rb.size_-uint32(masked_pos))])
   121  		}
   122  	}
   123  	{
   124  		var not_first_lap bool = rb.pos_&(1<<31) != 0
   125  		var rb_pos_mask uint32 = (1 << 31) - 1
   126  		rb.data_[0] = rb.buffer_[rb.size_-2]
   127  		rb.data_[1] = rb.buffer_[rb.size_-1]
   128  		rb.pos_ = (rb.pos_ & rb_pos_mask) + uint32(uint32(n)&rb_pos_mask)
   129  		if not_first_lap {
   130  			/* Wrap, but preserve not-a-first-lap feature. */
   131  			rb.pos_ |= 1 << 31
   132  		}
   133  	}
   134  }