mirror of
https://github.com/google/brotli.git
synced 2026-07-09 02:08:06 +00:00
Add Java port of Brotli decoder.
This commit is contained in:
171
java/dec/BitReader.java
Executable file
171
java/dec/BitReader.java
Executable file
@@ -0,0 +1,171 @@
|
||||
/* Copyright 2015 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
package org.brotli.dec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
/**
|
||||
* Bit reading helpers.
|
||||
*/
|
||||
class BitReader {
|
||||
|
||||
/**
|
||||
* Input byte buffer, consist of a ring-buffer and a "slack" region where bytes from the start of
|
||||
* the ring-buffer are copied.
|
||||
*/
|
||||
private static final int READ_SIZE = 4096;
|
||||
private static final int BUF_SIZE = READ_SIZE + 64;
|
||||
|
||||
private final ByteBuffer byteBuffer =
|
||||
ByteBuffer.allocateDirect(BUF_SIZE).order(ByteOrder.LITTLE_ENDIAN);
|
||||
private final IntBuffer intBuffer = byteBuffer.asIntBuffer();
|
||||
private final byte[] shadowBuffer = new byte[BUF_SIZE];
|
||||
|
||||
private InputStream input;
|
||||
|
||||
/**
|
||||
* Input stream is finished.
|
||||
*/
|
||||
private boolean endOfStreamReached;
|
||||
|
||||
/**
|
||||
* Pre-fetched bits.
|
||||
*/
|
||||
long accumulator;
|
||||
|
||||
/**
|
||||
* Current bit-reading position in accumulator.
|
||||
*/
|
||||
int bitOffset;
|
||||
|
||||
/**
|
||||
* Number of 32-bit integers availabale for reading.
|
||||
*/
|
||||
private int available;
|
||||
|
||||
/* Number of bytes in unfinished "int" item. */
|
||||
private int tailBytes = 0;
|
||||
|
||||
/**
|
||||
* Fills up the input buffer.
|
||||
*
|
||||
* <p> No-op if there are at least 36 bytes present after current position.
|
||||
*
|
||||
* <p> After encountering the end of the input stream, 64 additional zero bytes are copied to the
|
||||
* buffer.
|
||||
*/
|
||||
// TODO: Split to check and read; move read outside of decoding loop.
|
||||
static void readMoreInput(BitReader br) {
|
||||
if (br.available > 9) {
|
||||
return;
|
||||
}
|
||||
if (br.endOfStreamReached) {
|
||||
if (br.available > 4) {
|
||||
return;
|
||||
}
|
||||
throw new BrotliRuntimeException("No more input");
|
||||
}
|
||||
int readOffset = br.intBuffer.position() << 2;
|
||||
int bytesRead = READ_SIZE - readOffset;
|
||||
System.arraycopy(br.shadowBuffer, readOffset, br.shadowBuffer, 0, bytesRead);
|
||||
try {
|
||||
while (bytesRead < READ_SIZE) {
|
||||
int len = br.input.read(br.shadowBuffer, bytesRead, READ_SIZE - bytesRead);
|
||||
if (len == -1) {
|
||||
br.endOfStreamReached = true;
|
||||
Utils.fillWithZeroes(br.shadowBuffer, bytesRead, 64);
|
||||
bytesRead += 64;
|
||||
br.tailBytes = bytesRead & 3;
|
||||
break;
|
||||
}
|
||||
bytesRead += len;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new BrotliRuntimeException("Failed to read input", e);
|
||||
}
|
||||
br.byteBuffer.clear();
|
||||
br.byteBuffer.put(br.shadowBuffer, 0, bytesRead & 0xFFFC);
|
||||
br.intBuffer.rewind();
|
||||
br.available = bytesRead >> 2;
|
||||
}
|
||||
|
||||
static void checkHealth(BitReader br) {
|
||||
if (!br.endOfStreamReached) {
|
||||
return;
|
||||
}
|
||||
/* When end of stream is reached, we "borrow" up to 64 zeroes to bit reader.
|
||||
* If compressed stream is valid, then borrowed zeroes should remain unused. */
|
||||
int valentBytes = (br.available << 2) + ((64 - br.bitOffset) >> 3);
|
||||
int borrowedBytes = 64 - br.tailBytes;
|
||||
if (valentBytes != borrowedBytes) {
|
||||
throw new BrotliRuntimeException("Read after end");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances the Read buffer by 5 bytes to make room for reading next 24 bits.
|
||||
*/
|
||||
static void fillBitWindow(BitReader br) {
|
||||
if (br.bitOffset >= 32) {
|
||||
br.accumulator = ((long) br.intBuffer.get() << 32) | (br.accumulator >>> 32);
|
||||
br.bitOffset -= 32;
|
||||
br.available--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the specified number of bits from Read Buffer.
|
||||
*/
|
||||
static int readBits(BitReader br, int n) {
|
||||
fillBitWindow(br);
|
||||
int val = (int) (br.accumulator >>> br.bitOffset) & ((1 << n) - 1);
|
||||
br.bitOffset += n;
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize bit reader.
|
||||
*
|
||||
* <p> Initialisation turns bit reader to a ready state. Also a number of bytes is prefetched to
|
||||
* accumulator. Because of that this method may block until enough data could be read from input.
|
||||
*
|
||||
* @param br BitReader POJO
|
||||
* @param input data source
|
||||
*/
|
||||
static void init(BitReader br, InputStream input) {
|
||||
if (br.input != null) {
|
||||
throw new IllegalStateException("Bit reader already has associated input stream");
|
||||
}
|
||||
br.input = input;
|
||||
br.accumulator = 0;
|
||||
br.intBuffer.position(READ_SIZE >> 2);
|
||||
br.bitOffset = 64;
|
||||
br.available = 0;
|
||||
br.endOfStreamReached = false;
|
||||
readMoreInput(br);
|
||||
if (br.available == 0) {
|
||||
throw new BrotliRuntimeException("Can't initialize reader");
|
||||
}
|
||||
fillBitWindow(br);
|
||||
fillBitWindow(br);
|
||||
}
|
||||
|
||||
static void jumpToByteBoundry(BitReader br) {
|
||||
int padding = (64 - br.bitOffset) & 7;
|
||||
if (padding != 0) {
|
||||
int paddingBits = BitReader.readBits(br, padding);
|
||||
if (paddingBits != 0) {
|
||||
throw new BrotliRuntimeException("Corrupted padding bits ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user