Exemple #1
0
        private void WriteCompressedBlockHeader(ReadOnlySpan <byte> input, Span <byte> output, int compressedSize)
        {
            var blockSize = compressedSize + 4; // CRC

            BinaryPrimitives.WriteInt32LittleEndian(output.Slice(1), blockSize);
            output[0] = (byte)Constants.ChunkType.CompressedData;

            var crc = Crc32CAlgorithm.Compute(input);

            crc = Crc32CAlgorithm.ApplyMask(crc);
            BinaryPrimitives.WriteUInt32LittleEndian(output.Slice(4), crc);
        }
Exemple #2
0
        public unsafe int Decompress(Span <byte> buffer)
        {
            Debug.Assert(_decompressor != null);

            var chunkType           = _chunkType;
            var chunkSize           = _chunkSize;
            var chunkBytesProcessed = _chunkBytesProcessed;

            fixed(byte *bufferStart = buffer)
            {
                var bufferEnd = bufferStart + buffer.Length;
                var bufferPtr = bufferStart;

                fixed(byte *inputStart = _input.Span)
                {
                    var inputEnd = inputStart + _input.Length;
                    var inputPtr = inputStart;

                    while (bufferPtr < bufferEnd && (inputPtr < inputEnd || (chunkType == Constants.ChunkType.CompressedData && _decompressor.AllDataDecompressed)))
                    {
                        // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
                        switch (chunkType)
                        {
                        case null:
                            // Not in a chunk, read the chunk type and size

                            var rawChunkHeader =
                                ReadChunkHeader(ref inputPtr, inputEnd);

                            if (rawChunkHeader == 0)
                            {
                                // Not enough data, get some more
                                break;
                            }

                            chunkType           = (Constants.ChunkType)(rawChunkHeader & 0xff);
                            chunkSize           = unchecked ((int)(rawChunkHeader >> 8));
                            chunkBytesProcessed = 0;
                            _scratchLength      = 0;
                            _chunkCrc           = 0;
                            break;

                        case Constants.ChunkType.CompressedData:
                        {
                            if (chunkBytesProcessed < 4)
                            {
                                _decompressor.Reset();

                                if (!ReadChunkCrc(ref inputPtr, inputEnd, ref chunkBytesProcessed))
                                {
                                    // Incomplete CRC
                                    break;
                                }

                                if (inputPtr >= inputEnd)
                                {
                                    // No more data
                                    break;
                                }
                            }

                            while (bufferPtr < bufferEnd && !_decompressor.EndOfFile)
                            {
                                if (_decompressor.NeedMoreData)
                                {
                                    var availableInputBytes = unchecked ((int)(inputEnd - inputPtr));
                                    if (availableInputBytes <= 0)
                                    {
                                        // No more data to give
                                        break;
                                    }

                                    var availableChunkBytes = Math.Min(availableInputBytes,
                                                                       chunkSize - chunkBytesProcessed);
                                    Debug.Assert(availableChunkBytes > 0);


                                    _decompressor.Decompress(new ReadOnlySpan <byte>(inputPtr, availableChunkBytes));

                                    chunkBytesProcessed += availableChunkBytes;
                                    inputPtr            += availableChunkBytes;
                                }

                                var decompressedBytes = _decompressor.Read(new Span <byte>(bufferPtr,
                                                                                           unchecked ((int)(bufferEnd - bufferPtr))));

                                _chunkCrc = Crc32CAlgorithm.Append(_chunkCrc, new ReadOnlySpan <byte>(bufferPtr, decompressedBytes));

                                bufferPtr += decompressedBytes;
                            }

                            if (_decompressor.EndOfFile)
                            {
                                // Completed reading the chunk
                                chunkType = null;

                                var crc = Crc32CAlgorithm.ApplyMask(_chunkCrc);
                                if (_expectedChunkCrc != crc)
                                {
                                    throw new InvalidDataException("Chunk CRC mismatch.");
                                }
                            }

                            break;
                        }

                        case Constants.ChunkType.UncompressedData:
                        {
                            if (chunkBytesProcessed < 4)
                            {
                                if (!ReadChunkCrc(ref inputPtr, inputEnd, ref chunkBytesProcessed))
                                {
                                    // Incomplete CRC
                                    break;
                                }

                                if (inputPtr >= inputEnd)
                                {
                                    // No more data
                                    break;
                                }
                            }

                            var chunkBytes = unchecked (Math.Min(Math.Min((int)(bufferEnd - bufferPtr), (int)(inputEnd - inputPtr)),
                                                                 chunkSize - chunkBytesProcessed));

                            Unsafe.CopyBlockUnaligned(bufferPtr, inputPtr, unchecked ((uint)chunkBytes));

                            _chunkCrc = Crc32CAlgorithm.Append(_chunkCrc, new ReadOnlySpan <byte>(bufferPtr, chunkBytes));

                            bufferPtr           += chunkBytes;
                            inputPtr            += chunkBytes;
                            chunkBytesProcessed += chunkBytes;

                            if (chunkBytesProcessed >= chunkSize)
                            {
                                // Completed reading the chunk
                                chunkType = null;

                                var crc = Crc32CAlgorithm.ApplyMask(_chunkCrc);
                                if (_expectedChunkCrc != crc)
                                {
                                    throw new InvalidDataException("Chunk CRC mismatch.");
                                }
                            }

                            break;
                        }

                        default:
                        {
                            if (chunkType < Constants.ChunkType.SkippableChunk)
                            {
                                throw new InvalidDataException($"Unknown chunk type {(int) chunkType:x}");
                            }

                            var chunkBytes = Math.Min(unchecked ((int)(inputEnd - inputPtr)), chunkSize - chunkBytesProcessed);

                            inputPtr            += chunkBytes;
                            chunkBytesProcessed += chunkBytes;

                            if (chunkBytesProcessed >= chunkSize)
                            {
                                // Completed reading the chunk
                                chunkType = null;
                            }

                            break;
                        }
                        }
                    }

                    _chunkType           = chunkType;
                    _chunkSize           = chunkSize;
                    _chunkBytesProcessed = chunkBytesProcessed;

                    _input = _input.Slice(unchecked ((int)(inputPtr - inputStart)));
                    return(unchecked ((int)(bufferPtr - bufferStart)));
                }
            }
        }