mirror of https://github.com/octoleo/plantuml.git
273 lines
8.1 KiB
Java
273 lines
8.1 KiB
Java
/* Copyright 2015 Google Inc. All Rights Reserved.
|
|
|
|
https://github.com/google/brotli/blob/master/LICENSE
|
|
|
|
Distributed under MIT license.
|
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
package net.sourceforge.plantuml.brotli;
|
|
|
|
/**
|
|
* Bit reading helpers.
|
|
*/
|
|
final class BitReader {
|
|
// ::remove folder when WASM
|
|
|
|
// Added by Arnaud... not very beautifull
|
|
private static final boolean CHECK_UNUSED_BYTES_AFTER_END = false;
|
|
|
|
// Possible values: {5, 6}. 5 corresponds to 32-bit build, 6 to 64-bit. This value is used for
|
|
// conditional compilation -> produced artifacts might be binary INCOMPATIBLE (JLS 13.2).
|
|
private static final int LOG_BITNESS = 6;
|
|
private static final int BITNESS = 1 << LOG_BITNESS;
|
|
|
|
private static final int BYTENESS = BITNESS / 8;
|
|
private static final int CAPACITY = 4096;
|
|
// After encountering the end of the input stream, this amount of zero bytes will be appended.
|
|
private static final int SLACK = 64;
|
|
private static final int BUFFER_SIZE = CAPACITY + SLACK;
|
|
// Don't bother to replenish the buffer while this number of bytes is available.
|
|
private static final int SAFEGUARD = 36;
|
|
private static final int WATERLINE = CAPACITY - SAFEGUARD;
|
|
|
|
// "Half" refers to "half of native integer type", i.e. on 64-bit machines it is 32-bit type,
|
|
// on 32-bit machines it is 16-bit.
|
|
private static final int HALF_BITNESS = BITNESS / 2;
|
|
private static final int HALF_SIZE = BYTENESS / 2;
|
|
private static final int HALVES_CAPACITY = CAPACITY / HALF_SIZE;
|
|
private static final int HALF_BUFFER_SIZE = BUFFER_SIZE / HALF_SIZE;
|
|
private static final int HALF_WATERLINE = WATERLINE / HALF_SIZE;
|
|
|
|
private static final int LOG_HALF_SIZE = LOG_BITNESS - 4;
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
static void readMoreInput(State s) {
|
|
if (s.halfOffset > HALF_WATERLINE) {
|
|
doReadMoreInput(s);
|
|
}
|
|
}
|
|
|
|
static void doReadMoreInput(State s) {
|
|
if (s.endOfStreamReached != 0) {
|
|
if (halfAvailable(s) >= -2) {
|
|
return;
|
|
}
|
|
throw new BrotliRuntimeException("No more input");
|
|
}
|
|
int readOffset = s.halfOffset << LOG_HALF_SIZE;
|
|
int bytesInBuffer = CAPACITY - readOffset;
|
|
// Move unused bytes to the head of the buffer.
|
|
Utils.copyBytesWithin(s.byteBuffer, 0, readOffset, CAPACITY);
|
|
s.halfOffset = 0;
|
|
while (bytesInBuffer < CAPACITY) {
|
|
int spaceLeft = CAPACITY - bytesInBuffer;
|
|
int len = Utils.readInput(s.input, s.byteBuffer, bytesInBuffer, spaceLeft);
|
|
// EOF is -1 in Java, but 0 in C#.
|
|
if (len <= 0) {
|
|
s.endOfStreamReached = 1;
|
|
s.tailBytes = bytesInBuffer;
|
|
bytesInBuffer += HALF_SIZE - 1;
|
|
break;
|
|
}
|
|
bytesInBuffer += len;
|
|
}
|
|
bytesToNibbles(s, bytesInBuffer);
|
|
}
|
|
|
|
static void checkHealth(State s, int endOfStream) {
|
|
if (s.endOfStreamReached == 0) {
|
|
return;
|
|
}
|
|
int byteOffset = (s.halfOffset << LOG_HALF_SIZE) + ((s.bitOffset + 7) >> 3) - BYTENESS;
|
|
if (byteOffset > s.tailBytes) {
|
|
throw new BrotliRuntimeException("Read after end");
|
|
}
|
|
if (CHECK_UNUSED_BYTES_AFTER_END && (endOfStream != 0) && (byteOffset != s.tailBytes)) {
|
|
throw new BrotliRuntimeException("Unused bytes after end");
|
|
}
|
|
}
|
|
|
|
static void fillBitWindow(State s) {
|
|
if (s.bitOffset >= HALF_BITNESS) {
|
|
// Same as doFillBitWindow. JVM fails to inline it.
|
|
if (BITNESS == 64) {
|
|
s.accumulator64 = ((long) s.intBuffer[s.halfOffset++] << HALF_BITNESS)
|
|
| (s.accumulator64 >>> HALF_BITNESS);
|
|
} else {
|
|
s.accumulator32 = ((int) s.shortBuffer[s.halfOffset++] << HALF_BITNESS)
|
|
| (s.accumulator32 >>> HALF_BITNESS);
|
|
}
|
|
s.bitOffset -= HALF_BITNESS;
|
|
}
|
|
}
|
|
|
|
private static void doFillBitWindow(State s) {
|
|
if (BITNESS == 64) {
|
|
s.accumulator64 = ((long) s.intBuffer[s.halfOffset++] << HALF_BITNESS)
|
|
| (s.accumulator64 >>> HALF_BITNESS);
|
|
} else {
|
|
s.accumulator32 = ((int) s.shortBuffer[s.halfOffset++] << HALF_BITNESS)
|
|
| (s.accumulator32 >>> HALF_BITNESS);
|
|
}
|
|
s.bitOffset -= HALF_BITNESS;
|
|
}
|
|
|
|
static int peekBits(State s) {
|
|
if (BITNESS == 64) {
|
|
return (int) (s.accumulator64 >>> s.bitOffset);
|
|
} else {
|
|
return s.accumulator32 >>> s.bitOffset;
|
|
}
|
|
}
|
|
|
|
static int readFewBits(State s, int n) {
|
|
int val = peekBits(s) & ((1 << n) - 1);
|
|
s.bitOffset += n;
|
|
return val;
|
|
}
|
|
|
|
static int readBits(State s, int n) {
|
|
if (HALF_BITNESS >= 24) {
|
|
return readFewBits(s, n);
|
|
} else {
|
|
return (n <= 16) ? readFewBits(s, n) : readManyBits(s, n);
|
|
}
|
|
}
|
|
|
|
private static int readManyBits(State s, int n) {
|
|
int low = readFewBits(s, 16);
|
|
doFillBitWindow(s);
|
|
return low | (readFewBits(s, n - 16) << 16);
|
|
}
|
|
|
|
static void initBitReader(State s) {
|
|
s.byteBuffer = new byte[BUFFER_SIZE];
|
|
if (BITNESS == 64) {
|
|
s.accumulator64 = 0;
|
|
s.intBuffer = new int[HALF_BUFFER_SIZE];
|
|
} else {
|
|
s.accumulator32 = 0;
|
|
s.shortBuffer = new short[HALF_BUFFER_SIZE];
|
|
}
|
|
s.bitOffset = BITNESS;
|
|
s.halfOffset = HALVES_CAPACITY;
|
|
s.endOfStreamReached = 0;
|
|
prepare(s);
|
|
}
|
|
|
|
private static void prepare(State s) {
|
|
readMoreInput(s);
|
|
checkHealth(s, 0);
|
|
doFillBitWindow(s);
|
|
doFillBitWindow(s);
|
|
}
|
|
|
|
static void reload(State s) {
|
|
if (s.bitOffset == BITNESS) {
|
|
prepare(s);
|
|
}
|
|
}
|
|
|
|
static void jumpToByteBoundary(State s) {
|
|
int padding = (BITNESS - s.bitOffset) & 7;
|
|
if (padding != 0) {
|
|
int paddingBits = readFewBits(s, padding);
|
|
if (paddingBits != 0) {
|
|
throw new BrotliRuntimeException("Corrupted padding bits");
|
|
}
|
|
}
|
|
}
|
|
|
|
static int halfAvailable(State s) {
|
|
int limit = HALVES_CAPACITY;
|
|
if (s.endOfStreamReached != 0) {
|
|
limit = (s.tailBytes + (HALF_SIZE - 1)) >> LOG_HALF_SIZE;
|
|
}
|
|
return limit - s.halfOffset;
|
|
}
|
|
|
|
static void copyBytes(State s, byte[] data, int offset, int length) {
|
|
if ((s.bitOffset & 7) != 0) {
|
|
throw new BrotliRuntimeException("Unaligned copyBytes");
|
|
}
|
|
|
|
// Drain accumulator.
|
|
while ((s.bitOffset != BITNESS) && (length != 0)) {
|
|
data[offset++] = (byte) peekBits(s);
|
|
s.bitOffset += 8;
|
|
length--;
|
|
}
|
|
if (length == 0) {
|
|
return;
|
|
}
|
|
|
|
// Get data from shadow buffer with "sizeof(int)" granularity.
|
|
int copyNibbles = Math.min(halfAvailable(s), length >> LOG_HALF_SIZE);
|
|
if (copyNibbles > 0) {
|
|
int readOffset = s.halfOffset << LOG_HALF_SIZE;
|
|
int delta = copyNibbles << LOG_HALF_SIZE;
|
|
System.arraycopy(s.byteBuffer, readOffset, data, offset, delta);
|
|
offset += delta;
|
|
length -= delta;
|
|
s.halfOffset += copyNibbles;
|
|
}
|
|
if (length == 0) {
|
|
return;
|
|
}
|
|
|
|
// Read tail bytes.
|
|
if (halfAvailable(s) > 0) {
|
|
// length = 1..3
|
|
fillBitWindow(s);
|
|
while (length != 0) {
|
|
data[offset++] = (byte) peekBits(s);
|
|
s.bitOffset += 8;
|
|
length--;
|
|
}
|
|
checkHealth(s, 0);
|
|
return;
|
|
}
|
|
|
|
// Now it is possible to copy bytes directly.
|
|
while (length > 0) {
|
|
int len = Utils.readInput(s.input, data, offset, length);
|
|
if (len == -1) {
|
|
throw new BrotliRuntimeException("Unexpected end of input");
|
|
}
|
|
offset += len;
|
|
length -= len;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Translates bytes to halves (int/short).
|
|
*/
|
|
static void bytesToNibbles(State s, int byteLen) {
|
|
byte[] byteBuffer = s.byteBuffer;
|
|
int halfLen = byteLen >> LOG_HALF_SIZE;
|
|
if (BITNESS == 64) {
|
|
int[] intBuffer = s.intBuffer;
|
|
for (int i = 0; i < halfLen; ++i) {
|
|
intBuffer[i] = ((byteBuffer[i * 4] & 0xFF))
|
|
| ((byteBuffer[(i * 4) + 1] & 0xFF) << 8)
|
|
| ((byteBuffer[(i * 4) + 2] & 0xFF) << 16)
|
|
| ((byteBuffer[(i * 4) + 3] & 0xFF) << 24);
|
|
}
|
|
} else {
|
|
short[] shortBuffer = s.shortBuffer;
|
|
for (int i = 0; i < halfLen; ++i) {
|
|
shortBuffer[i] = (short) ((byteBuffer[i * 2] & 0xFF)
|
|
| ((byteBuffer[(i * 2) + 1] & 0xFF) << 8));
|
|
}
|
|
}
|
|
}
|
|
}
|