diff options
| author | Stefan Boberg <[email protected]> | 2023-05-02 10:01:47 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-05-02 10:01:47 +0200 |
| commit | 075d17f8ada47e990fe94606c3d21df409223465 (patch) | |
| tree | e50549b766a2f3c354798a54ff73404217b4c9af /zencore/compress.cpp | |
| parent | fix: bundle shouldn't append content zip to zen (diff) | |
| download | zen-075d17f8ada47e990fe94606c3d21df409223465.tar.xz zen-075d17f8ada47e990fe94606c3d21df409223465.zip | |
moved source directories into `/src` (#264)
* moved source directories into `/src`
* updated bundle.lua for new `src` path
* moved some docs, icon
* removed old test trees
Diffstat (limited to 'zencore/compress.cpp')
| -rw-r--r-- | zencore/compress.cpp | 1353 |
1 files changed, 0 insertions, 1353 deletions
diff --git a/zencore/compress.cpp b/zencore/compress.cpp deleted file mode 100644 index e89f02d68..000000000 --- a/zencore/compress.cpp +++ /dev/null @@ -1,1353 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include <zencore/compress.h> - -#include <zencore/blake3.h> -#include <zencore/compositebuffer.h> -#include <zencore/crc32.h> -#include <zencore/endian.h> -#include <zencore/iohash.h> -#include <zencore/testing.h> - -#include "../thirdparty/Oodle/include/oodle2.h" -#if ZEN_PLATFORM_WINDOWS -# pragma comment(lib, "oo2core_win64.lib") -#endif - -#include <lz4.h> -#include <functional> -#include <limits> - -namespace zen::detail { - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -static constexpr uint64_t DefaultBlockSize = 256 * 1024; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** Method used to compress the data in a compressed buffer. */ -enum class CompressionMethod : uint8_t -{ - /** Header is followed by one uncompressed block. */ - None = 0, - /** Header is followed by an array of compressed block sizes then the compressed blocks. */ - Oodle = 3, - /** Header is followed by an array of compressed block sizes then the compressed blocks. */ - LZ4 = 4, -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** Header used on every compressed buffer. Always stored in big-endian format. */ -struct BufferHeader -{ - static constexpr uint32_t ExpectedMagic = 0xb7756362; // <dot>ucb - - uint32_t Magic = ExpectedMagic; // A magic number to identify a compressed buffer. Always 0xb7756362. - uint32_t Crc32 = 0; // A CRC-32 used to check integrity of the buffer. Uses the polynomial 0x04c11db7 - CompressionMethod Method = - CompressionMethod::None; // The method used to compress the buffer. Affects layout of data following the header - uint8_t Compressor = 0; // The method-specific compressor used to compress the buffer. - uint8_t CompressionLevel = 0; // The method-specific compression level used to compress the buffer. - uint8_t BlockSizeExponent = 0; // The power of two size of every uncompressed block except the last. Size is 1 << BlockSizeExponent - uint32_t BlockCount = 0; // The number of blocks that follow the header - uint64_t TotalRawSize = 0; // The total size of the uncompressed data - uint64_t TotalCompressedSize = 0; // The total size of the compressed data including the header - BLAKE3 RawHash; // The hash of the uncompressed data - - /** Checks validity of the buffer based on the magic number, method, and CRC-32. */ - static bool IsValid(const CompositeBuffer& CompressedData, IoHash& OutRawHash, uint64_t& OutRawSize); - static bool IsValid(const SharedBuffer& CompressedData, IoHash& OutRawHash, uint64_t& OutRawSize) - { - return IsValid(CompositeBuffer(CompressedData), OutRawHash, OutRawSize); - } - - /** Read a header from a buffer that is at least sizeof(BufferHeader) without any validation. */ - static BufferHeader Read(const CompositeBuffer& CompressedData) - { - BufferHeader Header; - if (sizeof(BufferHeader) <= CompressedData.GetSize()) - { - // if (CompressedData.GetSegments()[0].AsIoBuffer().IsWholeFile()) - // { - // ZEN_ASSERT(true); - // } - CompositeBuffer::Iterator It; - CompressedData.CopyTo(MakeMutableMemoryView(&Header, &Header + 1), It); - Header.ByteSwap(); - } - return Header; - } - - /** - * Write a header to a memory view that is at least sizeof(BufferHeader). - * - * @param HeaderView View of the header to write, including any method-specific header data. - */ - void Write(MutableMemoryView HeaderView) const - { - BufferHeader Header = *this; - Header.ByteSwap(); - HeaderView.CopyFrom(MakeMemoryView(&Header, &Header + 1)); - Header.ByteSwap(); - Header.Crc32 = CalculateCrc32(HeaderView); - Header.ByteSwap(); - HeaderView.CopyFrom(MakeMemoryView(&Header, &Header + 1)); - } - - void ByteSwap() - { - Magic = zen::ByteSwap(Magic); - Crc32 = zen::ByteSwap(Crc32); - BlockCount = zen::ByteSwap(BlockCount); - TotalRawSize = zen::ByteSwap(TotalRawSize); - TotalCompressedSize = zen::ByteSwap(TotalCompressedSize); - } - - /** Calculate the CRC-32 from a view of a header including any method-specific header data. */ - static uint32_t CalculateCrc32(MemoryView HeaderView) - { - uint32_t Crc32 = 0; - constexpr uint64_t MethodOffset = offsetof(BufferHeader, Method); - for (MemoryView View = HeaderView + MethodOffset; const uint64_t ViewSize = View.GetSize();) - { - const int32_t Size = static_cast<int32_t>(zen::Min<uint64_t>(ViewSize, /* INT_MAX */ 2147483647u)); - Crc32 = zen::MemCrc32(View.GetData(), Size, Crc32); - View += Size; - } - return Crc32; - } -}; - -static_assert(sizeof(BufferHeader) == 64, "BufferHeader is the wrong size."); - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -class BaseEncoder -{ -public: - virtual CompositeBuffer Compress(const CompositeBuffer& RawData, uint64_t BlockSize = DefaultBlockSize) const = 0; -}; - -class BaseDecoder -{ -public: - virtual CompositeBuffer Decompress(const BufferHeader& Header, const CompositeBuffer& CompressedData) const = 0; - virtual bool TryDecompressTo(const BufferHeader& Header, - const CompositeBuffer& CompressedData, - MutableMemoryView RawView, - uint64_t RawOffset) const = 0; - virtual uint64_t GetHeaderSize(const BufferHeader& Header) const = 0; -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -class NoneEncoder final : public BaseEncoder -{ -public: - [[nodiscard]] CompositeBuffer Compress(const CompositeBuffer& RawData, uint64_t /* BlockSize */) const final - { - BufferHeader Header; - Header.Method = CompressionMethod::None; - Header.BlockCount = 1; - Header.TotalRawSize = RawData.GetSize(); - Header.TotalCompressedSize = Header.TotalRawSize + sizeof(BufferHeader); - Header.RawHash = BLAKE3::HashBuffer(RawData); - - UniqueBuffer HeaderData = UniqueBuffer::Alloc(sizeof(BufferHeader)); - Header.Write(HeaderData); - return CompositeBuffer(HeaderData.MoveToShared(), RawData.MakeOwned()); - } -}; - -class NoneDecoder final : public BaseDecoder -{ -public: - [[nodiscard]] CompositeBuffer Decompress(const BufferHeader& Header, const CompositeBuffer& CompressedData) const final - { - if (Header.Method == CompressionMethod::None && Header.TotalCompressedSize == CompressedData.GetSize() && - Header.TotalCompressedSize == Header.TotalRawSize + sizeof(BufferHeader)) - { - return CompressedData.Mid(sizeof(BufferHeader), Header.TotalRawSize).MakeOwned(); - } - return CompositeBuffer(); - } - - [[nodiscard]] bool TryDecompressTo(const BufferHeader& Header, - const CompositeBuffer& CompressedData, - MutableMemoryView RawView, - uint64_t RawOffset) const final - { - if (Header.Method == CompressionMethod::None && RawOffset + RawView.GetSize() <= Header.TotalRawSize && - Header.TotalCompressedSize == CompressedData.GetSize() && - Header.TotalCompressedSize == Header.TotalRawSize + sizeof(BufferHeader)) - { - CompressedData.CopyTo(RawView, sizeof(BufferHeader) + RawOffset); - return true; - } - return false; - } - - [[nodiscard]] uint64_t GetHeaderSize(const BufferHeader&) const final { return sizeof(BufferHeader); } -}; - -////////////////////////////////////////////////////////////////////////// - -class BlockEncoder : public BaseEncoder -{ -public: - CompositeBuffer Compress(const CompositeBuffer& RawData, uint64_t BlockSize = DefaultBlockSize) const final; - -protected: - virtual CompressionMethod GetMethod() const = 0; - virtual uint8_t GetCompressor() const = 0; - virtual uint8_t GetCompressionLevel() const = 0; - virtual uint64_t CompressBlockBound(uint64_t RawSize) const = 0; - virtual bool CompressBlock(MutableMemoryView& CompressedData, MemoryView RawData) const = 0; - -private: - uint64_t GetCompressedBlocksBound(uint64_t BlockCount, uint64_t BlockSize, uint64_t RawSize) const - { - switch (BlockCount) - { - case 0: - return 0; - case 1: - return CompressBlockBound(RawSize); - default: - return CompressBlockBound(BlockSize) - BlockSize + RawSize; - } - } -}; - -CompositeBuffer -BlockEncoder::Compress(const CompositeBuffer& RawData, const uint64_t BlockSize) const -{ - ZEN_ASSERT(IsPow2(BlockSize) && (BlockSize <= (1u << 31))); - - const uint64_t RawSize = RawData.GetSize(); - BLAKE3Stream RawHash; - - const uint64_t BlockCount = RoundUp(RawSize, BlockSize) / BlockSize; - ZEN_ASSERT(BlockCount <= ~uint32_t(0)); - - // Allocate the buffer for the header, metadata, and compressed blocks. - const uint64_t MetaSize = BlockCount * sizeof(uint32_t); - const uint64_t CompressedDataSize = sizeof(BufferHeader) + MetaSize + GetCompressedBlocksBound(BlockCount, BlockSize, RawSize); - UniqueBuffer CompressedData = UniqueBuffer::Alloc(CompressedDataSize); - - // Compress the raw data in blocks and store the raw data for incompressible blocks. - std::vector<uint32_t> CompressedBlockSizes; - CompressedBlockSizes.reserve(BlockCount); - uint64_t CompressedSize = 0; - { - UniqueBuffer RawBlockCopy; - MutableMemoryView CompressedBlocksView = CompressedData.GetMutableView() + sizeof(BufferHeader) + MetaSize; - - CompositeBuffer::Iterator It = RawData.GetIterator(0); - - for (uint64_t RawOffset = 0; RawOffset < RawSize;) - { - const uint64_t RawBlockSize = zen::Min(RawSize - RawOffset, BlockSize); - const MemoryView RawBlock = RawData.ViewOrCopyRange(It, RawBlockSize, RawBlockCopy); - RawHash.Append(RawBlock); - - MutableMemoryView CompressedBlock = CompressedBlocksView; - if (!CompressBlock(CompressedBlock, RawBlock)) - { - return CompositeBuffer(); - } - - uint64_t CompressedBlockSize = CompressedBlock.GetSize(); - if (RawBlockSize <= CompressedBlockSize) - { - CompressedBlockSize = RawBlockSize; - CompressedBlocksView = CompressedBlocksView.CopyFrom(RawBlock); - } - else - { - CompressedBlocksView += CompressedBlockSize; - } - - CompressedBlockSizes.push_back(static_cast<uint32_t>(CompressedBlockSize)); - CompressedSize += CompressedBlockSize; - RawOffset += RawBlockSize; - } - } - - // Return an uncompressed buffer if the compressed data is larger than the raw data. - if (RawSize <= MetaSize + CompressedSize) - { - CompressedData.Reset(); - return NoneEncoder().Compress(RawData, BlockSize); - } - - // Write the header and calculate the CRC-32. - for (uint32_t& Size : CompressedBlockSizes) - { - Size = ByteSwap(Size); - } - CompressedData.GetMutableView().Mid(sizeof(BufferHeader), MetaSize).CopyFrom(MakeMemoryView(CompressedBlockSizes)); - - BufferHeader Header; - Header.Method = GetMethod(); - Header.Compressor = GetCompressor(); - Header.CompressionLevel = GetCompressionLevel(); - Header.BlockSizeExponent = static_cast<uint8_t>(zen::FloorLog2_64(BlockSize)); - Header.BlockCount = static_cast<uint32_t>(BlockCount); - Header.TotalRawSize = RawSize; - Header.TotalCompressedSize = sizeof(BufferHeader) + MetaSize + CompressedSize; - Header.RawHash = RawHash.GetHash(); - Header.Write(CompressedData.GetMutableView().Left(sizeof(BufferHeader) + MetaSize)); - - const MemoryView CompositeView = CompressedData.GetView().Left(Header.TotalCompressedSize); - return CompositeBuffer(SharedBuffer::MakeView(CompositeView, CompressedData.MoveToShared())); -} - -class BlockDecoder : public BaseDecoder -{ -public: - CompositeBuffer Decompress(const BufferHeader& Header, const CompositeBuffer& CompressedData) const final; - [[nodiscard]] bool TryDecompressTo(const BufferHeader& Header, - const CompositeBuffer& CompressedData, - MutableMemoryView RawView, - uint64_t RawOffset) const final; - [[nodiscard]] uint64_t GetHeaderSize(const BufferHeader& Header) const final - { - return sizeof(BufferHeader) + sizeof(uint32_t) * uint64_t(Header.BlockCount); - } - -protected: - virtual bool DecompressBlock(MutableMemoryView RawData, MemoryView CompressedData) const = 0; -}; - -CompositeBuffer -BlockDecoder::Decompress(const BufferHeader& Header, const CompositeBuffer& CompressedData) const -{ - if (Header.BlockCount == 0 || Header.TotalCompressedSize != CompressedData.GetSize()) - { - return CompositeBuffer(); - } - - // The raw data cannot reference the compressed data unless it is owned. - // An empty raw buffer requires an empty segment, which this path creates. - if (!CompressedData.IsOwned() || Header.TotalRawSize == 0) - { - UniqueBuffer Buffer = UniqueBuffer::Alloc(Header.TotalRawSize); - return TryDecompressTo(Header, CompressedData, Buffer, 0) ? CompositeBuffer(Buffer.MoveToShared()) : CompositeBuffer(); - } - - std::vector<uint32_t> CompressedBlockSizes; - CompressedBlockSizes.resize(Header.BlockCount); - CompressedData.CopyTo(MakeMutableMemoryView(CompressedBlockSizes), sizeof(BufferHeader)); - - for (uint32_t& Size : CompressedBlockSizes) - { - Size = ByteSwap(Size); - } - - // Allocate the buffer for the raw blocks that were compressed. - SharedBuffer RawData; - MutableMemoryView RawDataView; - const uint64_t BlockSize = uint64_t(1) << Header.BlockSizeExponent; - { - uint64_t RawDataSize = 0; - uint64_t RemainingRawSize = Header.TotalRawSize; - for (const uint32_t CompressedBlockSize : CompressedBlockSizes) - { - const uint64_t RawBlockSize = zen::Min(RemainingRawSize, BlockSize); - if (CompressedBlockSize < BlockSize) - { - RawDataSize += RawBlockSize; - } - RemainingRawSize -= RawBlockSize; - } - UniqueBuffer RawDataBuffer = UniqueBuffer::Alloc(RawDataSize); - RawDataView = RawDataBuffer; - RawData = RawDataBuffer.MoveToShared(); - } - - // Decompress the compressed data in blocks and reference the uncompressed blocks. - uint64_t PendingCompressedSegmentOffset = sizeof(BufferHeader) + uint64_t(Header.BlockCount) * sizeof(uint32_t); - uint64_t PendingCompressedSegmentSize = 0; - uint64_t PendingRawSegmentOffset = 0; - uint64_t PendingRawSegmentSize = 0; - std::vector<SharedBuffer> Segments; - - const auto CommitPendingCompressedSegment = - [&PendingCompressedSegmentOffset, &PendingCompressedSegmentSize, &CompressedData, &Segments] { - if (PendingCompressedSegmentSize) - { - CompressedData.IterateRange(PendingCompressedSegmentOffset, - PendingCompressedSegmentSize, - [&Segments](MemoryView View, const SharedBuffer& ViewOuter) { - Segments.push_back(SharedBuffer::MakeView(View, ViewOuter)); - }); - PendingCompressedSegmentOffset += PendingCompressedSegmentSize; - PendingCompressedSegmentSize = 0; - } - }; - - const auto CommitPendingRawSegment = [&PendingRawSegmentOffset, &PendingRawSegmentSize, &RawData, &Segments] { - if (PendingRawSegmentSize) - { - const MemoryView PendingSegment = RawData.GetView().Mid(PendingRawSegmentOffset, PendingRawSegmentSize); - Segments.push_back(SharedBuffer::MakeView(PendingSegment, RawData)); - PendingRawSegmentOffset += PendingRawSegmentSize; - PendingRawSegmentSize = 0; - } - }; - - UniqueBuffer CompressedBlockCopy; - uint64_t RemainingRawSize = Header.TotalRawSize; - uint64_t RemainingCompressedSize = CompressedData.GetSize(); - for (const uint32_t CompressedBlockSize : CompressedBlockSizes) - { - if (RemainingCompressedSize < CompressedBlockSize) - { - return CompositeBuffer(); - } - - const uint64_t RawBlockSize = zen::Min(RemainingRawSize, BlockSize); - if (RawBlockSize == CompressedBlockSize) - { - CommitPendingRawSegment(); - PendingCompressedSegmentSize += RawBlockSize; - } - else - { - CommitPendingCompressedSegment(); - const MemoryView CompressedBlock = - CompressedData.ViewOrCopyRange(PendingCompressedSegmentOffset, CompressedBlockSize, CompressedBlockCopy); - if (!DecompressBlock(RawDataView.Left(RawBlockSize), CompressedBlock)) - { - return CompositeBuffer(); - } - PendingCompressedSegmentOffset += CompressedBlockSize; - PendingRawSegmentSize += RawBlockSize; - RawDataView += RawBlockSize; - } - - RemainingCompressedSize -= CompressedBlockSize; - RemainingRawSize -= RawBlockSize; - } - - CommitPendingCompressedSegment(); - CommitPendingRawSegment(); - - return CompositeBuffer(std::move(Segments)); -} - -bool -BlockDecoder::TryDecompressTo(const BufferHeader& Header, - const CompositeBuffer& CompressedData, - MutableMemoryView RawView, - uint64_t RawOffset) const -{ - if (Header.TotalRawSize < RawOffset + RawView.GetSize() || Header.TotalCompressedSize != CompressedData.GetSize()) - { - return false; - } - - const uint64_t BlockSize = uint64_t(1) << Header.BlockSizeExponent; - - UniqueBuffer BlockSizeBuffer; - MemoryView BlockSizeView = CompressedData.ViewOrCopyRange(sizeof(BufferHeader), Header.BlockCount * sizeof(uint32_t), BlockSizeBuffer); - std::span<uint32_t const> CompressedBlockSizes(reinterpret_cast<const uint32_t*>(BlockSizeView.GetData()), Header.BlockCount); - - UniqueBuffer CompressedBlockCopy; - UniqueBuffer UncompressedBlockCopy; - - const size_t FirstBlockIndex = uint64_t(RawOffset / BlockSize); - const size_t LastBlockIndex = uint64_t((RawOffset + RawView.GetSize() - 1) / BlockSize); - const uint64_t LastBlockSize = BlockSize - ((Header.BlockCount * BlockSize) - Header.TotalRawSize); - uint64_t OffsetInFirstBlock = RawOffset % BlockSize; - uint64_t CompressedOffset = sizeof(BufferHeader) + uint64_t(Header.BlockCount) * sizeof(uint32_t); - uint64_t RemainingRawSize = RawView.GetSize(); - - for (size_t BlockIndex = 0; BlockIndex < FirstBlockIndex; BlockIndex++) - { - const uint32_t CompressedBlockSize = ByteSwap(CompressedBlockSizes[BlockIndex]); - CompressedOffset += CompressedBlockSize; - } - - for (size_t BlockIndex = FirstBlockIndex; BlockIndex <= LastBlockIndex; BlockIndex++) - { - const uint64_t UncompressedBlockSize = BlockIndex == Header.BlockCount - 1 ? LastBlockSize : BlockSize; - const uint32_t CompressedBlockSize = ByteSwap(CompressedBlockSizes[BlockIndex]); - const bool IsCompressed = CompressedBlockSize < UncompressedBlockSize; - - const uint64_t BytesToUncompress = OffsetInFirstBlock > 0 ? zen::Min(RawView.GetSize(), UncompressedBlockSize - OffsetInFirstBlock) - : zen::Min(RemainingRawSize, BlockSize); - - MemoryView CompressedBlock = CompressedData.ViewOrCopyRange(CompressedOffset, CompressedBlockSize, CompressedBlockCopy); - - if (IsCompressed) - { - MutableMemoryView UncompressedBlock = RawView.Left(BytesToUncompress); - - const bool IsAligned = BytesToUncompress == UncompressedBlockSize; - if (!IsAligned) - { - // Decompress to a temporary buffer when the first or the last block reads are not aligned with the block boundaries. - if (UncompressedBlockCopy.IsNull()) - { - UncompressedBlockCopy = UniqueBuffer::Alloc(BlockSize); - } - UncompressedBlock = UncompressedBlockCopy.GetMutableView().Mid(0, UncompressedBlockSize); - } - - if (!DecompressBlock(UncompressedBlock, CompressedBlock)) - { - return false; - } - - if (!IsAligned) - { - RawView.CopyFrom(UncompressedBlock.Mid(OffsetInFirstBlock, BytesToUncompress)); - } - } - else - { - RawView.CopyFrom(CompressedBlock.Mid(OffsetInFirstBlock, BytesToUncompress)); - } - - OffsetInFirstBlock = 0; - RemainingRawSize -= BytesToUncompress; - CompressedOffset += CompressedBlockSize; - RawView += BytesToUncompress; - } - - return RemainingRawSize == 0; -} - -////////////////////////////////////////////////////////////////////////// - -struct OodleInit -{ - OodleInit() - { - OodleConfigValues Config; - Oodle_GetConfigValues(&Config); - // Always read/write Oodle v9 binary data. - Config.m_OodleLZ_BackwardsCompatible_MajorVersion = 9; - Oodle_SetConfigValues(&Config); - } -}; - -OodleInit InitOodle; - -class OodleEncoder final : public BlockEncoder -{ -public: - OodleEncoder(OodleCompressor InCompressor, OodleCompressionLevel InCompressionLevel) - : Compressor(InCompressor) - , CompressionLevel(InCompressionLevel) - { - } - -protected: - CompressionMethod GetMethod() const final { return CompressionMethod::Oodle; } - uint8_t GetCompressor() const final { return static_cast<uint8_t>(Compressor); } - uint8_t GetCompressionLevel() const final { return static_cast<uint8_t>(CompressionLevel); } - - uint64_t CompressBlockBound(uint64_t RawSize) const final - { - return static_cast<uint64_t>(OodleLZ_GetCompressedBufferSizeNeeded(OodleLZ_Compressor_Kraken, static_cast<OO_SINTa>(RawSize))); - } - - bool CompressBlock(MutableMemoryView& CompressedData, MemoryView RawData) const final - { - const OodleLZ_Compressor LZCompressor = GetOodleLZCompressor(Compressor); - const OodleLZ_CompressionLevel LZCompressionLevel = GetOodleLZCompressionLevel(CompressionLevel); - if (LZCompressor == OodleLZ_Compressor_Invalid || LZCompressionLevel == OodleLZ_CompressionLevel_Invalid || - LZCompressionLevel == OodleLZ_CompressionLevel_None) - { - return false; - } - - const OO_SINTa RawSize = static_cast<OO_SINTa>(RawData.GetSize()); - if (static_cast<OO_SINTa>(CompressedData.GetSize()) < OodleLZ_GetCompressedBufferSizeNeeded(LZCompressor, RawSize)) - { - return false; - } - - const OO_SINTa Size = OodleLZ_Compress(LZCompressor, RawData.GetData(), RawSize, CompressedData.GetData(), LZCompressionLevel); - CompressedData.LeftInline(static_cast<uint64_t>(Size)); - return Size > 0; - } - - static OodleLZ_Compressor GetOodleLZCompressor(OodleCompressor Compressor) - { - switch (Compressor) - { - case OodleCompressor::Selkie: - return OodleLZ_Compressor_Selkie; - case OodleCompressor::Mermaid: - return OodleLZ_Compressor_Mermaid; - case OodleCompressor::Kraken: - return OodleLZ_Compressor_Kraken; - case OodleCompressor::Leviathan: - return OodleLZ_Compressor_Leviathan; - case OodleCompressor::NotSet: - default: - return OodleLZ_Compressor_Invalid; - } - } - - static OodleLZ_CompressionLevel GetOodleLZCompressionLevel(OodleCompressionLevel Level) - { - const int IntLevel = (int)Level; - if (IntLevel < (int)OodleLZ_CompressionLevel_Min || IntLevel > (int)OodleLZ_CompressionLevel_Max) - { - return OodleLZ_CompressionLevel_Invalid; - } - return OodleLZ_CompressionLevel(IntLevel); - } - -private: - const OodleCompressor Compressor; - const OodleCompressionLevel CompressionLevel; -}; - -class OodleDecoder final : public BlockDecoder -{ -protected: - bool DecompressBlock(MutableMemoryView RawData, MemoryView CompressedData) const final - { - const OO_SINTa RawSize = static_cast<OO_SINTa>(RawData.GetSize()); - const OO_SINTa Size = OodleLZ_Decompress(CompressedData.GetData(), - static_cast<OO_SINTa>(CompressedData.GetSize()), - RawData.GetData(), - RawSize, - OodleLZ_FuzzSafe_Yes, - OodleLZ_CheckCRC_Yes, - OodleLZ_Verbosity_None); - return Size == RawSize; - } -}; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -class LZ4Decoder final : public BlockDecoder -{ -protected: - bool DecompressBlock(MutableMemoryView RawData, MemoryView CompressedData) const final - { - if (CompressedData.GetSize() <= std::numeric_limits<int>::max()) - { - const int Size = LZ4_decompress_safe(static_cast<const char*>(CompressedData.GetData()), - static_cast<char*>(RawData.GetData()), - static_cast<int>(CompressedData.GetSize()), - static_cast<int>(zen::Min<uint64_t>(RawData.GetSize(), uint64_t(LZ4_MAX_INPUT_SIZE)))); - return static_cast<uint64_t>(Size) == RawData.GetSize(); - } - return false; - } -}; - -////////////////////////////////////////////////////////////////////////// - -static const BaseDecoder* -GetDecoder(CompressionMethod Method) -{ - static NoneDecoder None; - static OodleDecoder Oodle; - static LZ4Decoder LZ4; - - switch (Method) - { - default: - return nullptr; - case CompressionMethod::None: - return &None; - case CompressionMethod::Oodle: - return &Oodle; - case CompressionMethod::LZ4: - return &LZ4; - } -} - -////////////////////////////////////////////////////////////////////////// - -bool -BufferHeader::IsValid(const CompositeBuffer& CompressedData, IoHash& OutRawHash, uint64_t& OutRawSize) -{ - uint64_t Size = CompressedData.GetSize(); - if (Size < sizeof(BufferHeader)) - { - return false; - } - const size_t StackBufferSize = 256; - uint8_t StackBuffer[StackBufferSize]; - uint64_t ReadSize = Min(Size, StackBufferSize); - BufferHeader* Header = reinterpret_cast<BufferHeader*>(StackBuffer); - { - CompositeBuffer::Iterator It; - CompressedData.CopyTo(MutableMemoryView(StackBuffer, StackBuffer + StackBufferSize), It); - } - Header->ByteSwap(); - if (Header->Magic != BufferHeader::ExpectedMagic) - { - return false; - } - const BaseDecoder* const Decoder = GetDecoder(Header->Method); - if (!Decoder) - { - return false; - } - uint32_t Crc32 = Header->Crc32; - OutRawHash = IoHash::FromBLAKE3(Header->RawHash); - OutRawSize = Header->TotalRawSize; - uint64_t HeaderSize = Decoder->GetHeaderSize(*Header); - Header->ByteSwap(); - - if (HeaderSize > ReadSize) - { - // 0.004% of cases on a Fortnite hot cache cook - UniqueBuffer HeaderCopy = UniqueBuffer::Alloc(HeaderSize); - CompositeBuffer::Iterator It; - CompressedData.CopyTo(HeaderCopy.GetMutableView(), It); - const MemoryView HeaderView = HeaderCopy.GetView(); - if (Crc32 != BufferHeader::CalculateCrc32(HeaderView)) - { - return false; - } - } - else - { - MemoryView FullHeaderView(StackBuffer, StackBuffer + HeaderSize); - if (Crc32 != BufferHeader::CalculateCrc32(FullHeaderView)) - { - return false; - } - } - return true; -} - -////////////////////////////////////////////////////////////////////////// - -template<typename BufferType> -inline CompositeBuffer -ValidBufferOrEmpty(BufferType&& CompressedData, IoHash& OutRawHash, uint64_t& OutRawSize) -{ - return BufferHeader::IsValid(CompressedData, OutRawHash, OutRawSize) ? CompositeBuffer(std::forward<BufferType>(CompressedData)) - : CompositeBuffer(); -} - -CompositeBuffer -CopyCompressedRange(const BufferHeader& Header, const CompositeBuffer& CompressedData, uint64_t RawOffset, uint64_t RawSize) -{ - if (Header.TotalRawSize < RawOffset + RawSize) - { - return CompositeBuffer(); - } - - if (Header.Method == CompressionMethod::None) - { - UniqueBuffer NewCompressedData = UniqueBuffer::Alloc(RawSize); - CompressedData.CopyTo(NewCompressedData.GetMutableView(), sizeof(Header) + RawOffset); - - BufferHeader NewHeader = Header; - NewHeader.Crc32 = 0; - NewHeader.TotalRawSize = RawSize; - NewHeader.TotalCompressedSize = NewHeader.TotalRawSize + sizeof(BufferHeader); - NewHeader.RawHash = BLAKE3(); - - UniqueBuffer HeaderData = UniqueBuffer::Alloc(sizeof(BufferHeader)); - NewHeader.Write(HeaderData); - - return CompositeBuffer(HeaderData.MoveToShared(), NewCompressedData.MoveToShared()); - } - else - { - UniqueBuffer BlockSizeBuffer; - MemoryView BlockSizeView = - CompressedData.ViewOrCopyRange(sizeof(BufferHeader), Header.BlockCount * sizeof(uint32_t), BlockSizeBuffer); - std::span<uint32_t const> CompressedBlockSizes(reinterpret_cast<const uint32_t*>(BlockSizeView.GetData()), Header.BlockCount); - - const uint64_t BlockSize = uint64_t(1) << Header.BlockSizeExponent; - const uint64_t LastBlockSize = BlockSize - ((Header.BlockCount * BlockSize) - Header.TotalRawSize); - const size_t FirstBlock = uint64_t(RawOffset / BlockSize); - const size_t LastBlock = uint64_t((RawOffset + RawSize - 1) / BlockSize); - uint64_t CompressedOffset = sizeof(BufferHeader) + uint64_t(Header.BlockCount) * sizeof(uint32_t); - - const uint64_t NewBlockCount = LastBlock - FirstBlock + 1; - const uint64_t NewMetaSize = NewBlockCount * sizeof(uint32_t); - uint64_t NewCompressedSize = 0; - uint64_t NewTotalRawSize = 0; - std::vector<uint32_t> NewCompressedBlockSizes; - - NewCompressedBlockSizes.reserve(NewBlockCount); - for (size_t BlockIndex = FirstBlock; BlockIndex <= LastBlock; ++BlockIndex) - { - const uint64_t UncompressedBlockSize = (BlockIndex == Header.BlockCount - 1) ? LastBlockSize : BlockSize; - NewTotalRawSize += UncompressedBlockSize; - - const uint32_t CompressedBlockSize = CompressedBlockSizes[BlockIndex]; - NewCompressedBlockSizes.push_back(CompressedBlockSize); - NewCompressedSize += ByteSwap(CompressedBlockSize); - } - - const uint64_t NewTotalCompressedSize = sizeof(BufferHeader) + NewBlockCount * sizeof(uint32_t) + NewCompressedSize; - UniqueBuffer NewCompressedData = UniqueBuffer::Alloc(NewTotalCompressedSize); - MutableMemoryView NewCompressedBlocks = NewCompressedData.GetMutableView() + sizeof(BufferHeader) + NewMetaSize; - - // Seek to first compressed block - for (size_t BlockIndex = 0; BlockIndex < FirstBlock; ++BlockIndex) - { - const uint64_t CompressedBlockSize = ByteSwap(CompressedBlockSizes[BlockIndex]); - CompressedOffset += CompressedBlockSize; - } - - // Copy blocks - UniqueBuffer CompressedBlockCopy; - const MemoryView CompressedRange = CompressedData.ViewOrCopyRange(CompressedOffset, NewCompressedSize, CompressedBlockCopy); - NewCompressedBlocks.CopyFrom(CompressedRange); - - // Copy block sizes - NewCompressedData.GetMutableView().Mid(sizeof(BufferHeader), NewMetaSize).CopyFrom(MakeMemoryView(NewCompressedBlockSizes)); - - BufferHeader NewHeader; - NewHeader.Crc32 = 0; - NewHeader.Method = Header.Method; - NewHeader.Compressor = Header.Compressor; - NewHeader.CompressionLevel = Header.CompressionLevel; - NewHeader.BlockSizeExponent = Header.BlockSizeExponent; - NewHeader.BlockCount = static_cast<uint32_t>(NewBlockCount); - NewHeader.TotalRawSize = NewTotalRawSize; - NewHeader.TotalCompressedSize = NewTotalCompressedSize; - NewHeader.RawHash = BLAKE3(); - NewHeader.Write(NewCompressedData.GetMutableView().Left(sizeof(BufferHeader) + NewMetaSize)); - - return CompositeBuffer(NewCompressedData.MoveToShared()); - } -} - -} // namespace zen::detail - -namespace zen { - -const CompressedBuffer CompressedBuffer::Null; - -CompressedBuffer -CompressedBuffer::Compress(const CompositeBuffer& RawData, - OodleCompressor Compressor, - OodleCompressionLevel CompressionLevel, - uint64_t BlockSize) -{ - using namespace detail; - - if (BlockSize == 0) - { - BlockSize = DefaultBlockSize; - } - - CompressedBuffer Local; - if (CompressionLevel == OodleCompressionLevel::None) - { - Local.CompressedData = NoneEncoder().Compress(RawData, BlockSize); - } - else - { - Local.CompressedData = OodleEncoder(Compressor, CompressionLevel).Compress(RawData, BlockSize); - } - return Local; -} - -CompressedBuffer -CompressedBuffer::Compress(const SharedBuffer& RawData, - OodleCompressor Compressor, - OodleCompressionLevel CompressionLevel, - uint64_t BlockSize) -{ - return Compress(CompositeBuffer(RawData), Compressor, CompressionLevel, BlockSize); -} - -CompressedBuffer -CompressedBuffer::FromCompressed(const CompositeBuffer& InCompressedData, IoHash& OutRawHash, uint64_t& OutRawSize) -{ - CompressedBuffer Local; - Local.CompressedData = detail::ValidBufferOrEmpty(InCompressedData, OutRawHash, OutRawSize); - return Local; -} - -CompressedBuffer -CompressedBuffer::FromCompressed(CompositeBuffer&& InCompressedData, IoHash& OutRawHash, uint64_t& OutRawSize) -{ - CompressedBuffer Local; - Local.CompressedData = detail::ValidBufferOrEmpty(std::move(InCompressedData), OutRawHash, OutRawSize); - return Local; -} - -CompressedBuffer -CompressedBuffer::FromCompressed(const SharedBuffer& InCompressedData, IoHash& OutRawHash, uint64_t& OutRawSize) -{ - CompressedBuffer Local; - Local.CompressedData = detail::ValidBufferOrEmpty(InCompressedData, OutRawHash, OutRawSize); - return Local; -} - -CompressedBuffer -CompressedBuffer::FromCompressed(SharedBuffer&& InCompressedData, IoHash& OutRawHash, uint64_t& OutRawSize) -{ - CompressedBuffer Local; - Local.CompressedData = detail::ValidBufferOrEmpty(std::move(InCompressedData), OutRawHash, OutRawSize); - return Local; -} - -CompressedBuffer -CompressedBuffer::FromCompressedNoValidate(IoBuffer&& InCompressedData) -{ - if (InCompressedData.GetSize() <= sizeof(detail::BufferHeader)) - { - return CompressedBuffer(); - } - CompressedBuffer Local; - Local.CompressedData = CompositeBuffer(SharedBuffer(std::move(InCompressedData))); - return Local; -} - -CompressedBuffer -CompressedBuffer::FromCompressedNoValidate(CompositeBuffer&& InCompressedData) -{ - if (InCompressedData.GetSize() <= sizeof(detail::BufferHeader)) - { - return CompressedBuffer(); - } - CompressedBuffer Local; - Local.CompressedData = std::move(InCompressedData); - return Local; -} - -bool -CompressedBuffer::ValidateCompressedHeader(IoBuffer&& CompressedData, IoHash& OutRawHash, uint64_t& OutRawSize) -{ - return detail::BufferHeader::IsValid(SharedBuffer(std::move(CompressedData)), OutRawHash, OutRawSize); -} - -bool -CompressedBuffer::ValidateCompressedHeader(const IoBuffer& CompressedData, IoHash& OutRawHash, uint64_t& OutRawSize) -{ - return detail::BufferHeader::IsValid(SharedBuffer(CompressedData), OutRawHash, OutRawSize); -} - -uint64_t -CompressedBuffer::DecodeRawSize() const -{ - return CompressedData ? detail::BufferHeader::Read(CompressedData).TotalRawSize : 0; -} - -IoHash -CompressedBuffer::DecodeRawHash() const -{ - return CompressedData ? IoHash::FromBLAKE3(detail::BufferHeader::Read(CompressedData).RawHash) : IoHash(); -} - -CompressedBuffer -CompressedBuffer::CopyRange(uint64_t RawOffset, uint64_t RawSize) const -{ - using namespace detail; - const BufferHeader Header = BufferHeader::Read(CompressedData); - const uint64_t TotalRawSize = RawSize < ~uint64_t(0) ? RawSize : Header.TotalRawSize - RawOffset; - - CompressedBuffer Range; - Range.CompressedData = CopyCompressedRange(Header, CompressedData, RawOffset, TotalRawSize); - - return Range; -} - -bool -CompressedBuffer::TryDecompressTo(MutableMemoryView RawView, uint64_t RawOffset) const -{ - using namespace detail; - if (CompressedData) - { - const BufferHeader Header = BufferHeader::Read(CompressedData); - if (Header.Magic == BufferHeader::ExpectedMagic) - { - if (const BaseDecoder* const Decoder = GetDecoder(Header.Method)) - { - return Decoder->TryDecompressTo(Header, CompressedData, RawView, RawOffset); - } - } - } - return false; -} - -SharedBuffer -CompressedBuffer::Decompress(uint64_t RawOffset, uint64_t RawSize) const -{ - using namespace detail; - if (CompressedData && RawSize > 0) - { - const BufferHeader Header = BufferHeader::Read(CompressedData); - if (Header.Magic == BufferHeader::ExpectedMagic) - { - if (const BaseDecoder* const Decoder = GetDecoder(Header.Method)) - { - const uint64_t TotalRawSize = RawSize < ~uint64_t(0) ? RawSize : Header.TotalRawSize - RawOffset; - UniqueBuffer RawData = UniqueBuffer::Alloc(TotalRawSize); - if (Decoder->TryDecompressTo(Header, CompressedData, RawData, RawOffset)) - { - return RawData.MoveToShared(); - } - } - } - } - return SharedBuffer(); -} - -CompositeBuffer -CompressedBuffer::DecompressToComposite() const -{ - using namespace detail; - if (CompressedData) - { - const BufferHeader Header = BufferHeader::Read(CompressedData); - if (Header.Magic == BufferHeader::ExpectedMagic) - { - if (const BaseDecoder* const Decoder = GetDecoder(Header.Method)) - { - return Decoder->Decompress(Header, CompressedData); - } - } - } - return CompositeBuffer(); -} - -bool -CompressedBuffer::TryGetCompressParameters(OodleCompressor& OutCompressor, - OodleCompressionLevel& OutCompressionLevel, - uint64_t& OutBlockSize) const -{ - using namespace detail; - if (CompressedData) - { - switch (const BufferHeader Header = BufferHeader::Read(CompressedData); Header.Method) - { - case CompressionMethod::None: - OutCompressor = OodleCompressor::NotSet; - OutCompressionLevel = OodleCompressionLevel::None; - OutBlockSize = 0; - return true; - case CompressionMethod::Oodle: - OutCompressor = OodleCompressor(Header.Compressor); - OutCompressionLevel = OodleCompressionLevel(Header.CompressionLevel); - OutBlockSize = uint64_t(1) << Header.BlockSizeExponent; - return true; - default: - break; - } - } - return false; -} - -/** - ______________________ _____________________________ - \__ ___/\_ _____// _____/\__ ___/ _____/ - | | | __)_ \_____ \ | | \_____ \ - | | | \/ \ | | / \ - |____| /_______ /_______ / |____| /_______ / - \/ \/ \/ - */ - -#if ZEN_WITH_TESTS - -TEST_CASE("CompressedBuffer") -{ - uint8_t Zeroes[1024]{}; - uint8_t Ones[1024]; - memset(Ones, 1, sizeof Ones); - - { - CompressedBuffer Buffer = CompressedBuffer::Compress(CompositeBuffer(SharedBuffer::MakeView(MakeMemoryView(Zeroes))), - OodleCompressor::NotSet, - OodleCompressionLevel::None); - - CHECK(Buffer.DecodeRawSize() == sizeof(Zeroes)); - CHECK(Buffer.GetCompressedSize() == (sizeof(Zeroes) + sizeof(detail::BufferHeader))); - - CompositeBuffer Compressed = Buffer.GetCompressed(); - IoHash DecodedHash; - uint64_t DecodedRawSize; - CompressedBuffer BufferD = CompressedBuffer::FromCompressed(Compressed, DecodedHash, DecodedRawSize); - - CHECK(BufferD.IsNull() == false); - - CompositeBuffer Decomp = BufferD.DecompressToComposite(); - - CHECK(Decomp.GetSize() == DecodedRawSize); - CHECK(IoHash::HashBuffer(Decomp) == DecodedHash); - } - - { - CompressedBuffer Buffer = CompressedBuffer::Compress( - CompositeBuffer(SharedBuffer::MakeView(MakeMemoryView(Zeroes)), SharedBuffer::MakeView(MakeMemoryView(Ones))), - OodleCompressor::NotSet, - OodleCompressionLevel::None); - - CHECK(Buffer.DecodeRawSize() == (sizeof(Zeroes) + sizeof(Ones))); - CHECK(Buffer.GetCompressedSize() == (sizeof(Zeroes) + sizeof(Ones) + sizeof(detail::BufferHeader))); - - CompositeBuffer Compressed = Buffer.GetCompressed(); - IoHash DecodedHash; - uint64_t DecodedRawSize; - CompressedBuffer BufferD = CompressedBuffer::FromCompressed(Compressed, DecodedHash, DecodedRawSize); - - CHECK(BufferD.IsNull() == false); - - CompositeBuffer Decomp = BufferD.DecompressToComposite(); - - CHECK(Decomp.GetSize() == DecodedRawSize); - CHECK(IoHash::HashBuffer(Decomp) == DecodedHash); - } - - { - CompressedBuffer Buffer = CompressedBuffer::Compress(CompositeBuffer(SharedBuffer::MakeView(MakeMemoryView(Zeroes)))); - - CHECK(Buffer.DecodeRawSize() == sizeof(Zeroes)); - CHECK(Buffer.GetCompressedSize() < sizeof(Zeroes)); - - CompositeBuffer Compressed = Buffer.GetCompressed(); - IoHash DecodedHash; - uint64_t DecodedRawSize; - CompressedBuffer BufferD = CompressedBuffer::FromCompressed(Compressed, DecodedHash, DecodedRawSize); - - CHECK(BufferD.IsNull() == false); - - CompositeBuffer Decomp = BufferD.DecompressToComposite(); - - CHECK(Decomp.GetSize() == DecodedRawSize); - CHECK(IoHash::HashBuffer(Decomp) == DecodedHash); - } - - { - CompressedBuffer Buffer = CompressedBuffer::Compress( - CompositeBuffer(SharedBuffer::MakeView(MakeMemoryView(Zeroes)), SharedBuffer::MakeView(MakeMemoryView(Ones)))); - - CHECK(Buffer.DecodeRawSize() == (sizeof(Zeroes) + sizeof(Ones))); - CHECK(Buffer.GetCompressedSize() < (sizeof(Zeroes) + sizeof(Ones))); - - CompositeBuffer Compressed = Buffer.GetCompressed(); - IoHash DecodedHash; - uint64_t DecodedRawSize; - CompressedBuffer BufferD = CompressedBuffer::FromCompressed(Compressed, DecodedHash, DecodedRawSize); - - CHECK(BufferD.IsNull() == false); - - CompositeBuffer Decomp = BufferD.DecompressToComposite(); - - CHECK(Decomp.GetSize() == DecodedRawSize); - CHECK(IoHash::HashBuffer(Decomp) == DecodedHash); - } - - auto GenerateData = [](uint64_t N) -> std::vector<uint64_t> { - std::vector<uint64_t> Data; - Data.resize(N); - for (size_t Idx = 0; Idx < Data.size(); ++Idx) - { - Data[Idx] = Idx; - } - return Data; - }; - - auto ValidateData = [](std::span<uint64_t const> Values, std::span<uint64_t const> ExpectedValues, uint64_t Offset) { - for (size_t Idx = Offset; uint64_t Value : Values) - { - const uint64_t ExpectedValue = ExpectedValues[Idx++]; - CHECK(Value == ExpectedValue); - } - }; - - SUBCASE("decompress with offset and size") - { - auto UncompressAndValidate = [&ValidateData](CompressedBuffer Compressed, - uint64_t OffsetCount, - uint64_t Count, - const std::vector<uint64_t>& ExpectedValues) { - SharedBuffer Uncompressed = Compressed.Decompress(OffsetCount * sizeof(uint64_t), Count * sizeof(uint64_t)); - CHECK(Uncompressed.GetSize() == Count * sizeof(uint64_t)); - std::span<uint64_t const> Values((const uint64_t*)Uncompressed.GetData(), Uncompressed.GetSize() / sizeof(uint64_t)); - ValidateData(Values, ExpectedValues, OffsetCount); - }; - - const uint64_t BlockSize = 64 * sizeof(uint64_t); - const uint64_t N = 5000; - std::vector<uint64_t> ExpectedValues = GenerateData(N); - CompressedBuffer Compressed = CompressedBuffer::Compress(SharedBuffer::MakeView(MakeMemoryView(ExpectedValues)), - OodleCompressor::Mermaid, - OodleCompressionLevel::Optimal4, - BlockSize); - UncompressAndValidate(Compressed, 0, N, ExpectedValues); - UncompressAndValidate(Compressed, 1, N - 1, ExpectedValues); - UncompressAndValidate(Compressed, N - 1, 1, ExpectedValues); - UncompressAndValidate(Compressed, 0, 1, ExpectedValues); - UncompressAndValidate(Compressed, 2, 4, ExpectedValues); - UncompressAndValidate(Compressed, 0, 512, ExpectedValues); - UncompressAndValidate(Compressed, 3, 514, ExpectedValues); - UncompressAndValidate(Compressed, 256, 512, ExpectedValues); - UncompressAndValidate(Compressed, 512, 512, ExpectedValues); - } - - SUBCASE("decompress with offset only") - { - const uint64_t BlockSize = 64 * sizeof(uint64_t); - const uint64_t N = 1000; - std::vector<uint64_t> ExpectedValues = GenerateData(N); - CompressedBuffer Compressed = CompressedBuffer::Compress(SharedBuffer::MakeView(MakeMemoryView(ExpectedValues)), - OodleCompressor::Mermaid, - OodleCompressionLevel::Optimal4, - BlockSize); - const uint64_t OffsetCount = 150; - SharedBuffer Uncompressed = Compressed.Decompress(OffsetCount * sizeof(uint64_t)); - std::span<uint64_t const> Values((const uint64_t*)Uncompressed.GetData(), Uncompressed.GetSize() / sizeof(uint64_t)); - ValidateData(Values, ExpectedValues, OffsetCount); - } - - SUBCASE("decompress buffer with one block") - { - const uint64_t BlockSize = 256 * sizeof(uint64_t); - const uint64_t N = 100; - std::vector<uint64_t> ExpectedValues = GenerateData(N); - CompressedBuffer Compressed = CompressedBuffer::Compress(SharedBuffer::MakeView(MakeMemoryView(ExpectedValues)), - OodleCompressor::Mermaid, - OodleCompressionLevel::Optimal4, - BlockSize); - const uint64_t OffsetCount = 2; - const uint64_t Count = 50; - SharedBuffer Uncompressed = Compressed.Decompress(OffsetCount * sizeof(uint64_t), Count * sizeof(uint64_t)); - std::span<uint64_t const> Values((const uint64_t*)Uncompressed.GetData(), Uncompressed.GetSize() / sizeof(uint64_t)); - ValidateData(Values, ExpectedValues, OffsetCount); - } - - SUBCASE("decompress uncompressed buffer") - { - const uint64_t N = 4242; - std::vector<uint64_t> ExpectedValues = GenerateData(N); - CompressedBuffer Compressed = CompressedBuffer::Compress(SharedBuffer::MakeView(MakeMemoryView(ExpectedValues)), - OodleCompressor::NotSet, - OodleCompressionLevel::None); - { - const uint64_t OffsetCount = 0; - const uint64_t Count = N; - SharedBuffer Uncompressed = Compressed.Decompress(OffsetCount * sizeof(uint64_t), Count * sizeof(uint64_t)); - std::span<uint64_t const> Values((const uint64_t*)Uncompressed.GetData(), Uncompressed.GetSize() / sizeof(uint64_t)); - ValidateData(Values, ExpectedValues, OffsetCount); - } - - { - const uint64_t OffsetCount = 21; - const uint64_t Count = 999; - SharedBuffer Uncompressed = Compressed.Decompress(OffsetCount * sizeof(uint64_t), Count * sizeof(uint64_t)); - std::span<uint64_t const> Values((const uint64_t*)Uncompressed.GetData(), Uncompressed.GetSize() / sizeof(uint64_t)); - ValidateData(Values, ExpectedValues, OffsetCount); - } - } - - SUBCASE("copy range") - { - const uint64_t BlockSize = 64 * sizeof(uint64_t); - const uint64_t N = 1000; - std::vector<uint64_t> ExpectedValues = GenerateData(N); - - CompressedBuffer Compressed = CompressedBuffer::Compress(SharedBuffer::MakeView(MakeMemoryView(ExpectedValues)), - OodleCompressor::Mermaid, - OodleCompressionLevel::Optimal4, - BlockSize); - - { - const uint64_t OffsetCount = 0; - const uint64_t Count = N; - SharedBuffer Uncompressed = Compressed.CopyRange(OffsetCount * sizeof(uint64_t), Count * sizeof(uint64_t)).Decompress(); - std::span<uint64_t const> Values((const uint64_t*)Uncompressed.GetData(), Uncompressed.GetSize() / sizeof(uint64_t)); - CHECK(Values.size() == Count); - ValidateData(Values, ExpectedValues, OffsetCount); - } - - { - const uint64_t OffsetCount = 64; - const uint64_t Count = N - 64; - SharedBuffer Uncompressed = Compressed.CopyRange(OffsetCount * sizeof(uint64_t), Count * sizeof(uint64_t)).Decompress(); - std::span<uint64_t const> Values((const uint64_t*)Uncompressed.GetData(), Uncompressed.GetSize() / sizeof(uint64_t)); - CHECK(Values.size() == Count); - ValidateData(Values, ExpectedValues, OffsetCount); - } - - { - const uint64_t OffsetCount = 64 * 2 + 32; - const uint64_t Count = N - OffsetCount; - const uint64_t RawOffset = OffsetCount * sizeof(uint64_t); - const uint64_t RawSize = Count * sizeof(uint64_t); - uint64_t FirstBlockOffset = RawOffset % BlockSize; - - SharedBuffer Uncompressed = Compressed.CopyRange(RawOffset, RawSize).Decompress(); - std::span<uint64_t const> AllValues((const uint64_t*)Uncompressed.GetData(), RawSize / sizeof(uint64_t)); - std::span<uint64_t const> Values((const uint64_t*)(((const uint8_t*)(Uncompressed.GetData()) + FirstBlockOffset)), - RawSize / sizeof(uint64_t)); - CHECK(Values.size() == Count); - ValidateData(Values, ExpectedValues, OffsetCount); - } - - { - const uint64_t OffsetCount = 64 * 2 + 63; - const uint64_t Count = N - OffsetCount - 5; - const uint64_t RawOffset = OffsetCount * sizeof(uint64_t); - const uint64_t RawSize = Count * sizeof(uint64_t); - uint64_t FirstBlockOffset = RawOffset % BlockSize; - - SharedBuffer Uncompressed = Compressed.CopyRange(RawOffset, RawSize).Decompress(); - std::span<uint64_t const> AllValues((const uint64_t*)Uncompressed.GetData(), RawSize / sizeof(uint64_t)); - std::span<uint64_t const> Values((const uint64_t*)(((const uint8_t*)(Uncompressed.GetData()) + FirstBlockOffset)), - RawSize / sizeof(uint64_t)); - CHECK(Values.size() == Count); - ValidateData(Values, ExpectedValues, OffsetCount); - } - } - - SUBCASE("copy uncompressed range") - { - const uint64_t N = 1000; - std::vector<uint64_t> ExpectedValues = GenerateData(N); - - CompressedBuffer Compressed = CompressedBuffer::Compress(SharedBuffer::MakeView(MakeMemoryView(ExpectedValues)), - OodleCompressor::NotSet, - OodleCompressionLevel::None); - - { - const uint64_t OffsetCount = 0; - const uint64_t Count = N; - SharedBuffer Uncompressed = Compressed.CopyRange(OffsetCount * sizeof(uint64_t), Count * sizeof(uint64_t)).Decompress(); - std::span<uint64_t const> Values((const uint64_t*)Uncompressed.GetData(), Uncompressed.GetSize() / sizeof(uint64_t)); - CHECK(Values.size() == Count); - ValidateData(Values, ExpectedValues, OffsetCount); - } - - { - const uint64_t OffsetCount = 1; - const uint64_t Count = N - OffsetCount; - SharedBuffer Uncompressed = Compressed.CopyRange(OffsetCount * sizeof(uint64_t), Count * sizeof(uint64_t)).Decompress(); - std::span<uint64_t const> Values((const uint64_t*)Uncompressed.GetData(), Uncompressed.GetSize() / sizeof(uint64_t)); - CHECK(Values.size() == Count); - ValidateData(Values, ExpectedValues, OffsetCount); - } - - { - const uint64_t OffsetCount = 42; - const uint64_t Count = 100; - SharedBuffer Uncompressed = Compressed.CopyRange(OffsetCount * sizeof(uint64_t), Count * sizeof(uint64_t)).Decompress(); - std::span<uint64_t const> Values((const uint64_t*)Uncompressed.GetData(), Uncompressed.GetSize() / sizeof(uint64_t)); - CHECK(Values.size() == Count); - ValidateData(Values, ExpectedValues, OffsetCount); - } - } -} - -void -compress_forcelink() -{ -} -#endif - -} // namespace zen |