private static BufferedStreamReader Decompress_Internal_Init(Stream stream, ArchiveCompressorOptions options, out byte[] decompressedBuffer) { var endianByteStream = GetStreamFromEndian(stream, options.BigEndian, options.BufferSize, out var bufferedStreamReader); endianByteStream.Seek(_decompSizeOffset, SeekOrigin.Current); decompressedBuffer = GC.AllocateUninitializedArray <byte>(endianByteStream.Read <int>()); return(bufferedStreamReader); }
/// <summary> /// Compresses the contents of a stream /// </summary> /// <param name="source">The source of where the data should be compressed from.</param> /// <param name="numBytes">Number of bytes from the stream that should be compressed.</param> /// <param name="options">Options for the file compressor.</param> public static unsafe Span <byte> CompressFast(Stream source, int numBytes, ArchiveCompressorOptions options) { // TODO: Optimize this. // Read the data to be compressed into memory. var dataToCompress = GC.AllocateUninitializedArray <byte>(numBytes); source.Read(dataToCompress); // Initialize Writer. using var memoryStream = new MemoryStream(numBytes); var byteStream = new StreamByteStream(memoryStream); var writer = new BitStream <StreamByteStream>(byteStream); // Write header // Writer is big endian by nature, so we inverse it. writer.Write(!options.BigEndian ? Endian.Reverse(_signature) : _signature); writer.Write(!options.BigEndian ? Endian.Reverse(numBytes) : numBytes); writer.Seek(options.StartOffset); // Compress the data. fixed(byte *dataPtr = &dataToCompress[0]) { int pointer = 0; int maxPointer = dataToCompress.Length; while (pointer < maxPointer) { var match = Lz77GetLongestMatch(dataPtr, maxPointer, pointer, byte.MaxValue, byte.MaxValue); /* * 1 XXXXXXXX YYYYYYYY | | | | | 1 byte length | 1 byte offset. | compression flag */ // Compressed 2 bytes: 17 / 16 = 1.0625 bits // Uncompressed 2 bytes: 18 / 16 = 1.125 bits // Conclusion: Compression effective if length >= 2. if (match.Length >= 2) { writer.WriteBit(1); writer.Write((byte)(match.Offset * -1)); writer.Write((byte)match.Length); pointer += match.Length; } else { writer.WriteBit(0); writer.Write(dataPtr[pointer++]); } } } return(memoryStream.GetBuffer().AsSpan(0, writer.NextByteIndex)); }
/// <summary> /// Reads a compressed archive and decompresses its contents. /// Also see <see cref="DecompressFast"/> for a slightly /// better performance version; should you know the file length. /// </summary> /// <param name="stream">The originating stream. Start of stream should be at start of file.</param> /// <param name="options">The options to use for decompressing.</param> public static unsafe byte[] DecompressSlow(Stream stream, ArchiveCompressorOptions options) { // Get Decompressed File Buffer using var endianByteStream = Decompress_Internal_Init(stream, options, out var decompressedBuffer); // Setup compressed buffer reading var byteStream = new BufferedStreamReaderByteStream(endianByteStream); var bitStream = new BitStream <BufferedStreamReaderByteStream>(byteStream); // Start decompressing fixed(byte *resultPtr = decompressedBuffer) Decompress_Internal(resultPtr, decompressedBuffer.Length, options.StartOffset, ref bitStream); stream.Position = endianByteStream.Position(); return(decompressedBuffer); }
/// <summary> /// Reads a compressed archive and decompresses its contents. /// </summary> /// <param name="stream">The originating stream. Start of stream should be at start of file.</param> /// <param name="fileLength">The length between the first byte of the file in the stream and the last byte.</param> /// <param name="options">The options to use for decompressing.</param> public static unsafe byte[] DecompressFast(Stream stream, int fileLength, ArchiveCompressorOptions options) { var startPos = stream.Position; // Get Decompressed File Buffer using var endianByteStream = Decompress_Internal_Init(stream, options, out var decompressedBuffer); // Setup compressed buffer reading var compressedData = endianByteStream.ReadBytes(startPos, fileLength); var byteStream = new ArrayByteStream(compressedData); var bitStream = new BitStream <ArrayByteStream>(byteStream); // Start decompressing fixed(byte *resultPtr = decompressedBuffer) Decompress_Internal(resultPtr, decompressedBuffer.Length, options.StartOffset, ref bitStream); // Advance underlying stream stream.Position = startPos + fileLength; return(decompressedBuffer); }