/// <summary>Reads the next chunk from stream.</summary> /// <returns><c>true</c> if next has been read, or <c>false</c> if it is legitimate end of file. /// Throws <see cref="EndOfStreamException"/> if end of stream was unexpected.</returns> private bool AcquireNextChunk() { do { ulong varint; if (!TryReadVarInt(out varint)) { return(false); } var flags = (ChunkFlags)varint; var isCompressed = (flags & ChunkFlags.Compressed) != 0; var originalLength = (int)ReadVarInt(); var compressedLength = isCompressed ? (int)ReadVarInt() : originalLength; if (compressedLength > originalLength) { throw EndOfStream(); // corrupted } if (compressedDataBuffer == null || compressedDataBuffer.Length < compressedLength) { compressedDataBuffer = new byte[compressedLength]; } var chunk = ReadBlock(compressedDataBuffer, 0, compressedLength); if (chunk != compressedLength) { throw EndOfStream(); // currupted } if (!isCompressed) { // swap the buffers var oldDataBuffer = dataBuffer; dataBuffer = compressedDataBuffer; // no compression on this chunk compressedDataBuffer = oldDataBuffer; // ensure that compressedDataBuffer and dataBuffer are different bufferLength = compressedLength; } else { if (dataBuffer == null || dataBuffer.Length < originalLength) { dataBuffer = new byte[originalLength]; } var passes = (int)flags >> 2; if (passes != 0) { throw new NotSupportedException("Chunks with multiple passes are not supported."); } LZ4Codec.Decode(compressedDataBuffer, 0, compressedLength, dataBuffer, 0, originalLength, true); bufferLength = originalLength; } bufferOffset = 0; } while (bufferLength == 0); // skip empty block (shouldn't happen but...) return(true); }
/// <summary>Flushes current chunk.</summary> private void FlushCurrentChunk() { if (bufferOffset <= 0) { return; } var compressed = new byte[bufferOffset]; var compressedLength = highCompression ? LZ4Codec.EncodeHC(dataBuffer, 0, bufferOffset, compressed, 0, bufferOffset) : LZ4Codec.Encode(dataBuffer, 0, bufferOffset, compressed, 0, bufferOffset); if (compressedLength <= 0 || compressedLength >= bufferOffset) { // uncompressible block compressed = dataBuffer; compressedLength = bufferOffset; } var isCompressed = compressedLength < bufferOffset; var flags = ChunkFlags.None; if (isCompressed) { flags |= ChunkFlags.Compressed; } if (highCompression) { flags |= ChunkFlags.HighCompression; } WriteVarInt((ulong)flags); WriteVarInt((ulong)bufferOffset); if (isCompressed) { WriteVarInt((ulong)compressedLength); } innerStream.Write(compressed, 0, compressedLength); innerStreamPosition += compressedLength; bufferOffset = 0; }