/* 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 static org.brotli.dec.RunningState.BLOCK_START; import static org.brotli.dec.RunningState.CLOSED; import static org.brotli.dec.RunningState.COMPRESSED_BLOCK_START; import static org.brotli.dec.RunningState.COPY_LOOP; import static org.brotli.dec.RunningState.COPY_UNCOMPRESSED; import static org.brotli.dec.RunningState.COPY_WRAP_BUFFER; import static org.brotli.dec.RunningState.FINISHED; import static org.brotli.dec.RunningState.INSERT_LOOP; import static org.brotli.dec.RunningState.MAIN_LOOP; import static org.brotli.dec.RunningState.READ_METADATA; import static org.brotli.dec.RunningState.TRANSFORM; import static org.brotli.dec.RunningState.UNINITIALIZED; import static org.brotli.dec.RunningState.WRITE; /** * API for Brotli decompression. */ final class Decode { private static final int DEFAULT_CODE_LENGTH = 8; private static final int CODE_LENGTH_REPEAT_CODE = 16; private static final int NUM_LITERAL_CODES = 256; private static final int NUM_INSERT_AND_COPY_CODES = 704; private static final int NUM_BLOCK_LENGTH_CODES = 26; private static final int LITERAL_CONTEXT_BITS = 6; private static final int DISTANCE_CONTEXT_BITS = 2; private static final int HUFFMAN_TABLE_BITS = 8; private static final int HUFFMAN_TABLE_MASK = 0xFF; private static final int CODE_LENGTH_CODES = 18; private static final int[] CODE_LENGTH_CODE_ORDER = { 1, 2, 3, 4, 0, 5, 17, 6, 16, 7, 8, 9, 10, 11, 12, 13, 14, 15, }; private static final int NUM_DISTANCE_SHORT_CODES = 16; private static final int[] DISTANCE_SHORT_CODE_INDEX_OFFSET = { 3, 2, 1, 0, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 }; private static final int[] DISTANCE_SHORT_CODE_VALUE_OFFSET = { 0, 0, 0, 0, -1, 1, -2, 2, -3, 3, -1, 1, -2, 2, -3, 3 }; /** * Static Huffman code for the code length code lengths. */ private static final int[] FIXED_TABLE = { 0x020000, 0x020004, 0x020003, 0x030002, 0x020000, 0x020004, 0x020003, 0x040001, 0x020000, 0x020004, 0x020003, 0x030002, 0x020000, 0x020004, 0x020003, 0x040005 }; /** * Decodes a number in the range [0..255], by reading 1 - 11 bits. */ private static int decodeVarLenUnsignedByte(BitReader br) { if (BitReader.readBits(br, 1) != 0) { int n = BitReader.readBits(br, 3); if (n == 0) { return 1; } else { return BitReader.readBits(br, n) + (1 << n); } } return 0; } private static void decodeMetaBlockLength(BitReader br, State state) { state.inputEnd = BitReader.readBits(br, 1) == 1; state.metaBlockLength = 0; state.isUncompressed = false; state.isMetadata = false; if (state.inputEnd && BitReader.readBits(br, 1) != 0) { return; } int sizeNibbles = BitReader.readBits(br, 2) + 4; if (sizeNibbles == 7) { state.isMetadata = true; if (BitReader.readBits(br, 1) != 0) { throw new BrotliRuntimeException("Corrupted reserved bit"); } int sizeBytes = BitReader.readBits(br, 2); if (sizeBytes == 0) { return; } for (int i = 0; i < sizeBytes; i++) { int bits = BitReader.readBits(br, 8); if (bits == 0 && i + 1 == sizeBytes && sizeBytes > 1) { throw new BrotliRuntimeException("Exuberant nibble"); } state.metaBlockLength |= bits << (i * 8); } } else { for (int i = 0; i < sizeNibbles; i++) { int bits = BitReader.readBits(br, 4); if (bits == 0 && i + 1 == sizeNibbles && sizeNibbles > 4) { throw new BrotliRuntimeException("Exuberant nibble"); } state.metaBlockLength |= bits << (i * 4); } } state.metaBlockLength++; if (!state.inputEnd) { state.isUncompressed = BitReader.readBits(br, 1) == 1; } } /** * Decodes the next Huffman code from bit-stream. */ private static int readSymbol(int[] table, int offset, BitReader br) { int val = (int) (br.accumulator >>> br.bitOffset); offset += val & HUFFMAN_TABLE_MASK; int bits = table[offset] >> 16; int sym = table[offset] & 0xFFFF; if (bits <= HUFFMAN_TABLE_BITS) { br.bitOffset += bits; return sym; } offset += sym; int mask = (1 << bits) - 1; offset += (val & mask) >>> HUFFMAN_TABLE_BITS; br.bitOffset += ((table[offset] >> 16) + HUFFMAN_TABLE_BITS); return table[offset] & 0xFFFF; } private static int readBlockLength(int[] table, int offset, BitReader br) { BitReader.fillBitWindow(br); int code = readSymbol(table, offset, br); int n = Prefix.BLOCK_LENGTH_N_BITS[code]; return Prefix.BLOCK_LENGTH_OFFSET[code] + BitReader.readBits(br, n); } private static int translateShortCodes(int code, int[] ringBuffer, int index) { if (code < NUM_DISTANCE_SHORT_CODES) { index += DISTANCE_SHORT_CODE_INDEX_OFFSET[code]; index &= 3; return ringBuffer[index] + DISTANCE_SHORT_CODE_VALUE_OFFSET[code]; } return code - NUM_DISTANCE_SHORT_CODES + 1; } private static void moveToFront(int[] v, int index) { int value = v[index]; for (; index > 0; index--) { v[index] = v[index - 1]; } v[0] = value; } private static void inverseMoveToFrontTransform(byte[] v, int vLen) { int[] mtf = new int[256]; for (int i = 0; i < 256; i++) { mtf[i] = i; } for (int i = 0; i < vLen; i++) { int index = v[i] & 0xFF; v[i] = (byte) mtf[index]; if (index != 0) { moveToFront(mtf, index); } } } private static void readHuffmanCodeLengths( int[] codeLengthCodeLengths, int numSymbols, int[] codeLengths, BitReader br) { int symbol = 0; int prevCodeLen = DEFAULT_CODE_LENGTH; int repeat = 0; int repeatCodeLen = 0; int space = 32768; int[] table = new int[32]; Huffman.buildHuffmanTable(table, 0, 5, codeLengthCodeLengths, CODE_LENGTH_CODES); while (symbol < numSymbols && space > 0) { BitReader.readMoreInput(br); BitReader.fillBitWindow(br); int p = (int) ((br.accumulator >>> br.bitOffset)) & 31; br.bitOffset += table[p] >> 16; int codeLen = table[p] & 0xFFFF; if (codeLen < CODE_LENGTH_REPEAT_CODE) { repeat = 0; codeLengths[symbol++] = codeLen; if (codeLen != 0) { prevCodeLen = codeLen; space -= 32768 >> codeLen; } } else { int extraBits = codeLen - 14; int newLen = 0; if (codeLen == CODE_LENGTH_REPEAT_CODE) { newLen = prevCodeLen; } if (repeatCodeLen != newLen) { repeat = 0; repeatCodeLen = newLen; } int oldRepeat = repeat; if (repeat > 0) { repeat -= 2; repeat <<= extraBits; } repeat += BitReader.readBits(br, extraBits) + 3; int repeatDelta = repeat - oldRepeat; if (symbol + repeatDelta > numSymbols) { throw new BrotliRuntimeException("symbol + repeatDelta > numSymbols"); // COV_NF_LINE } for (int i = 0; i < repeatDelta; i++) { codeLengths[symbol++] = repeatCodeLen; } if (repeatCodeLen != 0) { space -= repeatDelta << (15 - repeatCodeLen); } } } if (space != 0) { throw new BrotliRuntimeException("Unused space"); // COV_NF_LINE } // TODO: Pass max_symbol to Huffman table builder instead? Utils.fillWithZeroes(codeLengths, symbol, numSymbols - symbol); } // TODO: Use specialized versions for smaller tables. static void readHuffmanCode(int alphabetSize, int[] table, int offset, BitReader br) { boolean ok = true; int simpleCodeOrSkip; BitReader.readMoreInput(br); // TODO: Avoid allocation. int[] codeLengths = new int[alphabetSize]; simpleCodeOrSkip = BitReader.readBits(br, 2); if (simpleCodeOrSkip == 1) { // Read symbols, codes & code lengths directly. int maxBitsCounter = alphabetSize - 1; int maxBits = 0; int[] symbols = new int[4]; int numSymbols = BitReader.readBits(br, 2) + 1; while (maxBitsCounter != 0) { maxBitsCounter >>= 1; maxBits++; } // TODO: uncomment when codeLengths is reused. // Utils.fillWithZeroes(codeLengths, 0, alphabetSize); for (int i = 0; i < numSymbols; i++) { symbols[i] = BitReader.readBits(br, maxBits) % alphabetSize; codeLengths[symbols[i]] = 2; } codeLengths[symbols[0]] = 1; switch (numSymbols) { case 1: break; case 2: ok = symbols[0] != symbols[1]; codeLengths[symbols[1]] = 1; break; case 3: ok = symbols[0] != symbols[1] && symbols[0] != symbols[2] && symbols[1] != symbols[2]; break; case 4: default: ok = symbols[0] != symbols[1] && symbols[0] != symbols[2] && symbols[0] != symbols[3] && symbols[1] != symbols[2] && symbols[1] != symbols[3] && symbols[2] != symbols[3]; if (BitReader.readBits(br, 1) == 1) { codeLengths[symbols[2]] = 3; codeLengths[symbols[3]] = 3; } else { codeLengths[symbols[0]] = 2; } break; } } else { // Decode Huffman-coded code lengths. int[] codeLengthCodeLengths = new int[CODE_LENGTH_CODES]; int space = 32; int numCodes = 0; for (int i = simpleCodeOrSkip; i < CODE_LENGTH_CODES && space > 0; i++) { int codeLenIdx = CODE_LENGTH_CODE_ORDER[i]; BitReader.fillBitWindow(br); int p = (int) (br.accumulator >>> br.bitOffset) & 15; // TODO: Demultiplex FIXED_TABLE. br.bitOffset += FIXED_TABLE[p] >> 16; int v = FIXED_TABLE[p] & 0xFFFF; codeLengthCodeLengths[codeLenIdx] = v; if (v != 0) { space -= (32 >> v); numCodes++; } } ok = (numCodes == 1 || space == 0); readHuffmanCodeLengths(codeLengthCodeLengths, alphabetSize, codeLengths, br); } if (!ok) { throw new BrotliRuntimeException("Can't readHuffmanCode"); // COV_NF_LINE } Huffman.buildHuffmanTable(table, offset, HUFFMAN_TABLE_BITS, codeLengths, alphabetSize); } private static int decodeContextMap(int contextMapSize, byte[] contextMap, BitReader br) { BitReader.readMoreInput(br); int numTrees = decodeVarLenUnsignedByte(br) + 1; if (numTrees == 1) { Utils.fillWithZeroes(contextMap, 0, contextMapSize); return numTrees; } boolean useRleForZeros = BitReader.readBits(br, 1) == 1; int maxRunLengthPrefix = 0; if (useRleForZeros) { maxRunLengthPrefix = BitReader.readBits(br, 4) + 1; } int[] table = new int[Huffman.HUFFMAN_MAX_TABLE_SIZE]; readHuffmanCode(numTrees + maxRunLengthPrefix, table, 0, br); for (int i = 0; i < contextMapSize; ) { BitReader.readMoreInput(br); BitReader.fillBitWindow(br); int code = readSymbol(table, 0, br); if (code == 0) { contextMap[i] = 0; i++; } else if (code <= maxRunLengthPrefix) { int reps = (1 << code) + BitReader.readBits(br, code); while (reps != 0) { if (i >= contextMapSize) { throw new BrotliRuntimeException("Corrupted context map"); // COV_NF_LINE } contextMap[i] = 0; i++; reps--; } } else { contextMap[i] = (byte) (code - maxRunLengthPrefix); i++; } } if (BitReader.readBits(br, 1) == 1) { inverseMoveToFrontTransform(contextMap, contextMapSize); } return numTrees; } private static void decodeBlockTypeAndLength(State state, int treeType) { final BitReader br = state.br; final int[] ringBuffers = state.blockTypeRb; final int offset = treeType * 2; BitReader.fillBitWindow(br); int blockType = readSymbol( state.blockTypeTrees, treeType * Huffman.HUFFMAN_MAX_TABLE_SIZE, br); state.blockLength[treeType] = readBlockLength(state.blockLenTrees, treeType * Huffman.HUFFMAN_MAX_TABLE_SIZE, br); if (blockType == 1) { blockType = ringBuffers[offset + 1] + 1; } else if (blockType == 0) { blockType = ringBuffers[offset]; } else { blockType -= 2; } if (blockType >= state.numBlockTypes[treeType]) { blockType -= state.numBlockTypes[treeType]; } ringBuffers[offset] = ringBuffers[offset + 1]; ringBuffers[offset + 1] = blockType; } private static void decodeLiteralBlockSwitch(State state) { decodeBlockTypeAndLength(state, 0); int literalBlockType = state.blockTypeRb[1]; state.contextMapSlice = literalBlockType << LITERAL_CONTEXT_BITS; state.literalTreeIndex = state.contextMap[state.contextMapSlice] & 0xFF; state.literalTree = state.hGroup0.trees[state.literalTreeIndex]; int contextMode = state.contextModes[literalBlockType]; state.contextLookupOffset1 = Context.LOOKUP_OFFSETS[contextMode]; state.contextLookupOffset2 = Context.LOOKUP_OFFSETS[contextMode + 1]; } private static void decodeCommandBlockSwitch(State state) { decodeBlockTypeAndLength(state, 1); state.treeCommandOffset = state.hGroup1.trees[state.blockTypeRb[3]]; } private static void decodeDistanceBlockSwitch(State state) { decodeBlockTypeAndLength(state, 2); state.distContextMapSlice = state.blockTypeRb[5] << DISTANCE_CONTEXT_BITS; } private static void maybeReallocateRingBuffer(State state) { int newSize = state.maxRingBufferSize; if ((long) newSize > state.expectedTotalSize) { /* TODO: Handle 2GB+ cases more gracefully. */ int minimalNewSize = (int) state.expectedTotalSize + state.customDictionary.length; while ((newSize >> 1) > minimalNewSize) { newSize >>= 1; } if (!state.inputEnd && newSize < 16384 && state.maxRingBufferSize >= 16384) { newSize = 16384; } } if (newSize <= state.ringBufferSize) { return; } int ringBufferSizeWithSlack = newSize + Dictionary.MAX_TRANSFORMED_WORD_LENGTH; byte[] newBuffer = new byte[ringBufferSizeWithSlack]; if (state.ringBuffer != null) { System.arraycopy(state.ringBuffer, 0, newBuffer, 0, state.ringBufferSize); } else { /* Prepend custom dictionary, if any. */ if (state.customDictionary.length != 0) { int length = state.customDictionary.length; int offset = 0; if (length > state.maxBackwardDistance) { offset = length - state.maxBackwardDistance; length = state.maxBackwardDistance; } System.arraycopy(state.customDictionary, offset, newBuffer, 0, length); state.pos = length; state.bytesToIgnore = length; } } state.ringBuffer = newBuffer; state.ringBufferSize = newSize; } /** * Reads next metablock header. * * @param state decoding state */ private static void readMetablockInfo(State state) { final BitReader br = state.br; if (state.inputEnd) { state.nextRunningState = FINISHED; state.bytesToWrite = state.pos; state.bytesWritten = 0; state.runningState = WRITE; return; } // TODO: Reset? Do we need this? state.hGroup0.codes = null; state.hGroup0.trees = null; state.hGroup1.codes = null; state.hGroup1.trees = null; state.hGroup2.codes = null; state.hGroup2.trees = null; BitReader.readMoreInput(br); decodeMetaBlockLength(br, state); if (state.metaBlockLength == 0 && !state.isMetadata) { return; } if (state.isUncompressed || state.isMetadata) { BitReader.jumpToByteBoundary(br); state.runningState = state.isMetadata ? READ_METADATA : COPY_UNCOMPRESSED; } else { state.runningState = COMPRESSED_BLOCK_START; } if (state.isMetadata) { return; } state.expectedTotalSize += state.metaBlockLength; if (state.ringBufferSize < state.maxRingBufferSize) { maybeReallocateRingBuffer(state); } } private static void readMetablockHuffmanCodesAndContextMaps(State state) { final BitReader br = state.br; for (int i = 0; i < 3; i++) { state.numBlockTypes[i] = decodeVarLenUnsignedByte(br) + 1; state.blockLength[i] = 1 << 28; if (state.numBlockTypes[i] > 1) { readHuffmanCode(state.numBlockTypes[i] + 2, state.blockTypeTrees, i * Huffman.HUFFMAN_MAX_TABLE_SIZE, br); readHuffmanCode(NUM_BLOCK_LENGTH_CODES, state.blockLenTrees, i * Huffman.HUFFMAN_MAX_TABLE_SIZE, br); state.blockLength[i] = readBlockLength(state.blockLenTrees, i * Huffman.HUFFMAN_MAX_TABLE_SIZE, br); } } BitReader.readMoreInput(br); state.distancePostfixBits = BitReader.readBits(br, 2); state.numDirectDistanceCodes = NUM_DISTANCE_SHORT_CODES + (BitReader.readBits(br, 4) << state.distancePostfixBits); state.distancePostfixMask = (1 << state.distancePostfixBits) - 1; int numDistanceCodes = state.numDirectDistanceCodes + (48 << state.distancePostfixBits); // TODO: Reuse? state.contextModes = new byte[state.numBlockTypes[0]]; for (int i = 0; i < state.numBlockTypes[0];) { /* Ensure that less than 256 bits read between readMoreInput. */ int limit = Math.min(i + 96, state.numBlockTypes[0]); for (; i < limit; ++i) { state.contextModes[i] = (byte) (BitReader.readBits(br, 2) << 1); } BitReader.readMoreInput(br); } // TODO: Reuse? state.contextMap = new byte[state.numBlockTypes[0] << LITERAL_CONTEXT_BITS]; int numLiteralTrees = decodeContextMap(state.numBlockTypes[0] << LITERAL_CONTEXT_BITS, state.contextMap, br); state.trivialLiteralContext = true; for (int j = 0; j < state.numBlockTypes[0] << LITERAL_CONTEXT_BITS; j++) { if (state.contextMap[j] != j >> LITERAL_CONTEXT_BITS) { state.trivialLiteralContext = false; break; } } // TODO: Reuse? state.distContextMap = new byte[state.numBlockTypes[2] << DISTANCE_CONTEXT_BITS]; int numDistTrees = decodeContextMap(state.numBlockTypes[2] << DISTANCE_CONTEXT_BITS, state.distContextMap, br); HuffmanTreeGroup.init(state.hGroup0, NUM_LITERAL_CODES, numLiteralTrees); HuffmanTreeGroup.init(state.hGroup1, NUM_INSERT_AND_COPY_CODES, state.numBlockTypes[1]); HuffmanTreeGroup.init(state.hGroup2, numDistanceCodes, numDistTrees); HuffmanTreeGroup.decode(state.hGroup0, br); HuffmanTreeGroup.decode(state.hGroup1, br); HuffmanTreeGroup.decode(state.hGroup2, br); state.contextMapSlice = 0; state.distContextMapSlice = 0; state.contextLookupOffset1 = Context.LOOKUP_OFFSETS[state.contextModes[0]]; state.contextLookupOffset2 = Context.LOOKUP_OFFSETS[state.contextModes[0] + 1]; state.literalTreeIndex = 0; state.literalTree = state.hGroup0.trees[0]; state.treeCommandOffset = state.hGroup1.trees[0]; // TODO: == 0? state.blockTypeRb[0] = state.blockTypeRb[2] = state.blockTypeRb[4] = 1; state.blockTypeRb[1] = state.blockTypeRb[3] = state.blockTypeRb[5] = 0; } private static void copyUncompressedData(State state) { final BitReader br = state.br; final byte[] ringBuffer = state.ringBuffer; // Could happen if block ends at ring buffer end. if (state.metaBlockLength <= 0) { BitReader.reload(br); state.runningState = BLOCK_START; return; } int chunkLength = Math.min(state.ringBufferSize - state.pos, state.metaBlockLength); BitReader.copyBytes(br, ringBuffer, state.pos, chunkLength); state.metaBlockLength -= chunkLength; state.pos += chunkLength; if (state.pos == state.ringBufferSize) { state.nextRunningState = COPY_UNCOMPRESSED; state.bytesToWrite = state.ringBufferSize; state.bytesWritten = 0; state.runningState = WRITE; return; } BitReader.reload(br); state.runningState = BLOCK_START; } private static boolean writeRingBuffer(State state) { /* Ignore custom dictionary bytes. */ if (state.bytesToIgnore != 0) { state.bytesWritten += state.bytesToIgnore; state.bytesToIgnore = 0; } int toWrite = Math.min(state.outputLength - state.outputUsed, state.bytesToWrite - state.bytesWritten); if (toWrite != 0) { System.arraycopy(state.ringBuffer, state.bytesWritten, state.output, state.outputOffset + state.outputUsed, toWrite); state.outputUsed += toWrite; state.bytesWritten += toWrite; } return state.outputUsed < state.outputLength; } static void setCustomDictionary(State state, byte[] data) { state.customDictionary = (data == null) ? new byte[0] : data; } /** * Actual decompress implementation. */ static void decompress(State state) { if (state.runningState == UNINITIALIZED) { throw new IllegalStateException("Can't decompress until initialized"); } if (state.runningState == CLOSED) { throw new IllegalStateException("Can't decompress after close"); } final BitReader br = state.br; int ringBufferMask = state.ringBufferSize - 1; byte[] ringBuffer = state.ringBuffer; while (state.runningState != FINISHED) { // TODO: extract cases to methods for the better readability. switch (state.runningState) { case BLOCK_START: if (state.metaBlockLength < 0) { throw new BrotliRuntimeException("Invalid metablock length"); } readMetablockInfo(state); /* Ring-buffer would be reallocated here. */ ringBufferMask = state.ringBufferSize - 1; ringBuffer = state.ringBuffer; continue; case COMPRESSED_BLOCK_START: readMetablockHuffmanCodesAndContextMaps(state); state.runningState = MAIN_LOOP; // Fall through case MAIN_LOOP: if (state.metaBlockLength <= 0) { state.runningState = BLOCK_START; continue; } BitReader.readMoreInput(br); if (state.blockLength[1] == 0) { decodeCommandBlockSwitch(state); } state.blockLength[1]--; BitReader.fillBitWindow(br); int cmdCode = readSymbol(state.hGroup1.codes, state.treeCommandOffset, br); int rangeIdx = cmdCode >>> 6; state.distanceCode = 0; if (rangeIdx >= 2) { rangeIdx -= 2; state.distanceCode = -1; } int insertCode = Prefix.INSERT_RANGE_LUT[rangeIdx] + ((cmdCode >>> 3) & 7); int copyCode = Prefix.COPY_RANGE_LUT[rangeIdx] + (cmdCode & 7); state.insertLength = Prefix.INSERT_LENGTH_OFFSET[insertCode] + BitReader .readBits(br, Prefix.INSERT_LENGTH_N_BITS[insertCode]); state.copyLength = Prefix.COPY_LENGTH_OFFSET[copyCode] + BitReader .readBits(br, Prefix.COPY_LENGTH_N_BITS[copyCode]); state.j = 0; state.runningState = INSERT_LOOP; // Fall through case INSERT_LOOP: if (state.trivialLiteralContext) { while (state.j < state.insertLength) { BitReader.readMoreInput(br); if (state.blockLength[0] == 0) { decodeLiteralBlockSwitch(state); } state.blockLength[0]--; BitReader.fillBitWindow(br); ringBuffer[state.pos] = (byte) readSymbol(state.hGroup0.codes, state.literalTree, br); state.j++; if (state.pos++ == ringBufferMask) { state.nextRunningState = INSERT_LOOP; state.bytesToWrite = state.ringBufferSize; state.bytesWritten = 0; state.runningState = WRITE; break; } } } else { int prevByte1 = ringBuffer[(state.pos - 1) & ringBufferMask] & 0xFF; int prevByte2 = ringBuffer[(state.pos - 2) & ringBufferMask] & 0xFF; while (state.j < state.insertLength) { BitReader.readMoreInput(br); if (state.blockLength[0] == 0) { decodeLiteralBlockSwitch(state); } int literalTreeIndex = state.contextMap[state.contextMapSlice + (Context.LOOKUP[state.contextLookupOffset1 + prevByte1] | Context.LOOKUP[state.contextLookupOffset2 + prevByte2])] & 0xFF; state.blockLength[0]--; prevByte2 = prevByte1; BitReader.fillBitWindow(br); prevByte1 = readSymbol( state.hGroup0.codes, state.hGroup0.trees[literalTreeIndex], br); ringBuffer[state.pos] = (byte) prevByte1; state.j++; if (state.pos++ == ringBufferMask) { state.nextRunningState = INSERT_LOOP; state.bytesToWrite = state.ringBufferSize; state.bytesWritten = 0; state.runningState = WRITE; break; } } } if (state.runningState != INSERT_LOOP) { continue; } state.metaBlockLength -= state.insertLength; if (state.metaBlockLength <= 0) { state.runningState = MAIN_LOOP; continue; } if (state.distanceCode < 0) { BitReader.readMoreInput(br); if (state.blockLength[2] == 0) { decodeDistanceBlockSwitch(state); } state.blockLength[2]--; BitReader.fillBitWindow(br); state.distanceCode = readSymbol(state.hGroup2.codes, state.hGroup2.trees[ state.distContextMap[state.distContextMapSlice + (state.copyLength > 4 ? 3 : state.copyLength - 2)] & 0xFF], br); if (state.distanceCode >= state.numDirectDistanceCodes) { state.distanceCode -= state.numDirectDistanceCodes; int postfix = state.distanceCode & state.distancePostfixMask; state.distanceCode >>>= state.distancePostfixBits; int n = (state.distanceCode >>> 1) + 1; int offset = ((2 + (state.distanceCode & 1)) << n) - 4; state.distanceCode = state.numDirectDistanceCodes + postfix + ((offset + BitReader.readBits(br, n)) << state.distancePostfixBits); } } // Convert the distance code to the actual distance by possibly looking up past distances // from the ringBuffer. state.distance = translateShortCodes(state.distanceCode, state.distRb, state.distRbIdx); if (state.distance < 0) { throw new BrotliRuntimeException("Negative distance"); // COV_NF_LINE } if (state.maxDistance != state.maxBackwardDistance && state.pos < state.maxBackwardDistance) { state.maxDistance = state.pos; } else { state.maxDistance = state.maxBackwardDistance; } state.copyDst = state.pos; if (state.distance > state.maxDistance) { state.runningState = TRANSFORM; continue; } if (state.distanceCode > 0) { state.distRb[state.distRbIdx & 3] = state.distance; state.distRbIdx++; } if (state.copyLength > state.metaBlockLength) { throw new BrotliRuntimeException("Invalid backward reference"); // COV_NF_LINE } state.j = 0; state.runningState = COPY_LOOP; // fall through case COPY_LOOP: int src = (state.pos - state.distance) & ringBufferMask; int dst = state.pos; int copyLength = state.copyLength - state.j; if ((src + copyLength < ringBufferMask) && (dst + copyLength < ringBufferMask)) { for (int k = 0; k < copyLength; ++k) { ringBuffer[dst++] = ringBuffer[src++]; } state.j += copyLength; state.metaBlockLength -= copyLength; state.pos += copyLength; } else { for (; state.j < state.copyLength;) { ringBuffer[state.pos] = ringBuffer[(state.pos - state.distance) & ringBufferMask]; state.metaBlockLength--; state.j++; if (state.pos++ == ringBufferMask) { state.nextRunningState = COPY_LOOP; state.bytesToWrite = state.ringBufferSize; state.bytesWritten = 0; state.runningState = WRITE; break; } } } if (state.runningState == COPY_LOOP) { state.runningState = MAIN_LOOP; } continue; case TRANSFORM: if (state.copyLength >= Dictionary.MIN_WORD_LENGTH && state.copyLength <= Dictionary.MAX_WORD_LENGTH) { int offset = Dictionary.OFFSETS_BY_LENGTH[state.copyLength]; int wordId = state.distance - state.maxDistance - 1; int shift = Dictionary.SIZE_BITS_BY_LENGTH[state.copyLength]; int mask = (1 << shift) - 1; int wordIdx = wordId & mask; int transformIdx = wordId >>> shift; offset += wordIdx * state.copyLength; if (transformIdx < Transform.TRANSFORMS.length) { int len = Transform.transformDictionaryWord(ringBuffer, state.copyDst, Dictionary.getData(), offset, state.copyLength, Transform.TRANSFORMS[transformIdx]); state.copyDst += len; state.pos += len; state.metaBlockLength -= len; if (state.copyDst >= state.ringBufferSize) { state.nextRunningState = COPY_WRAP_BUFFER; state.bytesToWrite = state.ringBufferSize; state.bytesWritten = 0; state.runningState = WRITE; continue; } } else { throw new BrotliRuntimeException("Invalid backward reference"); // COV_NF_LINE } } else { throw new BrotliRuntimeException("Invalid backward reference"); // COV_NF_LINE } state.runningState = MAIN_LOOP; continue; case COPY_WRAP_BUFFER: System.arraycopy(ringBuffer, state.ringBufferSize, ringBuffer, 0, state.copyDst - state.ringBufferSize); state.runningState = MAIN_LOOP; continue; case READ_METADATA: while (state.metaBlockLength > 0) { BitReader.readMoreInput(br); // Optimize BitReader.readBits(br, 8); state.metaBlockLength--; } state.runningState = BLOCK_START; continue; case COPY_UNCOMPRESSED: copyUncompressedData(state); continue; case WRITE: if (!writeRingBuffer(state)) { // Output buffer is full. return; } if (state.pos >= state.maxBackwardDistance) { state.maxDistance = state.maxBackwardDistance; } state.pos &= ringBufferMask; state.runningState = state.nextRunningState; continue; default: throw new BrotliRuntimeException("Unexpected state " + state.runningState); } } if (state.runningState == FINISHED) { if (state.metaBlockLength < 0) { throw new BrotliRuntimeException("Invalid metablock length"); } BitReader.jumpToByteBoundary(br); BitReader.checkHealth(state.br, true); } } }