• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2015 Google Inc. All Rights Reserved.
2 
3    Distributed under MIT license.
4    See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
5 */
6 
7 package org.brotli.dec;
8 
9 /**
10  * Bit reading helpers.
11  */
12 final class BitReader {
13 
14   // Possible values: {5, 6}.  5 corresponds to 32-bit build, 6 to 64-bit. This value is used for
15   // conditional compilation -> produced artifacts might be binary INCOMPATIBLE (JLS 13.2).
16   private static final int LOG_BITNESS = 6;
17   private static final int BITNESS = 1 << LOG_BITNESS;
18 
19   private static final int BYTENESS = BITNESS / 8;
20   private static final int CAPACITY = 4096;
21   // After encountering the end of the input stream, this amount of zero bytes will be appended.
22   private static final int SLACK = 64;
23   private static final int BUFFER_SIZE = CAPACITY + SLACK;
24   // Don't bother to replenish the buffer while this number of bytes is available.
25   private static final int SAFEGUARD = 36;
26   private static final int WATERLINE = CAPACITY - SAFEGUARD;
27 
28   // "Half" refers to "half of native integer type", i.e. on 64-bit machines it is 32-bit type,
29   // on 32-bit machines it is 16-bit.
30   private static final int HALF_BITNESS = BITNESS / 2;
31   private static final int HALF_SIZE = BYTENESS / 2;
32   private static final int HALVES_CAPACITY = CAPACITY / HALF_SIZE;
33   private static final int HALF_BUFFER_SIZE = BUFFER_SIZE / HALF_SIZE;
34   private static final int HALF_WATERLINE = WATERLINE / HALF_SIZE;
35 
36   private static final int LOG_HALF_SIZE = LOG_BITNESS - 4;
37 
38   /**
39    * Fills up the input buffer.
40    *
41    * <p> No-op if there are at least 36 bytes present after current position.
42    *
43    * <p> After encountering the end of the input stream, 64 additional zero bytes are copied to the
44    * buffer.
45    */
readMoreInput(State s)46   static void readMoreInput(State s) {
47     if (s.halfOffset > HALF_WATERLINE) {
48       doReadMoreInput(s);
49     }
50   }
51 
doReadMoreInput(State s)52   static void doReadMoreInput(State s) {
53     if (s.endOfStreamReached != 0) {
54       if (halfAvailable(s) >= -2) {
55         return;
56       }
57       throw new BrotliRuntimeException("No more input");
58     }
59     int readOffset = s.halfOffset << LOG_HALF_SIZE;
60     int bytesInBuffer = CAPACITY - readOffset;
61     // Move unused bytes to the head of the buffer.
62     Utils.copyBytesWithin(s.byteBuffer, 0, readOffset, CAPACITY);
63     s.halfOffset = 0;
64     while (bytesInBuffer < CAPACITY) {
65       int spaceLeft = CAPACITY - bytesInBuffer;
66       int len = Utils.readInput(s.input, s.byteBuffer, bytesInBuffer, spaceLeft);
67       // EOF is -1 in Java, but 0 in C#.
68       if (len <= 0) {
69         s.endOfStreamReached = 1;
70         s.tailBytes = bytesInBuffer;
71         bytesInBuffer += HALF_SIZE - 1;
72         break;
73       }
74       bytesInBuffer += len;
75     }
76     bytesToNibbles(s, bytesInBuffer);
77   }
78 
checkHealth(State s, int endOfStream)79   static void checkHealth(State s, int endOfStream) {
80     if (s.endOfStreamReached == 0) {
81       return;
82     }
83     int byteOffset = (s.halfOffset << LOG_HALF_SIZE) + ((s.bitOffset + 7) >> 3) - BYTENESS;
84     if (byteOffset > s.tailBytes) {
85       throw new BrotliRuntimeException("Read after end");
86     }
87     if ((endOfStream != 0) && (byteOffset != s.tailBytes)) {
88       throw new BrotliRuntimeException("Unused bytes after end");
89     }
90   }
91 
fillBitWindow(State s)92   static void fillBitWindow(State s) {
93     if (s.bitOffset >= HALF_BITNESS) {
94       // Same as doFillBitWindow. JVM fails to inline it.
95       if (BITNESS == 64) {
96         s.accumulator64 = ((long) s.intBuffer[s.halfOffset++] << HALF_BITNESS)
97             | (s.accumulator64 >>> HALF_BITNESS);
98       } else {
99         s.accumulator32 = ((int) s.shortBuffer[s.halfOffset++] << HALF_BITNESS)
100             | (s.accumulator32 >>> HALF_BITNESS);
101       }
102       s.bitOffset -= HALF_BITNESS;
103     }
104   }
105 
doFillBitWindow(State s)106   private static void doFillBitWindow(State s) {
107     if (BITNESS == 64) {
108       s.accumulator64 = ((long) s.intBuffer[s.halfOffset++] << HALF_BITNESS)
109           | (s.accumulator64 >>> HALF_BITNESS);
110     } else {
111       s.accumulator32 = ((int) s.shortBuffer[s.halfOffset++] << HALF_BITNESS)
112           | (s.accumulator32 >>> HALF_BITNESS);
113     }
114     s.bitOffset -= HALF_BITNESS;
115   }
116 
peekBits(State s)117   static int peekBits(State s) {
118     if (BITNESS == 64) {
119       return (int) (s.accumulator64 >>> s.bitOffset);
120     } else {
121       return s.accumulator32 >>> s.bitOffset;
122     }
123   }
124 
readFewBits(State s, int n)125   static int readFewBits(State s, int n) {
126     int val = peekBits(s) & ((1 << n) - 1);
127     s.bitOffset += n;
128     return val;
129   }
130 
readBits(State s, int n)131   static int readBits(State s, int n) {
132     if (HALF_BITNESS >= 24) {
133       return readFewBits(s, n);
134     } else {
135       return (n <= 16) ? readFewBits(s, n) : readManyBits(s, n);
136     }
137   }
138 
readManyBits(State s, int n)139   private static int readManyBits(State s, int n) {
140     int low = readFewBits(s, 16);
141     doFillBitWindow(s);
142     return low | (readFewBits(s, n - 16) << 16);
143   }
144 
initBitReader(State s)145   static void initBitReader(State s) {
146     s.byteBuffer = new byte[BUFFER_SIZE];
147     if (BITNESS == 64) {
148       s.accumulator64 = 0;
149       s.intBuffer = new int[HALF_BUFFER_SIZE];
150     } else {
151       s.accumulator32 = 0;
152       s.shortBuffer = new short[HALF_BUFFER_SIZE];
153     }
154     s.bitOffset = BITNESS;
155     s.halfOffset = HALVES_CAPACITY;
156     s.endOfStreamReached = 0;
157     prepare(s);
158   }
159 
prepare(State s)160   private static void prepare(State s) {
161     readMoreInput(s);
162     checkHealth(s, 0);
163     doFillBitWindow(s);
164     doFillBitWindow(s);
165   }
166 
reload(State s)167   static void reload(State s) {
168     if (s.bitOffset == BITNESS) {
169       prepare(s);
170     }
171   }
172 
jumpToByteBoundary(State s)173   static void jumpToByteBoundary(State s) {
174     int padding = (BITNESS - s.bitOffset) & 7;
175     if (padding != 0) {
176       int paddingBits = readFewBits(s, padding);
177       if (paddingBits != 0) {
178         throw new BrotliRuntimeException("Corrupted padding bits");
179       }
180     }
181   }
182 
halfAvailable(State s)183   static int halfAvailable(State s) {
184     int limit = HALVES_CAPACITY;
185     if (s.endOfStreamReached != 0) {
186       limit = (s.tailBytes + (HALF_SIZE - 1)) >> LOG_HALF_SIZE;
187     }
188     return limit - s.halfOffset;
189   }
190 
copyBytes(State s, byte[] data, int offset, int length)191   static void copyBytes(State s, byte[] data, int offset, int length) {
192     if ((s.bitOffset & 7) != 0) {
193       throw new BrotliRuntimeException("Unaligned copyBytes");
194     }
195 
196     // Drain accumulator.
197     while ((s.bitOffset != BITNESS) && (length != 0)) {
198       data[offset++] = (byte) peekBits(s);
199       s.bitOffset += 8;
200       length--;
201     }
202     if (length == 0) {
203       return;
204     }
205 
206     // Get data from shadow buffer with "sizeof(int)" granularity.
207     int copyNibbles = Math.min(halfAvailable(s), length >> LOG_HALF_SIZE);
208     if (copyNibbles > 0) {
209       int readOffset = s.halfOffset << LOG_HALF_SIZE;
210       int delta = copyNibbles << LOG_HALF_SIZE;
211       System.arraycopy(s.byteBuffer, readOffset, data, offset, delta);
212       offset += delta;
213       length -= delta;
214       s.halfOffset += copyNibbles;
215     }
216     if (length == 0) {
217       return;
218     }
219 
220     // Read tail bytes.
221     if (halfAvailable(s) > 0) {
222       // length = 1..3
223       fillBitWindow(s);
224       while (length != 0) {
225         data[offset++] = (byte) peekBits(s);
226         s.bitOffset += 8;
227         length--;
228       }
229       checkHealth(s, 0);
230       return;
231     }
232 
233     // Now it is possible to copy bytes directly.
234     while (length > 0) {
235       int len = Utils.readInput(s.input, data, offset, length);
236       if (len == -1) {
237         throw new BrotliRuntimeException("Unexpected end of input");
238       }
239       offset += len;
240       length -= len;
241     }
242   }
243 
244   /**
245    * Translates bytes to halves (int/short).
246    */
bytesToNibbles(State s, int byteLen)247   static void bytesToNibbles(State s, int byteLen) {
248     byte[] byteBuffer = s.byteBuffer;
249     int halfLen = byteLen >> LOG_HALF_SIZE;
250     if (BITNESS == 64) {
251       int[] intBuffer = s.intBuffer;
252       for (int i = 0; i < halfLen; ++i) {
253         intBuffer[i] = ((byteBuffer[i * 4] & 0xFF))
254             | ((byteBuffer[(i * 4) + 1] & 0xFF) << 8)
255             | ((byteBuffer[(i * 4) + 2] & 0xFF) << 16)
256             | ((byteBuffer[(i * 4) + 3] & 0xFF) << 24);
257       }
258     } else {
259       short[] shortBuffer = s.shortBuffer;
260       for (int i = 0; i < halfLen; ++i) {
261         shortBuffer[i] = (short) ((byteBuffer[i * 2] & 0xFF)
262             | ((byteBuffer[(i * 2) + 1] & 0xFF) << 8));
263       }
264     }
265   }
266 }
267