xref: /aosp_15_r20/external/brotli/c/enc/ringbuffer.h (revision f4ee7fba7774faf2a30f13154332c0a06550dbc4)
1*f4ee7fbaSAndroid Build Coastguard Worker /* Copyright 2013 Google Inc. All Rights Reserved.
2*f4ee7fbaSAndroid Build Coastguard Worker 
3*f4ee7fbaSAndroid Build Coastguard Worker    Distributed under MIT license.
4*f4ee7fbaSAndroid Build Coastguard Worker    See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
5*f4ee7fbaSAndroid Build Coastguard Worker */
6*f4ee7fbaSAndroid Build Coastguard Worker 
7*f4ee7fbaSAndroid Build Coastguard Worker /* Sliding window over the input data. */
8*f4ee7fbaSAndroid Build Coastguard Worker 
9*f4ee7fbaSAndroid Build Coastguard Worker #ifndef BROTLI_ENC_RINGBUFFER_H_
10*f4ee7fbaSAndroid Build Coastguard Worker #define BROTLI_ENC_RINGBUFFER_H_
11*f4ee7fbaSAndroid Build Coastguard Worker 
12*f4ee7fbaSAndroid Build Coastguard Worker #include <string.h>  /* memcpy */
13*f4ee7fbaSAndroid Build Coastguard Worker 
14*f4ee7fbaSAndroid Build Coastguard Worker #include "../common/platform.h"
15*f4ee7fbaSAndroid Build Coastguard Worker #include <brotli/types.h>
16*f4ee7fbaSAndroid Build Coastguard Worker #include "./memory.h"
17*f4ee7fbaSAndroid Build Coastguard Worker #include "./quality.h"
18*f4ee7fbaSAndroid Build Coastguard Worker 
19*f4ee7fbaSAndroid Build Coastguard Worker #if defined(__cplusplus) || defined(c_plusplus)
20*f4ee7fbaSAndroid Build Coastguard Worker extern "C" {
21*f4ee7fbaSAndroid Build Coastguard Worker #endif
22*f4ee7fbaSAndroid Build Coastguard Worker 
23*f4ee7fbaSAndroid Build Coastguard Worker /* A RingBuffer(window_bits, tail_bits) contains `1 << window_bits' bytes of
24*f4ee7fbaSAndroid Build Coastguard Worker    data in a circular manner: writing a byte writes it to:
25*f4ee7fbaSAndroid Build Coastguard Worker      `position() % (1 << window_bits)'.
26*f4ee7fbaSAndroid Build Coastguard Worker    For convenience, the RingBuffer array contains another copy of the
27*f4ee7fbaSAndroid Build Coastguard Worker    first `1 << tail_bits' bytes:
28*f4ee7fbaSAndroid Build Coastguard Worker      buffer_[i] == buffer_[i + (1 << window_bits)], if i < (1 << tail_bits),
29*f4ee7fbaSAndroid Build Coastguard Worker    and another copy of the last two bytes:
30*f4ee7fbaSAndroid Build Coastguard Worker      buffer_[-1] == buffer_[(1 << window_bits) - 1] and
31*f4ee7fbaSAndroid Build Coastguard Worker      buffer_[-2] == buffer_[(1 << window_bits) - 2]. */
32*f4ee7fbaSAndroid Build Coastguard Worker typedef struct RingBuffer {
33*f4ee7fbaSAndroid Build Coastguard Worker   /* Size of the ring-buffer is (1 << window_bits) + tail_size_. */
34*f4ee7fbaSAndroid Build Coastguard Worker   const uint32_t size_;
35*f4ee7fbaSAndroid Build Coastguard Worker   const uint32_t mask_;
36*f4ee7fbaSAndroid Build Coastguard Worker   const uint32_t tail_size_;
37*f4ee7fbaSAndroid Build Coastguard Worker   const uint32_t total_size_;
38*f4ee7fbaSAndroid Build Coastguard Worker 
39*f4ee7fbaSAndroid Build Coastguard Worker   uint32_t cur_size_;
40*f4ee7fbaSAndroid Build Coastguard Worker   /* Position to write in the ring buffer. */
41*f4ee7fbaSAndroid Build Coastguard Worker   uint32_t pos_;
42*f4ee7fbaSAndroid Build Coastguard Worker   /* The actual ring buffer containing the copy of the last two bytes, the data,
43*f4ee7fbaSAndroid Build Coastguard Worker      and the copy of the beginning as a tail. */
44*f4ee7fbaSAndroid Build Coastguard Worker   uint8_t* data_;
45*f4ee7fbaSAndroid Build Coastguard Worker   /* The start of the ring-buffer. */
46*f4ee7fbaSAndroid Build Coastguard Worker   uint8_t* buffer_;
47*f4ee7fbaSAndroid Build Coastguard Worker } RingBuffer;
48*f4ee7fbaSAndroid Build Coastguard Worker 
RingBufferInit(RingBuffer * rb)49*f4ee7fbaSAndroid Build Coastguard Worker static BROTLI_INLINE void RingBufferInit(RingBuffer* rb) {
50*f4ee7fbaSAndroid Build Coastguard Worker   rb->cur_size_ = 0;
51*f4ee7fbaSAndroid Build Coastguard Worker   rb->pos_ = 0;
52*f4ee7fbaSAndroid Build Coastguard Worker   rb->data_ = 0;
53*f4ee7fbaSAndroid Build Coastguard Worker   rb->buffer_ = 0;
54*f4ee7fbaSAndroid Build Coastguard Worker }
55*f4ee7fbaSAndroid Build Coastguard Worker 
RingBufferSetup(const BrotliEncoderParams * params,RingBuffer * rb)56*f4ee7fbaSAndroid Build Coastguard Worker static BROTLI_INLINE void RingBufferSetup(
57*f4ee7fbaSAndroid Build Coastguard Worker     const BrotliEncoderParams* params, RingBuffer* rb) {
58*f4ee7fbaSAndroid Build Coastguard Worker   int window_bits = ComputeRbBits(params);
59*f4ee7fbaSAndroid Build Coastguard Worker   int tail_bits = params->lgblock;
60*f4ee7fbaSAndroid Build Coastguard Worker   *(uint32_t*)&rb->size_ = 1u << window_bits;
61*f4ee7fbaSAndroid Build Coastguard Worker   *(uint32_t*)&rb->mask_ = (1u << window_bits) - 1;
62*f4ee7fbaSAndroid Build Coastguard Worker   *(uint32_t*)&rb->tail_size_ = 1u << tail_bits;
63*f4ee7fbaSAndroid Build Coastguard Worker   *(uint32_t*)&rb->total_size_ = rb->size_ + rb->tail_size_;
64*f4ee7fbaSAndroid Build Coastguard Worker }
65*f4ee7fbaSAndroid Build Coastguard Worker 
RingBufferFree(MemoryManager * m,RingBuffer * rb)66*f4ee7fbaSAndroid Build Coastguard Worker static BROTLI_INLINE void RingBufferFree(MemoryManager* m, RingBuffer* rb) {
67*f4ee7fbaSAndroid Build Coastguard Worker   BROTLI_FREE(m, rb->data_);
68*f4ee7fbaSAndroid Build Coastguard Worker }
69*f4ee7fbaSAndroid Build Coastguard Worker 
70*f4ee7fbaSAndroid Build Coastguard Worker /* Allocates or re-allocates data_ to the given length + plus some slack
71*f4ee7fbaSAndroid Build Coastguard Worker    region before and after. Fills the slack regions with zeros. */
RingBufferInitBuffer(MemoryManager * m,const uint32_t buflen,RingBuffer * rb)72*f4ee7fbaSAndroid Build Coastguard Worker static BROTLI_INLINE void RingBufferInitBuffer(
73*f4ee7fbaSAndroid Build Coastguard Worker     MemoryManager* m, const uint32_t buflen, RingBuffer* rb) {
74*f4ee7fbaSAndroid Build Coastguard Worker   static const size_t kSlackForEightByteHashingEverywhere = 7;
75*f4ee7fbaSAndroid Build Coastguard Worker   uint8_t* new_data = BROTLI_ALLOC(
76*f4ee7fbaSAndroid Build Coastguard Worker       m, uint8_t, 2 + buflen + kSlackForEightByteHashingEverywhere);
77*f4ee7fbaSAndroid Build Coastguard Worker   size_t i;
78*f4ee7fbaSAndroid Build Coastguard Worker   if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(new_data)) return;
79*f4ee7fbaSAndroid Build Coastguard Worker   if (rb->data_) {
80*f4ee7fbaSAndroid Build Coastguard Worker     memcpy(new_data, rb->data_,
81*f4ee7fbaSAndroid Build Coastguard Worker         2 + rb->cur_size_ + kSlackForEightByteHashingEverywhere);
82*f4ee7fbaSAndroid Build Coastguard Worker     BROTLI_FREE(m, rb->data_);
83*f4ee7fbaSAndroid Build Coastguard Worker   }
84*f4ee7fbaSAndroid Build Coastguard Worker   rb->data_ = new_data;
85*f4ee7fbaSAndroid Build Coastguard Worker   rb->cur_size_ = buflen;
86*f4ee7fbaSAndroid Build Coastguard Worker   rb->buffer_ = rb->data_ + 2;
87*f4ee7fbaSAndroid Build Coastguard Worker   rb->buffer_[-2] = rb->buffer_[-1] = 0;
88*f4ee7fbaSAndroid Build Coastguard Worker   for (i = 0; i < kSlackForEightByteHashingEverywhere; ++i) {
89*f4ee7fbaSAndroid Build Coastguard Worker     rb->buffer_[rb->cur_size_ + i] = 0;
90*f4ee7fbaSAndroid Build Coastguard Worker   }
91*f4ee7fbaSAndroid Build Coastguard Worker }
92*f4ee7fbaSAndroid Build Coastguard Worker 
RingBufferWriteTail(const uint8_t * bytes,size_t n,RingBuffer * rb)93*f4ee7fbaSAndroid Build Coastguard Worker static BROTLI_INLINE void RingBufferWriteTail(
94*f4ee7fbaSAndroid Build Coastguard Worker     const uint8_t* bytes, size_t n, RingBuffer* rb) {
95*f4ee7fbaSAndroid Build Coastguard Worker   const size_t masked_pos = rb->pos_ & rb->mask_;
96*f4ee7fbaSAndroid Build Coastguard Worker   if (BROTLI_PREDICT_FALSE(masked_pos < rb->tail_size_)) {
97*f4ee7fbaSAndroid Build Coastguard Worker     /* Just fill the tail buffer with the beginning data. */
98*f4ee7fbaSAndroid Build Coastguard Worker     const size_t p = rb->size_ + masked_pos;
99*f4ee7fbaSAndroid Build Coastguard Worker     memcpy(&rb->buffer_[p], bytes,
100*f4ee7fbaSAndroid Build Coastguard Worker         BROTLI_MIN(size_t, n, rb->tail_size_ - masked_pos));
101*f4ee7fbaSAndroid Build Coastguard Worker   }
102*f4ee7fbaSAndroid Build Coastguard Worker }
103*f4ee7fbaSAndroid Build Coastguard Worker 
104*f4ee7fbaSAndroid Build Coastguard Worker /* Push bytes into the ring buffer. */
RingBufferWrite(MemoryManager * m,const uint8_t * bytes,size_t n,RingBuffer * rb)105*f4ee7fbaSAndroid Build Coastguard Worker static BROTLI_INLINE void RingBufferWrite(
106*f4ee7fbaSAndroid Build Coastguard Worker     MemoryManager* m, const uint8_t* bytes, size_t n, RingBuffer* rb) {
107*f4ee7fbaSAndroid Build Coastguard Worker   if (rb->pos_ == 0 && n < rb->tail_size_) {
108*f4ee7fbaSAndroid Build Coastguard Worker     /* Special case for the first write: to process the first block, we don't
109*f4ee7fbaSAndroid Build Coastguard Worker        need to allocate the whole ring-buffer and we don't need the tail
110*f4ee7fbaSAndroid Build Coastguard Worker        either. However, we do this memory usage optimization only if the
111*f4ee7fbaSAndroid Build Coastguard Worker        first write is less than the tail size, which is also the input block
112*f4ee7fbaSAndroid Build Coastguard Worker        size, otherwise it is likely that other blocks will follow and we
113*f4ee7fbaSAndroid Build Coastguard Worker        will need to reallocate to the full size anyway. */
114*f4ee7fbaSAndroid Build Coastguard Worker     rb->pos_ = (uint32_t)n;
115*f4ee7fbaSAndroid Build Coastguard Worker     RingBufferInitBuffer(m, rb->pos_, rb);
116*f4ee7fbaSAndroid Build Coastguard Worker     if (BROTLI_IS_OOM(m)) return;
117*f4ee7fbaSAndroid Build Coastguard Worker     memcpy(rb->buffer_, bytes, n);
118*f4ee7fbaSAndroid Build Coastguard Worker     return;
119*f4ee7fbaSAndroid Build Coastguard Worker   }
120*f4ee7fbaSAndroid Build Coastguard Worker   if (rb->cur_size_ < rb->total_size_) {
121*f4ee7fbaSAndroid Build Coastguard Worker     /* Lazily allocate the full buffer. */
122*f4ee7fbaSAndroid Build Coastguard Worker     RingBufferInitBuffer(m, rb->total_size_, rb);
123*f4ee7fbaSAndroid Build Coastguard Worker     if (BROTLI_IS_OOM(m)) return;
124*f4ee7fbaSAndroid Build Coastguard Worker     /* Initialize the last two bytes to zero, so that we don't have to worry
125*f4ee7fbaSAndroid Build Coastguard Worker        later when we copy the last two bytes to the first two positions. */
126*f4ee7fbaSAndroid Build Coastguard Worker     rb->buffer_[rb->size_ - 2] = 0;
127*f4ee7fbaSAndroid Build Coastguard Worker     rb->buffer_[rb->size_ - 1] = 0;
128*f4ee7fbaSAndroid Build Coastguard Worker     /* Initialize tail; might be touched by "best_len++" optimization when
129*f4ee7fbaSAndroid Build Coastguard Worker        ring buffer is "full". */
130*f4ee7fbaSAndroid Build Coastguard Worker     rb->buffer_[rb->size_] = 241;
131*f4ee7fbaSAndroid Build Coastguard Worker   }
132*f4ee7fbaSAndroid Build Coastguard Worker   {
133*f4ee7fbaSAndroid Build Coastguard Worker     const size_t masked_pos = rb->pos_ & rb->mask_;
134*f4ee7fbaSAndroid Build Coastguard Worker     /* The length of the writes is limited so that we do not need to worry
135*f4ee7fbaSAndroid Build Coastguard Worker        about a write */
136*f4ee7fbaSAndroid Build Coastguard Worker     RingBufferWriteTail(bytes, n, rb);
137*f4ee7fbaSAndroid Build Coastguard Worker     if (BROTLI_PREDICT_TRUE(masked_pos + n <= rb->size_)) {
138*f4ee7fbaSAndroid Build Coastguard Worker       /* A single write fits. */
139*f4ee7fbaSAndroid Build Coastguard Worker       memcpy(&rb->buffer_[masked_pos], bytes, n);
140*f4ee7fbaSAndroid Build Coastguard Worker     } else {
141*f4ee7fbaSAndroid Build Coastguard Worker       /* Split into two writes.
142*f4ee7fbaSAndroid Build Coastguard Worker          Copy into the end of the buffer, including the tail buffer. */
143*f4ee7fbaSAndroid Build Coastguard Worker       memcpy(&rb->buffer_[masked_pos], bytes,
144*f4ee7fbaSAndroid Build Coastguard Worker              BROTLI_MIN(size_t, n, rb->total_size_ - masked_pos));
145*f4ee7fbaSAndroid Build Coastguard Worker       /* Copy into the beginning of the buffer */
146*f4ee7fbaSAndroid Build Coastguard Worker       memcpy(&rb->buffer_[0], bytes + (rb->size_ - masked_pos),
147*f4ee7fbaSAndroid Build Coastguard Worker              n - (rb->size_ - masked_pos));
148*f4ee7fbaSAndroid Build Coastguard Worker     }
149*f4ee7fbaSAndroid Build Coastguard Worker   }
150*f4ee7fbaSAndroid Build Coastguard Worker   {
151*f4ee7fbaSAndroid Build Coastguard Worker     BROTLI_BOOL not_first_lap = (rb->pos_ & (1u << 31)) != 0;
152*f4ee7fbaSAndroid Build Coastguard Worker     uint32_t rb_pos_mask = (1u << 31) - 1;
153*f4ee7fbaSAndroid Build Coastguard Worker     rb->buffer_[-2] = rb->buffer_[rb->size_ - 2];
154*f4ee7fbaSAndroid Build Coastguard Worker     rb->buffer_[-1] = rb->buffer_[rb->size_ - 1];
155*f4ee7fbaSAndroid Build Coastguard Worker     rb->pos_ = (rb->pos_ & rb_pos_mask) + (uint32_t)(n & rb_pos_mask);
156*f4ee7fbaSAndroid Build Coastguard Worker     if (not_first_lap) {
157*f4ee7fbaSAndroid Build Coastguard Worker       /* Wrap, but preserve not-a-first-lap feature. */
158*f4ee7fbaSAndroid Build Coastguard Worker       rb->pos_ |= 1u << 31;
159*f4ee7fbaSAndroid Build Coastguard Worker     }
160*f4ee7fbaSAndroid Build Coastguard Worker   }
161*f4ee7fbaSAndroid Build Coastguard Worker }
162*f4ee7fbaSAndroid Build Coastguard Worker 
163*f4ee7fbaSAndroid Build Coastguard Worker #if defined(__cplusplus) || defined(c_plusplus)
164*f4ee7fbaSAndroid Build Coastguard Worker }  /* extern "C" */
165*f4ee7fbaSAndroid Build Coastguard Worker #endif
166*f4ee7fbaSAndroid Build Coastguard Worker 
167*f4ee7fbaSAndroid Build Coastguard Worker #endif  /* BROTLI_ENC_RINGBUFFER_H_ */
168