private static void CopyUncompressedData(Brotli.State state) { Brotli.BitReader br = state.br; byte[] ringBuffer = state.ringBuffer; // Could happen if block ends at ring buffer end. if (state.metaBlockLength <= 0) { Brotli.BitReader.Reload(br); state.runningState = Brotli.RunningState.BlockStart; return; } int chunkLength = System.Math.Min(state.ringBufferSize - state.pos, state.metaBlockLength); Brotli.BitReader.CopyBytes(br, ringBuffer, state.pos, chunkLength); state.metaBlockLength -= chunkLength; state.pos += chunkLength; if (state.pos == state.ringBufferSize) { state.nextRunningState = Brotli.RunningState.CopyUncompressed; state.bytesToWrite = state.ringBufferSize; state.bytesWritten = 0; state.runningState = Brotli.RunningState.Write; return; } Brotli.BitReader.Reload(br); state.runningState = Brotli.RunningState.BlockStart; }
internal static void Reload(Brotli.BitReader br) { if (br.bitOffset == 64) { Prepare(br); } }
private static void DecodeBlockTypeAndLength(Brotli.State state, int treeType) { Brotli.BitReader br = state.br; int[] ringBuffers = state.blockTypeRb; int offset = treeType * 2; Brotli.BitReader.FillBitWindow(br); int blockType = ReadSymbol(state.blockTypeTrees, treeType * Brotli.Huffman.HuffmanMaxTableSize, br); state.blockLength[treeType] = ReadBlockLength(state.blockLenTrees, treeType * Brotli.Huffman.HuffmanMaxTableSize, 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 Prepare(Brotli.BitReader br) { ReadMoreInput(br); CheckHealth(br, false); FillBitWindow(br); FillBitWindow(br); }
/// <summary>Advances the Read buffer by 5 bytes to make room for reading next 24 bits.</summary> internal static void FillBitWindow(Brotli.BitReader br) { if (br.bitOffset >= 32) { br.accumulator = ((long)br.intBuffer[br.intOffset++] << 32) | ((long)(((ulong)br.accumulator) >> 32)); br.bitOffset -= 32; } }
/// <summary>Reads the specified number of bits from Read Buffer.</summary> internal static int ReadBits(Brotli.BitReader br, int n) { FillBitWindow(br); int val = (int)((long)(((ulong)br.accumulator) >> br.bitOffset)) & ((1 << n) - 1); br.bitOffset += n; return(val); }
private static int ReadBlockLength(int[] table, int offset, Brotli.BitReader br) { Brotli.BitReader.FillBitWindow(br); int code = ReadSymbol(table, offset, br); int n = Brotli.Prefix.BlockLengthNBits[code]; return(Brotli.Prefix.BlockLengthOffset[code] + Brotli.BitReader.ReadBits(br, n)); }
/// <exception cref="System.IO.IOException"/> internal static void Close(Brotli.BitReader br) { System.IO.Stream @is = br.input; br.input = null; if (@is != null) { @is.Close(); } }
private static int DecodeContextMap(int contextMapSize, byte[] contextMap, Brotli.BitReader br) { Brotli.BitReader.ReadMoreInput(br); int numTrees = DecodeVarLenUnsignedByte(br) + 1; if (numTrees == 1) { Brotli.Utils.FillWithZeroes(contextMap, 0, contextMapSize); return(numTrees); } bool useRleForZeros = Brotli.BitReader.ReadBits(br, 1) == 1; int maxRunLengthPrefix = 0; if (useRleForZeros) { maxRunLengthPrefix = Brotli.BitReader.ReadBits(br, 4) + 1; } int[] table = new int[Brotli.Huffman.HuffmanMaxTableSize]; ReadHuffmanCode(numTrees + maxRunLengthPrefix, table, 0, br); for (int i = 0; i < contextMapSize;) { Brotli.BitReader.ReadMoreInput(br); Brotli.BitReader.FillBitWindow(br); int code = ReadSymbol(table, 0, br); if (code == 0) { contextMap[i] = 0; i++; } else if (code <= maxRunLengthPrefix) { int reps = (1 << code) + Brotli.BitReader.ReadBits(br, code); while (reps != 0) { if (i >= contextMapSize) { throw new Brotli.BrotliRuntimeException("Corrupted context map"); } // COV_NF_LINE contextMap[i] = 0; i++; reps--; } } else { contextMap[i] = unchecked ((byte)(code - maxRunLengthPrefix)); i++; } } if (Brotli.BitReader.ReadBits(br, 1) == 1) { InverseMoveToFrontTransform(contextMap, contextMapSize); } return(numTrees); }
internal static int IntAvailable(Brotli.BitReader br) { int limit = Capacity; if (br.endOfStreamReached) { limit = (br.tailBytes + 3) >> 2; } return(limit - br.intOffset); }
/// <summary>Decodes Huffman trees from input stream and constructs lookup tables.</summary> /// <param name="group">target POJO</param> /// <param name="br">data source</param> internal static void Decode(Brotli.HuffmanTreeGroup group, Brotli.BitReader br) { int next = 0; int n = group.trees.Length; for (int i = 0; i < n; i++) { group.trees[i] = next; Brotli.Decode.ReadHuffmanCode(group.alphabetSize, group.codes, next, br); next += Brotli.Huffman.HuffmanMaxTableSize; } }
private static void DecodeMetaBlockLength(Brotli.BitReader br, Brotli.State state) { state.inputEnd = Brotli.BitReader.ReadBits(br, 1) == 1; state.metaBlockLength = 0; state.isUncompressed = false; state.isMetadata = false; if (state.inputEnd && Brotli.BitReader.ReadBits(br, 1) != 0) { return; } int sizeNibbles = Brotli.BitReader.ReadBits(br, 2) + 4; if (sizeNibbles == 7) { state.isMetadata = true; if (Brotli.BitReader.ReadBits(br, 1) != 0) { throw new Brotli.BrotliRuntimeException("Corrupted reserved bit"); } int sizeBytes = Brotli.BitReader.ReadBits(br, 2); if (sizeBytes == 0) { return; } for (int i = 0; i < sizeBytes; i++) { int bits = Brotli.BitReader.ReadBits(br, 8); if (bits == 0 && i + 1 == sizeBytes && sizeBytes > 1) { throw new Brotli.BrotliRuntimeException("Exuberant nibble"); } state.metaBlockLength |= bits << (i * 8); } } else { for (int i = 0; i < sizeNibbles; i++) { int bits = Brotli.BitReader.ReadBits(br, 4); if (bits == 0 && i + 1 == sizeNibbles && sizeNibbles > 4) { throw new Brotli.BrotliRuntimeException("Exuberant nibble"); } state.metaBlockLength |= bits << (i * 4); } } state.metaBlockLength++; if (!state.inputEnd) { state.isUncompressed = Brotli.BitReader.ReadBits(br, 1) == 1; } }
internal static void JumpToByteBoundary(Brotli.BitReader br) { int padding = (64 - br.bitOffset) & 7; if (padding != 0) { int paddingBits = Brotli.BitReader.ReadBits(br, padding); if (paddingBits != 0) { throw new Brotli.BrotliRuntimeException("Corrupted padding bits"); } } }
/// <summary>Initialize bit reader.</summary> /// <remarks> /// 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. /// </remarks> /// <param name="br">BitReader POJO</param> /// <param name="input">data source</param> internal static void Init(Brotli.BitReader br, System.IO.Stream input) { if (br.input != null) { throw new System.InvalidOperationException("Bit reader already has associated input stream"); } Brotli.IntReader.Init(br.intReader, br.byteBuffer, br.intBuffer); br.input = input; br.accumulator = 0; br.bitOffset = 64; br.intOffset = Capacity; br.endOfStreamReached = false; Prepare(br); }
/// <summary>Decodes a number in the range [0..255], by reading 1 - 11 bits.</summary> private static int DecodeVarLenUnsignedByte(Brotli.BitReader br) { if (Brotli.BitReader.ReadBits(br, 1) != 0) { int n = Brotli.BitReader.ReadBits(br, 3); if (n == 0) { return(1); } else { return(Brotli.BitReader.ReadBits(br, n) + (1 << n)); } } return(0); }
internal static void CheckHealth(Brotli.BitReader br, bool endOfStream) { if (!br.endOfStreamReached) { return; } int byteOffset = (br.intOffset << 2) + ((br.bitOffset + 7) >> 3) - 8; if (byteOffset > br.tailBytes) { throw new Brotli.BrotliRuntimeException("Read after end"); } if (endOfStream && (byteOffset != br.tailBytes)) { throw new Brotli.BrotliRuntimeException("Unused bytes after end"); } }
/// <summary>Reads next metablock header.</summary> /// <param name="state">decoding state</param> private static void ReadMetablockInfo(Brotli.State state) { Brotli.BitReader br = state.br; if (state.inputEnd) { state.nextRunningState = Brotli.RunningState.Finished; state.bytesToWrite = state.pos; state.bytesWritten = 0; state.runningState = Brotli.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; Brotli.BitReader.ReadMoreInput(br); DecodeMetaBlockLength(br, state); if (state.metaBlockLength == 0 && !state.isMetadata) { return; } if (state.isUncompressed || state.isMetadata) { Brotli.BitReader.JumpToByteBoundary(br); state.runningState = state.isMetadata ? Brotli.RunningState.ReadMetadata : Brotli.RunningState.CopyUncompressed; } else { state.runningState = Brotli.RunningState.CompressedBlockStart; } if (state.isMetadata) { return; } state.expectedTotalSize += state.metaBlockLength; if (state.ringBufferSize < state.maxRingBufferSize) { MaybeReallocateRingBuffer(state); } }
// Current meta-block header information. // TODO: Update to current spec. private static int DecodeWindowBits(Brotli.BitReader br) { if (Brotli.BitReader.ReadBits(br, 1) == 0) { return(16); } int n = Brotli.BitReader.ReadBits(br, 3); if (n != 0) { return(17 + n); } n = Brotli.BitReader.ReadBits(br, 3); if (n != 0) { return(8 + n); } return(17); }
/* Number of bytes in unfinished "int" item. */ /// <summary>Fills up the input buffer.</summary> /// <remarks> /// 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. /// </remarks> internal static void ReadMoreInput(Brotli.BitReader br) { // TODO: Split to check and read; move read outside of decoding loop. if (br.intOffset <= Capacity - 9) { return; } if (br.endOfStreamReached) { if (IntAvailable(br) >= -2) { return; } throw new Brotli.BrotliRuntimeException("No more input"); } int readOffset = br.intOffset << 2; int bytesRead = ByteReadSize - readOffset; System.Array.Copy(br.byteBuffer, readOffset, br.byteBuffer, 0, bytesRead); br.intOffset = 0; try { while (bytesRead < ByteReadSize) { int len = br.input.Read(br.byteBuffer, bytesRead, ByteReadSize - bytesRead); // EOF is -1 in Java, but 0 in C#. if (len <= 0) { br.endOfStreamReached = true; br.tailBytes = bytesRead; bytesRead += 3; break; } bytesRead += len; } } catch (System.IO.IOException e) { throw new Brotli.BrotliRuntimeException("Failed to read input", e); } Brotli.IntReader.Convert(br.intReader, bytesRead >> 2); }
/// <summary>Decodes the next Huffman code from bit-stream.</summary> private static int ReadSymbol(int[] table, int offset, Brotli.BitReader br) { int val = (int)((long)(((ulong)br.accumulator) >> br.bitOffset)); offset += val & HuffmanTableMask; int bits = table[offset] >> 16; int sym = table[offset] & unchecked ((int)(0xFFFF)); if (bits <= HuffmanTableBits) { br.bitOffset += bits; return(sym); } offset += sym; int mask = (1 << bits) - 1; offset += (int)(((uint)(val & mask)) >> HuffmanTableBits); br.bitOffset += ((table[offset] >> 16) + HuffmanTableBits); return(table[offset] & unchecked ((int)(0xFFFF))); }
/// <summary>Actual decompress implementation.</summary> internal static void Decompress(Brotli.State state) { if (state.runningState == Brotli.RunningState.Uninitialized) { throw new System.InvalidOperationException("Can't decompress until initialized"); } if (state.runningState == Brotli.RunningState.Closed) { throw new System.InvalidOperationException("Can't decompress after close"); } Brotli.BitReader br = state.br; int ringBufferMask = state.ringBufferSize - 1; byte[] ringBuffer = state.ringBuffer; while (state.runningState != Brotli.RunningState.Finished) { switch (state.runningState) { case Brotli.RunningState.BlockStart: { // TODO: extract cases to methods for the better readability. if (state.metaBlockLength < 0) { throw new Brotli.BrotliRuntimeException("Invalid metablock length"); } ReadMetablockInfo(state); /* Ring-buffer would be reallocated here. */ ringBufferMask = state.ringBufferSize - 1; ringBuffer = state.ringBuffer; continue; } case Brotli.RunningState.CompressedBlockStart: { ReadMetablockHuffmanCodesAndContextMaps(state); state.runningState = Brotli.RunningState.MainLoop; goto case Brotli.RunningState.MainLoop; } case Brotli.RunningState.MainLoop: { // Fall through if (state.metaBlockLength <= 0) { state.runningState = Brotli.RunningState.BlockStart; continue; } Brotli.BitReader.ReadMoreInput(br); if (state.blockLength[1] == 0) { DecodeCommandBlockSwitch(state); } state.blockLength[1]--; Brotli.BitReader.FillBitWindow(br); int cmdCode = ReadSymbol(state.hGroup1.codes, state.treeCommandOffset, br); int rangeIdx = (int)(((uint)cmdCode) >> 6); state.distanceCode = 0; if (rangeIdx >= 2) { rangeIdx -= 2; state.distanceCode = -1; } int insertCode = Brotli.Prefix.InsertRangeLut[rangeIdx] + (((int)(((uint)cmdCode) >> 3)) & 7); int copyCode = Brotli.Prefix.CopyRangeLut[rangeIdx] + (cmdCode & 7); state.insertLength = Brotli.Prefix.InsertLengthOffset[insertCode] + Brotli.BitReader.ReadBits(br, Brotli.Prefix.InsertLengthNBits[insertCode]); state.copyLength = Brotli.Prefix.CopyLengthOffset[copyCode] + Brotli.BitReader.ReadBits(br, Brotli.Prefix.CopyLengthNBits[copyCode]); state.j = 0; state.runningState = Brotli.RunningState.InsertLoop; goto case Brotli.RunningState.InsertLoop; } case Brotli.RunningState.InsertLoop: { // Fall through if (state.trivialLiteralContext) { while (state.j < state.insertLength) { Brotli.BitReader.ReadMoreInput(br); if (state.blockLength[0] == 0) { DecodeLiteralBlockSwitch(state); } state.blockLength[0]--; Brotli.BitReader.FillBitWindow(br); ringBuffer[state.pos] = unchecked ((byte)ReadSymbol(state.hGroup0.codes, state.literalTree, br)); state.j++; if (state.pos++ == ringBufferMask) { state.nextRunningState = Brotli.RunningState.InsertLoop; state.bytesToWrite = state.ringBufferSize; state.bytesWritten = 0; state.runningState = Brotli.RunningState.Write; break; } } } else { int prevByte1 = ringBuffer[(state.pos - 1) & ringBufferMask] & unchecked ((int)(0xFF)); int prevByte2 = ringBuffer[(state.pos - 2) & ringBufferMask] & unchecked ((int)(0xFF)); while (state.j < state.insertLength) { Brotli.BitReader.ReadMoreInput(br); if (state.blockLength[0] == 0) { DecodeLiteralBlockSwitch(state); } int literalTreeIndex = state.contextMap[state.contextMapSlice + (Brotli.Context.Lookup[state.contextLookupOffset1 + prevByte1] | Brotli.Context.Lookup[state.contextLookupOffset2 + prevByte2])] & unchecked ((int)(0xFF)); state.blockLength[0]--; prevByte2 = prevByte1; Brotli.BitReader.FillBitWindow(br); prevByte1 = ReadSymbol(state.hGroup0.codes, state.hGroup0.trees[literalTreeIndex], br); ringBuffer[state.pos] = unchecked ((byte)prevByte1); state.j++; if (state.pos++ == ringBufferMask) { state.nextRunningState = Brotli.RunningState.InsertLoop; state.bytesToWrite = state.ringBufferSize; state.bytesWritten = 0; state.runningState = Brotli.RunningState.Write; break; } } } if (state.runningState != Brotli.RunningState.InsertLoop) { continue; } state.metaBlockLength -= state.insertLength; if (state.metaBlockLength <= 0) { state.runningState = Brotli.RunningState.MainLoop; continue; } if (state.distanceCode < 0) { Brotli.BitReader.ReadMoreInput(br); if (state.blockLength[2] == 0) { DecodeDistanceBlockSwitch(state); } state.blockLength[2]--; Brotli.BitReader.FillBitWindow(br); state.distanceCode = ReadSymbol(state.hGroup2.codes, state.hGroup2.trees[state.distContextMap[state.distContextMapSlice + (state.copyLength > 4 ? 3 : state.copyLength - 2)] & unchecked ((int)(0xFF))], br); if (state.distanceCode >= state.numDirectDistanceCodes) { state.distanceCode -= state.numDirectDistanceCodes; int postfix = state.distanceCode & state.distancePostfixMask; state.distanceCode = (int)(((uint)state.distanceCode) >> state.distancePostfixBits); int n = ((int)(((uint)state.distanceCode) >> 1)) + 1; int offset = ((2 + (state.distanceCode & 1)) << n) - 4; state.distanceCode = state.numDirectDistanceCodes + postfix + ((offset + Brotli.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 Brotli.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 = Brotli.RunningState.Transform; continue; } if (state.distanceCode > 0) { state.distRb[state.distRbIdx & 3] = state.distance; state.distRbIdx++; } if (state.copyLength > state.metaBlockLength) { throw new Brotli.BrotliRuntimeException("Invalid backward reference"); } // COV_NF_LINE state.j = 0; state.runningState = Brotli.RunningState.CopyLoop; goto case Brotli.RunningState.CopyLoop; } case Brotli.RunningState.CopyLoop: { // fall through 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 = Brotli.RunningState.CopyLoop; state.bytesToWrite = state.ringBufferSize; state.bytesWritten = 0; state.runningState = Brotli.RunningState.Write; break; } } } if (state.runningState == Brotli.RunningState.CopyLoop) { state.runningState = Brotli.RunningState.MainLoop; } continue; } case Brotli.RunningState.Transform: { if (state.copyLength >= Brotli.Dictionary.MinWordLength && state.copyLength <= Brotli.Dictionary.MaxWordLength) { int offset = Brotli.Dictionary.OffsetsByLength[state.copyLength]; int wordId = state.distance - state.maxDistance - 1; int shift = Brotli.Dictionary.SizeBitsByLength[state.copyLength]; int mask = (1 << shift) - 1; int wordIdx = wordId & mask; int transformIdx = (int)(((uint)wordId) >> shift); offset += wordIdx * state.copyLength; if (transformIdx < Brotli.Transform.Transforms.Length) { int len = Brotli.Transform.TransformDictionaryWord(ringBuffer, state.copyDst, Brotli.Dictionary.GetData(), offset, state.copyLength, Brotli.Transform.Transforms[transformIdx]); state.copyDst += len; state.pos += len; state.metaBlockLength -= len; if (state.copyDst >= state.ringBufferSize) { state.nextRunningState = Brotli.RunningState.CopyWrapBuffer; state.bytesToWrite = state.ringBufferSize; state.bytesWritten = 0; state.runningState = Brotli.RunningState.Write; continue; } } else { throw new Brotli.BrotliRuntimeException("Invalid backward reference"); } } else { // COV_NF_LINE throw new Brotli.BrotliRuntimeException("Invalid backward reference"); } // COV_NF_LINE state.runningState = Brotli.RunningState.MainLoop; continue; } case Brotli.RunningState.CopyWrapBuffer: { System.Array.Copy(ringBuffer, state.ringBufferSize, ringBuffer, 0, state.copyDst - state.ringBufferSize); state.runningState = Brotli.RunningState.MainLoop; continue; } case Brotli.RunningState.ReadMetadata: { while (state.metaBlockLength > 0) { Brotli.BitReader.ReadMoreInput(br); // Optimize Brotli.BitReader.ReadBits(br, 8); state.metaBlockLength--; } state.runningState = Brotli.RunningState.BlockStart; continue; } case Brotli.RunningState.CopyUncompressed: { CopyUncompressedData(state); continue; } case Brotli.RunningState.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 Brotli.BrotliRuntimeException("Unexpected state " + state.runningState); } } } if (state.runningState == Brotli.RunningState.Finished) { if (state.metaBlockLength < 0) { throw new Brotli.BrotliRuntimeException("Invalid metablock length"); } Brotli.BitReader.JumpToByteBoundary(br); Brotli.BitReader.CheckHealth(state.br, true); } }
private static void ReadHuffmanCodeLengths(int[] codeLengthCodeLengths, int numSymbols, int[] codeLengths, Brotli.BitReader br) { int symbol = 0; int prevCodeLen = DefaultCodeLength; int repeat = 0; int repeatCodeLen = 0; int space = 32768; int[] table = new int[32]; Brotli.Huffman.BuildHuffmanTable(table, 0, 5, codeLengthCodeLengths, CodeLengthCodes); while (symbol < numSymbols && space > 0) { Brotli.BitReader.ReadMoreInput(br); Brotli.BitReader.FillBitWindow(br); int p = (int)(((long)(((ulong)br.accumulator) >> br.bitOffset))) & 31; br.bitOffset += table[p] >> 16; int codeLen = table[p] & unchecked ((int)(0xFFFF)); if (codeLen < CodeLengthRepeatCode) { repeat = 0; codeLengths[symbol++] = codeLen; if (codeLen != 0) { prevCodeLen = codeLen; space -= 32768 >> codeLen; } } else { int extraBits = codeLen - 14; int newLen = 0; if (codeLen == CodeLengthRepeatCode) { newLen = prevCodeLen; } if (repeatCodeLen != newLen) { repeat = 0; repeatCodeLen = newLen; } int oldRepeat = repeat; if (repeat > 0) { repeat -= 2; repeat <<= extraBits; } repeat += Brotli.BitReader.ReadBits(br, extraBits) + 3; int repeatDelta = repeat - oldRepeat; if (symbol + repeatDelta > numSymbols) { throw new Brotli.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 Brotli.BrotliRuntimeException("Unused space"); } // COV_NF_LINE // TODO: Pass max_symbol to Huffman table builder instead? Brotli.Utils.FillWithZeroes(codeLengths, symbol, numSymbols - symbol); }
internal static void CopyBytes(Brotli.BitReader br, byte[] data, int offset, int length) { if ((br.bitOffset & 7) != 0) { throw new Brotli.BrotliRuntimeException("Unaligned copyBytes"); } // Drain accumulator. while ((br.bitOffset != 64) && (length != 0)) { data[offset++] = unchecked ((byte)((long)(((ulong)br.accumulator) >> br.bitOffset))); br.bitOffset += 8; length--; } if (length == 0) { return; } // Get data from shadow buffer with "sizeof(int)" granularity. int copyInts = System.Math.Min(IntAvailable(br), length >> 2); if (copyInts > 0) { int readOffset = br.intOffset << 2; System.Array.Copy(br.byteBuffer, readOffset, data, offset, copyInts << 2); offset += copyInts << 2; length -= copyInts << 2; br.intOffset += copyInts; } if (length == 0) { return; } // Read tail bytes. if (IntAvailable(br) > 0) { // length = 1..3 FillBitWindow(br); while (length != 0) { data[offset++] = unchecked ((byte)((long)(((ulong)br.accumulator) >> br.bitOffset))); br.bitOffset += 8; length--; } CheckHealth(br, false); return; } // Now it is possible to copy bytes directly. try { while (length > 0) { int len = br.input.Read(data, offset, length); if (len == -1) { throw new Brotli.BrotliRuntimeException("Unexpected end of input"); } offset += len; length -= len; } } catch (System.IO.IOException e) { throw new Brotli.BrotliRuntimeException("Failed to read input", e); } }
// TODO: Use specialized versions for smaller tables. internal static void ReadHuffmanCode(int alphabetSize, int[] table, int offset, Brotli.BitReader br) { bool ok = true; int simpleCodeOrSkip; Brotli.BitReader.ReadMoreInput(br); // TODO: Avoid allocation. int[] codeLengths = new int[alphabetSize]; simpleCodeOrSkip = Brotli.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 = Brotli.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] = Brotli.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 (Brotli.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[CodeLengthCodes]; int space = 32; int numCodes = 0; for (int i = simpleCodeOrSkip; i < CodeLengthCodes && space > 0; i++) { int codeLenIdx = CodeLengthCodeOrder[i]; Brotli.BitReader.FillBitWindow(br); int p = (int)((long)(((ulong)br.accumulator) >> br.bitOffset)) & 15; // TODO: Demultiplex FIXED_TABLE. br.bitOffset += FixedTable[p] >> 16; int v = FixedTable[p] & unchecked ((int)(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 Brotli.BrotliRuntimeException("Can't readHuffmanCode"); } // COV_NF_LINE Brotli.Huffman.BuildHuffmanTable(table, offset, HuffmanTableBits, codeLengths, alphabetSize); }
private static void ReadMetablockHuffmanCodesAndContextMaps(Brotli.State state) { Brotli.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 * Brotli.Huffman.HuffmanMaxTableSize, br); ReadHuffmanCode(NumBlockLengthCodes, state.blockLenTrees, i * Brotli.Huffman.HuffmanMaxTableSize, br); state.blockLength[i] = ReadBlockLength(state.blockLenTrees, i * Brotli.Huffman.HuffmanMaxTableSize, br); } } Brotli.BitReader.ReadMoreInput(br); state.distancePostfixBits = Brotli.BitReader.ReadBits(br, 2); state.numDirectDistanceCodes = NumDistanceShortCodes + (Brotli.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 = System.Math.Min(i + 96, state.numBlockTypes[0]); for (; i < limit; ++i) { state.contextModes[i] = unchecked ((byte)(Brotli.BitReader.ReadBits(br, 2) << 1)); } Brotli.BitReader.ReadMoreInput(br); } // TODO: Reuse? state.contextMap = new byte[state.numBlockTypes[0] << LiteralContextBits]; int numLiteralTrees = DecodeContextMap(state.numBlockTypes[0] << LiteralContextBits, state.contextMap, br); state.trivialLiteralContext = true; for (int j = 0; j < state.numBlockTypes[0] << LiteralContextBits; j++) { if (state.contextMap[j] != j >> LiteralContextBits) { state.trivialLiteralContext = false; break; } } // TODO: Reuse? state.distContextMap = new byte[state.numBlockTypes[2] << DistanceContextBits]; int numDistTrees = DecodeContextMap(state.numBlockTypes[2] << DistanceContextBits, state.distContextMap, br); Brotli.HuffmanTreeGroup.Init(state.hGroup0, NumLiteralCodes, numLiteralTrees); Brotli.HuffmanTreeGroup.Init(state.hGroup1, NumInsertAndCopyCodes, state.numBlockTypes[1]); Brotli.HuffmanTreeGroup.Init(state.hGroup2, numDistanceCodes, numDistTrees); Brotli.HuffmanTreeGroup.Decode(state.hGroup0, br); Brotli.HuffmanTreeGroup.Decode(state.hGroup1, br); Brotli.HuffmanTreeGroup.Decode(state.hGroup2, br); state.contextMapSlice = 0; state.distContextMapSlice = 0; state.contextLookupOffset1 = Brotli.Context.LookupOffsets[state.contextModes[0]]; state.contextLookupOffset2 = Brotli.Context.LookupOffsets[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; }