View Javadoc
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.htmlunit.util.brotli;
8   
9   import static org.htmlunit.util.brotli.BrotliError.BROTLI_ERROR;
10  import static org.htmlunit.util.brotli.BrotliError.BROTLI_ERROR_CORRUPTED_PADDING_BITS;
11  import static org.htmlunit.util.brotli.BrotliError.BROTLI_ERROR_READ_AFTER_END;
12  import static org.htmlunit.util.brotli.BrotliError.BROTLI_ERROR_TRUNCATED_INPUT;
13  import static org.htmlunit.util.brotli.BrotliError.BROTLI_ERROR_UNUSED_BYTES_AFTER_END;
14  import static org.htmlunit.util.brotli.BrotliError.BROTLI_OK;
15  import static org.htmlunit.util.brotli.BrotliError.BROTLI_PANIC_UNALIGNED_COPY_BYTES;
16  
17  /**
18   * Bit reading helpers.
19   */
20  final class BitReader {
21  
22    // Possible values: {5, 6}.  5 corresponds to 32-bit build, 6 to 64-bit. This value is used for
23    // JIT conditional compilation.
24    private static final int LOG_BITNESS = Utils.getLogBintness();
25  
26    // Not only Java compiler prunes "if (const false)" code, but JVM as well.
27    // Code under "if (BIT_READER_DEBUG != 0)" have zero performance impact (outside unit tests).
28    private static final int BIT_READER_DEBUG = Utils.isDebugMode();
29  
30    static final int BITNESS = 1 << LOG_BITNESS;
31  
32    private static final int BYTENESS = BITNESS / 8;
33    private static final int CAPACITY = 4096;
34    // After encountering the end of the input stream, this amount of zero bytes will be appended.
35    private static final int SLACK = 64;
36    private static final int BUFFER_SIZE = CAPACITY + SLACK;
37    // Don't bother to replenish the buffer while this number of bytes is available.
38    private static final int SAFEGUARD = 36;
39    private static final int WATERLINE = CAPACITY - SAFEGUARD;
40  
41    // "Half" refers to "half of native integer type", i.e. on 64-bit machines it is 32-bit type,
42    // on 32-bit machines it is 16-bit.
43    private static final int HALF_BITNESS = BITNESS / 2;
44    private static final int HALF_SIZE = BYTENESS / 2;
45    private static final int HALVES_CAPACITY = CAPACITY / HALF_SIZE;
46    private static final int HALF_BUFFER_SIZE = BUFFER_SIZE / HALF_SIZE;
47  
48    private static final int LOG_HALF_SIZE = LOG_BITNESS - 4;
49  
50    static final int HALF_WATERLINE = WATERLINE / HALF_SIZE;
51  
52    /**
53     * Fills up the input buffer.
54     *
55     * <p> Should not be called if there are at least 36 bytes present after current position.
56     *
57     * <p> After encountering the end of the input stream, 64 additional zero bytes are copied to the
58     * buffer.
59     */
60    static int readMoreInput(State s) {
61      if (s.endOfStreamReached != 0) {
62        if (halfAvailable(s) >= -2) {
63          return BROTLI_OK;
64        }
65        return Utils.makeError(s, BROTLI_ERROR_TRUNCATED_INPUT);
66      }
67      final int readOffset = s.halfOffset << LOG_HALF_SIZE;
68      int bytesInBuffer = CAPACITY - readOffset;
69      // Move unused bytes to the head of the buffer.
70      Utils.copyBytesWithin(s.byteBuffer, 0, readOffset, CAPACITY);
71      s.halfOffset = 0;
72      while (bytesInBuffer < CAPACITY) {
73        final int spaceLeft = CAPACITY - bytesInBuffer;
74        final int len = Utils.readInput(s, s.byteBuffer, bytesInBuffer, spaceLeft);
75        // EOF is -1 in Java, but 0 in C#.
76        if (len < BROTLI_ERROR) {
77          return len;
78        }
79        if (len <= 0) {
80          s.endOfStreamReached = 1;
81          s.tailBytes = bytesInBuffer;
82          bytesInBuffer += HALF_SIZE - 1;
83          break;
84        }
85        bytesInBuffer += len;
86      }
87      bytesToNibbles(s, bytesInBuffer);
88      return BROTLI_OK;
89    }
90  
91    static int checkHealth(State s, int endOfStream) {
92      if (s.endOfStreamReached == 0) {
93        return BROTLI_OK;
94      }
95      final int byteOffset = (s.halfOffset << LOG_HALF_SIZE) + ((s.bitOffset + 7) >> 3) - BYTENESS;
96      if (byteOffset > s.tailBytes) {
97        return Utils.makeError(s, BROTLI_ERROR_READ_AFTER_END);
98      }
99      if ((endOfStream != 0) && (byteOffset != s.tailBytes)) {
100       return Utils.makeError(s, BROTLI_ERROR_UNUSED_BYTES_AFTER_END);
101     }
102     return BROTLI_OK;
103   }
104 
105   static void assertAccumulatorHealthy(State s) {
106     if (s.bitOffset > BITNESS) {
107       throw new IllegalStateException("Accumulator underloaded: " + s.bitOffset);
108     }
109   }
110 
111   static void fillBitWindow(State s) {
112     if (BIT_READER_DEBUG != 0) {
113       assertAccumulatorHealthy(s);
114     }
115     if (s.bitOffset >= HALF_BITNESS) {
116       // Same as doFillBitWindow. JVM fails to inline it.
117       if (BITNESS == 64) {
118         s.accumulator64 = ((long) s.intBuffer[s.halfOffset++] << HALF_BITNESS)
119             | Utils.shr64(s.accumulator64, HALF_BITNESS);
120       } else {
121         s.accumulator32 = (s.shortBuffer[s.halfOffset++] << HALF_BITNESS)
122             | Utils.shr32(s.accumulator32, HALF_BITNESS);
123       }
124       s.bitOffset -= HALF_BITNESS;
125     }
126   }
127 
128   static void doFillBitWindow(State s) {
129     if (BIT_READER_DEBUG != 0) {
130       assertAccumulatorHealthy(s);
131     }
132     if (BITNESS == 64) {
133       s.accumulator64 = ((long) s.intBuffer[s.halfOffset++] << HALF_BITNESS)
134           | Utils.shr64(s.accumulator64, HALF_BITNESS);
135     } else {
136       s.accumulator32 = (s.shortBuffer[s.halfOffset++] << HALF_BITNESS)
137           | Utils.shr32(s.accumulator32, HALF_BITNESS);
138     }
139     s.bitOffset -= HALF_BITNESS;
140   }
141 
142   static int peekBits(State s) {
143     if (BITNESS == 64) {
144       return (int) Utils.shr64(s.accumulator64, s.bitOffset);
145     }
146     return Utils.shr32(s.accumulator32, s.bitOffset);
147   }
148 
149   /**
150    * Fetches bits from accumulator.
151    *
152    * WARNING: accumulator MUST contain at least the specified amount of bits,
153    * otherwise BitReader will become broken.
154    */
155   static int readFewBits(State s, int n) {
156     final int v = peekBits(s) & ((1 << n) - 1);
157     s.bitOffset += n;
158     return v;
159   }
160 
161   static int readBits(State s, int n) {
162     if (HALF_BITNESS >= 24) {
163       return readFewBits(s, n);
164     }
165     return (n <= 16) ? readFewBits(s, n) : readManyBits(s, n);
166   }
167 
168   private static int readManyBits(State s, int n) {
169     final int low = readFewBits(s, 16);
170     doFillBitWindow(s);
171     return low | (readFewBits(s, n - 16) << 16);
172   }
173 
174   static int initBitReader(State s) {
175     s.byteBuffer = new byte[BUFFER_SIZE];
176     if (BITNESS == 64) {
177       s.accumulator64 = 0;
178       s.intBuffer = new int[HALF_BUFFER_SIZE];
179     } else {
180       s.accumulator32 = 0;
181       s.shortBuffer = new short[HALF_BUFFER_SIZE];
182     }
183     s.bitOffset = BITNESS;
184     s.halfOffset = HALVES_CAPACITY;
185     s.endOfStreamReached = 0;
186     return prepare(s);
187   }
188 
189   private static int prepare(State s) {
190     if (s.halfOffset > BitReader.HALF_WATERLINE) {
191       final int result = readMoreInput(s);
192       if (result != BROTLI_OK) {
193         return result;
194       }
195     }
196     int health = checkHealth(s, 0);
197     if (health != BROTLI_OK) {
198       return health;
199     }
200     doFillBitWindow(s);
201     doFillBitWindow(s);
202     return BROTLI_OK;
203   }
204 
205   static int reload(State s) {
206     if (s.bitOffset == BITNESS) {
207       return prepare(s);
208     }
209     return BROTLI_OK;
210   }
211 
212   static int jumpToByteBoundary(State s) {
213     final int padding = (BITNESS - s.bitOffset) & 7;
214     if (padding != 0) {
215       final int paddingBits = readFewBits(s, padding);
216       if (paddingBits != 0) {
217         return Utils.makeError(s, BROTLI_ERROR_CORRUPTED_PADDING_BITS);
218       }
219     }
220     return BROTLI_OK;
221   }
222 
223   static int halfAvailable(State s) {
224     int limit = HALVES_CAPACITY;
225     if (s.endOfStreamReached != 0) {
226       limit = (s.tailBytes + (HALF_SIZE - 1)) >> LOG_HALF_SIZE;
227     }
228     return limit - s.halfOffset;
229   }
230 
231   static int copyRawBytes(State s, byte[] data, int offset, int length) {
232     int pos = offset;
233     int len = length;
234     if ((s.bitOffset & 7) != 0) {
235       return Utils.makeError(s, BROTLI_PANIC_UNALIGNED_COPY_BYTES);
236     }
237 
238     // Drain accumulator.
239     while ((s.bitOffset != BITNESS) && (len != 0)) {
240       data[pos++] = (byte) peekBits(s);
241       s.bitOffset += 8;
242       len--;
243     }
244     if (len == 0) {
245       return BROTLI_OK;
246     }
247 
248     // Get data from shadow buffer with "sizeof(int)" granularity.
249     final int copyNibbles = Utils.min(halfAvailable(s), len >> LOG_HALF_SIZE);
250     if (copyNibbles > 0) {
251       final int readOffset = s.halfOffset << LOG_HALF_SIZE;
252       final int delta = copyNibbles << LOG_HALF_SIZE;
253       Utils.copyBytes(data, pos, s.byteBuffer, readOffset, readOffset + delta);
254       pos += delta;
255       len -= delta;
256       s.halfOffset += copyNibbles;
257     }
258     if (len == 0) {
259       return BROTLI_OK;
260     }
261 
262     // Read tail bytes.
263     if (halfAvailable(s) > 0) {
264       // length = 1..3
265       fillBitWindow(s);
266       while (len != 0) {
267         data[pos++] = (byte) peekBits(s);
268         s.bitOffset += 8;
269         len--;
270       }
271       return checkHealth(s, 0);
272     }
273 
274     // Now it is possible to copy bytes directly.
275     while (len > 0) {
276       final int chunkLen = Utils.readInput(s, data, pos, len);
277       // EOF is -1 in Java, but 0 in C#.
278       if (len < BROTLI_ERROR) {
279         return len;
280       }
281       if (chunkLen <= 0) {
282         return Utils.makeError(s, BROTLI_ERROR_TRUNCATED_INPUT);
283       }
284       pos += chunkLen;
285       len -= chunkLen;
286     }
287     return BROTLI_OK;
288   }
289 
290   /**
291    * Translates bytes to halves (int/short).
292    */
293   static void bytesToNibbles(State s, int byteLen) {
294     final byte[] byteBuffer = s.byteBuffer;
295     final int halfLen = byteLen >> LOG_HALF_SIZE;
296     if (BITNESS == 64) {
297       final int[] intBuffer = s.intBuffer;
298       for (int i = 0; i < halfLen; ++i) {
299         intBuffer[i] = (byteBuffer[i * 4] & 0xFF)
300             | ((byteBuffer[(i * 4) + 1] & 0xFF) << 8)
301             | ((byteBuffer[(i * 4) + 2] & 0xFF) << 16)
302             | ((byteBuffer[(i * 4) + 3] & 0xFF) << 24);
303       }
304     } else {
305       final short[] shortBuffer = s.shortBuffer;
306       for (int i = 0; i < halfLen; ++i) {
307         shortBuffer[i] = (short) ((byteBuffer[i * 2] & 0xFF)
308             | ((byteBuffer[(i * 2) + 1] & 0xFF) << 8));
309       }
310     }
311   }
312 }