diff options
| author | Stefan Boberg <[email protected]> | 2021-05-25 09:54:09 +0200 |
|---|---|---|
| committer | Stefan Boberg <[email protected]> | 2021-05-25 09:54:09 +0200 |
| commit | 882e93e4786f9e67e0edf6c276b16bb40848bae9 (patch) | |
| tree | c5d4c45679c676c6aeb804c7601f43340b78ea0b | |
| parent | Updated structured cache description (diff) | |
| parent | Compile out all rocksdb code for a smaller binary (diff) | |
| download | zen-882e93e4786f9e67e0edf6c276b16bb40848bae9.tar.xz zen-882e93e4786f9e67e0edf6c276b16bb40848bae9.zip | |
Merged from origin/main
54 files changed, 3853 insertions, 1514 deletions
diff --git a/zencore/blake3.cpp b/zencore/blake3.cpp index ec5d496d5..090eb6897 100644 --- a/zencore/blake3.cpp +++ b/zencore/blake3.cpp @@ -2,10 +2,11 @@ #include <zencore/blake3.h> +#include <zencore/compositebuffer.h> #include <zencore/string.h> #include <zencore/zencore.h> -#include "../3rdparty/BLAKE3/c/blake3.h" +#include "../3rdparty/BLAKE3/c/blake3.h" #pragma comment(lib, "blake3.lib") #include <doctest/doctest.h> @@ -36,6 +37,24 @@ BLAKE3::HashMemory(const void* data, size_t byteCount) } BLAKE3 +BLAKE3::HashBuffer(const CompositeBuffer& Buffer) +{ + BLAKE3 Hash; + + blake3_hasher Hasher; + blake3_hasher_init(&Hasher); + + for (const SharedBuffer& Segment : Buffer.GetSegments()) + { + blake3_hasher_update(&Hasher, Segment.GetData(), Segment.GetSize()); + } + + blake3_hasher_finalize(&Hasher, Hash.Hash, sizeof Hash.Hash); + + return Hash; +} + +BLAKE3 BLAKE3::FromHexString(const char* string) { BLAKE3 b3; diff --git a/zencore/compositebuffer.cpp b/zencore/compositebuffer.cpp new file mode 100644 index 000000000..9349c014f --- /dev/null +++ b/zencore/compositebuffer.cpp @@ -0,0 +1,341 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include <zencore/compositebuffer.h> + +#include <zencore/sharedbuffer.h> + +#include <doctest/doctest.h> + +namespace zen { + +const CompositeBuffer CompositeBuffer::Null; + +void +CompositeBuffer::Reset() +{ + m_Segments.clear(); +} + +uint64_t +CompositeBuffer::GetSize() const +{ + uint64_t Accum = 0; + + for (const SharedBuffer& It : m_Segments) + { + Accum += It.GetSize(); + } + + return Accum; +} + +bool +CompositeBuffer::IsOwned() const +{ + for (const SharedBuffer& It : m_Segments) + { + if (It.IsOwned() == false) + { + return false; + } + } + return true; +} + +CompositeBuffer +CompositeBuffer::MakeOwned() const& +{ + return CompositeBuffer(*this).MakeOwned(); +} + +CompositeBuffer +CompositeBuffer::MakeOwned() && +{ + for (SharedBuffer& Segment : m_Segments) + { + Segment = std::move(Segment).MakeOwned(); + } + return std::move(*this); +} + +SharedBuffer +CompositeBuffer::Flatten() const& +{ + switch (m_Segments.size()) + { + case 0: + return SharedBuffer(); + case 1: + return m_Segments[0]; + default: + UniqueBuffer Buffer = UniqueBuffer::Alloc(GetSize()); + MutableMemoryView OutView = Buffer.GetMutableView(); + + for (const SharedBuffer& Segment : m_Segments) + { + OutView.CopyFrom(Segment.GetView()); + OutView += Segment.GetSize(); + } + + return Buffer.MoveToShared(); + } +} + +SharedBuffer +CompositeBuffer::Flatten() && +{ + return m_Segments.size() == 1 ? std::move(m_Segments[0]) : std::as_const(*this).Flatten(); +} + +CompositeBuffer +CompositeBuffer::Mid(uint64_t Offset, uint64_t Size) const +{ + const uint64_t BufferSize = GetSize(); + Offset = zen::Min(Offset, BufferSize); + Size = zen::Min(Size, BufferSize - Offset); + CompositeBuffer Buffer; + IterateRange(Offset, Size, [&Buffer](MemoryView View, const SharedBuffer& ViewOuter) { + Buffer.m_Segments.push_back(SharedBuffer::MakeView(View, ViewOuter)); + }); + return Buffer; +} + +MemoryView +CompositeBuffer::ViewOrCopyRange(uint64_t Offset, uint64_t Size, UniqueBuffer& CopyBuffer) const +{ + MemoryView View; + IterateRange(Offset, Size, [Size, &View, &CopyBuffer, WriteView = MutableMemoryView()](MemoryView Segment) mutable { + if (Size == Segment.GetSize()) + { + View = Segment; + } + else + { + if (WriteView.IsEmpty()) + { + if (CopyBuffer.GetSize() < Size) + { + CopyBuffer = UniqueBuffer::Alloc(Size); + } + View = WriteView = CopyBuffer.GetMutableView().Left(Size); + } + WriteView = WriteView.CopyFrom(Segment); + } + }); + return View; +} + +void +CompositeBuffer::CopyTo(MutableMemoryView Target, uint64_t Offset) const +{ + IterateRange(Offset, Target.GetSize(), [Target](MemoryView View, [[maybe_unused]] const SharedBuffer& ViewOuter) mutable { + Target = Target.CopyFrom(View); + }); +} + +void +CompositeBuffer::IterateRange(uint64_t Offset, uint64_t Size, std::function<void(MemoryView View)> Visitor) const +{ + IterateRange(Offset, Size, [Visitor](MemoryView View, [[maybe_unused]] const SharedBuffer& ViewOuter) { Visitor(View); }); +} + +void +CompositeBuffer::IterateRange(uint64_t Offset, + uint64_t Size, + std::function<void(MemoryView View, const SharedBuffer& ViewOuter)> Visitor) const +{ + ZEN_ASSERT(Offset + Size <= GetSize()); + for (const SharedBuffer& Segment : m_Segments) + { + if (const uint64_t SegmentSize = Segment.GetSize(); Offset <= SegmentSize) + { + const MemoryView View = Segment.GetView().Mid(Offset, Size); + Offset = 0; + if (Size == 0 || !View.IsEmpty()) + { + Visitor(View, Segment); + } + Size -= View.GetSize(); + if (Size == 0) + { + break; + } + } + else + { + Offset -= SegmentSize; + } + } +} + +TEST_CASE("CompositeBuffer Null") +{ + CompositeBuffer Buffer; + CHECK(Buffer.IsNull()); + CHECK(Buffer.IsOwned()); + CHECK(Buffer.MakeOwned().IsNull()); + CHECK(Buffer.Flatten().IsNull()); + CHECK(Buffer.Mid(0, 0).IsNull()); + CHECK(Buffer.GetSize() == 0); + CHECK(Buffer.GetSegments().size() == 0); + + UniqueBuffer CopyBuffer; + CHECK(Buffer.ViewOrCopyRange(0, 0, CopyBuffer).IsEmpty()); + CHECK(CopyBuffer.IsNull()); + + MutableMemoryView CopyView; + Buffer.CopyTo(CopyView); + + uint32_t VisitCount = 0; + Buffer.IterateRange(0, 0, [&VisitCount](MemoryView) { ++VisitCount; }); + CHECK(VisitCount == 0); +} + +TEST_CASE("CompositeBuffer Empty") +{ + const uint8_t EmptyArray[]{0}; + const SharedBuffer EmptyView = SharedBuffer::MakeView(EmptyArray, 0); + CompositeBuffer Buffer(EmptyView); + CHECK(Buffer.IsNull() == false); + CHECK(Buffer.IsOwned() == false); + CHECK(Buffer.MakeOwned().IsNull() == false); + CHECK(Buffer.MakeOwned().IsOwned() == true); + CHECK(Buffer.Flatten() == EmptyView); + CHECK(Buffer.Mid(0, 0).Flatten() == EmptyView); + CHECK(Buffer.GetSize() == 0); + CHECK(Buffer.GetSegments().size() == 1); + CHECK(Buffer.GetSegments()[0] == EmptyView); + + UniqueBuffer CopyBuffer; + CHECK(Buffer.ViewOrCopyRange(0, 0, CopyBuffer) == EmptyView.GetView()); + CHECK(CopyBuffer.IsNull()); + + MutableMemoryView CopyView; + Buffer.CopyTo(CopyView); + + uint32_t VisitCount = 0; + Buffer.IterateRange(0, 0, [&VisitCount](MemoryView) { ++VisitCount; }); + CHECK(VisitCount == 1); +} + +TEST_CASE("CompositeBuffer Empty[1]") +{ + const uint8_t EmptyArray[1]{}; + const SharedBuffer EmptyView1 = SharedBuffer::MakeView(EmptyArray, 0); + const SharedBuffer EmptyView2 = SharedBuffer::MakeView(EmptyArray + 1, 0); + CompositeBuffer Buffer(EmptyView1, EmptyView2); + CHECK(Buffer.Mid(0, 0).Flatten() == EmptyView1); + CHECK(Buffer.GetSize() == 0); + CHECK(Buffer.GetSegments().size() == 2); + CHECK(Buffer.GetSegments()[0] == EmptyView1); + CHECK(Buffer.GetSegments()[1] == EmptyView2); + + UniqueBuffer CopyBuffer; + CHECK(Buffer.ViewOrCopyRange(0, 0, CopyBuffer) == EmptyView1.GetView()); + CHECK(CopyBuffer.IsNull()); + + MutableMemoryView CopyView; + Buffer.CopyTo(CopyView); + + uint32_t VisitCount = 0; + Buffer.IterateRange(0, 0, [&VisitCount](MemoryView) { ++VisitCount; }); + CHECK(VisitCount == 1); +} + +TEST_CASE("CompositeBuffer Flat") +{ + const uint8_t FlatArray[]{1, 2, 3, 4, 5, 6, 7, 8}; + const SharedBuffer FlatView = SharedBuffer::Clone(MakeMemoryView(FlatArray)); + CompositeBuffer Buffer(FlatView); + + CHECK(Buffer.IsNull() == false); + CHECK(Buffer.IsOwned() == true); + CHECK(Buffer.Flatten() == FlatView); + CHECK(Buffer.MakeOwned().Flatten() == FlatView); + CHECK(Buffer.Mid(0).Flatten() == FlatView); + CHECK(Buffer.Mid(4).Flatten().GetView() == FlatView.GetView().Mid(4)); + CHECK(Buffer.Mid(8).Flatten().GetView() == FlatView.GetView().Mid(8)); + CHECK(Buffer.Mid(4, 2).Flatten().GetView() == FlatView.GetView().Mid(4, 2)); + CHECK(Buffer.Mid(8, 0).Flatten().GetView() == FlatView.GetView().Mid(8, 0)); + CHECK(Buffer.GetSize() == sizeof(FlatArray)); + CHECK(Buffer.GetSegments().size() == 1); + CHECK(Buffer.GetSegments()[0] == FlatView); + + UniqueBuffer CopyBuffer; + CHECK(Buffer.ViewOrCopyRange(0, sizeof(FlatArray), CopyBuffer) == FlatView.GetView()); + CHECK(CopyBuffer.IsNull()); + + uint8_t CopyArray[sizeof(FlatArray) - 3]; + Buffer.CopyTo(MakeMutableMemoryView(CopyArray), 3); + CHECK(MakeMemoryView(CopyArray).EqualBytes(MakeMemoryView(FlatArray) + 3)); + + uint32_t VisitCount = 0; + Buffer.IterateRange(0, sizeof(FlatArray), [&VisitCount](MemoryView) { ++VisitCount; }); + CHECK(VisitCount == 1); +} + +TEST_CASE("CompositeBuffer Composite") +{ + const uint8_t FlatArray[]{1, 2, 3, 4, 5, 6, 7, 8}; + const SharedBuffer FlatView1 = SharedBuffer::MakeView(MakeMemoryView(FlatArray).Left(4)); + const SharedBuffer FlatView2 = SharedBuffer::MakeView(MakeMemoryView(FlatArray).Right(4)); + CompositeBuffer Buffer(FlatView1, FlatView2); + + CHECK(Buffer.IsNull() == false); + CHECK(Buffer.IsOwned() == false); + CHECK(Buffer.Flatten().GetView().EqualBytes(MakeMemoryView(FlatArray))); + CHECK(Buffer.Mid(2, 4).Flatten().GetView().EqualBytes(MakeMemoryView(FlatArray).Mid(2, 4))); + CHECK(Buffer.Mid(0, 4).Flatten() == FlatView1); + CHECK(Buffer.Mid(4, 4).Flatten() == FlatView2); + CHECK(Buffer.GetSize() == sizeof(FlatArray)); + CHECK(Buffer.GetSegments().size() == 2); + CHECK(Buffer.GetSegments()[0] == FlatView1); + CHECK(Buffer.GetSegments()[1] == FlatView2); + + UniqueBuffer CopyBuffer; + + CHECK(Buffer.ViewOrCopyRange(0, 4, CopyBuffer) == FlatView1.GetView()); + CHECK(CopyBuffer.IsNull() == true); + CHECK(Buffer.ViewOrCopyRange(4, 4, CopyBuffer) == FlatView2.GetView()); + CHECK(CopyBuffer.IsNull() == true); + CHECK(Buffer.ViewOrCopyRange(3, 2, CopyBuffer).EqualBytes(MakeMemoryView(FlatArray).Mid(3, 2))); + CHECK(CopyBuffer.GetSize() == 2); + CHECK(Buffer.ViewOrCopyRange(1, 6, CopyBuffer).EqualBytes(MakeMemoryView(FlatArray).Mid(1, 6))); + CHECK(CopyBuffer.GetSize() == 6); + CHECK(Buffer.ViewOrCopyRange(2, 4, CopyBuffer).EqualBytes(MakeMemoryView(FlatArray).Mid(2, 4))); + CHECK(CopyBuffer.GetSize() == 6); + + uint8_t CopyArray[4]; + Buffer.CopyTo(MakeMutableMemoryView(CopyArray), 2); + CHECK(MakeMemoryView(CopyArray).EqualBytes(MakeMemoryView(FlatArray).Mid(2, 4))); + + uint32_t VisitCount = 0; + Buffer.IterateRange(0, sizeof(FlatArray), [&VisitCount](MemoryView) { ++VisitCount; }); + CHECK(VisitCount == 2); + + const auto TestIterateRange = + [&Buffer](uint64_t Offset, uint64_t Size, MemoryView ExpectedView, const SharedBuffer& ExpectedViewOuter) { + uint32_t VisitCount = 0; + MemoryView ActualView; + SharedBuffer ActualViewOuter; + Buffer.IterateRange(Offset, Size, [&VisitCount, &ActualView, &ActualViewOuter](MemoryView View, const SharedBuffer& ViewOuter) { + ++VisitCount; + ActualView = View; + ActualViewOuter = ViewOuter; + }); + CHECK(VisitCount == 1); + CHECK(ActualView == ExpectedView); + CHECK(ActualViewOuter == ExpectedViewOuter); + }; + TestIterateRange(0, 4, MakeMemoryView(FlatArray).Mid(0, 4), FlatView1); + TestIterateRange(4, 0, MakeMemoryView(FlatArray).Mid(4, 0), FlatView1); + TestIterateRange(4, 4, MakeMemoryView(FlatArray).Mid(4, 4), FlatView2); + TestIterateRange(8, 0, MakeMemoryView(FlatArray).Mid(8, 0), FlatView2); +} + +void +compositebuffer_forcelink() +{ +} + +} // namespace zen diff --git a/zencore/compress.cpp b/zencore/compress.cpp index 0a9c43949..805e962cd 100644 --- a/zencore/compress.cpp +++ b/zencore/compress.cpp @@ -2,10 +2,768 @@ #include <zencore/compress.h> +#include <zencore/blake3.h> +#include <zencore/compositebuffer.h> +#include <zencore/crc32.h> +#include <zencore/endian.h> + #include <doctest/doctest.h> #include <lz4.h> #include <functional> +namespace zen::detail { + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static constexpr uint64_t DefaultBlockSize = 256 * 1024; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** 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 Reserved[2]{}; // The reserved bytes must be initialized to zero + 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); + static bool IsValid(const SharedBuffer& CompressedData) { return IsValid(CompositeBuffer(CompressedData)); } + + /** 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()) + { + CompressedData.CopyTo(MakeMutableMemoryView(&Header, &Header + 1)); + 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)); + Crc32 = zen::MemCrc32(View.GetData(), Size, Crc32); + View += Size; + } + return Crc32; + } +}; + +static_assert(sizeof(BufferHeader) == 64, "BufferHeader is the wrong size."); + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class CompressionMethodBase +{ +public: + virtual CompositeBuffer Compress(const CompositeBuffer& RawData, uint64_t BlockSize = DefaultBlockSize) const = 0; + virtual CompositeBuffer Decompress(const BufferHeader& Header, const CompositeBuffer& CompressedData) const = 0; + virtual bool TryDecompressTo(const BufferHeader& Header, const CompositeBuffer& CompressedData, MutableMemoryView RawView) const = 0; + virtual uint64_t GetHeaderSize(const BufferHeader& Header) const = 0; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class MethodNone final : public CompressionMethodBase +{ +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()); + } + + [[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) const final + { + if (Header.Method == CompressionMethod::None && Header.TotalRawSize == RawView.GetSize() && + Header.TotalCompressedSize == CompressedData.GetSize() && + Header.TotalCompressedSize == Header.TotalRawSize + sizeof(BufferHeader)) + { + CompressedData.CopyTo(RawView, sizeof(BufferHeader)); + return true; + } + return false; + } + + [[nodiscard]] uint64_t GetHeaderSize(const BufferHeader&) const final { return sizeof(BufferHeader); } +}; + +////////////////////////////////////////////////////////////////////////// + +class MethodBlockBase : public CompressionMethodBase +{ +public: + CompositeBuffer Compress(const CompositeBuffer& RawData, uint64_t BlockSize = DefaultBlockSize) const final; + CompositeBuffer Decompress(const BufferHeader& Header, const CompositeBuffer& CompressedData) const final; + [[nodiscard]] bool TryDecompressTo(const BufferHeader& Header, + const CompositeBuffer& CompressedData, + MutableMemoryView RawView) const final; + [[nodiscard]] uint64_t GetHeaderSize(const BufferHeader& Header) const final + { + return sizeof(BufferHeader) + sizeof(uint32_t) * uint64_t(Header.BlockCount); + } + +protected: + virtual CompressionMethod GetMethod() const = 0; + virtual uint64_t CompressBlockBound(uint64_t RawSize) const = 0; + virtual bool CompressBlock(MutableMemoryView& CompressedData, MemoryView RawData) const = 0; + virtual bool DecompressBlock(MutableMemoryView RawData, MemoryView CompressedData) 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 +MethodBlockBase::Compress(const CompositeBuffer& RawData, const uint64_t BlockSize) const +{ + ZEN_ASSERT(IsPow2(BlockSize) && BlockSize <= (1 << 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; + for (uint64_t RawOffset = 0; RawOffset < RawSize;) + { + const uint64_t RawBlockSize = zen::Min(RawSize - RawOffset, BlockSize); + const MemoryView RawBlock = RawData.ViewOrCopyRange(RawOffset, 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 MethodNone().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.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())); +} + +CompositeBuffer +MethodBlockBase::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) ? 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 +MethodBlockBase::TryDecompressTo(const BufferHeader& Header, const CompositeBuffer& CompressedData, MutableMemoryView RawView) const +{ + if (Header.TotalRawSize != RawView.GetSize() || Header.TotalCompressedSize != CompressedData.GetSize()) + { + return false; + } + + std::vector<uint32_t> CompressedBlockSizes; + CompressedBlockSizes.resize(Header.BlockCount); + CompressedData.CopyTo(MakeMutableMemoryView(CompressedBlockSizes), sizeof(BufferHeader)); + + for (uint32_t& Size : CompressedBlockSizes) + { + Size = ByteSwap(Size); + } + + UniqueBuffer CompressedBlockCopy; + const uint64_t BlockSize = uint64_t(1) << Header.BlockSizeExponent; + uint64_t CompressedOffset = sizeof(BufferHeader) + uint64_t(Header.BlockCount) * sizeof(uint32_t); + uint64_t RemainingRawSize = Header.TotalRawSize; + uint64_t RemainingCompressedSize = CompressedData.GetSize(); + for (uint32_t CompressedBlockSize : CompressedBlockSizes) + { + if (RemainingCompressedSize < CompressedBlockSize) + { + return false; + } + + const uint64_t RawBlockSize = zen::Min(RemainingRawSize, BlockSize); + if (RawBlockSize == CompressedBlockSize) + { + CompressedData.CopyTo(RawView.Left(RawBlockSize), CompressedOffset); + } + else + { + const MemoryView CompressedBlock = CompressedData.ViewOrCopyRange(CompressedOffset, CompressedBlockSize, CompressedBlockCopy); + if (!DecompressBlock(RawView.Left(RawBlockSize), CompressedBlock)) + { + return false; + } + } + + RemainingCompressedSize -= CompressedBlockSize; + RemainingRawSize -= RawBlockSize; + CompressedOffset += CompressedBlockSize; + RawView += RawBlockSize; + } + + return RemainingRawSize == 0; +} + +////////////////////////////////////////////////////////////////////////// + +class MethodLZ4 final : public MethodBlockBase +{ +protected: + CompressionMethod GetMethod() const final { return CompressionMethod::LZ4; } + + uint64_t CompressBlockBound(uint64_t RawSize) const final + { + if (RawSize <= LZ4_MAX_INPUT_SIZE) + { + return static_cast<uint64_t>(LZ4_compressBound(static_cast<int>(RawSize))); + } + return 0; + } + + bool CompressBlock(MutableMemoryView& CompressedData, MemoryView RawData) const final + { + if (RawData.GetSize() <= LZ4_MAX_INPUT_SIZE) + { + const int Size = + LZ4_compress_default(static_cast<const char*>(RawData.GetData()), + static_cast<char*>(CompressedData.GetData()), + static_cast<int>(RawData.GetSize()), + static_cast<int>(zen::Min<uint64_t>(CompressedData.GetSize(), std::numeric_limits<int>::max()))); + CompressedData.LeftInline(static_cast<uint64_t>(Size)); + return Size > 0; + } + return false; + } + + 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(), LZ4_MAX_INPUT_SIZE))); + return static_cast<uint64_t>(Size) == RawData.GetSize(); + } + return false; + } +}; + +////////////////////////////////////////////////////////////////////////// + +static const CompressionMethodBase* +GetMethod(CompressionMethod Method) +{ + static MethodNone None; + static MethodLZ4 LZ4; + + switch (Method) + { + default: + return nullptr; + case CompressionMethod::None: + return &None; + case CompressionMethod::LZ4: + return &LZ4; + } +} + +static const char* +GetMethodName(CompressionMethod Method) +{ + switch (Method) + { + default: + return "error"; + case CompressionMethod::None: + return "None"; + case CompressionMethod::LZ4: + return "LZ4"; + } +} + +////////////////////////////////////////////////////////////////////////// + +bool +BufferHeader::IsValid(const CompositeBuffer& CompressedData) +{ + if (sizeof(BufferHeader) <= CompressedData.GetSize()) + { + const BufferHeader Header = Read(CompressedData); + if (Header.Magic == BufferHeader::ExpectedMagic) + { + if (const CompressionMethodBase* const Method = GetMethod(Header.Method)) + { + UniqueBuffer HeaderCopy; + const MemoryView HeaderView = CompressedData.ViewOrCopyRange(0, Method->GetHeaderSize(Header), HeaderCopy); + if (Header.Crc32 == BufferHeader::CalculateCrc32(HeaderView)) + { + return true; + } + } + } + } + return false; +} + +////////////////////////////////////////////////////////////////////////// + +template<typename BufferType> +inline CompositeBuffer +ValidBufferOrEmpty(BufferType&& CompressedData) +{ + return BufferHeader::IsValid(CompressedData) ? CompositeBuffer(std::forward<BufferType>(CompressedData)) : CompositeBuffer(); +} + +} // namespace zen::detail + namespace zen { +CompressedBuffer +CompressedBuffer::Compress(CompressionMethod Method, const CompositeBuffer& RawData) +{ + using namespace detail; + + CompressedBuffer Local; + if (const CompressionMethodBase* const Impl = GetMethod(Method)) + { + Local.CompressedData = Impl->Compress(RawData); + } + return Local; +} + +CompressedBuffer +CompressedBuffer::Compress(CompressionMethod Method, const SharedBuffer& RawData) +{ + return Compress(Method, CompositeBuffer(RawData)); +} + +CompressedBuffer +CompressedBuffer::FromCompressed(const CompositeBuffer& InCompressedData) +{ + CompressedBuffer Local; + Local.CompressedData = detail::ValidBufferOrEmpty(InCompressedData); + return Local; +} + +CompressedBuffer +CompressedBuffer::FromCompressed(CompositeBuffer&& InCompressedData) +{ + CompressedBuffer Local; + Local.CompressedData = detail::ValidBufferOrEmpty(std::move(InCompressedData)); + return Local; +} + +CompressedBuffer +CompressedBuffer::FromCompressed(const SharedBuffer& InCompressedData) +{ + CompressedBuffer Local; + Local.CompressedData = detail::ValidBufferOrEmpty(InCompressedData); + return Local; +} + +CompressedBuffer +CompressedBuffer::FromCompressed(SharedBuffer&& InCompressedData) +{ + CompressedBuffer Local; + Local.CompressedData = detail::ValidBufferOrEmpty(std::move(InCompressedData)); + return Local; +} + +uint64_t +CompressedBuffer::GetRawSize() const +{ + return CompressedData ? detail::BufferHeader::Read(CompressedData).TotalRawSize : 0; +} + +BLAKE3 +CompressedBuffer::GetRawHash() const +{ + return CompressedData ? detail::BufferHeader::Read(CompressedData).RawHash : BLAKE3(); +} + +bool +CompressedBuffer::TryDecompressTo(MutableMemoryView RawView) const +{ + using namespace detail; + if (CompressedData) + { + const BufferHeader Header = BufferHeader::Read(CompressedData); + if (const CompressionMethodBase* const Method = GetMethod(Header.Method)) + { + return Method->TryDecompressTo(Header, CompressedData, RawView); + } + } + return false; +} + +SharedBuffer +CompressedBuffer::Decompress() const +{ + using namespace detail; + if (CompressedData) + { + const BufferHeader Header = BufferHeader::Read(CompressedData); + if (const CompressionMethodBase* const Method = GetMethod(Header.Method)) + { + if (Header.Method == CompressionMethod::None) + { + return Method->Decompress(Header, CompressedData).Flatten(); + } + UniqueBuffer RawData = UniqueBuffer::Alloc(Header.TotalRawSize); + if (Method->TryDecompressTo(Header, CompressedData, RawData)) + { + return RawData.MoveToShared(); + } + } + } + return SharedBuffer(); +} + +CompositeBuffer +CompressedBuffer::DecompressToComposite() const +{ + using namespace detail; + if (CompressedData) + { + const BufferHeader Header = BufferHeader::Read(CompressedData); + if (const CompressionMethodBase* const Method = GetMethod(Header.Method)) + { + return Method->Decompress(Header, CompressedData); + } + } + return CompositeBuffer(); +} + +const char* +CompressedBuffer::GetFormatName() const +{ + return detail::GetMethodName(CompressedData ? detail::BufferHeader::Read(CompressedData).Method : CompressionMethod::None); +} + +/** + ______________________ _____________________________ + \__ ___/\_ _____// _____/\__ ___/ _____/ + | | | __)_ \_____ \ | | \_____ \ + | | | \/ \ | | / \ + |____| /_______ /_______ / |____| /_______ / + \/ \/ \/ + */ + +TEST_CASE("CompressedBuffer") +{ + uint8_t Zeroes[256]{}; + uint8_t Ones[256]; + memset(Ones, 1, sizeof Ones); + + { + CompressedBuffer Buffer = + CompressedBuffer::Compress(CompressionMethod::None, CompositeBuffer(SharedBuffer::MakeView(MakeMemoryView(Zeroes)))); + + CHECK(Buffer.GetRawSize() == sizeof(Zeroes)); + CHECK(Buffer.GetCompressedSize() == (sizeof(Zeroes) + sizeof(detail::BufferHeader))); + + CompositeBuffer Compressed = Buffer.GetCompressed(); + CompressedBuffer BufferD = CompressedBuffer::FromCompressed(Compressed); + + CHECK(BufferD.IsNull() == false); + + CompositeBuffer Decomp = BufferD.DecompressToComposite(); + + CHECK(Decomp.GetSize() == Buffer.GetRawSize()); + CHECK(BLAKE3::HashBuffer(Decomp) == BufferD.GetRawHash()); + } + + { + CompressedBuffer Buffer = CompressedBuffer::Compress( + CompressionMethod::None, + CompositeBuffer(SharedBuffer::MakeView(MakeMemoryView(Zeroes)), SharedBuffer::MakeView(MakeMemoryView(Ones)))); + + CHECK(Buffer.GetRawSize() == (sizeof(Zeroes) + sizeof(Ones))); + CHECK(Buffer.GetCompressedSize() == (sizeof(Zeroes) + sizeof(Ones) + sizeof(detail::BufferHeader))); + + CompositeBuffer Compressed = Buffer.GetCompressed(); + CompressedBuffer BufferD = CompressedBuffer::FromCompressed(Compressed); + + CHECK(BufferD.IsNull() == false); + + CompositeBuffer Decomp = BufferD.DecompressToComposite(); + + CHECK(Decomp.GetSize() == Buffer.GetRawSize()); + CHECK(BLAKE3::HashBuffer(Decomp) == BufferD.GetRawHash()); + } + + { + CompressedBuffer Buffer = + CompressedBuffer::Compress(CompressionMethod::LZ4, CompositeBuffer(SharedBuffer::MakeView(MakeMemoryView(Zeroes)))); + + CHECK(Buffer.GetRawSize() == sizeof(Zeroes)); + CHECK(Buffer.GetCompressedSize() == (15 + sizeof(detail::BufferHeader))); + + CompositeBuffer Compressed = Buffer.GetCompressed(); + CompressedBuffer BufferD = CompressedBuffer::FromCompressed(Compressed); + + CHECK(BufferD.IsNull() == false); + + CompositeBuffer Decomp = BufferD.DecompressToComposite(); + + CHECK(Decomp.GetSize() == Buffer.GetRawSize()); + CHECK(BLAKE3::HashBuffer(Decomp) == BufferD.GetRawHash()); + } + + { + CompressedBuffer Buffer = CompressedBuffer::Compress( + CompressionMethod::LZ4, + CompositeBuffer(SharedBuffer::MakeView(MakeMemoryView(Zeroes)), SharedBuffer::MakeView(MakeMemoryView(Ones)))); + + CHECK(Buffer.GetRawSize() == (sizeof(Zeroes) + sizeof(Ones))); + CHECK(Buffer.GetCompressedSize() == (20 + sizeof(detail::BufferHeader))); + + CompositeBuffer Compressed = Buffer.GetCompressed(); + CompressedBuffer BufferD = CompressedBuffer::FromCompressed(Compressed); + + CHECK(BufferD.IsNull() == false); + + CompositeBuffer Decomp = BufferD.DecompressToComposite(); + + CHECK(Decomp.GetSize() == Buffer.GetRawSize()); + CHECK(BLAKE3::HashBuffer(Decomp) == BufferD.GetRawHash()); + } +} + +void +compress_forcelink() +{ +} + } // namespace zen diff --git a/zencore/crc32.cpp b/zencore/crc32.cpp new file mode 100644 index 000000000..a6c3fcc47 --- /dev/null +++ b/zencore/crc32.cpp @@ -0,0 +1,546 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + + +#include "zencore/crc32.h" + +namespace CRC32 { + +static const uint32_t CRCTable_DEPRECATED[256] = { + 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005, 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, + 0x2B4BCB61, 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD, 0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9, 0x5F15ADAC, 0x5BD4B01B, + 0x569796C2, 0x52568B75, 0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011, 0x791D4014, 0x7DDC5DA3, 0x709F7B7A, 0x745E66CD, 0x9823B6E0, + 0x9CE2AB57, 0x91A18D8E, 0x95609039, 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, 0xBE2B5B58, 0xBAEA46EF, 0xB7A96036, 0xB3687D81, + 0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, 0xA06C0B5D, 0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49, 0xC7361B4C, 0xC3F706FB, 0xCEB42022, + 0xCA753D95, 0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1, 0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, 0x34867077, 0x30476DC0, + 0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5, 0x2AC12072, 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, 0x018AEB13, + 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA, 0x7897AB07, 0x7C56B6B0, 0x71159069, 0x75D48DDE, 0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02, + 0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1, 0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA, 0xACA5C697, 0xA864DB20, 0xA527FDF9, + 0xA1E6E04E, 0xBFA1B04B, 0xBB60ADFC, 0xB6238B25, 0xB2E29692, 0x8AAD2B2F, 0x8E6C3698, 0x832F1041, 0x87EE0DF6, 0x99A95DF3, 0x9D684044, + 0x902B669D, 0x94EA7B2A, 0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E, 0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2, 0xC6BCF05F, + 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, 0xD1799B34, 0xDC3ABDED, 0xD8FBA05A, 0x690CE0EE, 0x6DCDFD59, 0x608EDB80, 0x644FC637, + 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB, 0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F, 0x5C007B8A, 0x58C1663D, 0x558240E4, + 0x51435D53, 0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47, 0x36194D42, 0x32D850F5, 0x3F9B762C, 0x3B5A6B9B, 0x0315D626, 0x07D4CB91, + 0x0A97ED48, 0x0E56F0FF, 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, 0xF12F560E, 0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, + 0xE6EA3D65, 0xEBA91BBC, 0xEF68060B, 0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F, 0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3, + 0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7, 0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, 0x9B3660C6, 0x9FF77D71, 0x92B45BA8, + 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74, 0x857130C3, 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640, 0x4E8EE645, 0x4A4FFBF2, + 0x470CDD2B, 0x43CDC09C, 0x7B827D21, 0x7F436096, 0x7200464F, 0x76C15BF8, 0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24, 0x119B4BE9, + 0x155A565E, 0x18197087, 0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC, 0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, + 0x2497D08D, 0x2056CD3A, 0x2D15EBE3, 0x29D4F654, 0xC5A92679, 0xC1683BCE, 0xCC2B1D17, 0xC8EA00A0, 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, + 0xDBEE767C, 0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18, 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4, 0x89B8FD09, 0x8D79E0BE, + 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, 0x9E7D9662, 0x933EB0BB, 0x97FFAD0C, 0xAFB010B1, 0xAB710D06, 0xA6322BDF, 0xA2F33668, 0xBCB4666D, + 0xB8757BDA, 0xB5365D03, 0xB1F740B4}; + +static const uint32_t CRCTablesSB8_DEPRECATED[8][256] = { + {0x00000000, 0xb71dc104, 0x6e3b8209, 0xd926430d, 0xdc760413, 0x6b6bc517, 0xb24d861a, 0x0550471e, 0xb8ed0826, 0x0ff0c922, 0xd6d68a2f, + 0x61cb4b2b, 0x649b0c35, 0xd386cd31, 0x0aa08e3c, 0xbdbd4f38, 0x70db114c, 0xc7c6d048, 0x1ee09345, 0xa9fd5241, 0xacad155f, 0x1bb0d45b, + 0xc2969756, 0x758b5652, 0xc836196a, 0x7f2bd86e, 0xa60d9b63, 0x11105a67, 0x14401d79, 0xa35ddc7d, 0x7a7b9f70, 0xcd665e74, 0xe0b62398, + 0x57abe29c, 0x8e8da191, 0x39906095, 0x3cc0278b, 0x8bdde68f, 0x52fba582, 0xe5e66486, 0x585b2bbe, 0xef46eaba, 0x3660a9b7, 0x817d68b3, + 0x842d2fad, 0x3330eea9, 0xea16ada4, 0x5d0b6ca0, 0x906d32d4, 0x2770f3d0, 0xfe56b0dd, 0x494b71d9, 0x4c1b36c7, 0xfb06f7c3, 0x2220b4ce, + 0x953d75ca, 0x28803af2, 0x9f9dfbf6, 0x46bbb8fb, 0xf1a679ff, 0xf4f63ee1, 0x43ebffe5, 0x9acdbce8, 0x2dd07dec, 0x77708634, 0xc06d4730, + 0x194b043d, 0xae56c539, 0xab068227, 0x1c1b4323, 0xc53d002e, 0x7220c12a, 0xcf9d8e12, 0x78804f16, 0xa1a60c1b, 0x16bbcd1f, 0x13eb8a01, + 0xa4f64b05, 0x7dd00808, 0xcacdc90c, 0x07ab9778, 0xb0b6567c, 0x69901571, 0xde8dd475, 0xdbdd936b, 0x6cc0526f, 0xb5e61162, 0x02fbd066, + 0xbf469f5e, 0x085b5e5a, 0xd17d1d57, 0x6660dc53, 0x63309b4d, 0xd42d5a49, 0x0d0b1944, 0xba16d840, 0x97c6a5ac, 0x20db64a8, 0xf9fd27a5, + 0x4ee0e6a1, 0x4bb0a1bf, 0xfcad60bb, 0x258b23b6, 0x9296e2b2, 0x2f2bad8a, 0x98366c8e, 0x41102f83, 0xf60dee87, 0xf35da999, 0x4440689d, + 0x9d662b90, 0x2a7bea94, 0xe71db4e0, 0x500075e4, 0x892636e9, 0x3e3bf7ed, 0x3b6bb0f3, 0x8c7671f7, 0x555032fa, 0xe24df3fe, 0x5ff0bcc6, + 0xe8ed7dc2, 0x31cb3ecf, 0x86d6ffcb, 0x8386b8d5, 0x349b79d1, 0xedbd3adc, 0x5aa0fbd8, 0xeee00c69, 0x59fdcd6d, 0x80db8e60, 0x37c64f64, + 0x3296087a, 0x858bc97e, 0x5cad8a73, 0xebb04b77, 0x560d044f, 0xe110c54b, 0x38368646, 0x8f2b4742, 0x8a7b005c, 0x3d66c158, 0xe4408255, + 0x535d4351, 0x9e3b1d25, 0x2926dc21, 0xf0009f2c, 0x471d5e28, 0x424d1936, 0xf550d832, 0x2c769b3f, 0x9b6b5a3b, 0x26d61503, 0x91cbd407, + 0x48ed970a, 0xfff0560e, 0xfaa01110, 0x4dbdd014, 0x949b9319, 0x2386521d, 0x0e562ff1, 0xb94beef5, 0x606dadf8, 0xd7706cfc, 0xd2202be2, + 0x653deae6, 0xbc1ba9eb, 0x0b0668ef, 0xb6bb27d7, 0x01a6e6d3, 0xd880a5de, 0x6f9d64da, 0x6acd23c4, 0xddd0e2c0, 0x04f6a1cd, 0xb3eb60c9, + 0x7e8d3ebd, 0xc990ffb9, 0x10b6bcb4, 0xa7ab7db0, 0xa2fb3aae, 0x15e6fbaa, 0xccc0b8a7, 0x7bdd79a3, 0xc660369b, 0x717df79f, 0xa85bb492, + 0x1f467596, 0x1a163288, 0xad0bf38c, 0x742db081, 0xc3307185, 0x99908a5d, 0x2e8d4b59, 0xf7ab0854, 0x40b6c950, 0x45e68e4e, 0xf2fb4f4a, + 0x2bdd0c47, 0x9cc0cd43, 0x217d827b, 0x9660437f, 0x4f460072, 0xf85bc176, 0xfd0b8668, 0x4a16476c, 0x93300461, 0x242dc565, 0xe94b9b11, + 0x5e565a15, 0x87701918, 0x306dd81c, 0x353d9f02, 0x82205e06, 0x5b061d0b, 0xec1bdc0f, 0x51a69337, 0xe6bb5233, 0x3f9d113e, 0x8880d03a, + 0x8dd09724, 0x3acd5620, 0xe3eb152d, 0x54f6d429, 0x7926a9c5, 0xce3b68c1, 0x171d2bcc, 0xa000eac8, 0xa550add6, 0x124d6cd2, 0xcb6b2fdf, + 0x7c76eedb, 0xc1cba1e3, 0x76d660e7, 0xaff023ea, 0x18ede2ee, 0x1dbda5f0, 0xaaa064f4, 0x738627f9, 0xc49be6fd, 0x09fdb889, 0xbee0798d, + 0x67c63a80, 0xd0dbfb84, 0xd58bbc9a, 0x62967d9e, 0xbbb03e93, 0x0cadff97, 0xb110b0af, 0x060d71ab, 0xdf2b32a6, 0x6836f3a2, 0x6d66b4bc, + 0xda7b75b8, 0x035d36b5, 0xb440f7b1}, + {0x00000000, 0xdcc119d2, 0x0f9ef2a0, 0xd35feb72, 0xa9212445, 0x75e03d97, 0xa6bfd6e5, 0x7a7ecf37, 0x5243488a, 0x8e825158, 0x5dddba2a, + 0x811ca3f8, 0xfb626ccf, 0x27a3751d, 0xf4fc9e6f, 0x283d87bd, 0x139b5110, 0xcf5a48c2, 0x1c05a3b0, 0xc0c4ba62, 0xbaba7555, 0x667b6c87, + 0xb52487f5, 0x69e59e27, 0x41d8199a, 0x9d190048, 0x4e46eb3a, 0x9287f2e8, 0xe8f93ddf, 0x3438240d, 0xe767cf7f, 0x3ba6d6ad, 0x2636a320, + 0xfaf7baf2, 0x29a85180, 0xf5694852, 0x8f178765, 0x53d69eb7, 0x808975c5, 0x5c486c17, 0x7475ebaa, 0xa8b4f278, 0x7beb190a, 0xa72a00d8, + 0xdd54cfef, 0x0195d63d, 0xd2ca3d4f, 0x0e0b249d, 0x35adf230, 0xe96cebe2, 0x3a330090, 0xe6f21942, 0x9c8cd675, 0x404dcfa7, 0x931224d5, + 0x4fd33d07, 0x67eebaba, 0xbb2fa368, 0x6870481a, 0xb4b151c8, 0xcecf9eff, 0x120e872d, 0xc1516c5f, 0x1d90758d, 0x4c6c4641, 0x90ad5f93, + 0x43f2b4e1, 0x9f33ad33, 0xe54d6204, 0x398c7bd6, 0xead390a4, 0x36128976, 0x1e2f0ecb, 0xc2ee1719, 0x11b1fc6b, 0xcd70e5b9, 0xb70e2a8e, + 0x6bcf335c, 0xb890d82e, 0x6451c1fc, 0x5ff71751, 0x83360e83, 0x5069e5f1, 0x8ca8fc23, 0xf6d63314, 0x2a172ac6, 0xf948c1b4, 0x2589d866, + 0x0db45fdb, 0xd1754609, 0x022aad7b, 0xdeebb4a9, 0xa4957b9e, 0x7854624c, 0xab0b893e, 0x77ca90ec, 0x6a5ae561, 0xb69bfcb3, 0x65c417c1, + 0xb9050e13, 0xc37bc124, 0x1fbad8f6, 0xcce53384, 0x10242a56, 0x3819adeb, 0xe4d8b439, 0x37875f4b, 0xeb464699, 0x913889ae, 0x4df9907c, + 0x9ea67b0e, 0x426762dc, 0x79c1b471, 0xa500ada3, 0x765f46d1, 0xaa9e5f03, 0xd0e09034, 0x0c2189e6, 0xdf7e6294, 0x03bf7b46, 0x2b82fcfb, + 0xf743e529, 0x241c0e5b, 0xf8dd1789, 0x82a3d8be, 0x5e62c16c, 0x8d3d2a1e, 0x51fc33cc, 0x98d88c82, 0x44199550, 0x97467e22, 0x4b8767f0, + 0x31f9a8c7, 0xed38b115, 0x3e675a67, 0xe2a643b5, 0xca9bc408, 0x165addda, 0xc50536a8, 0x19c42f7a, 0x63bae04d, 0xbf7bf99f, 0x6c2412ed, + 0xb0e50b3f, 0x8b43dd92, 0x5782c440, 0x84dd2f32, 0x581c36e0, 0x2262f9d7, 0xfea3e005, 0x2dfc0b77, 0xf13d12a5, 0xd9009518, 0x05c18cca, + 0xd69e67b8, 0x0a5f7e6a, 0x7021b15d, 0xace0a88f, 0x7fbf43fd, 0xa37e5a2f, 0xbeee2fa2, 0x622f3670, 0xb170dd02, 0x6db1c4d0, 0x17cf0be7, + 0xcb0e1235, 0x1851f947, 0xc490e095, 0xecad6728, 0x306c7efa, 0xe3339588, 0x3ff28c5a, 0x458c436d, 0x994d5abf, 0x4a12b1cd, 0x96d3a81f, + 0xad757eb2, 0x71b46760, 0xa2eb8c12, 0x7e2a95c0, 0x04545af7, 0xd8954325, 0x0bcaa857, 0xd70bb185, 0xff363638, 0x23f72fea, 0xf0a8c498, + 0x2c69dd4a, 0x5617127d, 0x8ad60baf, 0x5989e0dd, 0x8548f90f, 0xd4b4cac3, 0x0875d311, 0xdb2a3863, 0x07eb21b1, 0x7d95ee86, 0xa154f754, + 0x720b1c26, 0xaeca05f4, 0x86f78249, 0x5a369b9b, 0x896970e9, 0x55a8693b, 0x2fd6a60c, 0xf317bfde, 0x204854ac, 0xfc894d7e, 0xc72f9bd3, + 0x1bee8201, 0xc8b16973, 0x147070a1, 0x6e0ebf96, 0xb2cfa644, 0x61904d36, 0xbd5154e4, 0x956cd359, 0x49adca8b, 0x9af221f9, 0x4633382b, + 0x3c4df71c, 0xe08ceece, 0x33d305bc, 0xef121c6e, 0xf28269e3, 0x2e437031, 0xfd1c9b43, 0x21dd8291, 0x5ba34da6, 0x87625474, 0x543dbf06, + 0x88fca6d4, 0xa0c12169, 0x7c0038bb, 0xaf5fd3c9, 0x739eca1b, 0x09e0052c, 0xd5211cfe, 0x067ef78c, 0xdabfee5e, 0xe11938f3, 0x3dd82121, + 0xee87ca53, 0x3246d381, 0x48381cb6, 0x94f90564, 0x47a6ee16, 0x9b67f7c4, 0xb35a7079, 0x6f9b69ab, 0xbcc482d9, 0x60059b0b, 0x1a7b543c, + 0xc6ba4dee, 0x15e5a69c, 0xc924bf4e}, + {0x00000000, 0x87acd801, 0x0e59b103, 0x89f56902, 0x1cb26207, 0x9b1eba06, 0x12ebd304, 0x95470b05, 0x3864c50e, 0xbfc81d0f, 0x363d740d, + 0xb191ac0c, 0x24d6a709, 0xa37a7f08, 0x2a8f160a, 0xad23ce0b, 0x70c88a1d, 0xf764521c, 0x7e913b1e, 0xf93de31f, 0x6c7ae81a, 0xebd6301b, + 0x62235919, 0xe58f8118, 0x48ac4f13, 0xcf009712, 0x46f5fe10, 0xc1592611, 0x541e2d14, 0xd3b2f515, 0x5a479c17, 0xddeb4416, 0xe090153b, + 0x673ccd3a, 0xeec9a438, 0x69657c39, 0xfc22773c, 0x7b8eaf3d, 0xf27bc63f, 0x75d71e3e, 0xd8f4d035, 0x5f580834, 0xd6ad6136, 0x5101b937, + 0xc446b232, 0x43ea6a33, 0xca1f0331, 0x4db3db30, 0x90589f26, 0x17f44727, 0x9e012e25, 0x19adf624, 0x8ceafd21, 0x0b462520, 0x82b34c22, + 0x051f9423, 0xa83c5a28, 0x2f908229, 0xa665eb2b, 0x21c9332a, 0xb48e382f, 0x3322e02e, 0xbad7892c, 0x3d7b512d, 0xc0212b76, 0x478df377, + 0xce789a75, 0x49d44274, 0xdc934971, 0x5b3f9170, 0xd2caf872, 0x55662073, 0xf845ee78, 0x7fe93679, 0xf61c5f7b, 0x71b0877a, 0xe4f78c7f, + 0x635b547e, 0xeaae3d7c, 0x6d02e57d, 0xb0e9a16b, 0x3745796a, 0xbeb01068, 0x391cc869, 0xac5bc36c, 0x2bf71b6d, 0xa202726f, 0x25aeaa6e, + 0x888d6465, 0x0f21bc64, 0x86d4d566, 0x01780d67, 0x943f0662, 0x1393de63, 0x9a66b761, 0x1dca6f60, 0x20b13e4d, 0xa71de64c, 0x2ee88f4e, + 0xa944574f, 0x3c035c4a, 0xbbaf844b, 0x325aed49, 0xb5f63548, 0x18d5fb43, 0x9f792342, 0x168c4a40, 0x91209241, 0x04679944, 0x83cb4145, + 0x0a3e2847, 0x8d92f046, 0x5079b450, 0xd7d56c51, 0x5e200553, 0xd98cdd52, 0x4ccbd657, 0xcb670e56, 0x42926754, 0xc53ebf55, 0x681d715e, + 0xefb1a95f, 0x6644c05d, 0xe1e8185c, 0x74af1359, 0xf303cb58, 0x7af6a25a, 0xfd5a7a5b, 0x804356ec, 0x07ef8eed, 0x8e1ae7ef, 0x09b63fee, + 0x9cf134eb, 0x1b5decea, 0x92a885e8, 0x15045de9, 0xb82793e2, 0x3f8b4be3, 0xb67e22e1, 0x31d2fae0, 0xa495f1e5, 0x233929e4, 0xaacc40e6, + 0x2d6098e7, 0xf08bdcf1, 0x772704f0, 0xfed26df2, 0x797eb5f3, 0xec39bef6, 0x6b9566f7, 0xe2600ff5, 0x65ccd7f4, 0xc8ef19ff, 0x4f43c1fe, + 0xc6b6a8fc, 0x411a70fd, 0xd45d7bf8, 0x53f1a3f9, 0xda04cafb, 0x5da812fa, 0x60d343d7, 0xe77f9bd6, 0x6e8af2d4, 0xe9262ad5, 0x7c6121d0, + 0xfbcdf9d1, 0x723890d3, 0xf59448d2, 0x58b786d9, 0xdf1b5ed8, 0x56ee37da, 0xd142efdb, 0x4405e4de, 0xc3a93cdf, 0x4a5c55dd, 0xcdf08ddc, + 0x101bc9ca, 0x97b711cb, 0x1e4278c9, 0x99eea0c8, 0x0ca9abcd, 0x8b0573cc, 0x02f01ace, 0x855cc2cf, 0x287f0cc4, 0xafd3d4c5, 0x2626bdc7, + 0xa18a65c6, 0x34cd6ec3, 0xb361b6c2, 0x3a94dfc0, 0xbd3807c1, 0x40627d9a, 0xc7cea59b, 0x4e3bcc99, 0xc9971498, 0x5cd01f9d, 0xdb7cc79c, + 0x5289ae9e, 0xd525769f, 0x7806b894, 0xffaa6095, 0x765f0997, 0xf1f3d196, 0x64b4da93, 0xe3180292, 0x6aed6b90, 0xed41b391, 0x30aaf787, + 0xb7062f86, 0x3ef34684, 0xb95f9e85, 0x2c189580, 0xabb44d81, 0x22412483, 0xa5edfc82, 0x08ce3289, 0x8f62ea88, 0x0697838a, 0x813b5b8b, + 0x147c508e, 0x93d0888f, 0x1a25e18d, 0x9d89398c, 0xa0f268a1, 0x275eb0a0, 0xaeabd9a2, 0x290701a3, 0xbc400aa6, 0x3becd2a7, 0xb219bba5, + 0x35b563a4, 0x9896adaf, 0x1f3a75ae, 0x96cf1cac, 0x1163c4ad, 0x8424cfa8, 0x038817a9, 0x8a7d7eab, 0x0dd1a6aa, 0xd03ae2bc, 0x57963abd, + 0xde6353bf, 0x59cf8bbe, 0xcc8880bb, 0x4b2458ba, 0xc2d131b8, 0x457de9b9, 0xe85e27b2, 0x6ff2ffb3, 0xe60796b1, 0x61ab4eb0, 0xf4ec45b5, + 0x73409db4, 0xfab5f4b6, 0x7d192cb7}, + {0x00000000, 0xb79a6ddc, 0xd9281abc, 0x6eb27760, 0x054cf57c, 0xb2d698a0, 0xdc64efc0, 0x6bfe821c, 0x0a98eaf9, 0xbd028725, 0xd3b0f045, + 0x642a9d99, 0x0fd41f85, 0xb84e7259, 0xd6fc0539, 0x616668e5, 0xa32d14f7, 0x14b7792b, 0x7a050e4b, 0xcd9f6397, 0xa661e18b, 0x11fb8c57, + 0x7f49fb37, 0xc8d396eb, 0xa9b5fe0e, 0x1e2f93d2, 0x709de4b2, 0xc707896e, 0xacf90b72, 0x1b6366ae, 0x75d111ce, 0xc24b7c12, 0xf146e9ea, + 0x46dc8436, 0x286ef356, 0x9ff49e8a, 0xf40a1c96, 0x4390714a, 0x2d22062a, 0x9ab86bf6, 0xfbde0313, 0x4c446ecf, 0x22f619af, 0x956c7473, + 0xfe92f66f, 0x49089bb3, 0x27baecd3, 0x9020810f, 0x526bfd1d, 0xe5f190c1, 0x8b43e7a1, 0x3cd98a7d, 0x57270861, 0xe0bd65bd, 0x8e0f12dd, + 0x39957f01, 0x58f317e4, 0xef697a38, 0x81db0d58, 0x36416084, 0x5dbfe298, 0xea258f44, 0x8497f824, 0x330d95f8, 0x559013d1, 0xe20a7e0d, + 0x8cb8096d, 0x3b2264b1, 0x50dce6ad, 0xe7468b71, 0x89f4fc11, 0x3e6e91cd, 0x5f08f928, 0xe89294f4, 0x8620e394, 0x31ba8e48, 0x5a440c54, + 0xedde6188, 0x836c16e8, 0x34f67b34, 0xf6bd0726, 0x41276afa, 0x2f951d9a, 0x980f7046, 0xf3f1f25a, 0x446b9f86, 0x2ad9e8e6, 0x9d43853a, + 0xfc25eddf, 0x4bbf8003, 0x250df763, 0x92979abf, 0xf96918a3, 0x4ef3757f, 0x2041021f, 0x97db6fc3, 0xa4d6fa3b, 0x134c97e7, 0x7dfee087, + 0xca648d5b, 0xa19a0f47, 0x1600629b, 0x78b215fb, 0xcf287827, 0xae4e10c2, 0x19d47d1e, 0x77660a7e, 0xc0fc67a2, 0xab02e5be, 0x1c988862, + 0x722aff02, 0xc5b092de, 0x07fbeecc, 0xb0618310, 0xded3f470, 0x694999ac, 0x02b71bb0, 0xb52d766c, 0xdb9f010c, 0x6c056cd0, 0x0d630435, + 0xbaf969e9, 0xd44b1e89, 0x63d17355, 0x082ff149, 0xbfb59c95, 0xd107ebf5, 0x669d8629, 0x1d3de6a6, 0xaaa78b7a, 0xc415fc1a, 0x738f91c6, + 0x187113da, 0xafeb7e06, 0xc1590966, 0x76c364ba, 0x17a50c5f, 0xa03f6183, 0xce8d16e3, 0x79177b3f, 0x12e9f923, 0xa57394ff, 0xcbc1e39f, + 0x7c5b8e43, 0xbe10f251, 0x098a9f8d, 0x6738e8ed, 0xd0a28531, 0xbb5c072d, 0x0cc66af1, 0x62741d91, 0xd5ee704d, 0xb48818a8, 0x03127574, + 0x6da00214, 0xda3a6fc8, 0xb1c4edd4, 0x065e8008, 0x68ecf768, 0xdf769ab4, 0xec7b0f4c, 0x5be16290, 0x355315f0, 0x82c9782c, 0xe937fa30, + 0x5ead97ec, 0x301fe08c, 0x87858d50, 0xe6e3e5b5, 0x51798869, 0x3fcbff09, 0x885192d5, 0xe3af10c9, 0x54357d15, 0x3a870a75, 0x8d1d67a9, + 0x4f561bbb, 0xf8cc7667, 0x967e0107, 0x21e46cdb, 0x4a1aeec7, 0xfd80831b, 0x9332f47b, 0x24a899a7, 0x45cef142, 0xf2549c9e, 0x9ce6ebfe, + 0x2b7c8622, 0x4082043e, 0xf71869e2, 0x99aa1e82, 0x2e30735e, 0x48adf577, 0xff3798ab, 0x9185efcb, 0x261f8217, 0x4de1000b, 0xfa7b6dd7, + 0x94c91ab7, 0x2353776b, 0x42351f8e, 0xf5af7252, 0x9b1d0532, 0x2c8768ee, 0x4779eaf2, 0xf0e3872e, 0x9e51f04e, 0x29cb9d92, 0xeb80e180, + 0x5c1a8c5c, 0x32a8fb3c, 0x853296e0, 0xeecc14fc, 0x59567920, 0x37e40e40, 0x807e639c, 0xe1180b79, 0x568266a5, 0x383011c5, 0x8faa7c19, + 0xe454fe05, 0x53ce93d9, 0x3d7ce4b9, 0x8ae68965, 0xb9eb1c9d, 0x0e717141, 0x60c30621, 0xd7596bfd, 0xbca7e9e1, 0x0b3d843d, 0x658ff35d, + 0xd2159e81, 0xb373f664, 0x04e99bb8, 0x6a5becd8, 0xddc18104, 0xb63f0318, 0x01a56ec4, 0x6f1719a4, 0xd88d7478, 0x1ac6086a, 0xad5c65b6, + 0xc3ee12d6, 0x74747f0a, 0x1f8afd16, 0xa81090ca, 0xc6a2e7aa, 0x71388a76, 0x105ee293, 0xa7c48f4f, 0xc976f82f, 0x7eec95f3, 0x151217ef, + 0xa2887a33, 0xcc3a0d53, 0x7ba0608f}, + {0x00000000, 0x8d670d49, 0x1acf1a92, 0x97a817db, 0x8383f420, 0x0ee4f969, 0x994ceeb2, 0x142be3fb, 0x0607e941, 0x8b60e408, 0x1cc8f3d3, + 0x91affe9a, 0x85841d61, 0x08e31028, 0x9f4b07f3, 0x122c0aba, 0x0c0ed283, 0x8169dfca, 0x16c1c811, 0x9ba6c558, 0x8f8d26a3, 0x02ea2bea, + 0x95423c31, 0x18253178, 0x0a093bc2, 0x876e368b, 0x10c62150, 0x9da12c19, 0x898acfe2, 0x04edc2ab, 0x9345d570, 0x1e22d839, 0xaf016503, + 0x2266684a, 0xb5ce7f91, 0x38a972d8, 0x2c829123, 0xa1e59c6a, 0x364d8bb1, 0xbb2a86f8, 0xa9068c42, 0x2461810b, 0xb3c996d0, 0x3eae9b99, + 0x2a857862, 0xa7e2752b, 0x304a62f0, 0xbd2d6fb9, 0xa30fb780, 0x2e68bac9, 0xb9c0ad12, 0x34a7a05b, 0x208c43a0, 0xadeb4ee9, 0x3a435932, + 0xb724547b, 0xa5085ec1, 0x286f5388, 0xbfc74453, 0x32a0491a, 0x268baae1, 0xabeca7a8, 0x3c44b073, 0xb123bd3a, 0x5e03ca06, 0xd364c74f, + 0x44ccd094, 0xc9abdddd, 0xdd803e26, 0x50e7336f, 0xc74f24b4, 0x4a2829fd, 0x58042347, 0xd5632e0e, 0x42cb39d5, 0xcfac349c, 0xdb87d767, + 0x56e0da2e, 0xc148cdf5, 0x4c2fc0bc, 0x520d1885, 0xdf6a15cc, 0x48c20217, 0xc5a50f5e, 0xd18eeca5, 0x5ce9e1ec, 0xcb41f637, 0x4626fb7e, + 0x540af1c4, 0xd96dfc8d, 0x4ec5eb56, 0xc3a2e61f, 0xd78905e4, 0x5aee08ad, 0xcd461f76, 0x4021123f, 0xf102af05, 0x7c65a24c, 0xebcdb597, + 0x66aab8de, 0x72815b25, 0xffe6566c, 0x684e41b7, 0xe5294cfe, 0xf7054644, 0x7a624b0d, 0xedca5cd6, 0x60ad519f, 0x7486b264, 0xf9e1bf2d, + 0x6e49a8f6, 0xe32ea5bf, 0xfd0c7d86, 0x706b70cf, 0xe7c36714, 0x6aa46a5d, 0x7e8f89a6, 0xf3e884ef, 0x64409334, 0xe9279e7d, 0xfb0b94c7, + 0x766c998e, 0xe1c48e55, 0x6ca3831c, 0x788860e7, 0xf5ef6dae, 0x62477a75, 0xef20773c, 0xbc06940d, 0x31619944, 0xa6c98e9f, 0x2bae83d6, + 0x3f85602d, 0xb2e26d64, 0x254a7abf, 0xa82d77f6, 0xba017d4c, 0x37667005, 0xa0ce67de, 0x2da96a97, 0x3982896c, 0xb4e58425, 0x234d93fe, + 0xae2a9eb7, 0xb008468e, 0x3d6f4bc7, 0xaac75c1c, 0x27a05155, 0x338bb2ae, 0xbeecbfe7, 0x2944a83c, 0xa423a575, 0xb60fafcf, 0x3b68a286, + 0xacc0b55d, 0x21a7b814, 0x358c5bef, 0xb8eb56a6, 0x2f43417d, 0xa2244c34, 0x1307f10e, 0x9e60fc47, 0x09c8eb9c, 0x84afe6d5, 0x9084052e, + 0x1de30867, 0x8a4b1fbc, 0x072c12f5, 0x1500184f, 0x98671506, 0x0fcf02dd, 0x82a80f94, 0x9683ec6f, 0x1be4e126, 0x8c4cf6fd, 0x012bfbb4, + 0x1f09238d, 0x926e2ec4, 0x05c6391f, 0x88a13456, 0x9c8ad7ad, 0x11eddae4, 0x8645cd3f, 0x0b22c076, 0x190ecacc, 0x9469c785, 0x03c1d05e, + 0x8ea6dd17, 0x9a8d3eec, 0x17ea33a5, 0x8042247e, 0x0d252937, 0xe2055e0b, 0x6f625342, 0xf8ca4499, 0x75ad49d0, 0x6186aa2b, 0xece1a762, + 0x7b49b0b9, 0xf62ebdf0, 0xe402b74a, 0x6965ba03, 0xfecdadd8, 0x73aaa091, 0x6781436a, 0xeae64e23, 0x7d4e59f8, 0xf02954b1, 0xee0b8c88, + 0x636c81c1, 0xf4c4961a, 0x79a39b53, 0x6d8878a8, 0xe0ef75e1, 0x7747623a, 0xfa206f73, 0xe80c65c9, 0x656b6880, 0xf2c37f5b, 0x7fa47212, + 0x6b8f91e9, 0xe6e89ca0, 0x71408b7b, 0xfc278632, 0x4d043b08, 0xc0633641, 0x57cb219a, 0xdaac2cd3, 0xce87cf28, 0x43e0c261, 0xd448d5ba, + 0x592fd8f3, 0x4b03d249, 0xc664df00, 0x51ccc8db, 0xdcabc592, 0xc8802669, 0x45e72b20, 0xd24f3cfb, 0x5f2831b2, 0x410ae98b, 0xcc6de4c2, + 0x5bc5f319, 0xd6a2fe50, 0xc2891dab, 0x4fee10e2, 0xd8460739, 0x55210a70, 0x470d00ca, 0xca6a0d83, 0x5dc21a58, 0xd0a51711, 0xc48ef4ea, + 0x49e9f9a3, 0xde41ee78, 0x5326e331}, + {0x00000000, 0x780d281b, 0xf01a5036, 0x8817782d, 0xe035a06c, 0x98388877, 0x102ff05a, 0x6822d841, 0xc06b40d9, 0xb86668c2, 0x307110ef, + 0x487c38f4, 0x205ee0b5, 0x5853c8ae, 0xd044b083, 0xa8499898, 0x37ca41b6, 0x4fc769ad, 0xc7d01180, 0xbfdd399b, 0xd7ffe1da, 0xaff2c9c1, + 0x27e5b1ec, 0x5fe899f7, 0xf7a1016f, 0x8fac2974, 0x07bb5159, 0x7fb67942, 0x1794a103, 0x6f998918, 0xe78ef135, 0x9f83d92e, 0xd9894268, + 0xa1846a73, 0x2993125e, 0x519e3a45, 0x39bce204, 0x41b1ca1f, 0xc9a6b232, 0xb1ab9a29, 0x19e202b1, 0x61ef2aaa, 0xe9f85287, 0x91f57a9c, + 0xf9d7a2dd, 0x81da8ac6, 0x09cdf2eb, 0x71c0daf0, 0xee4303de, 0x964e2bc5, 0x1e5953e8, 0x66547bf3, 0x0e76a3b2, 0x767b8ba9, 0xfe6cf384, + 0x8661db9f, 0x2e284307, 0x56256b1c, 0xde321331, 0xa63f3b2a, 0xce1de36b, 0xb610cb70, 0x3e07b35d, 0x460a9b46, 0xb21385d0, 0xca1eadcb, + 0x4209d5e6, 0x3a04fdfd, 0x522625bc, 0x2a2b0da7, 0xa23c758a, 0xda315d91, 0x7278c509, 0x0a75ed12, 0x8262953f, 0xfa6fbd24, 0x924d6565, + 0xea404d7e, 0x62573553, 0x1a5a1d48, 0x85d9c466, 0xfdd4ec7d, 0x75c39450, 0x0dcebc4b, 0x65ec640a, 0x1de14c11, 0x95f6343c, 0xedfb1c27, + 0x45b284bf, 0x3dbfaca4, 0xb5a8d489, 0xcda5fc92, 0xa58724d3, 0xdd8a0cc8, 0x559d74e5, 0x2d905cfe, 0x6b9ac7b8, 0x1397efa3, 0x9b80978e, + 0xe38dbf95, 0x8baf67d4, 0xf3a24fcf, 0x7bb537e2, 0x03b81ff9, 0xabf18761, 0xd3fcaf7a, 0x5bebd757, 0x23e6ff4c, 0x4bc4270d, 0x33c90f16, + 0xbbde773b, 0xc3d35f20, 0x5c50860e, 0x245dae15, 0xac4ad638, 0xd447fe23, 0xbc652662, 0xc4680e79, 0x4c7f7654, 0x34725e4f, 0x9c3bc6d7, + 0xe436eecc, 0x6c2196e1, 0x142cbefa, 0x7c0e66bb, 0x04034ea0, 0x8c14368d, 0xf4191e96, 0xd33acba5, 0xab37e3be, 0x23209b93, 0x5b2db388, + 0x330f6bc9, 0x4b0243d2, 0xc3153bff, 0xbb1813e4, 0x13518b7c, 0x6b5ca367, 0xe34bdb4a, 0x9b46f351, 0xf3642b10, 0x8b69030b, 0x037e7b26, + 0x7b73533d, 0xe4f08a13, 0x9cfda208, 0x14eada25, 0x6ce7f23e, 0x04c52a7f, 0x7cc80264, 0xf4df7a49, 0x8cd25252, 0x249bcaca, 0x5c96e2d1, + 0xd4819afc, 0xac8cb2e7, 0xc4ae6aa6, 0xbca342bd, 0x34b43a90, 0x4cb9128b, 0x0ab389cd, 0x72bea1d6, 0xfaa9d9fb, 0x82a4f1e0, 0xea8629a1, + 0x928b01ba, 0x1a9c7997, 0x6291518c, 0xcad8c914, 0xb2d5e10f, 0x3ac29922, 0x42cfb139, 0x2aed6978, 0x52e04163, 0xdaf7394e, 0xa2fa1155, + 0x3d79c87b, 0x4574e060, 0xcd63984d, 0xb56eb056, 0xdd4c6817, 0xa541400c, 0x2d563821, 0x555b103a, 0xfd1288a2, 0x851fa0b9, 0x0d08d894, + 0x7505f08f, 0x1d2728ce, 0x652a00d5, 0xed3d78f8, 0x953050e3, 0x61294e75, 0x1924666e, 0x91331e43, 0xe93e3658, 0x811cee19, 0xf911c602, + 0x7106be2f, 0x090b9634, 0xa1420eac, 0xd94f26b7, 0x51585e9a, 0x29557681, 0x4177aec0, 0x397a86db, 0xb16dfef6, 0xc960d6ed, 0x56e30fc3, + 0x2eee27d8, 0xa6f95ff5, 0xdef477ee, 0xb6d6afaf, 0xcedb87b4, 0x46ccff99, 0x3ec1d782, 0x96884f1a, 0xee856701, 0x66921f2c, 0x1e9f3737, + 0x76bdef76, 0x0eb0c76d, 0x86a7bf40, 0xfeaa975b, 0xb8a00c1d, 0xc0ad2406, 0x48ba5c2b, 0x30b77430, 0x5895ac71, 0x2098846a, 0xa88ffc47, + 0xd082d45c, 0x78cb4cc4, 0x00c664df, 0x88d11cf2, 0xf0dc34e9, 0x98feeca8, 0xe0f3c4b3, 0x68e4bc9e, 0x10e99485, 0x8f6a4dab, 0xf76765b0, + 0x7f701d9d, 0x077d3586, 0x6f5fedc7, 0x1752c5dc, 0x9f45bdf1, 0xe74895ea, 0x4f010d72, 0x370c2569, 0xbf1b5d44, 0xc716755f, 0xaf34ad1e, + 0xd7398505, 0x5f2efd28, 0x2723d533}, + {0x00000000, 0x1168574f, 0x22d0ae9e, 0x33b8f9d1, 0xf3bd9c39, 0xe2d5cb76, 0xd16d32a7, 0xc00565e8, 0xe67b3973, 0xf7136e3c, 0xc4ab97ed, + 0xd5c3c0a2, 0x15c6a54a, 0x04aef205, 0x37160bd4, 0x267e5c9b, 0xccf772e6, 0xdd9f25a9, 0xee27dc78, 0xff4f8b37, 0x3f4aeedf, 0x2e22b990, + 0x1d9a4041, 0x0cf2170e, 0x2a8c4b95, 0x3be41cda, 0x085ce50b, 0x1934b244, 0xd931d7ac, 0xc85980e3, 0xfbe17932, 0xea892e7d, 0x2ff224c8, + 0x3e9a7387, 0x0d228a56, 0x1c4add19, 0xdc4fb8f1, 0xcd27efbe, 0xfe9f166f, 0xeff74120, 0xc9891dbb, 0xd8e14af4, 0xeb59b325, 0xfa31e46a, + 0x3a348182, 0x2b5cd6cd, 0x18e42f1c, 0x098c7853, 0xe305562e, 0xf26d0161, 0xc1d5f8b0, 0xd0bdafff, 0x10b8ca17, 0x01d09d58, 0x32686489, + 0x230033c6, 0x057e6f5d, 0x14163812, 0x27aec1c3, 0x36c6968c, 0xf6c3f364, 0xe7aba42b, 0xd4135dfa, 0xc57b0ab5, 0xe9f98894, 0xf891dfdb, + 0xcb29260a, 0xda417145, 0x1a4414ad, 0x0b2c43e2, 0x3894ba33, 0x29fced7c, 0x0f82b1e7, 0x1eeae6a8, 0x2d521f79, 0x3c3a4836, 0xfc3f2dde, + 0xed577a91, 0xdeef8340, 0xcf87d40f, 0x250efa72, 0x3466ad3d, 0x07de54ec, 0x16b603a3, 0xd6b3664b, 0xc7db3104, 0xf463c8d5, 0xe50b9f9a, + 0xc375c301, 0xd21d944e, 0xe1a56d9f, 0xf0cd3ad0, 0x30c85f38, 0x21a00877, 0x1218f1a6, 0x0370a6e9, 0xc60bac5c, 0xd763fb13, 0xe4db02c2, + 0xf5b3558d, 0x35b63065, 0x24de672a, 0x17669efb, 0x060ec9b4, 0x2070952f, 0x3118c260, 0x02a03bb1, 0x13c86cfe, 0xd3cd0916, 0xc2a55e59, + 0xf11da788, 0xe075f0c7, 0x0afcdeba, 0x1b9489f5, 0x282c7024, 0x3944276b, 0xf9414283, 0xe82915cc, 0xdb91ec1d, 0xcaf9bb52, 0xec87e7c9, + 0xfdefb086, 0xce574957, 0xdf3f1e18, 0x1f3a7bf0, 0x0e522cbf, 0x3dead56e, 0x2c828221, 0x65eed02d, 0x74868762, 0x473e7eb3, 0x565629fc, + 0x96534c14, 0x873b1b5b, 0xb483e28a, 0xa5ebb5c5, 0x8395e95e, 0x92fdbe11, 0xa14547c0, 0xb02d108f, 0x70287567, 0x61402228, 0x52f8dbf9, + 0x43908cb6, 0xa919a2cb, 0xb871f584, 0x8bc90c55, 0x9aa15b1a, 0x5aa43ef2, 0x4bcc69bd, 0x7874906c, 0x691cc723, 0x4f629bb8, 0x5e0accf7, + 0x6db23526, 0x7cda6269, 0xbcdf0781, 0xadb750ce, 0x9e0fa91f, 0x8f67fe50, 0x4a1cf4e5, 0x5b74a3aa, 0x68cc5a7b, 0x79a40d34, 0xb9a168dc, + 0xa8c93f93, 0x9b71c642, 0x8a19910d, 0xac67cd96, 0xbd0f9ad9, 0x8eb76308, 0x9fdf3447, 0x5fda51af, 0x4eb206e0, 0x7d0aff31, 0x6c62a87e, + 0x86eb8603, 0x9783d14c, 0xa43b289d, 0xb5537fd2, 0x75561a3a, 0x643e4d75, 0x5786b4a4, 0x46eee3eb, 0x6090bf70, 0x71f8e83f, 0x424011ee, + 0x532846a1, 0x932d2349, 0x82457406, 0xb1fd8dd7, 0xa095da98, 0x8c1758b9, 0x9d7f0ff6, 0xaec7f627, 0xbfafa168, 0x7faac480, 0x6ec293cf, + 0x5d7a6a1e, 0x4c123d51, 0x6a6c61ca, 0x7b043685, 0x48bccf54, 0x59d4981b, 0x99d1fdf3, 0x88b9aabc, 0xbb01536d, 0xaa690422, 0x40e02a5f, + 0x51887d10, 0x623084c1, 0x7358d38e, 0xb35db666, 0xa235e129, 0x918d18f8, 0x80e54fb7, 0xa69b132c, 0xb7f34463, 0x844bbdb2, 0x9523eafd, + 0x55268f15, 0x444ed85a, 0x77f6218b, 0x669e76c4, 0xa3e57c71, 0xb28d2b3e, 0x8135d2ef, 0x905d85a0, 0x5058e048, 0x4130b707, 0x72884ed6, + 0x63e01999, 0x459e4502, 0x54f6124d, 0x674eeb9c, 0x7626bcd3, 0xb623d93b, 0xa74b8e74, 0x94f377a5, 0x859b20ea, 0x6f120e97, 0x7e7a59d8, + 0x4dc2a009, 0x5caaf746, 0x9caf92ae, 0x8dc7c5e1, 0xbe7f3c30, 0xaf176b7f, 0x896937e4, 0x980160ab, 0xabb9997a, 0xbad1ce35, 0x7ad4abdd, + 0x6bbcfc92, 0x58040543, 0x496c520c}, + {0x00000000, 0xcadca15b, 0x94b943b7, 0x5e65e2ec, 0x9f6e466a, 0x55b2e731, 0x0bd705dd, 0xc10ba486, 0x3edd8cd4, 0xf4012d8f, 0xaa64cf63, + 0x60b86e38, 0xa1b3cabe, 0x6b6f6be5, 0x350a8909, 0xffd62852, 0xcba7d8ad, 0x017b79f6, 0x5f1e9b1a, 0x95c23a41, 0x54c99ec7, 0x9e153f9c, + 0xc070dd70, 0x0aac7c2b, 0xf57a5479, 0x3fa6f522, 0x61c317ce, 0xab1fb695, 0x6a141213, 0xa0c8b348, 0xfead51a4, 0x3471f0ff, 0x2152705f, + 0xeb8ed104, 0xb5eb33e8, 0x7f3792b3, 0xbe3c3635, 0x74e0976e, 0x2a857582, 0xe059d4d9, 0x1f8ffc8b, 0xd5535dd0, 0x8b36bf3c, 0x41ea1e67, + 0x80e1bae1, 0x4a3d1bba, 0x1458f956, 0xde84580d, 0xeaf5a8f2, 0x202909a9, 0x7e4ceb45, 0xb4904a1e, 0x759bee98, 0xbf474fc3, 0xe122ad2f, + 0x2bfe0c74, 0xd4282426, 0x1ef4857d, 0x40916791, 0x8a4dc6ca, 0x4b46624c, 0x819ac317, 0xdfff21fb, 0x152380a0, 0x42a4e0be, 0x887841e5, + 0xd61da309, 0x1cc10252, 0xddcaa6d4, 0x1716078f, 0x4973e563, 0x83af4438, 0x7c796c6a, 0xb6a5cd31, 0xe8c02fdd, 0x221c8e86, 0xe3172a00, + 0x29cb8b5b, 0x77ae69b7, 0xbd72c8ec, 0x89033813, 0x43df9948, 0x1dba7ba4, 0xd766daff, 0x166d7e79, 0xdcb1df22, 0x82d43dce, 0x48089c95, + 0xb7deb4c7, 0x7d02159c, 0x2367f770, 0xe9bb562b, 0x28b0f2ad, 0xe26c53f6, 0xbc09b11a, 0x76d51041, 0x63f690e1, 0xa92a31ba, 0xf74fd356, + 0x3d93720d, 0xfc98d68b, 0x364477d0, 0x6821953c, 0xa2fd3467, 0x5d2b1c35, 0x97f7bd6e, 0xc9925f82, 0x034efed9, 0xc2455a5f, 0x0899fb04, + 0x56fc19e8, 0x9c20b8b3, 0xa851484c, 0x628de917, 0x3ce80bfb, 0xf634aaa0, 0x373f0e26, 0xfde3af7d, 0xa3864d91, 0x695aecca, 0x968cc498, + 0x5c5065c3, 0x0235872f, 0xc8e92674, 0x09e282f2, 0xc33e23a9, 0x9d5bc145, 0x5787601e, 0x33550079, 0xf989a122, 0xa7ec43ce, 0x6d30e295, + 0xac3b4613, 0x66e7e748, 0x388205a4, 0xf25ea4ff, 0x0d888cad, 0xc7542df6, 0x9931cf1a, 0x53ed6e41, 0x92e6cac7, 0x583a6b9c, 0x065f8970, + 0xcc83282b, 0xf8f2d8d4, 0x322e798f, 0x6c4b9b63, 0xa6973a38, 0x679c9ebe, 0xad403fe5, 0xf325dd09, 0x39f97c52, 0xc62f5400, 0x0cf3f55b, + 0x529617b7, 0x984ab6ec, 0x5941126a, 0x939db331, 0xcdf851dd, 0x0724f086, 0x12077026, 0xd8dbd17d, 0x86be3391, 0x4c6292ca, 0x8d69364c, + 0x47b59717, 0x19d075fb, 0xd30cd4a0, 0x2cdafcf2, 0xe6065da9, 0xb863bf45, 0x72bf1e1e, 0xb3b4ba98, 0x79681bc3, 0x270df92f, 0xedd15874, + 0xd9a0a88b, 0x137c09d0, 0x4d19eb3c, 0x87c54a67, 0x46ceeee1, 0x8c124fba, 0xd277ad56, 0x18ab0c0d, 0xe77d245f, 0x2da18504, 0x73c467e8, + 0xb918c6b3, 0x78136235, 0xb2cfc36e, 0xecaa2182, 0x267680d9, 0x71f1e0c7, 0xbb2d419c, 0xe548a370, 0x2f94022b, 0xee9fa6ad, 0x244307f6, + 0x7a26e51a, 0xb0fa4441, 0x4f2c6c13, 0x85f0cd48, 0xdb952fa4, 0x11498eff, 0xd0422a79, 0x1a9e8b22, 0x44fb69ce, 0x8e27c895, 0xba56386a, + 0x708a9931, 0x2eef7bdd, 0xe433da86, 0x25387e00, 0xefe4df5b, 0xb1813db7, 0x7b5d9cec, 0x848bb4be, 0x4e5715e5, 0x1032f709, 0xdaee5652, + 0x1be5f2d4, 0xd139538f, 0x8f5cb163, 0x45801038, 0x50a39098, 0x9a7f31c3, 0xc41ad32f, 0x0ec67274, 0xcfcdd6f2, 0x051177a9, 0x5b749545, + 0x91a8341e, 0x6e7e1c4c, 0xa4a2bd17, 0xfac75ffb, 0x301bfea0, 0xf1105a26, 0x3bccfb7d, 0x65a91991, 0xaf75b8ca, 0x9b044835, 0x51d8e96e, + 0x0fbd0b82, 0xc561aad9, 0x046a0e5f, 0xceb6af04, 0x90d34de8, 0x5a0fecb3, 0xa5d9c4e1, 0x6f0565ba, 0x31608756, 0xfbbc260d, 0x3ab7828b, + 0xf06b23d0, 0xae0ec13c, 0x64d26067}}; + +static const uint32_t CRCTablesSB8[8][256] = { + {0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, + 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, + 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, + 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, + 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, + 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, + 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, + 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, + 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, + 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, + 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, + 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, + 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, + 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, + 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, + 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, + 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}, + {0x00000000, 0x191b3141, 0x32366282, 0x2b2d53c3, 0x646cc504, 0x7d77f445, 0x565aa786, 0x4f4196c7, 0xc8d98a08, 0xd1c2bb49, 0xfaefe88a, + 0xe3f4d9cb, 0xacb54f0c, 0xb5ae7e4d, 0x9e832d8e, 0x87981ccf, 0x4ac21251, 0x53d92310, 0x78f470d3, 0x61ef4192, 0x2eaed755, 0x37b5e614, + 0x1c98b5d7, 0x05838496, 0x821b9859, 0x9b00a918, 0xb02dfadb, 0xa936cb9a, 0xe6775d5d, 0xff6c6c1c, 0xd4413fdf, 0xcd5a0e9e, 0x958424a2, + 0x8c9f15e3, 0xa7b24620, 0xbea97761, 0xf1e8e1a6, 0xe8f3d0e7, 0xc3de8324, 0xdac5b265, 0x5d5daeaa, 0x44469feb, 0x6f6bcc28, 0x7670fd69, + 0x39316bae, 0x202a5aef, 0x0b07092c, 0x121c386d, 0xdf4636f3, 0xc65d07b2, 0xed705471, 0xf46b6530, 0xbb2af3f7, 0xa231c2b6, 0x891c9175, + 0x9007a034, 0x179fbcfb, 0x0e848dba, 0x25a9de79, 0x3cb2ef38, 0x73f379ff, 0x6ae848be, 0x41c51b7d, 0x58de2a3c, 0xf0794f05, 0xe9627e44, + 0xc24f2d87, 0xdb541cc6, 0x94158a01, 0x8d0ebb40, 0xa623e883, 0xbf38d9c2, 0x38a0c50d, 0x21bbf44c, 0x0a96a78f, 0x138d96ce, 0x5ccc0009, + 0x45d73148, 0x6efa628b, 0x77e153ca, 0xbabb5d54, 0xa3a06c15, 0x888d3fd6, 0x91960e97, 0xded79850, 0xc7cca911, 0xece1fad2, 0xf5facb93, + 0x7262d75c, 0x6b79e61d, 0x4054b5de, 0x594f849f, 0x160e1258, 0x0f152319, 0x243870da, 0x3d23419b, 0x65fd6ba7, 0x7ce65ae6, 0x57cb0925, + 0x4ed03864, 0x0191aea3, 0x188a9fe2, 0x33a7cc21, 0x2abcfd60, 0xad24e1af, 0xb43fd0ee, 0x9f12832d, 0x8609b26c, 0xc94824ab, 0xd05315ea, + 0xfb7e4629, 0xe2657768, 0x2f3f79f6, 0x362448b7, 0x1d091b74, 0x04122a35, 0x4b53bcf2, 0x52488db3, 0x7965de70, 0x607eef31, 0xe7e6f3fe, + 0xfefdc2bf, 0xd5d0917c, 0xcccba03d, 0x838a36fa, 0x9a9107bb, 0xb1bc5478, 0xa8a76539, 0x3b83984b, 0x2298a90a, 0x09b5fac9, 0x10aecb88, + 0x5fef5d4f, 0x46f46c0e, 0x6dd93fcd, 0x74c20e8c, 0xf35a1243, 0xea412302, 0xc16c70c1, 0xd8774180, 0x9736d747, 0x8e2de606, 0xa500b5c5, + 0xbc1b8484, 0x71418a1a, 0x685abb5b, 0x4377e898, 0x5a6cd9d9, 0x152d4f1e, 0x0c367e5f, 0x271b2d9c, 0x3e001cdd, 0xb9980012, 0xa0833153, + 0x8bae6290, 0x92b553d1, 0xddf4c516, 0xc4eff457, 0xefc2a794, 0xf6d996d5, 0xae07bce9, 0xb71c8da8, 0x9c31de6b, 0x852aef2a, 0xca6b79ed, + 0xd37048ac, 0xf85d1b6f, 0xe1462a2e, 0x66de36e1, 0x7fc507a0, 0x54e85463, 0x4df36522, 0x02b2f3e5, 0x1ba9c2a4, 0x30849167, 0x299fa026, + 0xe4c5aeb8, 0xfdde9ff9, 0xd6f3cc3a, 0xcfe8fd7b, 0x80a96bbc, 0x99b25afd, 0xb29f093e, 0xab84387f, 0x2c1c24b0, 0x350715f1, 0x1e2a4632, + 0x07317773, 0x4870e1b4, 0x516bd0f5, 0x7a468336, 0x635db277, 0xcbfad74e, 0xd2e1e60f, 0xf9ccb5cc, 0xe0d7848d, 0xaf96124a, 0xb68d230b, + 0x9da070c8, 0x84bb4189, 0x03235d46, 0x1a386c07, 0x31153fc4, 0x280e0e85, 0x674f9842, 0x7e54a903, 0x5579fac0, 0x4c62cb81, 0x8138c51f, + 0x9823f45e, 0xb30ea79d, 0xaa1596dc, 0xe554001b, 0xfc4f315a, 0xd7626299, 0xce7953d8, 0x49e14f17, 0x50fa7e56, 0x7bd72d95, 0x62cc1cd4, + 0x2d8d8a13, 0x3496bb52, 0x1fbbe891, 0x06a0d9d0, 0x5e7ef3ec, 0x4765c2ad, 0x6c48916e, 0x7553a02f, 0x3a1236e8, 0x230907a9, 0x0824546a, + 0x113f652b, 0x96a779e4, 0x8fbc48a5, 0xa4911b66, 0xbd8a2a27, 0xf2cbbce0, 0xebd08da1, 0xc0fdde62, 0xd9e6ef23, 0x14bce1bd, 0x0da7d0fc, + 0x268a833f, 0x3f91b27e, 0x70d024b9, 0x69cb15f8, 0x42e6463b, 0x5bfd777a, 0xdc656bb5, 0xc57e5af4, 0xee530937, 0xf7483876, 0xb809aeb1, + 0xa1129ff0, 0x8a3fcc33, 0x9324fd72}, + {0x00000000, 0x01c26a37, 0x0384d46e, 0x0246be59, 0x0709a8dc, 0x06cbc2eb, 0x048d7cb2, 0x054f1685, 0x0e1351b8, 0x0fd13b8f, 0x0d9785d6, + 0x0c55efe1, 0x091af964, 0x08d89353, 0x0a9e2d0a, 0x0b5c473d, 0x1c26a370, 0x1de4c947, 0x1fa2771e, 0x1e601d29, 0x1b2f0bac, 0x1aed619b, + 0x18abdfc2, 0x1969b5f5, 0x1235f2c8, 0x13f798ff, 0x11b126a6, 0x10734c91, 0x153c5a14, 0x14fe3023, 0x16b88e7a, 0x177ae44d, 0x384d46e0, + 0x398f2cd7, 0x3bc9928e, 0x3a0bf8b9, 0x3f44ee3c, 0x3e86840b, 0x3cc03a52, 0x3d025065, 0x365e1758, 0x379c7d6f, 0x35dac336, 0x3418a901, + 0x3157bf84, 0x3095d5b3, 0x32d36bea, 0x331101dd, 0x246be590, 0x25a98fa7, 0x27ef31fe, 0x262d5bc9, 0x23624d4c, 0x22a0277b, 0x20e69922, + 0x2124f315, 0x2a78b428, 0x2bbade1f, 0x29fc6046, 0x283e0a71, 0x2d711cf4, 0x2cb376c3, 0x2ef5c89a, 0x2f37a2ad, 0x709a8dc0, 0x7158e7f7, + 0x731e59ae, 0x72dc3399, 0x7793251c, 0x76514f2b, 0x7417f172, 0x75d59b45, 0x7e89dc78, 0x7f4bb64f, 0x7d0d0816, 0x7ccf6221, 0x798074a4, + 0x78421e93, 0x7a04a0ca, 0x7bc6cafd, 0x6cbc2eb0, 0x6d7e4487, 0x6f38fade, 0x6efa90e9, 0x6bb5866c, 0x6a77ec5b, 0x68315202, 0x69f33835, + 0x62af7f08, 0x636d153f, 0x612bab66, 0x60e9c151, 0x65a6d7d4, 0x6464bde3, 0x662203ba, 0x67e0698d, 0x48d7cb20, 0x4915a117, 0x4b531f4e, + 0x4a917579, 0x4fde63fc, 0x4e1c09cb, 0x4c5ab792, 0x4d98dda5, 0x46c49a98, 0x4706f0af, 0x45404ef6, 0x448224c1, 0x41cd3244, 0x400f5873, + 0x4249e62a, 0x438b8c1d, 0x54f16850, 0x55330267, 0x5775bc3e, 0x56b7d609, 0x53f8c08c, 0x523aaabb, 0x507c14e2, 0x51be7ed5, 0x5ae239e8, + 0x5b2053df, 0x5966ed86, 0x58a487b1, 0x5deb9134, 0x5c29fb03, 0x5e6f455a, 0x5fad2f6d, 0xe1351b80, 0xe0f771b7, 0xe2b1cfee, 0xe373a5d9, + 0xe63cb35c, 0xe7fed96b, 0xe5b86732, 0xe47a0d05, 0xef264a38, 0xeee4200f, 0xeca29e56, 0xed60f461, 0xe82fe2e4, 0xe9ed88d3, 0xebab368a, + 0xea695cbd, 0xfd13b8f0, 0xfcd1d2c7, 0xfe976c9e, 0xff5506a9, 0xfa1a102c, 0xfbd87a1b, 0xf99ec442, 0xf85cae75, 0xf300e948, 0xf2c2837f, + 0xf0843d26, 0xf1465711, 0xf4094194, 0xf5cb2ba3, 0xf78d95fa, 0xf64fffcd, 0xd9785d60, 0xd8ba3757, 0xdafc890e, 0xdb3ee339, 0xde71f5bc, + 0xdfb39f8b, 0xddf521d2, 0xdc374be5, 0xd76b0cd8, 0xd6a966ef, 0xd4efd8b6, 0xd52db281, 0xd062a404, 0xd1a0ce33, 0xd3e6706a, 0xd2241a5d, + 0xc55efe10, 0xc49c9427, 0xc6da2a7e, 0xc7184049, 0xc25756cc, 0xc3953cfb, 0xc1d382a2, 0xc011e895, 0xcb4dafa8, 0xca8fc59f, 0xc8c97bc6, + 0xc90b11f1, 0xcc440774, 0xcd866d43, 0xcfc0d31a, 0xce02b92d, 0x91af9640, 0x906dfc77, 0x922b422e, 0x93e92819, 0x96a63e9c, 0x976454ab, + 0x9522eaf2, 0x94e080c5, 0x9fbcc7f8, 0x9e7eadcf, 0x9c381396, 0x9dfa79a1, 0x98b56f24, 0x99770513, 0x9b31bb4a, 0x9af3d17d, 0x8d893530, + 0x8c4b5f07, 0x8e0de15e, 0x8fcf8b69, 0x8a809dec, 0x8b42f7db, 0x89044982, 0x88c623b5, 0x839a6488, 0x82580ebf, 0x801eb0e6, 0x81dcdad1, + 0x8493cc54, 0x8551a663, 0x8717183a, 0x86d5720d, 0xa9e2d0a0, 0xa820ba97, 0xaa6604ce, 0xaba46ef9, 0xaeeb787c, 0xaf29124b, 0xad6fac12, + 0xacadc625, 0xa7f18118, 0xa633eb2f, 0xa4755576, 0xa5b73f41, 0xa0f829c4, 0xa13a43f3, 0xa37cfdaa, 0xa2be979d, 0xb5c473d0, 0xb40619e7, + 0xb640a7be, 0xb782cd89, 0xb2cddb0c, 0xb30fb13b, 0xb1490f62, 0xb08b6555, 0xbbd72268, 0xba15485f, 0xb853f606, 0xb9919c31, 0xbcde8ab4, + 0xbd1ce083, 0xbf5a5eda, 0xbe9834ed}, + {0x00000000, 0xb8bc6765, 0xaa09c88b, 0x12b5afee, 0x8f629757, 0x37def032, 0x256b5fdc, 0x9dd738b9, 0xc5b428ef, 0x7d084f8a, 0x6fbde064, + 0xd7018701, 0x4ad6bfb8, 0xf26ad8dd, 0xe0df7733, 0x58631056, 0x5019579f, 0xe8a530fa, 0xfa109f14, 0x42acf871, 0xdf7bc0c8, 0x67c7a7ad, + 0x75720843, 0xcdce6f26, 0x95ad7f70, 0x2d111815, 0x3fa4b7fb, 0x8718d09e, 0x1acfe827, 0xa2738f42, 0xb0c620ac, 0x087a47c9, 0xa032af3e, + 0x188ec85b, 0x0a3b67b5, 0xb28700d0, 0x2f503869, 0x97ec5f0c, 0x8559f0e2, 0x3de59787, 0x658687d1, 0xdd3ae0b4, 0xcf8f4f5a, 0x7733283f, + 0xeae41086, 0x525877e3, 0x40edd80d, 0xf851bf68, 0xf02bf8a1, 0x48979fc4, 0x5a22302a, 0xe29e574f, 0x7f496ff6, 0xc7f50893, 0xd540a77d, + 0x6dfcc018, 0x359fd04e, 0x8d23b72b, 0x9f9618c5, 0x272a7fa0, 0xbafd4719, 0x0241207c, 0x10f48f92, 0xa848e8f7, 0x9b14583d, 0x23a83f58, + 0x311d90b6, 0x89a1f7d3, 0x1476cf6a, 0xaccaa80f, 0xbe7f07e1, 0x06c36084, 0x5ea070d2, 0xe61c17b7, 0xf4a9b859, 0x4c15df3c, 0xd1c2e785, + 0x697e80e0, 0x7bcb2f0e, 0xc377486b, 0xcb0d0fa2, 0x73b168c7, 0x6104c729, 0xd9b8a04c, 0x446f98f5, 0xfcd3ff90, 0xee66507e, 0x56da371b, + 0x0eb9274d, 0xb6054028, 0xa4b0efc6, 0x1c0c88a3, 0x81dbb01a, 0x3967d77f, 0x2bd27891, 0x936e1ff4, 0x3b26f703, 0x839a9066, 0x912f3f88, + 0x299358ed, 0xb4446054, 0x0cf80731, 0x1e4da8df, 0xa6f1cfba, 0xfe92dfec, 0x462eb889, 0x549b1767, 0xec277002, 0x71f048bb, 0xc94c2fde, + 0xdbf98030, 0x6345e755, 0x6b3fa09c, 0xd383c7f9, 0xc1366817, 0x798a0f72, 0xe45d37cb, 0x5ce150ae, 0x4e54ff40, 0xf6e89825, 0xae8b8873, + 0x1637ef16, 0x048240f8, 0xbc3e279d, 0x21e91f24, 0x99557841, 0x8be0d7af, 0x335cb0ca, 0xed59b63b, 0x55e5d15e, 0x47507eb0, 0xffec19d5, + 0x623b216c, 0xda874609, 0xc832e9e7, 0x708e8e82, 0x28ed9ed4, 0x9051f9b1, 0x82e4565f, 0x3a58313a, 0xa78f0983, 0x1f336ee6, 0x0d86c108, + 0xb53aa66d, 0xbd40e1a4, 0x05fc86c1, 0x1749292f, 0xaff54e4a, 0x322276f3, 0x8a9e1196, 0x982bbe78, 0x2097d91d, 0x78f4c94b, 0xc048ae2e, + 0xd2fd01c0, 0x6a4166a5, 0xf7965e1c, 0x4f2a3979, 0x5d9f9697, 0xe523f1f2, 0x4d6b1905, 0xf5d77e60, 0xe762d18e, 0x5fdeb6eb, 0xc2098e52, + 0x7ab5e937, 0x680046d9, 0xd0bc21bc, 0x88df31ea, 0x3063568f, 0x22d6f961, 0x9a6a9e04, 0x07bda6bd, 0xbf01c1d8, 0xadb46e36, 0x15080953, + 0x1d724e9a, 0xa5ce29ff, 0xb77b8611, 0x0fc7e174, 0x9210d9cd, 0x2aacbea8, 0x38191146, 0x80a57623, 0xd8c66675, 0x607a0110, 0x72cfaefe, + 0xca73c99b, 0x57a4f122, 0xef189647, 0xfdad39a9, 0x45115ecc, 0x764dee06, 0xcef18963, 0xdc44268d, 0x64f841e8, 0xf92f7951, 0x41931e34, + 0x5326b1da, 0xeb9ad6bf, 0xb3f9c6e9, 0x0b45a18c, 0x19f00e62, 0xa14c6907, 0x3c9b51be, 0x842736db, 0x96929935, 0x2e2efe50, 0x2654b999, + 0x9ee8defc, 0x8c5d7112, 0x34e11677, 0xa9362ece, 0x118a49ab, 0x033fe645, 0xbb838120, 0xe3e09176, 0x5b5cf613, 0x49e959fd, 0xf1553e98, + 0x6c820621, 0xd43e6144, 0xc68bceaa, 0x7e37a9cf, 0xd67f4138, 0x6ec3265d, 0x7c7689b3, 0xc4caeed6, 0x591dd66f, 0xe1a1b10a, 0xf3141ee4, + 0x4ba87981, 0x13cb69d7, 0xab770eb2, 0xb9c2a15c, 0x017ec639, 0x9ca9fe80, 0x241599e5, 0x36a0360b, 0x8e1c516e, 0x866616a7, 0x3eda71c2, + 0x2c6fde2c, 0x94d3b949, 0x090481f0, 0xb1b8e695, 0xa30d497b, 0x1bb12e1e, 0x43d23e48, 0xfb6e592d, 0xe9dbf6c3, 0x516791a6, 0xccb0a91f, + 0x740cce7a, 0x66b96194, 0xde0506f1}, + {0x00000000, 0x3d6029b0, 0x7ac05360, 0x47a07ad0, 0xf580a6c0, 0xc8e08f70, 0x8f40f5a0, 0xb220dc10, 0x30704bc1, 0x0d106271, 0x4ab018a1, + 0x77d03111, 0xc5f0ed01, 0xf890c4b1, 0xbf30be61, 0x825097d1, 0x60e09782, 0x5d80be32, 0x1a20c4e2, 0x2740ed52, 0x95603142, 0xa80018f2, + 0xefa06222, 0xd2c04b92, 0x5090dc43, 0x6df0f5f3, 0x2a508f23, 0x1730a693, 0xa5107a83, 0x98705333, 0xdfd029e3, 0xe2b00053, 0xc1c12f04, + 0xfca106b4, 0xbb017c64, 0x866155d4, 0x344189c4, 0x0921a074, 0x4e81daa4, 0x73e1f314, 0xf1b164c5, 0xccd14d75, 0x8b7137a5, 0xb6111e15, + 0x0431c205, 0x3951ebb5, 0x7ef19165, 0x4391b8d5, 0xa121b886, 0x9c419136, 0xdbe1ebe6, 0xe681c256, 0x54a11e46, 0x69c137f6, 0x2e614d26, + 0x13016496, 0x9151f347, 0xac31daf7, 0xeb91a027, 0xd6f18997, 0x64d15587, 0x59b17c37, 0x1e1106e7, 0x23712f57, 0x58f35849, 0x659371f9, + 0x22330b29, 0x1f532299, 0xad73fe89, 0x9013d739, 0xd7b3ade9, 0xead38459, 0x68831388, 0x55e33a38, 0x124340e8, 0x2f236958, 0x9d03b548, + 0xa0639cf8, 0xe7c3e628, 0xdaa3cf98, 0x3813cfcb, 0x0573e67b, 0x42d39cab, 0x7fb3b51b, 0xcd93690b, 0xf0f340bb, 0xb7533a6b, 0x8a3313db, + 0x0863840a, 0x3503adba, 0x72a3d76a, 0x4fc3feda, 0xfde322ca, 0xc0830b7a, 0x872371aa, 0xba43581a, 0x9932774d, 0xa4525efd, 0xe3f2242d, + 0xde920d9d, 0x6cb2d18d, 0x51d2f83d, 0x167282ed, 0x2b12ab5d, 0xa9423c8c, 0x9422153c, 0xd3826fec, 0xeee2465c, 0x5cc29a4c, 0x61a2b3fc, + 0x2602c92c, 0x1b62e09c, 0xf9d2e0cf, 0xc4b2c97f, 0x8312b3af, 0xbe729a1f, 0x0c52460f, 0x31326fbf, 0x7692156f, 0x4bf23cdf, 0xc9a2ab0e, + 0xf4c282be, 0xb362f86e, 0x8e02d1de, 0x3c220dce, 0x0142247e, 0x46e25eae, 0x7b82771e, 0xb1e6b092, 0x8c869922, 0xcb26e3f2, 0xf646ca42, + 0x44661652, 0x79063fe2, 0x3ea64532, 0x03c66c82, 0x8196fb53, 0xbcf6d2e3, 0xfb56a833, 0xc6368183, 0x74165d93, 0x49767423, 0x0ed60ef3, + 0x33b62743, 0xd1062710, 0xec660ea0, 0xabc67470, 0x96a65dc0, 0x248681d0, 0x19e6a860, 0x5e46d2b0, 0x6326fb00, 0xe1766cd1, 0xdc164561, + 0x9bb63fb1, 0xa6d61601, 0x14f6ca11, 0x2996e3a1, 0x6e369971, 0x5356b0c1, 0x70279f96, 0x4d47b626, 0x0ae7ccf6, 0x3787e546, 0x85a73956, + 0xb8c710e6, 0xff676a36, 0xc2074386, 0x4057d457, 0x7d37fde7, 0x3a978737, 0x07f7ae87, 0xb5d77297, 0x88b75b27, 0xcf1721f7, 0xf2770847, + 0x10c70814, 0x2da721a4, 0x6a075b74, 0x576772c4, 0xe547aed4, 0xd8278764, 0x9f87fdb4, 0xa2e7d404, 0x20b743d5, 0x1dd76a65, 0x5a7710b5, + 0x67173905, 0xd537e515, 0xe857cca5, 0xaff7b675, 0x92979fc5, 0xe915e8db, 0xd475c16b, 0x93d5bbbb, 0xaeb5920b, 0x1c954e1b, 0x21f567ab, + 0x66551d7b, 0x5b3534cb, 0xd965a31a, 0xe4058aaa, 0xa3a5f07a, 0x9ec5d9ca, 0x2ce505da, 0x11852c6a, 0x562556ba, 0x6b457f0a, 0x89f57f59, + 0xb49556e9, 0xf3352c39, 0xce550589, 0x7c75d999, 0x4115f029, 0x06b58af9, 0x3bd5a349, 0xb9853498, 0x84e51d28, 0xc34567f8, 0xfe254e48, + 0x4c059258, 0x7165bbe8, 0x36c5c138, 0x0ba5e888, 0x28d4c7df, 0x15b4ee6f, 0x521494bf, 0x6f74bd0f, 0xdd54611f, 0xe03448af, 0xa794327f, + 0x9af41bcf, 0x18a48c1e, 0x25c4a5ae, 0x6264df7e, 0x5f04f6ce, 0xed242ade, 0xd044036e, 0x97e479be, 0xaa84500e, 0x4834505d, 0x755479ed, + 0x32f4033d, 0x0f942a8d, 0xbdb4f69d, 0x80d4df2d, 0xc774a5fd, 0xfa148c4d, 0x78441b9c, 0x4524322c, 0x028448fc, 0x3fe4614c, 0x8dc4bd5c, + 0xb0a494ec, 0xf704ee3c, 0xca64c78c}, + {0x00000000, 0xcb5cd3a5, 0x4dc8a10b, 0x869472ae, 0x9b914216, 0x50cd91b3, 0xd659e31d, 0x1d0530b8, 0xec53826d, 0x270f51c8, 0xa19b2366, + 0x6ac7f0c3, 0x77c2c07b, 0xbc9e13de, 0x3a0a6170, 0xf156b2d5, 0x03d6029b, 0xc88ad13e, 0x4e1ea390, 0x85427035, 0x9847408d, 0x531b9328, + 0xd58fe186, 0x1ed33223, 0xef8580f6, 0x24d95353, 0xa24d21fd, 0x6911f258, 0x7414c2e0, 0xbf481145, 0x39dc63eb, 0xf280b04e, 0x07ac0536, + 0xccf0d693, 0x4a64a43d, 0x81387798, 0x9c3d4720, 0x57619485, 0xd1f5e62b, 0x1aa9358e, 0xebff875b, 0x20a354fe, 0xa6372650, 0x6d6bf5f5, + 0x706ec54d, 0xbb3216e8, 0x3da66446, 0xf6fab7e3, 0x047a07ad, 0xcf26d408, 0x49b2a6a6, 0x82ee7503, 0x9feb45bb, 0x54b7961e, 0xd223e4b0, + 0x197f3715, 0xe82985c0, 0x23755665, 0xa5e124cb, 0x6ebdf76e, 0x73b8c7d6, 0xb8e41473, 0x3e7066dd, 0xf52cb578, 0x0f580a6c, 0xc404d9c9, + 0x4290ab67, 0x89cc78c2, 0x94c9487a, 0x5f959bdf, 0xd901e971, 0x125d3ad4, 0xe30b8801, 0x28575ba4, 0xaec3290a, 0x659ffaaf, 0x789aca17, + 0xb3c619b2, 0x35526b1c, 0xfe0eb8b9, 0x0c8e08f7, 0xc7d2db52, 0x4146a9fc, 0x8a1a7a59, 0x971f4ae1, 0x5c439944, 0xdad7ebea, 0x118b384f, + 0xe0dd8a9a, 0x2b81593f, 0xad152b91, 0x6649f834, 0x7b4cc88c, 0xb0101b29, 0x36846987, 0xfdd8ba22, 0x08f40f5a, 0xc3a8dcff, 0x453cae51, + 0x8e607df4, 0x93654d4c, 0x58399ee9, 0xdeadec47, 0x15f13fe2, 0xe4a78d37, 0x2ffb5e92, 0xa96f2c3c, 0x6233ff99, 0x7f36cf21, 0xb46a1c84, + 0x32fe6e2a, 0xf9a2bd8f, 0x0b220dc1, 0xc07ede64, 0x46eaacca, 0x8db67f6f, 0x90b34fd7, 0x5bef9c72, 0xdd7beedc, 0x16273d79, 0xe7718fac, + 0x2c2d5c09, 0xaab92ea7, 0x61e5fd02, 0x7ce0cdba, 0xb7bc1e1f, 0x31286cb1, 0xfa74bf14, 0x1eb014d8, 0xd5ecc77d, 0x5378b5d3, 0x98246676, + 0x852156ce, 0x4e7d856b, 0xc8e9f7c5, 0x03b52460, 0xf2e396b5, 0x39bf4510, 0xbf2b37be, 0x7477e41b, 0x6972d4a3, 0xa22e0706, 0x24ba75a8, + 0xefe6a60d, 0x1d661643, 0xd63ac5e6, 0x50aeb748, 0x9bf264ed, 0x86f75455, 0x4dab87f0, 0xcb3ff55e, 0x006326fb, 0xf135942e, 0x3a69478b, + 0xbcfd3525, 0x77a1e680, 0x6aa4d638, 0xa1f8059d, 0x276c7733, 0xec30a496, 0x191c11ee, 0xd240c24b, 0x54d4b0e5, 0x9f886340, 0x828d53f8, + 0x49d1805d, 0xcf45f2f3, 0x04192156, 0xf54f9383, 0x3e134026, 0xb8873288, 0x73dbe12d, 0x6eded195, 0xa5820230, 0x2316709e, 0xe84aa33b, + 0x1aca1375, 0xd196c0d0, 0x5702b27e, 0x9c5e61db, 0x815b5163, 0x4a0782c6, 0xcc93f068, 0x07cf23cd, 0xf6999118, 0x3dc542bd, 0xbb513013, + 0x700de3b6, 0x6d08d30e, 0xa65400ab, 0x20c07205, 0xeb9ca1a0, 0x11e81eb4, 0xdab4cd11, 0x5c20bfbf, 0x977c6c1a, 0x8a795ca2, 0x41258f07, + 0xc7b1fda9, 0x0ced2e0c, 0xfdbb9cd9, 0x36e74f7c, 0xb0733dd2, 0x7b2fee77, 0x662adecf, 0xad760d6a, 0x2be27fc4, 0xe0beac61, 0x123e1c2f, + 0xd962cf8a, 0x5ff6bd24, 0x94aa6e81, 0x89af5e39, 0x42f38d9c, 0xc467ff32, 0x0f3b2c97, 0xfe6d9e42, 0x35314de7, 0xb3a53f49, 0x78f9ecec, + 0x65fcdc54, 0xaea00ff1, 0x28347d5f, 0xe368aefa, 0x16441b82, 0xdd18c827, 0x5b8cba89, 0x90d0692c, 0x8dd55994, 0x46898a31, 0xc01df89f, + 0x0b412b3a, 0xfa1799ef, 0x314b4a4a, 0xb7df38e4, 0x7c83eb41, 0x6186dbf9, 0xaada085c, 0x2c4e7af2, 0xe712a957, 0x15921919, 0xdececabc, + 0x585ab812, 0x93066bb7, 0x8e035b0f, 0x455f88aa, 0xc3cbfa04, 0x089729a1, 0xf9c19b74, 0x329d48d1, 0xb4093a7f, 0x7f55e9da, 0x6250d962, + 0xa90c0ac7, 0x2f987869, 0xe4c4abcc}, + {0x00000000, 0xa6770bb4, 0x979f1129, 0x31e81a9d, 0xf44f2413, 0x52382fa7, 0x63d0353a, 0xc5a73e8e, 0x33ef4e67, 0x959845d3, 0xa4705f4e, + 0x020754fa, 0xc7a06a74, 0x61d761c0, 0x503f7b5d, 0xf64870e9, 0x67de9cce, 0xc1a9977a, 0xf0418de7, 0x56368653, 0x9391b8dd, 0x35e6b369, + 0x040ea9f4, 0xa279a240, 0x5431d2a9, 0xf246d91d, 0xc3aec380, 0x65d9c834, 0xa07ef6ba, 0x0609fd0e, 0x37e1e793, 0x9196ec27, 0xcfbd399c, + 0x69ca3228, 0x582228b5, 0xfe552301, 0x3bf21d8f, 0x9d85163b, 0xac6d0ca6, 0x0a1a0712, 0xfc5277fb, 0x5a257c4f, 0x6bcd66d2, 0xcdba6d66, + 0x081d53e8, 0xae6a585c, 0x9f8242c1, 0x39f54975, 0xa863a552, 0x0e14aee6, 0x3ffcb47b, 0x998bbfcf, 0x5c2c8141, 0xfa5b8af5, 0xcbb39068, + 0x6dc49bdc, 0x9b8ceb35, 0x3dfbe081, 0x0c13fa1c, 0xaa64f1a8, 0x6fc3cf26, 0xc9b4c492, 0xf85cde0f, 0x5e2bd5bb, 0x440b7579, 0xe27c7ecd, + 0xd3946450, 0x75e36fe4, 0xb044516a, 0x16335ade, 0x27db4043, 0x81ac4bf7, 0x77e43b1e, 0xd19330aa, 0xe07b2a37, 0x460c2183, 0x83ab1f0d, + 0x25dc14b9, 0x14340e24, 0xb2430590, 0x23d5e9b7, 0x85a2e203, 0xb44af89e, 0x123df32a, 0xd79acda4, 0x71edc610, 0x4005dc8d, 0xe672d739, + 0x103aa7d0, 0xb64dac64, 0x87a5b6f9, 0x21d2bd4d, 0xe47583c3, 0x42028877, 0x73ea92ea, 0xd59d995e, 0x8bb64ce5, 0x2dc14751, 0x1c295dcc, + 0xba5e5678, 0x7ff968f6, 0xd98e6342, 0xe86679df, 0x4e11726b, 0xb8590282, 0x1e2e0936, 0x2fc613ab, 0x89b1181f, 0x4c162691, 0xea612d25, + 0xdb8937b8, 0x7dfe3c0c, 0xec68d02b, 0x4a1fdb9f, 0x7bf7c102, 0xdd80cab6, 0x1827f438, 0xbe50ff8c, 0x8fb8e511, 0x29cfeea5, 0xdf879e4c, + 0x79f095f8, 0x48188f65, 0xee6f84d1, 0x2bc8ba5f, 0x8dbfb1eb, 0xbc57ab76, 0x1a20a0c2, 0x8816eaf2, 0x2e61e146, 0x1f89fbdb, 0xb9fef06f, + 0x7c59cee1, 0xda2ec555, 0xebc6dfc8, 0x4db1d47c, 0xbbf9a495, 0x1d8eaf21, 0x2c66b5bc, 0x8a11be08, 0x4fb68086, 0xe9c18b32, 0xd82991af, + 0x7e5e9a1b, 0xefc8763c, 0x49bf7d88, 0x78576715, 0xde206ca1, 0x1b87522f, 0xbdf0599b, 0x8c184306, 0x2a6f48b2, 0xdc27385b, 0x7a5033ef, + 0x4bb82972, 0xedcf22c6, 0x28681c48, 0x8e1f17fc, 0xbff70d61, 0x198006d5, 0x47abd36e, 0xe1dcd8da, 0xd034c247, 0x7643c9f3, 0xb3e4f77d, + 0x1593fcc9, 0x247be654, 0x820cede0, 0x74449d09, 0xd23396bd, 0xe3db8c20, 0x45ac8794, 0x800bb91a, 0x267cb2ae, 0x1794a833, 0xb1e3a387, + 0x20754fa0, 0x86024414, 0xb7ea5e89, 0x119d553d, 0xd43a6bb3, 0x724d6007, 0x43a57a9a, 0xe5d2712e, 0x139a01c7, 0xb5ed0a73, 0x840510ee, + 0x22721b5a, 0xe7d525d4, 0x41a22e60, 0x704a34fd, 0xd63d3f49, 0xcc1d9f8b, 0x6a6a943f, 0x5b828ea2, 0xfdf58516, 0x3852bb98, 0x9e25b02c, + 0xafcdaab1, 0x09baa105, 0xfff2d1ec, 0x5985da58, 0x686dc0c5, 0xce1acb71, 0x0bbdf5ff, 0xadcafe4b, 0x9c22e4d6, 0x3a55ef62, 0xabc30345, + 0x0db408f1, 0x3c5c126c, 0x9a2b19d8, 0x5f8c2756, 0xf9fb2ce2, 0xc813367f, 0x6e643dcb, 0x982c4d22, 0x3e5b4696, 0x0fb35c0b, 0xa9c457bf, + 0x6c636931, 0xca146285, 0xfbfc7818, 0x5d8b73ac, 0x03a0a617, 0xa5d7ada3, 0x943fb73e, 0x3248bc8a, 0xf7ef8204, 0x519889b0, 0x6070932d, + 0xc6079899, 0x304fe870, 0x9638e3c4, 0xa7d0f959, 0x01a7f2ed, 0xc400cc63, 0x6277c7d7, 0x539fdd4a, 0xf5e8d6fe, 0x647e3ad9, 0xc209316d, + 0xf3e12bf0, 0x55962044, 0x90311eca, 0x3646157e, 0x07ae0fe3, 0xa1d90457, 0x579174be, 0xf1e67f0a, 0xc00e6597, 0x66796e23, 0xa3de50ad, + 0x05a95b19, 0x34414184, 0x92364a30}, + {0x00000000, 0xccaa009e, 0x4225077d, 0x8e8f07e3, 0x844a0efa, 0x48e00e64, 0xc66f0987, 0x0ac50919, 0xd3e51bb5, 0x1f4f1b2b, 0x91c01cc8, + 0x5d6a1c56, 0x57af154f, 0x9b0515d1, 0x158a1232, 0xd92012ac, 0x7cbb312b, 0xb01131b5, 0x3e9e3656, 0xf23436c8, 0xf8f13fd1, 0x345b3f4f, + 0xbad438ac, 0x767e3832, 0xaf5e2a9e, 0x63f42a00, 0xed7b2de3, 0x21d12d7d, 0x2b142464, 0xe7be24fa, 0x69312319, 0xa59b2387, 0xf9766256, + 0x35dc62c8, 0xbb53652b, 0x77f965b5, 0x7d3c6cac, 0xb1966c32, 0x3f196bd1, 0xf3b36b4f, 0x2a9379e3, 0xe639797d, 0x68b67e9e, 0xa41c7e00, + 0xaed97719, 0x62737787, 0xecfc7064, 0x205670fa, 0x85cd537d, 0x496753e3, 0xc7e85400, 0x0b42549e, 0x01875d87, 0xcd2d5d19, 0x43a25afa, + 0x8f085a64, 0x562848c8, 0x9a824856, 0x140d4fb5, 0xd8a74f2b, 0xd2624632, 0x1ec846ac, 0x9047414f, 0x5ced41d1, 0x299dc2ed, 0xe537c273, + 0x6bb8c590, 0xa712c50e, 0xadd7cc17, 0x617dcc89, 0xeff2cb6a, 0x2358cbf4, 0xfa78d958, 0x36d2d9c6, 0xb85dde25, 0x74f7debb, 0x7e32d7a2, + 0xb298d73c, 0x3c17d0df, 0xf0bdd041, 0x5526f3c6, 0x998cf358, 0x1703f4bb, 0xdba9f425, 0xd16cfd3c, 0x1dc6fda2, 0x9349fa41, 0x5fe3fadf, + 0x86c3e873, 0x4a69e8ed, 0xc4e6ef0e, 0x084cef90, 0x0289e689, 0xce23e617, 0x40ace1f4, 0x8c06e16a, 0xd0eba0bb, 0x1c41a025, 0x92cea7c6, + 0x5e64a758, 0x54a1ae41, 0x980baedf, 0x1684a93c, 0xda2ea9a2, 0x030ebb0e, 0xcfa4bb90, 0x412bbc73, 0x8d81bced, 0x8744b5f4, 0x4beeb56a, + 0xc561b289, 0x09cbb217, 0xac509190, 0x60fa910e, 0xee7596ed, 0x22df9673, 0x281a9f6a, 0xe4b09ff4, 0x6a3f9817, 0xa6959889, 0x7fb58a25, + 0xb31f8abb, 0x3d908d58, 0xf13a8dc6, 0xfbff84df, 0x37558441, 0xb9da83a2, 0x7570833c, 0x533b85da, 0x9f918544, 0x111e82a7, 0xddb48239, + 0xd7718b20, 0x1bdb8bbe, 0x95548c5d, 0x59fe8cc3, 0x80de9e6f, 0x4c749ef1, 0xc2fb9912, 0x0e51998c, 0x04949095, 0xc83e900b, 0x46b197e8, + 0x8a1b9776, 0x2f80b4f1, 0xe32ab46f, 0x6da5b38c, 0xa10fb312, 0xabcaba0b, 0x6760ba95, 0xe9efbd76, 0x2545bde8, 0xfc65af44, 0x30cfafda, + 0xbe40a839, 0x72eaa8a7, 0x782fa1be, 0xb485a120, 0x3a0aa6c3, 0xf6a0a65d, 0xaa4de78c, 0x66e7e712, 0xe868e0f1, 0x24c2e06f, 0x2e07e976, + 0xe2ade9e8, 0x6c22ee0b, 0xa088ee95, 0x79a8fc39, 0xb502fca7, 0x3b8dfb44, 0xf727fbda, 0xfde2f2c3, 0x3148f25d, 0xbfc7f5be, 0x736df520, + 0xd6f6d6a7, 0x1a5cd639, 0x94d3d1da, 0x5879d144, 0x52bcd85d, 0x9e16d8c3, 0x1099df20, 0xdc33dfbe, 0x0513cd12, 0xc9b9cd8c, 0x4736ca6f, + 0x8b9ccaf1, 0x8159c3e8, 0x4df3c376, 0xc37cc495, 0x0fd6c40b, 0x7aa64737, 0xb60c47a9, 0x3883404a, 0xf42940d4, 0xfeec49cd, 0x32464953, + 0xbcc94eb0, 0x70634e2e, 0xa9435c82, 0x65e95c1c, 0xeb665bff, 0x27cc5b61, 0x2d095278, 0xe1a352e6, 0x6f2c5505, 0xa386559b, 0x061d761c, + 0xcab77682, 0x44387161, 0x889271ff, 0x825778e6, 0x4efd7878, 0xc0727f9b, 0x0cd87f05, 0xd5f86da9, 0x19526d37, 0x97dd6ad4, 0x5b776a4a, + 0x51b26353, 0x9d1863cd, 0x1397642e, 0xdf3d64b0, 0x83d02561, 0x4f7a25ff, 0xc1f5221c, 0x0d5f2282, 0x079a2b9b, 0xcb302b05, 0x45bf2ce6, + 0x89152c78, 0x50353ed4, 0x9c9f3e4a, 0x121039a9, 0xdeba3937, 0xd47f302e, 0x18d530b0, 0x965a3753, 0x5af037cd, 0xff6b144a, 0x33c114d4, + 0xbd4e1337, 0x71e413a9, 0x7b211ab0, 0xb78b1a2e, 0x39041dcd, 0xf5ae1d53, 0x2c8e0fff, 0xe0240f61, 0x6eab0882, 0xa201081c, 0xa8c40105, + 0x646e019b, 0xeae10678, 0x264b06e6}}; + +#define BYTESWAP_ORDER32(x) (((x) >> 24) + (((x) >> 8) & 0xff00) + (((x) << 8) & 0xff0000) + ((x) << 24)) +#define UE_PTRDIFF_TO_INT32(argument) static_cast<int32_t>(argument) + +template<typename T> +constexpr T +Align(T Val, uint64_t Alignment) +{ + return (T)(((uint64_t)Val + Alignment - 1) & ~(Alignment - 1)); +} + +} // namespace CRC32 + +namespace zen { + +uint32_t +StrCrc_Deprecated(const char* Data) +{ + using namespace CRC32; + + uint32_t CRC = 0xFFFFFFFF; + while (*Data) + { + char16_t C = *Data++; + int32_t CL = (C & 255); + CRC = (CRC << 8) ^ CRCTable_DEPRECATED[(CRC >> 24) ^ CL]; + int32_t CH = (C >> 8) & 255; + CRC = (CRC << 8) ^ CRCTable_DEPRECATED[(CRC >> 24) ^ CH]; + } + return ~CRC; +} + +uint32_t +MemCrc32(const void* InData, size_t Length, uint32_t CRC /*=0 */) +{ + using namespace CRC32; + + // Based on the Slicing-by-8 implementation found here: + // http://slicing-by-8.sourceforge.net/ + + CRC = ~CRC; + + const uint8_t* __restrict Data = (uint8_t*)InData; + + // First we need to align to 32-bits + int32_t InitBytes = UE_PTRDIFF_TO_INT32(Align(Data, 4) - Data); + + if (Length > InitBytes) + { + Length -= InitBytes; + + for (; InitBytes; --InitBytes) + { + CRC = (CRC >> 8) ^ CRCTablesSB8[0][(CRC & 0xFF) ^ *Data++]; + } + + auto Data4 = (const uint32_t*)Data; + for (size_t Repeat = Length / 8; Repeat; --Repeat) + { + uint32_t V1 = *Data4++ ^ CRC; + uint32_t V2 = *Data4++; + CRC = CRCTablesSB8[7][V1 & 0xFF] ^ CRCTablesSB8[6][(V1 >> 8) & 0xFF] ^ CRCTablesSB8[5][(V1 >> 16) & 0xFF] ^ + CRCTablesSB8[4][V1 >> 24] ^ CRCTablesSB8[3][V2 & 0xFF] ^ CRCTablesSB8[2][(V2 >> 8) & 0xFF] ^ + CRCTablesSB8[1][(V2 >> 16) & 0xFF] ^ CRCTablesSB8[0][V2 >> 24]; + } + Data = (const uint8_t*)Data4; + + Length %= 8; + } + + for (; Length; --Length) + { + CRC = (CRC >> 8) ^ CRCTablesSB8[0][(CRC & 0xFF) ^ *Data++]; + } + + return ~CRC; +} + +uint32_t +MemCrc32_Deprecated(const void* InData, size_t Length, uint32_t CRC) +{ + using namespace CRC32; + + // Based on the Slicing-by-8 implementation found here: + // http://slicing-by-8.sourceforge.net/ + + CRC = ~BYTESWAP_ORDER32(CRC); + + const uint8_t* __restrict Data = (uint8_t*)InData; + + // First we need to align to 32-bits + int32_t InitBytes = UE_PTRDIFF_TO_INT32(Align(Data, 4) - Data); + + if (Length > InitBytes) + { + Length -= InitBytes; + + for (; InitBytes; --InitBytes) + { + CRC = (CRC >> 8) ^ CRCTablesSB8_DEPRECATED[0][(CRC & 0xFF) ^ *Data++]; + } + + auto Data4 = (const uint32_t*)Data; + for (size_t Repeat = Length / 8; Repeat; --Repeat) + { + uint32_t V1 = *Data4++ ^ CRC; + uint32_t V2 = *Data4++; + CRC = CRCTablesSB8_DEPRECATED[7][V1 & 0xFF] ^ CRCTablesSB8_DEPRECATED[6][(V1 >> 8) & 0xFF] ^ + CRCTablesSB8_DEPRECATED[5][(V1 >> 16) & 0xFF] ^ CRCTablesSB8_DEPRECATED[4][V1 >> 24] ^ + CRCTablesSB8_DEPRECATED[3][V2 & 0xFF] ^ CRCTablesSB8_DEPRECATED[2][(V2 >> 8) & 0xFF] ^ + CRCTablesSB8_DEPRECATED[1][(V2 >> 16) & 0xFF] ^ CRCTablesSB8_DEPRECATED[0][V2 >> 24]; + } + Data = (const uint8_t*)Data4; + + Length %= 8; + } + + for (; Length; --Length) + { + CRC = (CRC >> 8) ^ CRCTablesSB8_DEPRECATED[0][(CRC & 0xFF) ^ *Data++]; + } + + return BYTESWAP_ORDER32(~CRC); +} + +} // namespace zen diff --git a/zencore/except.cpp b/zencore/except.cpp index b02122f58..882f69f9a 100644 --- a/zencore/except.cpp +++ b/zencore/except.cpp @@ -1,17 +1,27 @@ // Copyright Epic Games, Inc. All Rights Reserved. #include <zencore/except.h> +#include <system_error> namespace zen { void -ThrowSystemException([[maybe_unused]] HRESULT hRes, [[maybe_unused]] const char* Message) +ThrowSystemException([[maybe_unused]] HRESULT hRes, [[maybe_unused]] std::string_view Message) { - // TODO - - int ErrValue = hRes; + if (HRESULT_FACILITY(hRes) == FACILITY_WIN32) + { + throw std::system_error(std::error_code(hRes & 0xffff, std::system_category()), std::string(Message)); + } + else + { + throw WindowsException(hRes, Message); + } +} - throw std::system_error(ErrValue, std::system_category(), Message); +void +ThrowLastError(std::string_view Message) +{ + throw std::system_error(std::error_code(::GetLastError(), std::system_category()), std::string(Message)); } } // namespace zen diff --git a/zencore/filesystem.cpp b/zencore/filesystem.cpp index 578b28277..cb806b276 100644 --- a/zencore/filesystem.cpp +++ b/zencore/filesystem.cpp @@ -409,7 +409,7 @@ WriteFile(std::filesystem::path Path, const IoBuffer* const* Data, size_t Buffer HRESULT hRes = Outfile.Create(Path.c_str(), GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS); if (FAILED(hRes)) { - zen::ThrowIfFailed(hRes, "File open failed for '{}'"_format(Path).c_str()); + zen::ThrowSystemException(hRes, "File open failed for '{}'"_format(Path).c_str()); } // TODO: this could be block-enlightened @@ -427,7 +427,7 @@ WriteFile(std::filesystem::path Path, const IoBuffer* const* Data, size_t Buffer if (FAILED(hRes)) { - zen::ThrowIfFailed(hRes, "File write failed for '{}'"_format(Path).c_str()); + zen::ThrowSystemException(hRes, "File write failed for '{}'"_format(Path).c_str()); } WriteSize -= ChunkSize; @@ -517,7 +517,7 @@ FileSystemTraversal::TraverseFileSystem(const std::filesystem::path& RootDir, Tr OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS); - zen::ThrowIfFailed(hRes, "Failed to open handle to volume root"); + zen::ThrowSystemException(hRes, "Failed to open handle to volume root"); while (Continue) { diff --git a/zencore/httpserver.cpp b/zencore/httpserver.cpp index 1bfe224f8..bd54c90ab 100644 --- a/zencore/httpserver.cpp +++ b/zencore/httpserver.cpp @@ -933,6 +933,7 @@ using namespace std::literals; static constinit uint32_t HashBinary = HashStringDjb2("application/octet-stream"sv); static constinit uint32_t HashJson = HashStringDjb2("application/json"sv); +static constinit uint32_t HashYaml = HashStringDjb2("text/yaml"sv); static constinit uint32_t HashText = HashStringDjb2("text/plain"sv); static constinit uint32_t HashCompactBinary = HashStringDjb2("application/x-ue-cb"sv); static constinit uint32_t HashCompactBinaryPackage = HashStringDjb2("application/x-ue-cbpkg"sv); @@ -960,6 +961,10 @@ MapContentType(const std::string_view& ContentTypeString) { return HttpContentType::kJSON; } + else if (CtHash == HashYaml) + { + return HttpContentType::kYAML; + } else if (CtHash == HashText) { return HttpContentType::kText; @@ -1057,11 +1062,14 @@ public: HTTP_REQUEST* const HttpReq = m_HttpTx.HttpRequest(); - IoBuffer buffer(m_ContentLength); + IoBuffer PayloadBuffer(m_ContentLength); + + HttpContentType ContentType = RequestContentType(); + PayloadBuffer.SetContentType(ContentType); uint64_t BytesToRead = m_ContentLength; - uint8_t* ReadPointer = (uint8_t*)buffer.Data(); + uint8_t* ReadPointer = reinterpret_cast<uint8_t*>(PayloadBuffer.MutableData()); // First deal with any payload which has already been copied // into our request buffer @@ -1112,7 +1120,9 @@ public: ReadPointer += BytesRead; } - return buffer; + PayloadBuffer.MakeImmutable(); + + return PayloadBuffer; } virtual void WriteResponse(HttpResponse HttpResponseCode) override diff --git a/zencore/include/zencore/blake3.h b/zencore/include/zencore/blake3.h index 1ef921c30..b31b710a7 100644 --- a/zencore/include/zencore/blake3.h +++ b/zencore/include/zencore/blake3.h @@ -6,8 +6,11 @@ #include <compare> #include <cstring> +#include <zencore/memory.h> + namespace zen { +class CompositeBuffer; class StringBuilderBase; /** @@ -17,12 +20,13 @@ struct BLAKE3 { uint8_t Hash[32]; - inline auto operator<=>(const BLAKE3& rhs) const = default; + inline auto operator<=>(const BLAKE3& Rhs) const = default; - static BLAKE3 HashMemory(const void* data, size_t byteCount); - static BLAKE3 FromHexString(const char* string); - const char* ToHexString(char* outString /* 40 characters + NUL terminator */) const; - StringBuilderBase& ToHexString(StringBuilderBase& outBuilder) const; + static BLAKE3 HashBuffer(const CompositeBuffer& Buffer); + static BLAKE3 HashMemory(const void* Data, size_t ByteCount); + static BLAKE3 FromHexString(const char* String); + const char* ToHexString(char* OutString /* 40 characters + NUL terminator */) const; + StringBuilderBase& ToHexString(StringBuilderBase& OutBuilder) const; static const int StringLength = 64; typedef char String_t[StringLength + 1]; @@ -44,9 +48,10 @@ struct BLAKE3Stream { BLAKE3Stream(); - void Reset(); /// Begin streaming hash compute (not needed on freshly constructed instance) - BLAKE3Stream& Append(const void* data, size_t byteCount); /// Append another chunk - BLAKE3 GetHash(); /// Obtain final hash. If you wish to reuse the instance call reset() + void Reset(); // Begin streaming hash compute (not needed on freshly constructed instance) + BLAKE3Stream& Append(const void* data, size_t byteCount); // Append another chunk + BLAKE3Stream& Append(MemoryView DataView) { return Append(DataView.GetData(), DataView.GetSize()); } // Append another chunk + BLAKE3 GetHash(); // Obtain final hash. If you wish to reuse the instance call reset() private: alignas(16) uint8_t m_HashState[2048]; diff --git a/zencore/include/zencore/compositebuffer.h b/zencore/include/zencore/compositebuffer.h new file mode 100644 index 000000000..4a3b60428 --- /dev/null +++ b/zencore/include/zencore/compositebuffer.h @@ -0,0 +1,133 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include <zencore/sharedbuffer.h> +#include <zencore/zencore.h> + +#include <functional> +#include <span> +#include <vector> + +namespace zen { + +/** + * CompositeBuffer is a non-contiguous buffer composed of zero or more immutable shared buffers. + * + * A composite buffer is most efficient when its segments are consumed as they are, but it can be + * flattened into a contiguous buffer, when necessary, by calling Flatten(). Ownership of segment + * buffers is not changed on construction, but if ownership of segments is required then that can + * be guaranteed by calling MakeOwned(). + */ + +class CompositeBuffer +{ +public: + /** + * Construct a composite buffer by concatenating the buffers. Does not enforce ownership. + * + * Buffer parameters may be SharedBuffer, CompositeBuffer, or std::vector<SharedBuffer>. + */ + template<typename... BufferTypes> + inline explicit CompositeBuffer(BufferTypes&&... Buffers) + { + if constexpr (sizeof...(Buffers) > 0) + { + m_Segments.reserve((GetBufferCount(std::forward<BufferTypes>(Buffers)) + ...)); + (AppendBuffers(std::forward<BufferTypes>(Buffers)), ...); + std::erase_if(m_Segments, [](const SharedBuffer& It) { return It.IsNull(); }); + } + } + + /** Reset this to null. */ + ZENCORE_API void Reset(); + + /** Returns the total size of the composite buffer in bytes. */ + [[nodiscard]] ZENCORE_API uint64_t GetSize() const; + + /** Returns the segments that the buffer is composed from. */ + [[nodiscard]] inline std::span<const SharedBuffer> GetSegments() const { return std::span<const SharedBuffer>{m_Segments}; } + + /** Returns true if the composite buffer is not null. */ + [[nodiscard]] inline explicit operator bool() const { return !IsNull(); } + + /** Returns true if the composite buffer is null. */ + [[nodiscard]] inline bool IsNull() const { return m_Segments.empty(); } + + /** Returns true if every segment in the composite buffer is owned. */ + [[nodiscard]] ZENCORE_API bool IsOwned() const; + + /** Returns a copy of the buffer where every segment is owned. */ + [[nodiscard]] ZENCORE_API CompositeBuffer MakeOwned() const&; + [[nodiscard]] ZENCORE_API CompositeBuffer MakeOwned() &&; + + /** Returns the concatenation of the segments into a contiguous buffer. */ + [[nodiscard]] ZENCORE_API SharedBuffer Flatten() const&; + [[nodiscard]] ZENCORE_API SharedBuffer Flatten() &&; + + /** Returns the middle part of the buffer by taking the size starting at the offset. */ + [[nodiscard]] ZENCORE_API CompositeBuffer Mid(uint64_t Offset, uint64_t Size = ~uint64_t(0)) const; + + /** + * Returns a view of the range if contained by one segment, otherwise a view of a copy of the range. + * + * @note CopyBuffer is reused if large enough, and otherwise allocated when needed. + * + * @param Offset The byte offset in this buffer that the range starts at. + * @param Size The number of bytes in the range to view or copy. + * @param CopyBuffer The buffer to write the copy into if a copy is required. + */ + [[nodiscard]] ZENCORE_API MemoryView ViewOrCopyRange(uint64_t Offset, uint64_t Size, UniqueBuffer& CopyBuffer) const; + + /** + * Copies a range of the buffer to a contiguous region of memory. + * + * @param Target The view to copy to. Must be no larger than the data available at the offset. + * @param Offset The byte offset in this buffer to start copying from. + */ + ZENCORE_API void CopyTo(MutableMemoryView Target, uint64_t Offset = 0) const; + + /** + * Invokes a visitor with a view of each segment that intersects with a range. + * + * @param Offset The byte offset in this buffer to start visiting from. + * @param Size The number of bytes in the range to visit. + * @param Visitor The visitor to invoke from zero to GetSegments().Num() times. + */ + ZENCORE_API void IterateRange(uint64_t Offset, uint64_t Size, std::function<void(MemoryView View)> Visitor) const; + ZENCORE_API void IterateRange(uint64_t Offset, + uint64_t Size, + std::function<void(MemoryView View, const SharedBuffer& ViewOuter)> Visitor) const; + + /** A null composite buffer. */ + static const CompositeBuffer Null; + +private: + static inline size_t GetBufferCount(const CompositeBuffer& Buffer) { return Buffer.m_Segments.size(); } + inline void AppendBuffers(const CompositeBuffer& Buffer) + { + m_Segments.insert(m_Segments.end(), begin(Buffer.m_Segments), end(Buffer.m_Segments)); + } + inline void AppendBuffers(CompositeBuffer&& Buffer) + { + // TODO: this operates just like the by-reference version above + m_Segments.insert(m_Segments.end(), begin(Buffer.m_Segments), end(Buffer.m_Segments)); + } + + static inline size_t GetBufferCount(const SharedBuffer&) { return 1; } + inline void AppendBuffers(const SharedBuffer& Buffer) { m_Segments.push_back(Buffer); } + inline void AppendBuffers(SharedBuffer&& Buffer) { m_Segments.push_back(std::move(Buffer)); } + + static inline size_t GetBufferCount(std::vector<SharedBuffer>&& Container) { return Container.size(); } + inline void AppendBuffers(std::vector<SharedBuffer>&& Container) + { + m_Segments.insert(m_Segments.end(), begin(Container), end(Container)); + } + +private: + std::vector<SharedBuffer> m_Segments; +}; + +void compositebuffer_forcelink(); // internal + +} // namespace zen diff --git a/zencore/include/zencore/compress.h b/zencore/include/zencore/compress.h index 759cf8444..1ab61e1be 100644 --- a/zencore/include/zencore/compress.h +++ b/zencore/include/zencore/compress.h @@ -4,16 +4,13 @@ #include "zencore/zencore.h" -namespace zen::CompressedBuffer { +#include "zencore/blake3.h" +#include "zencore/compositebuffer.h" -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -static constexpr uint64_t DefaultBlockSize = 256 * 1024; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +namespace zen { /** Method used to compress the data in a compressed buffer. */ -enum class Method : uint8_t +enum class CompressionMethod : uint8_t { /** Header is followed by one uncompressed block. */ None = 0, @@ -21,33 +18,101 @@ enum class Method : uint8_t LZ4 = 4, }; -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -/** Header used on every compressed buffer. Always stored in big-endian format */ -struct BufferHeader +/** + * A compressed buffer stores compressed data in a self-contained format. + * + * A buffer is self-contained in the sense that it can be decompressed without external knowledge + * of the compression format or the size of the raw data. + */ +class CompressedBuffer { - static constexpr uint32_t ExpectedMagic = 0xb7756362; - - /** A magic number to identify a compressed buffer. Always 0xb7756362 */ - uint32_t Magic = ExpectedMagic; - /** A CRC-32 used to check integrity of the buffer. Uses the polynomial 0x04c11db7 */ - uint32_t Crc32 = 0; - /** The method used to compress the buffer. Affects layout of data following the header */ - Method Method = Method::None; - /** The reserved bytes must be initialized to zero */ - uint8_t Reserved[2]{}; - /** The power of two size of every uncompressed block except the last. Size is 1 << BlockSizeExponent */ - uint8_t BlockSizeExponent = 0; - /** The number of blocks that follow the header */ - uint32_t BlockCount = 0; - /** The total size of the uncompressed data */ - uint64_t TotalRawSize = 0; - /** The total size of the compressed data including the header */ - uint64_t TotalCompressedSize = 0; -}; +public: + /** + * Compress the buffer using the requested compression format. + * + * @param FormatName One of NAME_None, NAME_Default, NAME_LZ4. + * @param RawData Raw data to compress. NAME_None will reference owned raw data. + * @return An owned compressed buffer, or null on error. + */ + [[nodiscard]] ZENCORE_API static CompressedBuffer Compress(CompressionMethod Method, const CompositeBuffer& RawData); + [[nodiscard]] ZENCORE_API static CompressedBuffer Compress(CompressionMethod Method, const SharedBuffer& RawData); + + /** + * Construct from a compressed buffer previously created by Compress(). + * + * @return A compressed buffer, or null on error, such as an invalid format or corrupt header. + */ + [[nodiscard]] ZENCORE_API static CompressedBuffer FromCompressed(const CompositeBuffer& CompressedData); + [[nodiscard]] ZENCORE_API static CompressedBuffer FromCompressed(CompositeBuffer&& CompressedData); + [[nodiscard]] ZENCORE_API static CompressedBuffer FromCompressed(const SharedBuffer& CompressedData); + [[nodiscard]] ZENCORE_API static CompressedBuffer FromCompressed(SharedBuffer&& CompressedData); + + /** Reset this to null. */ + inline void Reset() { CompressedData.Reset(); } + + /** Returns true if the compressed buffer is not null. */ + [[nodiscard]] inline explicit operator bool() const { return !IsNull(); } + + /** Returns true if the compressed buffer is null. */ + [[nodiscard]] inline bool IsNull() const { return CompressedData.IsNull(); } + + /** Returns true if the composite buffer is owned. */ + [[nodiscard]] inline bool IsOwned() const { return CompressedData.IsOwned(); } + + /** Returns a copy of the compressed buffer that owns its underlying memory. */ + [[nodiscard]] inline CompressedBuffer MakeOwned() const& { return FromCompressed(CompressedData.MakeOwned()); } + [[nodiscard]] inline CompressedBuffer MakeOwned() && { return FromCompressed(std::move(CompressedData).MakeOwned()); } -static_assert(sizeof(BufferHeader) == 32, "BufferHeader is the wrong size"); + /** Returns a composite buffer containing the compressed data. May be null. May not be owned. */ + [[nodiscard]] inline const CompositeBuffer& GetCompressed() const& { return CompressedData; } + [[nodiscard]] inline CompositeBuffer GetCompressed() && { return std::move(CompressedData); } + + /** Returns the size of the compressed data. Zero if this is null. */ + [[nodiscard]] inline uint64_t GetCompressedSize() const { return CompressedData.GetSize(); } + + /** Returns the size of the raw data. Zero on error or if this is empty or null. */ + [[nodiscard]] ZENCORE_API uint64_t GetRawSize() const; + + /** Returns the hash of the raw data. Zero on error or if this is null. */ + [[nodiscard]] ZENCORE_API BLAKE3 GetRawHash() const; + + /** + * Returns the name of the compression format used by this buffer. + * + * The format name may differ from the format name specified when creating the compressed buffer + * because an incompressible buffer is stored with NAME_None, and a request of NAME_Default will + * be stored in a specific format such as NAME_LZ4. + * + * @return The format name, or NAME_None if this null, or NAME_Error if the format is unknown. + */ + [[nodiscard]] ZENCORE_API const char* GetFormatName() const; + + /** + * Decompress into a memory view that is exactly GetRawSize() bytes. + */ + [[nodiscard]] ZENCORE_API bool TryDecompressTo(MutableMemoryView RawView) const; + + /** + * Decompress into an owned buffer. + * + * @return An owned buffer containing the raw data, or null on error. + */ + [[nodiscard]] ZENCORE_API SharedBuffer Decompress() const; + + /** + * Decompress into an owned composite buffer. + * + * @return An owned buffer containing the raw data, or null on error. + */ + [[nodiscard]] ZENCORE_API CompositeBuffer DecompressToComposite() const; + + /** A null compressed buffer. */ + static const CompressedBuffer Null; + +private: + CompositeBuffer CompressedData; +}; -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void compress_forcelink(); // internal -} // namespace zen::CompressedBuffer +} // namespace zen diff --git a/zencore/include/zencore/crc32.h b/zencore/include/zencore/crc32.h new file mode 100644 index 000000000..336bda77e --- /dev/null +++ b/zencore/include/zencore/crc32.h @@ -0,0 +1,13 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include <zencore/zencore.h> + +namespace zen { + +uint32_t MemCrc32(const void* InData, size_t Length, uint32_t Crc = 0); +uint32_t MemCrc32_Deprecated(const void* InData, size_t Length, uint32_t Crc = 0); +uint32_t StrCrc_Deprecated(const char* Data); + +} // namespace zen diff --git a/zencore/include/zencore/except.h b/zencore/include/zencore/except.h index 8f5f50e86..782dbeed0 100644 --- a/zencore/include/zencore/except.h +++ b/zencore/include/zencore/except.h @@ -11,18 +11,12 @@ namespace zen { class WindowsException : public std::exception { public: - WindowsException(const char* Message) + WindowsException(std::string_view Message) { m_hResult = HRESULT_FROM_WIN32(GetLastError()); m_Message = Message; } - WindowsException(HRESULT hRes, const char* Message) - { - m_hResult = hRes; - m_Message = Message; - } - WindowsException(HRESULT hRes, std::string_view Message) { m_hResult = hRes; @@ -49,18 +43,14 @@ private: HRESULT m_hResult; }; -ZENCORE_API void ThrowSystemException(HRESULT hRes, const char* Message); +ZENCORE_API void ThrowSystemException(HRESULT hRes, std::string_view Message); + inline void ThrowSystemException(const char* Message) { throw WindowsException(Message); } -inline void -ThrowIfFailed(HRESULT hRes, const char* Message) -{ - if (FAILED(hRes)) - ThrowSystemException(hRes, Message); -} +ZENCORE_API void ThrowLastError(std::string_view Message); } // namespace zen diff --git a/zencore/include/zencore/httpserver.h b/zencore/include/zencore/httpserver.h index 8f762c2e4..38fdcf457 100644 --- a/zencore/include/zencore/httpserver.h +++ b/zencore/include/zencore/httpserver.h @@ -5,6 +5,7 @@ #include "zencore.h" #include <zencore/enumflags.h> +#include <zencore/iobuffer.h> #include <zencore/refcount.h> #include <zencore/string.h> @@ -17,6 +18,8 @@ namespace zen { +using HttpContentType = ZenContentType; + class IoBuffer; class CbObject; class StringBuilderBase; @@ -161,16 +164,6 @@ enum class HttpResponse NetworkAuthenticationRequired = 511, //!< Indicates that the client needs to authenticate to gain network access. }; -enum class HttpContentType -{ - kBinary, - kText, - kJSON, - kCbObject, - kCbPackage, - kUnknownContentType -}; - /** HTTP Server Request */ class HttpServerRequest diff --git a/zencore/include/zencore/intmath.h b/zencore/include/zencore/intmath.h index 9d39b8226..792b8b2b4 100644 --- a/zencore/include/zencore/intmath.h +++ b/zencore/include/zencore/intmath.h @@ -125,13 +125,13 @@ IsPointerAligned(const void* Ptr, uint64_t Alignment) # error "Looks like you did #include <windows.h> -- use <zencore/windows.h> instead" #endif -auto +constexpr auto Min(auto x, auto y) { return x < y ? x : y; } -auto +constexpr auto Max(auto x, auto y) { return x > y ? x : y; diff --git a/zencore/include/zencore/iobuffer.h b/zencore/include/zencore/iobuffer.h index 7cf497527..960918239 100644 --- a/zencore/include/zencore/iobuffer.h +++ b/zencore/include/zencore/iobuffer.h @@ -3,6 +3,7 @@ #pragma once #include <memory.h> +#include <zencore/memory.h> #include "refcount.h" #include "zencore.h" @@ -10,6 +11,17 @@ namespace zen { struct IoBufferExtendedCore; +enum class ZenContentType : uint8_t +{ + kBinary, // Note that since this is zero, this will be the default value in IoBuffer + kText, + kJSON, + kCbObject, + kCbPackage, + kYAML, + kUnknownContentType +}; + struct IoBufferFileReference { void* FileHandle; @@ -136,6 +148,14 @@ public: } } + inline void SetContentType(ZenContentType ContentType) + { + ZEN_ASSERT_SLOW((uint32_t(ContentType) & kContentTypeMask) == uint32_t(ContentType)); + m_Flags = (m_Flags & ~(kContentTypeMask << kContentTypeShift)) | (uint32_t(ContentType) << kContentTypeShift); + } + + inline ZenContentType GetContentType() const { return ZenContentType((m_Flags >> kContentTypeShift) & kContentTypeMask); } + inline uint32_t GetRefCount() const { return m_RefCount; } protected: @@ -145,6 +165,14 @@ protected: size_t m_DataBytes = 0; RefPtr<const IoBufferCore> m_OuterCore; + enum + { + kContentTypeShift = 24, + kContentTypeMask = 0xf + }; + + static_assert((uint32_t(ZenContentType::kUnknownContentType) & ~kContentTypeMask) == 0); + enum Flags { kIsOwnedByThis = 1 << 0, @@ -153,6 +181,11 @@ protected: kIsMaterialized = 1 << 3, // Data pointers are valid kLowLevelAlloc = 1 << 4, // Using direct memory allocation kIsWholeFile = 1 << 5, // References an entire file + + kContentTypeBit0 = 1 << (24 + 0), // These constants + kContentTypeBit1 = 1 << (24 + 1), // are here mostly to + kContentTypeBit2 = 1 << (24 + 2), // indicate that these + kContentTypeBit3 = 1 << (24 + 3), // bits are reserved }; void* AllocateBuffer(size_t InSize, size_t Alignment); @@ -171,8 +204,8 @@ struct IoBufferExtendedCore : public IoBufferCore enum ExtendedFlags { - kOwnsFile = 1 << 8, - kOwnsMmap = 1 << 9 + kOwnsFile = 1 << 16, + kOwnsMmap = 1 << 17 }; void Materialize() const; @@ -273,13 +306,18 @@ public: ZENCORE_API IoBuffer(EFileTag, void* FileHandle, uint64_t ChunkFileOffset, uint64_t ChunkSize); ZENCORE_API IoBuffer(EBorrowedFileTag, void* FileHandle, uint64_t ChunkFileOffset, uint64_t ChunkSize); - inline operator bool() const { return !m_Core->IsNull(); } - ZENCORE_API void MakeOwned() { return m_Core->MakeOwned(); } - inline bool IsOwned() const { return m_Core->IsOwned(); } - inline bool IsWholeFile() const { return m_Core->IsWholeFile(); } - const void* Data() const { return m_Core->DataPointer(); } - const size_t Size() const { return m_Core->DataBytes(); } - ZENCORE_API bool GetFileReference(IoBufferFileReference& OutRef) const; + inline operator bool() const { return !m_Core->IsNull(); } + inline operator MemoryView() const& { return MemoryView(m_Core->DataPointer(), m_Core->DataBytes()); } + inline void MakeOwned() { return m_Core->MakeOwned(); } + [[nodiscard]] inline bool IsOwned() const { return m_Core->IsOwned(); } + [[nodiscard]] inline bool IsWholeFile() const { return m_Core->IsWholeFile(); } + [[nodiscard]] void* MutableData() const { return m_Core->MutableDataPointer(); } + void MakeImmutable() { m_Core->SetIsImmutable(true); } + [[nodiscard]] const void* Data() const { return m_Core->DataPointer(); } + [[nodiscard]] const size_t Size() const { return m_Core->DataBytes(); } + inline void SetContentType(ZenContentType ContentType) { m_Core->SetContentType(ContentType); } + [[nodiscard]] inline ZenContentType GetContentType() const { return m_Core->GetContentType(); } + [[nodiscard]] ZENCORE_API bool GetFileReference(IoBufferFileReference& OutRef) const; private: RefPtr<IoBufferCore> m_Core = new IoBufferCore; diff --git a/zencore/include/zencore/iohash.h b/zencore/include/zencore/iohash.h index 4eac7e328..14049e2b2 100644 --- a/zencore/include/zencore/iohash.h +++ b/zencore/include/zencore/iohash.h @@ -32,6 +32,13 @@ struct IoHash return Io; } + static IoHash FromBLAKE3(const BLAKE3& Blake3) + { + IoHash Io; + memcpy(Io.Hash, Blake3.Hash, sizeof Io.Hash); + return Io; + } + static IoHash HashMemory(const void* data, size_t byteCount); static IoHash HashMemory(MemoryView Data) { return HashMemory(Data.GetData(), Data.GetSize()); } static IoHash FromHexString(const char* string); @@ -93,3 +100,12 @@ private: }; } // namespace zen + +namespace std { + +template<> +struct hash<zen::IoHash> : public zen::IoHash::Hasher +{ +}; + +} // namespace std diff --git a/zencore/include/zencore/memory.h b/zencore/include/zencore/memory.h index 8a16126ef..b060006b0 100644 --- a/zencore/include/zencore/memory.h +++ b/zencore/include/zencore/memory.h @@ -4,13 +4,17 @@ #include "zencore.h" +#include <zencore/intmath.h> #include <zencore/thread.h> #include <algorithm> +#include <span> #include <vector> namespace zen { +struct MemoryView; + class MemoryArena { public: @@ -68,6 +72,12 @@ struct MutableMemoryView { } + MutableMemoryView(void* DataPtr, void* DataEndPtr) + : m_Data(reinterpret_cast<uint8_t*>(DataPtr)) + , m_DataEnd(reinterpret_cast<uint8_t*>(DataEndPtr)) + { + } + inline bool IsEmpty() const { return m_Data == m_DataEnd; } void* GetData() const { return m_Data; } void* GetDataEnd() const { return m_DataEnd; } @@ -80,10 +90,27 @@ struct MutableMemoryView return Size == InView.GetSize() && (memcmp(m_Data, InView.m_Data, Size) == 0); } + /** Modifies the view to be the given number of bytes from the right. */ + inline void RightInline(uint64_t InSize) + { + const uint64_t OldSize = GetSize(); + const uint64_t NewSize = zen::Min(OldSize, InSize); + m_Data = GetDataAtOffsetNoCheck(OldSize - NewSize); + m_DataEnd = m_Data + NewSize; + } + + /** Returns the right-most part of the view by taking the given number of bytes from the right. */ + [[nodiscard]] inline MutableMemoryView Right(uint64_t InSize) const + { + MutableMemoryView View(*this); + View.RightChopInline(InSize); + return View; + } + /** Modifies the view by chopping the given number of bytes from the left. */ - inline void RightChopInline(uint64_t InSize) + inline constexpr void RightChopInline(uint64_t InSize) { - const uint64_t Offset = std::min(GetSize(), InSize); + const uint64_t Offset = zen::Min(GetSize(), InSize); m_Data = GetDataAtOffsetNoCheck(Offset); } @@ -98,14 +125,48 @@ struct MutableMemoryView /** Modifies the view to be the given number of bytes from the left. */ constexpr inline void LeftInline(uint64_t InSize) { m_DataEnd = std::min(m_DataEnd, m_Data + InSize); } + /** Modifies the view to be the middle part by taking up to the given number of bytes from the given offset. */ + inline void MidInline(uint64_t InOffset, uint64_t InSize = ~uint64_t(0)) + { + RightChopInline(InOffset); + LeftInline(InSize); + } + + /** Returns the middle part of the view by taking up to the given number of bytes from the given position. */ + [[nodiscard]] inline MutableMemoryView Mid(uint64_t InOffset, uint64_t InSize = ~uint64_t(0)) const + { + MutableMemoryView View(*this); + View.MidInline(InOffset, InSize); + return View; + } + + /** Returns the right-most part of the view by chopping the given number of bytes from the left. */ + [[nodiscard]] inline MutableMemoryView RightChop(uint64_t InSize) const + { + MutableMemoryView View(*this); + View.RightChopInline(InSize); + return View; + } + + inline constexpr MutableMemoryView& operator+=(size_t InSize) + { + RightChopInline(InSize); + return *this; + } + + /** Copies bytes from the input view into this view, and returns the remainder of this view. */ + inline MutableMemoryView CopyFrom(MemoryView InView) const; + private: uint8_t* m_Data = nullptr; uint8_t* m_DataEnd = nullptr; /** Returns the data pointer advanced by an offset in bytes. */ - inline uint8_t* GetDataAtOffsetNoCheck(uint64_t InOffset) const { return m_Data + InOffset; } + inline constexpr uint8_t* GetDataAtOffsetNoCheck(uint64_t InOffset) const { return m_Data + InOffset; } }; +////////////////////////////////////////////////////////////////////////// + struct MemoryView { MemoryView() = default; @@ -142,14 +203,14 @@ struct MemoryView return Size == InView.GetSize() && (memcmp(m_Data, InView.GetData(), Size) == 0); } - inline MemoryView& operator+=(size_t InSize) + inline constexpr MemoryView& operator+=(size_t InSize) { RightChopInline(InSize); return *this; } /** Modifies the view by chopping the given number of bytes from the left. */ - inline void RightChopInline(uint64_t InSize) + inline constexpr void RightChopInline(uint64_t InSize) { const uint64_t Offset = std::min(GetSize(), InSize); m_Data = GetDataAtOffsetNoCheck(Offset); @@ -162,6 +223,23 @@ struct MemoryView return View; } + /** Returns the right-most part of the view by taking the given number of bytes from the right. */ + [[nodiscard]] inline MemoryView Right(uint64_t InSize) const + { + MemoryView View(*this); + View.RightInline(InSize); + return View; + } + + /** Modifies the view to be the given number of bytes from the right. */ + inline void RightInline(uint64_t InSize) + { + const uint64_t OldSize = GetSize(); + const uint64_t NewSize = zen::Min(OldSize, InSize); + m_Data = GetDataAtOffsetNoCheck(OldSize - NewSize); + m_DataEnd = m_Data + NewSize; + } + /** Returns the left-most part of the view by taking the given number of bytes from the left. */ constexpr inline MemoryView Left(uint64_t InSize) const { @@ -171,7 +249,26 @@ struct MemoryView } /** Modifies the view to be the given number of bytes from the left. */ - constexpr inline void LeftInline(uint64_t InSize) { m_DataEnd = std::min(m_DataEnd, m_Data + InSize); } + constexpr inline void LeftInline(uint64_t InSize) + { + InSize = zen::Min(GetSize(), InSize); + m_DataEnd = std::min(m_DataEnd, m_Data + InSize); + } + + /** Modifies the view to be the middle part by taking up to the given number of bytes from the given offset. */ + inline void MidInline(uint64_t InOffset, uint64_t InSize = ~uint64_t(0)) + { + RightChopInline(InOffset); + LeftInline(InSize); + } + + /** Returns the middle part of the view by taking up to the given number of bytes from the given position. */ + [[nodiscard]] inline MemoryView Mid(uint64_t InOffset, uint64_t InSize = ~uint64_t(0)) const + { + MemoryView View(*this); + View.MidInline(InOffset, InSize); + return View; + } constexpr void Reset() { @@ -184,9 +281,45 @@ private: const uint8_t* m_DataEnd = nullptr; /** Returns the data pointer advanced by an offset in bytes. */ - inline const uint8_t* GetDataAtOffsetNoCheck(uint64_t InOffset) const { return m_Data + InOffset; } + inline constexpr const uint8_t* GetDataAtOffsetNoCheck(uint64_t InOffset) const { return m_Data + InOffset; } }; +inline MutableMemoryView +MutableMemoryView::CopyFrom(MemoryView InView) const +{ + ZEN_ASSERT(InView.GetSize() <= GetSize()); + memcpy(m_Data, InView.GetData(), InView.GetSize()); + return RightChop(InView.GetSize()); +} + +/** Advances the start of the view by an offset, which is clamped to stay within the view. */ +constexpr inline MemoryView +operator+(const MemoryView& View, uint64_t Offset) +{ + return MemoryView(View) += Offset; +} + +/** Advances the start of the view by an offset, which is clamped to stay within the view. */ +constexpr inline MemoryView +operator+(uint64_t Offset, const MemoryView& View) +{ + return MemoryView(View) += Offset; +} + +/** Advances the start of the view by an offset, which is clamped to stay within the view. */ +constexpr inline MutableMemoryView +operator+(const MutableMemoryView& View, uint64_t Offset) +{ + return MutableMemoryView(View) += Offset; +} + +/** Advances the start of the view by an offset, which is clamped to stay within the view. */ +constexpr inline MutableMemoryView +operator+(uint64_t Offset, const MutableMemoryView& View) +{ + return MutableMemoryView(View) += Offset; +} + /** * Make a non-owning view of the memory of the initializer list. * @@ -204,8 +337,45 @@ template<std::ranges::contiguous_range R> [[nodiscard]] constexpr inline MemoryView MakeMemoryView(const R& Container) { - const auto& Front = *std::begin(Container); - return MemoryView(std::addressof(Front), std::size(Container) * sizeof(Front)); + std::span Span = Container; + return MemoryView(Span.data(), Span.size() * sizeof(decltype(Span)::element_type)); +} + +/** Make a non-owning const view starting at Data and ending at DataEnd. */ + +[[nodiscard]] inline MemoryView +MakeMemoryView(const void* Data, const void* DataEnd) +{ + return MemoryView(Data, DataEnd); +} + +/** + * Make a non-owning mutable view of the memory of the initializer list. + * + * This overload is only available when the element type does not need to be deduced. + */ +template<typename T> +[[nodiscard]] inline MutableMemoryView +MakeMutableMemoryView(std::initializer_list<typename std::type_identity<T>::type> List) +{ + return MutableMemoryView(List.begin(), List.size() * sizeof(T)); +} + +/** Make a non-owning mutable view of the memory of the contiguous container. */ +template<std::ranges::contiguous_range R> +[[nodiscard]] constexpr inline MutableMemoryView +MakeMutableMemoryView(R& Container) +{ + std::span Span = Container; + return MutableMemoryView(Span.data(), Span.size() * sizeof(decltype(Span)::element_type)); +} + +/** Make a non-owning mutable view starting at Data and ending at DataEnd. */ + +[[nodiscard]] inline MutableMemoryView +MakeMutableMemoryView(void* Data, void* DataEnd) +{ + return MutableMemoryView(Data, DataEnd); } void memory_forcelink(); // internal diff --git a/zencore/include/zencore/refcount.h b/zencore/include/zencore/refcount.h index de7d315f4..288b649c6 100644 --- a/zencore/include/zencore/refcount.h +++ b/zencore/include/zencore/refcount.h @@ -58,9 +58,10 @@ public: inline RefPtr(T* Ptr) : m_Ref(Ptr) { m_Ref && m_Ref->AddRef(); } inline ~RefPtr() { m_Ref && m_Ref->Release(); } - inline explicit operator bool() const { return m_Ref != nullptr; } - inline operator T*() const { return m_Ref; } - inline T* operator->() const { return m_Ref; } + [[nodiscard]] inline bool IsNull() const { return m_Ref == nullptr; } + inline explicit operator bool() const { return m_Ref != nullptr; } + inline operator T*() const { return m_Ref; } + inline T* operator->() const { return m_Ref; } inline std::strong_ordering operator<=>(const RefPtr& Rhs) const = default; @@ -73,16 +74,22 @@ public: } inline RefPtr& operator=(const RefPtr& Rhs) { - m_Ref && m_Ref->Release(); - auto Ref = m_Ref = Rhs.m_Ref; - Ref && Ref->AddRef(); + if (&Rhs != this) + { + Rhs && Rhs->AddRef(); + m_Ref && m_Ref->Release(); + m_Ref = Rhs.m_Ref; + } return *this; } inline RefPtr& operator=(RefPtr&& Rhs) noexcept { - m_Ref && m_Ref->Release(); - m_Ref = Rhs.m_Ref; - Rhs.m_Ref = nullptr; + if (&Rhs != this) + { + m_Ref && m_Ref->Release(); + m_Ref = Rhs.m_Ref; + Rhs.m_Ref = nullptr; + } return *this; } inline RefPtr(RefPtr&& Rhs) noexcept : m_Ref(Rhs.m_Ref) { Rhs.m_Ref = nullptr; } @@ -107,8 +114,9 @@ public: inline Ref(T* Ptr) : m_Ref(Ptr) { m_Ref && m_Ref->AddRef(); } inline ~Ref() { m_Ref && m_Ref->Release(); } - inline explicit operator bool() const { return m_Ref != nullptr; } - inline T* operator->() const { return m_Ref; } + [[nodiscard]] inline bool IsNull() const { return m_Ref == nullptr; } + inline explicit operator bool() const { return m_Ref != nullptr; } + inline T* operator->() const { return m_Ref; } inline std::strong_ordering operator<=>(const Ref& Rhs) const = default; @@ -121,16 +129,22 @@ public: } inline Ref& operator=(const Ref& Rhs) { - m_Ref && m_Ref->Release(); - auto Ref = m_Ref = Rhs.m_Ref; - Ref && Ref->AddRef(); + if (&Rhs != this) + { + Rhs && Rhs->AddRef(); + m_Ref && m_Ref->Release(); + m_Ref = Rhs.m_Ref; + } return *this; } inline Ref& operator=(Ref&& Rhs) noexcept { - m_Ref && m_Ref->Release(); - m_Ref = Rhs.m_Ref; - Rhs.m_Ref = nullptr; + if (&Rhs != this) + { + m_Ref && m_Ref->Release(); + m_Ref = Rhs.m_Ref; + Rhs.m_Ref = nullptr; + } return *this; } inline Ref(Ref&& Rhs) noexcept : m_Ref(Rhs.m_Ref) { Rhs.m_Ref = nullptr; } diff --git a/zencore/include/zencore/sharedbuffer.h b/zencore/include/zencore/sharedbuffer.h index cdcf66da4..2c10c4469 100644 --- a/zencore/include/zencore/sharedbuffer.h +++ b/zencore/include/zencore/sharedbuffer.h @@ -20,20 +20,33 @@ namespace zen { class UniqueBuffer { public: - UniqueBuffer(const UniqueBuffer&) = delete; + UniqueBuffer() = default; + UniqueBuffer(UniqueBuffer&&) = default; + UniqueBuffer& operator=(UniqueBuffer&&) = default; + UniqueBuffer(const UniqueBuffer&) = delete; UniqueBuffer& operator=(const UniqueBuffer&) = delete; - UniqueBuffer() = delete; ZENCORE_API explicit UniqueBuffer(IoBufferCore* Owner); - void* GetData() { return m_Buffer->MutableDataPointer(); } - const void* GetData() const { return m_Buffer->DataPointer(); } - size_t GetSize() const { return m_Buffer->DataBytes(); } - operator MutableMemoryView() { return GetMutableView(); } - operator MemoryView() const { return GetView(); } + void* GetData() { return m_Buffer ? m_Buffer->MutableDataPointer() : nullptr; } + const void* GetData() const { return m_Buffer ? m_Buffer->DataPointer() : nullptr; } + size_t GetSize() const { return m_Buffer ? m_Buffer->DataBytes() : 0; } - inline MutableMemoryView GetMutableView() { return MutableMemoryView(m_Buffer->MutableDataPointer(), m_Buffer->DataBytes()); } - inline MemoryView GetView() const { return MemoryView(m_Buffer->MutableDataPointer(), m_Buffer->DataBytes()); } + operator MutableMemoryView() { return GetMutableView(); } + operator MemoryView() const { return GetView(); } + + /** + * Returns true if this does not point to a buffer owner. + * + * A null buffer is always owned, materialized, and empty. + */ + [[nodiscard]] inline bool IsNull() const { return m_Buffer.IsNull(); } + + /** Reset this to null. */ + ZENCORE_API void Reset(); + + inline MutableMemoryView GetMutableView() { return MutableMemoryView(GetData(), GetSize()); } + inline MemoryView GetView() const { return MemoryView(GetData(), GetSize()); } /** Make an uninitialized owned buffer of the specified size. */ ZENCORE_API static UniqueBuffer Alloc(uint64_t Size); @@ -41,8 +54,15 @@ public: /** Make a non-owned view of the input. */ ZENCORE_API static UniqueBuffer MakeMutableView(void* DataPtr, uint64_t Size); + /** + * Convert this to an immutable shared buffer, leaving this null. + * + * Steals the buffer owner from the unique buffer. + */ + [[nodiscard]] ZENCORE_API SharedBuffer MoveToShared(); + private: - // This may never be null + // This may be null, for a default constructed UniqueBuffer only RefPtr<IoBufferCore> m_Buffer; friend class SharedBuffer; @@ -58,6 +78,8 @@ public: ZENCORE_API explicit SharedBuffer(UniqueBuffer&&); inline explicit SharedBuffer(IoBufferCore* Owner) : m_Buffer(Owner) {} ZENCORE_API explicit SharedBuffer(IoBuffer&& Buffer) : m_Buffer(std::move(Buffer.m_Core)) {} + ZENCORE_API explicit SharedBuffer(const IoBuffer& Buffer) : m_Buffer(Buffer.m_Core) {} + ZENCORE_API explicit SharedBuffer(RefPtr<IoBufferCore>&& Owner) : m_Buffer(std::move(Owner)) {} const void* GetData() const { @@ -83,11 +105,11 @@ public: m_Buffer->SetIsImmutable(true); } - ZENCORE_API void MakeOwned(); - bool IsOwned() const { return m_Buffer && m_Buffer->IsOwned(); } - inline explicit operator bool() const { return m_Buffer; } - inline bool IsNull() const { return !m_Buffer; } - inline void Reset() { m_Buffer = nullptr; } + ZENCORE_API SharedBuffer& MakeOwned(); + bool IsOwned() const { return m_Buffer && m_Buffer->IsOwned(); } + inline explicit operator bool() const { return m_Buffer; } + inline bool IsNull() const { return !m_Buffer; } + inline void Reset() { m_Buffer = nullptr; } MemoryView GetView() const { diff --git a/zencore/include/zencore/thread.h b/zencore/include/zencore/thread.h index 48afad33f..46b251e07 100644 --- a/zencore/include/zencore/thread.h +++ b/zencore/include/zencore/thread.h @@ -25,20 +25,38 @@ public: struct SharedLockScope { - SharedLockScope(RwLock& lock) : m_Lock(lock) { m_Lock.AcquireShared(); } - ~SharedLockScope() { m_Lock.ReleaseShared(); } + SharedLockScope(RwLock& Lock) : m_Lock(&Lock) { Lock.AcquireShared(); } + ~SharedLockScope() { ReleaseNow(); } + + void ReleaseNow() + { + if (m_Lock) + { + m_Lock->ReleaseShared(); + m_Lock = nullptr; + } + } private: - RwLock& m_Lock; + RwLock* m_Lock; }; struct ExclusiveLockScope { - ExclusiveLockScope(RwLock& lock) : m_Lock(lock) { m_Lock.AcquireExclusive(); } - ~ExclusiveLockScope() { m_Lock.ReleaseExclusive(); } + ExclusiveLockScope(RwLock& Lock) : m_Lock(&Lock) { Lock.AcquireExclusive(); } + ~ExclusiveLockScope() { ReleaseNow(); } + + void ReleaseNow() + { + if (m_Lock) + { + m_Lock->ReleaseExclusive(); + m_Lock = nullptr; + } + } private: - RwLock& m_Lock; + RwLock* m_Lock; }; private: diff --git a/zencore/include/zencore/uid.h b/zencore/include/zencore/uid.h index a793b160a..7fc42293f 100644 --- a/zencore/include/zencore/uid.h +++ b/zencore/include/zencore/uid.h @@ -55,7 +55,8 @@ struct Oid [[nodiscard]] static Oid FromHexString(const std::string_view String); StringBuilderBase& ToString(StringBuilderBase& OutString) const; - auto operator<=>(const Oid& rhs) const = default; + auto operator<=>(const Oid& rhs) const = default; + [[nodiscard]] inline operator bool() const { return *this == Zero; } static const Oid Zero; // Min (can be used to signify a "null" value, or for open range queries) static const Oid Max; // Max (can be used for open range queries) diff --git a/zencore/iobuffer.cpp b/zencore/iobuffer.cpp index d32a30358..732d4d603 100644 --- a/zencore/iobuffer.cpp +++ b/zencore/iobuffer.cpp @@ -237,10 +237,12 @@ IoBufferExtendedCore::GetFileReference(IoBufferFileReference& OutRef) const IoBuffer::IoBuffer(size_t InSize) : m_Core(new IoBufferCore(InSize)) { + m_Core->SetIsImmutable(false); } IoBuffer::IoBuffer(size_t InSize, uint64_t InAlignment) : m_Core(new IoBufferCore(InSize, InAlignment)) { + m_Core->SetIsImmutable(false); } IoBuffer::IoBuffer(const IoBuffer& OuterBuffer, size_t Offset, size_t Size) diff --git a/zencore/sharedbuffer.cpp b/zencore/sharedbuffer.cpp index bbbaa0b24..9c788d29f 100644 --- a/zencore/sharedbuffer.cpp +++ b/zencore/sharedbuffer.cpp @@ -35,17 +35,31 @@ UniqueBuffer::UniqueBuffer(IoBufferCore* Owner) : m_Buffer(Owner) { } +SharedBuffer +UniqueBuffer::MoveToShared() +{ + return SharedBuffer(std::move(m_Buffer)); +} + +void +UniqueBuffer::Reset() +{ + m_Buffer = nullptr; +} + ////////////////////////////////////////////////////////////////////////// SharedBuffer::SharedBuffer(UniqueBuffer&& InBuffer) : m_Buffer(std::move(InBuffer.m_Buffer)) { } -void +SharedBuffer& SharedBuffer::MakeOwned() { if (IsOwned() || !m_Buffer) - return; + { + return *this; + } const uint64_t Size = m_Buffer->DataBytes(); void* Buffer = Memory::Alloc(Size, 16); @@ -55,6 +69,8 @@ SharedBuffer::MakeOwned() memcpy(Buffer, m_Buffer->DataPointer(), Size); m_Buffer = NewOwner; + + return *this; } SharedBuffer @@ -65,6 +81,12 @@ SharedBuffer::MakeView(MemoryView View, SharedBuffer OuterBuffer) ZEN_ASSERT(OuterBuffer.GetView().Contains(View)); } + if (View == OuterBuffer.GetView()) + { + // Reference to the full buffer contents, so just return the "outer" + return OuterBuffer; + } + IoBufferCore* NewCore = new IoBufferCore(OuterBuffer.m_Buffer, View.GetData(), View.GetSize()); NewCore->SetIsImmutable(true); return SharedBuffer(NewCore); diff --git a/zencore/thread.cpp b/zencore/thread.cpp index 80cf6f100..4451fd302 100644 --- a/zencore/thread.cpp +++ b/zencore/thread.cpp @@ -58,13 +58,15 @@ Event::Reset() bool Event::Wait(int TimeoutMs) { + using namespace std::literals; + const DWORD Timeout = (TimeoutMs < 0) ? INFINITE : TimeoutMs; DWORD Result = WaitForSingleObject(m_EventHandle, Timeout); if (Result == WAIT_FAILED) { - throw WindowsException("Event wait failed"); + zen::ThrowLastError("Event wait failed"sv); } return (Result == WAIT_OBJECT_0); diff --git a/zencore/uid.cpp b/zencore/uid.cpp index 9506b305c..347333445 100644 --- a/zencore/uid.cpp +++ b/zencore/uid.cpp @@ -143,9 +143,14 @@ Oid::FromHexString(const std::string_view String) Oid Id; - ParseHexBytes(String.data(), String.size(), reinterpret_cast<uint8_t*>(Id.OidBits)); - - return Id; + if (ParseHexBytes(String.data(), String.size(), reinterpret_cast<uint8_t*>(Id.OidBits))) + { + return Id; + } + else + { + return Oid::Zero; + } } StringBuilderBase& diff --git a/zencore/zencore.cpp b/zencore/zencore.cpp index d4853b043..ec8d84c0e 100644 --- a/zencore/zencore.cpp +++ b/zencore/zencore.cpp @@ -8,6 +8,8 @@ #include <zencore/compactbinary.h> #include <zencore/compactbinarybuilder.h> #include <zencore/compactbinarypackage.h> +#include <zencore/compositebuffer.h> +#include <zencore/compress.h> #include <zencore/iobuffer.h> #include <zencore/memory.h> #include <zencore/refcount.h> @@ -51,20 +53,22 @@ RequestApplicationExit(int ExitCode) void zencore_forcelinktests() { - zen::sha1_forcelink(); zen::blake3_forcelink(); - zen::trace_forcelink(); - zen::timer_forcelink(); - zen::uid_forcelink(); - zen::string_forcelink(); - zen::thread_forcelink(); - zen::stream_forcelink(); + zen::compositebuffer_forcelink(); + zen::compress_forcelink(); + zen::iobuffer_forcelink(); + zen::memory_forcelink(); zen::refcount_forcelink(); + zen::sha1_forcelink(); zen::snapshotmanifest_forcelink(); - zen::iobuffer_forcelink(); zen::stats_forcelink(); + zen::stream_forcelink(); + zen::string_forcelink(); + zen::thread_forcelink(); + zen::timer_forcelink(); + zen::trace_forcelink(); + zen::uid_forcelink(); zen::uson_forcelink(); zen::usonbuilder_forcelink(); zen::usonpackage_forcelink(); - zen::memory_forcelink(); } diff --git a/zencore/zencore.vcxproj b/zencore/zencore.vcxproj index c9d51e0bb..84593a12f 100644 --- a/zencore/zencore.vcxproj +++ b/zencore/zencore.vcxproj @@ -112,6 +112,9 @@ <ItemGroup> <ClInclude Include="include\zencore\atomic.h" /> <ClInclude Include="include\zencore\blake3.h" /> + <ClInclude Include="include\zencore\compositebuffer.h" /> + <ClInclude Include="include\zencore\crc32.h" /> + <ClInclude Include="include\zencore\endian.h" /> <ClInclude Include="include\zencore\enumflags.h" /> <ClInclude Include="include\zencore\except.h" /> <ClInclude Include="include\zencore\compress.h" /> @@ -123,6 +126,7 @@ <ClInclude Include="include\zencore\iohash.h" /> <ClInclude Include="include\zencore\md5.h" /> <ClInclude Include="include\zencore\memory.h" /> + <ClInclude Include="include\zencore\meta.h" /> <ClInclude Include="include\zencore\refcount.h" /> <ClInclude Include="include\zencore\scopeguard.h" /> <ClInclude Include="include\zencore\sha1.h" /> @@ -150,7 +154,9 @@ </ItemGroup> <ItemGroup> <ClCompile Include="blake3.cpp" /> + <ClCompile Include="compositebuffer.cpp" /> <ClCompile Include="compress.cpp" /> + <ClCompile Include="crc32.cpp" /> <ClCompile Include="except.cpp" /> <ClCompile Include="filesystem.cpp" /> <ClCompile Include="httpclient.cpp" /> diff --git a/zencore/zencore.vcxproj.filters b/zencore/zencore.vcxproj.filters index c25f99e77..01d37994c 100644 --- a/zencore/zencore.vcxproj.filters +++ b/zencore/zencore.vcxproj.filters @@ -38,6 +38,10 @@ <ClInclude Include="include\zencore\xxhash.h" /> <ClInclude Include="iothreadpool.h" /> <ClInclude Include="include\zencore\varint.h" /> + <ClInclude Include="include\zencore\endian.h" /> + <ClInclude Include="include\zencore\compositebuffer.h" /> + <ClInclude Include="include\zencore\crc32.h" /> + <ClInclude Include="include\zencore\meta.h" /> </ItemGroup> <ItemGroup> <ClCompile Include="snapshot_manifest.cpp" /> @@ -69,6 +73,8 @@ <ClCompile Include="xxhash.cpp" /> <ClCompile Include="iothreadpool.cpp" /> <ClCompile Include="compress.cpp" /> + <ClCompile Include="compositebuffer.cpp" /> + <ClCompile Include="crc32.cpp" /> </ItemGroup> <ItemGroup> <Filter Include="CAS"> diff --git a/zenserver-test/zenserver-test.cpp b/zenserver-test/zenserver-test.cpp index cb4ff06da..dbe7ac9b9 100644 --- a/zenserver-test/zenserver-test.cpp +++ b/zenserver-test/zenserver-test.cpp @@ -7,6 +7,7 @@ #include <zencore/compactbinarypackage.h> #include <zencore/except.h> #include <zencore/filesystem.h> +#include <zencore/fmtutils.h> #include <zencore/iohash.h> #include <zencore/string.h> #include <zencore/thread.h> @@ -1068,6 +1069,7 @@ TEST_CASE("project.basic") zen::NiceRate(RequestCount, (uint32_t)Elapsed, "req")); } +# if 0 // this is extremely WIP TEST_CASE("project.pipe") { using namespace std::literals; @@ -1088,5 +1090,76 @@ TEST_CASE("project.pipe") zen::CbObject Response = LocalClient.MessageTransaction(Cbow.Save()); } +# endif + +TEST_CASE("z$.basic") +{ + using namespace std::literals; + + std::filesystem::path TestDir = TestEnv.CreateNewTestDir(); + + const uint16_t PortNumber = 13337; + + const int kIterationCount = 100; + const auto BaseUri = "http://localhost:{}/z$"_format(PortNumber); + + { + ZenServerInstance Instance1(TestEnv); + Instance1.SetTestDir(TestDir); + Instance1.SpawnServer(PortNumber); + Instance1.WaitUntilReady(); + + // Populate with some simple data + + for (int i = 0; i < kIterationCount; ++i) + { + zen::CbObjectWriter Cbo; + Cbo << "index" << i; + + zen::MemoryOutStream MemOut; + zen::BinaryWriter Writer{MemOut}; + Cbo.Save(Writer); + + zen::IoHash Key = zen::IoHash::HashMemory(&i, sizeof i); + + cpr::Response Result = cpr::Put(cpr::Url{"{}/{}/{}"_format(BaseUri, "test", Key)}, + cpr::Body{(const char*)MemOut.Data(), MemOut.Size()}, + cpr::Header{{"Content-Type", "application/x-ue-cb"}}); + + CHECK(Result.status_code == 201); + } + + // Retrieve data + + for (int i = 0; i < kIterationCount; ++i) + { + zen::IoHash Key = zen::IoHash::HashMemory(&i, sizeof i); + + cpr::Response Result = cpr::Get(cpr::Url{"{}/{}/{}"_format(BaseUri, "test", Key)}); + + CHECK(Result.status_code == 200); + } + } + + // Verify that the data persists between process runs (the previous server has exited at this point) + + { + ZenServerInstance Instance1(TestEnv); + Instance1.SetTestDir(TestDir); + Instance1.SpawnServer(PortNumber); + Instance1.WaitUntilReady(); + + // Retrieve data again + + for (int i = 0; i < kIterationCount; ++i) + { + zen::IoHash Key = zen::IoHash::HashMemory(&i, sizeof i); + + cpr::Response Result = cpr::Get(cpr::Url{"{}/{}/{}"_format(BaseUri, "test", Key)}); + + CHECK(Result.status_code == 200); + } + } +} #endif diff --git a/zenserver/cache/cachestore.cpp b/zenserver/cache/cachestore.cpp index 5b0358994..9180835d9 100644 --- a/zenserver/cache/cachestore.cpp +++ b/zenserver/cache/cachestore.cpp @@ -2,16 +2,22 @@ #include "cachestore.h" +#include <zencore/crc32.h> +#include <zencore/except.h> #include <zencore/windows.h> -#include <fmt/core.h> -#include <spdlog/spdlog.h> #include <zencore/filesystem.h> #include <zencore/fmtutils.h> #include <zencore/iobuffer.h> #include <zencore/string.h> #include <zencore/thread.h> +#include <zenstore/basicfile.h> #include <zenstore/cas.h> +#include <zenstore/caslog.h> + +#include <fmt/core.h> +#include <spdlog/spdlog.h> +#include <concepts> #include <filesystem> #include <gsl/gsl-lite.hpp> #include <unordered_map> @@ -23,295 +29,6 @@ using namespace fmt::literals; namespace UE { -static const uint32_t CRCTable[256] = { - 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005, 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, - 0x2B4BCB61, 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD, 0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9, 0x5F15ADAC, 0x5BD4B01B, - 0x569796C2, 0x52568B75, 0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011, 0x791D4014, 0x7DDC5DA3, 0x709F7B7A, 0x745E66CD, 0x9823B6E0, - 0x9CE2AB57, 0x91A18D8E, 0x95609039, 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, 0xBE2B5B58, 0xBAEA46EF, 0xB7A96036, 0xB3687D81, - 0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, 0xA06C0B5D, 0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49, 0xC7361B4C, 0xC3F706FB, 0xCEB42022, - 0xCA753D95, 0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1, 0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, 0x34867077, 0x30476DC0, - 0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5, 0x2AC12072, 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, 0x018AEB13, - 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA, 0x7897AB07, 0x7C56B6B0, 0x71159069, 0x75D48DDE, 0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02, - 0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1, 0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA, 0xACA5C697, 0xA864DB20, 0xA527FDF9, - 0xA1E6E04E, 0xBFA1B04B, 0xBB60ADFC, 0xB6238B25, 0xB2E29692, 0x8AAD2B2F, 0x8E6C3698, 0x832F1041, 0x87EE0DF6, 0x99A95DF3, 0x9D684044, - 0x902B669D, 0x94EA7B2A, 0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E, 0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2, 0xC6BCF05F, - 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, 0xD1799B34, 0xDC3ABDED, 0xD8FBA05A, 0x690CE0EE, 0x6DCDFD59, 0x608EDB80, 0x644FC637, - 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB, 0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F, 0x5C007B8A, 0x58C1663D, 0x558240E4, - 0x51435D53, 0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47, 0x36194D42, 0x32D850F5, 0x3F9B762C, 0x3B5A6B9B, 0x0315D626, 0x07D4CB91, - 0x0A97ED48, 0x0E56F0FF, 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, 0xF12F560E, 0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, - 0xE6EA3D65, 0xEBA91BBC, 0xEF68060B, 0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F, 0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3, - 0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7, 0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, 0x9B3660C6, 0x9FF77D71, 0x92B45BA8, - 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74, 0x857130C3, 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640, 0x4E8EE645, 0x4A4FFBF2, - 0x470CDD2B, 0x43CDC09C, 0x7B827D21, 0x7F436096, 0x7200464F, 0x76C15BF8, 0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24, 0x119B4BE9, - 0x155A565E, 0x18197087, 0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC, 0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, - 0x2497D08D, 0x2056CD3A, 0x2D15EBE3, 0x29D4F654, 0xC5A92679, 0xC1683BCE, 0xCC2B1D17, 0xC8EA00A0, 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, - 0xDBEE767C, 0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18, 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4, 0x89B8FD09, 0x8D79E0BE, - 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, 0x9E7D9662, 0x933EB0BB, 0x97FFAD0C, 0xAFB010B1, 0xAB710D06, 0xA6322BDF, 0xA2F33668, 0xBCB4666D, - 0xB8757BDA, 0xB5365D03, 0xB1F740B4}; - -static const uint32_t CRCTablesSB8[8][256] = { - {0x00000000, 0xb71dc104, 0x6e3b8209, 0xd926430d, 0xdc760413, 0x6b6bc517, 0xb24d861a, 0x0550471e, 0xb8ed0826, 0x0ff0c922, 0xd6d68a2f, - 0x61cb4b2b, 0x649b0c35, 0xd386cd31, 0x0aa08e3c, 0xbdbd4f38, 0x70db114c, 0xc7c6d048, 0x1ee09345, 0xa9fd5241, 0xacad155f, 0x1bb0d45b, - 0xc2969756, 0x758b5652, 0xc836196a, 0x7f2bd86e, 0xa60d9b63, 0x11105a67, 0x14401d79, 0xa35ddc7d, 0x7a7b9f70, 0xcd665e74, 0xe0b62398, - 0x57abe29c, 0x8e8da191, 0x39906095, 0x3cc0278b, 0x8bdde68f, 0x52fba582, 0xe5e66486, 0x585b2bbe, 0xef46eaba, 0x3660a9b7, 0x817d68b3, - 0x842d2fad, 0x3330eea9, 0xea16ada4, 0x5d0b6ca0, 0x906d32d4, 0x2770f3d0, 0xfe56b0dd, 0x494b71d9, 0x4c1b36c7, 0xfb06f7c3, 0x2220b4ce, - 0x953d75ca, 0x28803af2, 0x9f9dfbf6, 0x46bbb8fb, 0xf1a679ff, 0xf4f63ee1, 0x43ebffe5, 0x9acdbce8, 0x2dd07dec, 0x77708634, 0xc06d4730, - 0x194b043d, 0xae56c539, 0xab068227, 0x1c1b4323, 0xc53d002e, 0x7220c12a, 0xcf9d8e12, 0x78804f16, 0xa1a60c1b, 0x16bbcd1f, 0x13eb8a01, - 0xa4f64b05, 0x7dd00808, 0xcacdc90c, 0x07ab9778, 0xb0b6567c, 0x69901571, 0xde8dd475, 0xdbdd936b, 0x6cc0526f, 0xb5e61162, 0x02fbd066, - 0xbf469f5e, 0x085b5e5a, 0xd17d1d57, 0x6660dc53, 0x63309b4d, 0xd42d5a49, 0x0d0b1944, 0xba16d840, 0x97c6a5ac, 0x20db64a8, 0xf9fd27a5, - 0x4ee0e6a1, 0x4bb0a1bf, 0xfcad60bb, 0x258b23b6, 0x9296e2b2, 0x2f2bad8a, 0x98366c8e, 0x41102f83, 0xf60dee87, 0xf35da999, 0x4440689d, - 0x9d662b90, 0x2a7bea94, 0xe71db4e0, 0x500075e4, 0x892636e9, 0x3e3bf7ed, 0x3b6bb0f3, 0x8c7671f7, 0x555032fa, 0xe24df3fe, 0x5ff0bcc6, - 0xe8ed7dc2, 0x31cb3ecf, 0x86d6ffcb, 0x8386b8d5, 0x349b79d1, 0xedbd3adc, 0x5aa0fbd8, 0xeee00c69, 0x59fdcd6d, 0x80db8e60, 0x37c64f64, - 0x3296087a, 0x858bc97e, 0x5cad8a73, 0xebb04b77, 0x560d044f, 0xe110c54b, 0x38368646, 0x8f2b4742, 0x8a7b005c, 0x3d66c158, 0xe4408255, - 0x535d4351, 0x9e3b1d25, 0x2926dc21, 0xf0009f2c, 0x471d5e28, 0x424d1936, 0xf550d832, 0x2c769b3f, 0x9b6b5a3b, 0x26d61503, 0x91cbd407, - 0x48ed970a, 0xfff0560e, 0xfaa01110, 0x4dbdd014, 0x949b9319, 0x2386521d, 0x0e562ff1, 0xb94beef5, 0x606dadf8, 0xd7706cfc, 0xd2202be2, - 0x653deae6, 0xbc1ba9eb, 0x0b0668ef, 0xb6bb27d7, 0x01a6e6d3, 0xd880a5de, 0x6f9d64da, 0x6acd23c4, 0xddd0e2c0, 0x04f6a1cd, 0xb3eb60c9, - 0x7e8d3ebd, 0xc990ffb9, 0x10b6bcb4, 0xa7ab7db0, 0xa2fb3aae, 0x15e6fbaa, 0xccc0b8a7, 0x7bdd79a3, 0xc660369b, 0x717df79f, 0xa85bb492, - 0x1f467596, 0x1a163288, 0xad0bf38c, 0x742db081, 0xc3307185, 0x99908a5d, 0x2e8d4b59, 0xf7ab0854, 0x40b6c950, 0x45e68e4e, 0xf2fb4f4a, - 0x2bdd0c47, 0x9cc0cd43, 0x217d827b, 0x9660437f, 0x4f460072, 0xf85bc176, 0xfd0b8668, 0x4a16476c, 0x93300461, 0x242dc565, 0xe94b9b11, - 0x5e565a15, 0x87701918, 0x306dd81c, 0x353d9f02, 0x82205e06, 0x5b061d0b, 0xec1bdc0f, 0x51a69337, 0xe6bb5233, 0x3f9d113e, 0x8880d03a, - 0x8dd09724, 0x3acd5620, 0xe3eb152d, 0x54f6d429, 0x7926a9c5, 0xce3b68c1, 0x171d2bcc, 0xa000eac8, 0xa550add6, 0x124d6cd2, 0xcb6b2fdf, - 0x7c76eedb, 0xc1cba1e3, 0x76d660e7, 0xaff023ea, 0x18ede2ee, 0x1dbda5f0, 0xaaa064f4, 0x738627f9, 0xc49be6fd, 0x09fdb889, 0xbee0798d, - 0x67c63a80, 0xd0dbfb84, 0xd58bbc9a, 0x62967d9e, 0xbbb03e93, 0x0cadff97, 0xb110b0af, 0x060d71ab, 0xdf2b32a6, 0x6836f3a2, 0x6d66b4bc, - 0xda7b75b8, 0x035d36b5, 0xb440f7b1}, - {0x00000000, 0xdcc119d2, 0x0f9ef2a0, 0xd35feb72, 0xa9212445, 0x75e03d97, 0xa6bfd6e5, 0x7a7ecf37, 0x5243488a, 0x8e825158, 0x5dddba2a, - 0x811ca3f8, 0xfb626ccf, 0x27a3751d, 0xf4fc9e6f, 0x283d87bd, 0x139b5110, 0xcf5a48c2, 0x1c05a3b0, 0xc0c4ba62, 0xbaba7555, 0x667b6c87, - 0xb52487f5, 0x69e59e27, 0x41d8199a, 0x9d190048, 0x4e46eb3a, 0x9287f2e8, 0xe8f93ddf, 0x3438240d, 0xe767cf7f, 0x3ba6d6ad, 0x2636a320, - 0xfaf7baf2, 0x29a85180, 0xf5694852, 0x8f178765, 0x53d69eb7, 0x808975c5, 0x5c486c17, 0x7475ebaa, 0xa8b4f278, 0x7beb190a, 0xa72a00d8, - 0xdd54cfef, 0x0195d63d, 0xd2ca3d4f, 0x0e0b249d, 0x35adf230, 0xe96cebe2, 0x3a330090, 0xe6f21942, 0x9c8cd675, 0x404dcfa7, 0x931224d5, - 0x4fd33d07, 0x67eebaba, 0xbb2fa368, 0x6870481a, 0xb4b151c8, 0xcecf9eff, 0x120e872d, 0xc1516c5f, 0x1d90758d, 0x4c6c4641, 0x90ad5f93, - 0x43f2b4e1, 0x9f33ad33, 0xe54d6204, 0x398c7bd6, 0xead390a4, 0x36128976, 0x1e2f0ecb, 0xc2ee1719, 0x11b1fc6b, 0xcd70e5b9, 0xb70e2a8e, - 0x6bcf335c, 0xb890d82e, 0x6451c1fc, 0x5ff71751, 0x83360e83, 0x5069e5f1, 0x8ca8fc23, 0xf6d63314, 0x2a172ac6, 0xf948c1b4, 0x2589d866, - 0x0db45fdb, 0xd1754609, 0x022aad7b, 0xdeebb4a9, 0xa4957b9e, 0x7854624c, 0xab0b893e, 0x77ca90ec, 0x6a5ae561, 0xb69bfcb3, 0x65c417c1, - 0xb9050e13, 0xc37bc124, 0x1fbad8f6, 0xcce53384, 0x10242a56, 0x3819adeb, 0xe4d8b439, 0x37875f4b, 0xeb464699, 0x913889ae, 0x4df9907c, - 0x9ea67b0e, 0x426762dc, 0x79c1b471, 0xa500ada3, 0x765f46d1, 0xaa9e5f03, 0xd0e09034, 0x0c2189e6, 0xdf7e6294, 0x03bf7b46, 0x2b82fcfb, - 0xf743e529, 0x241c0e5b, 0xf8dd1789, 0x82a3d8be, 0x5e62c16c, 0x8d3d2a1e, 0x51fc33cc, 0x98d88c82, 0x44199550, 0x97467e22, 0x4b8767f0, - 0x31f9a8c7, 0xed38b115, 0x3e675a67, 0xe2a643b5, 0xca9bc408, 0x165addda, 0xc50536a8, 0x19c42f7a, 0x63bae04d, 0xbf7bf99f, 0x6c2412ed, - 0xb0e50b3f, 0x8b43dd92, 0x5782c440, 0x84dd2f32, 0x581c36e0, 0x2262f9d7, 0xfea3e005, 0x2dfc0b77, 0xf13d12a5, 0xd9009518, 0x05c18cca, - 0xd69e67b8, 0x0a5f7e6a, 0x7021b15d, 0xace0a88f, 0x7fbf43fd, 0xa37e5a2f, 0xbeee2fa2, 0x622f3670, 0xb170dd02, 0x6db1c4d0, 0x17cf0be7, - 0xcb0e1235, 0x1851f947, 0xc490e095, 0xecad6728, 0x306c7efa, 0xe3339588, 0x3ff28c5a, 0x458c436d, 0x994d5abf, 0x4a12b1cd, 0x96d3a81f, - 0xad757eb2, 0x71b46760, 0xa2eb8c12, 0x7e2a95c0, 0x04545af7, 0xd8954325, 0x0bcaa857, 0xd70bb185, 0xff363638, 0x23f72fea, 0xf0a8c498, - 0x2c69dd4a, 0x5617127d, 0x8ad60baf, 0x5989e0dd, 0x8548f90f, 0xd4b4cac3, 0x0875d311, 0xdb2a3863, 0x07eb21b1, 0x7d95ee86, 0xa154f754, - 0x720b1c26, 0xaeca05f4, 0x86f78249, 0x5a369b9b, 0x896970e9, 0x55a8693b, 0x2fd6a60c, 0xf317bfde, 0x204854ac, 0xfc894d7e, 0xc72f9bd3, - 0x1bee8201, 0xc8b16973, 0x147070a1, 0x6e0ebf96, 0xb2cfa644, 0x61904d36, 0xbd5154e4, 0x956cd359, 0x49adca8b, 0x9af221f9, 0x4633382b, - 0x3c4df71c, 0xe08ceece, 0x33d305bc, 0xef121c6e, 0xf28269e3, 0x2e437031, 0xfd1c9b43, 0x21dd8291, 0x5ba34da6, 0x87625474, 0x543dbf06, - 0x88fca6d4, 0xa0c12169, 0x7c0038bb, 0xaf5fd3c9, 0x739eca1b, 0x09e0052c, 0xd5211cfe, 0x067ef78c, 0xdabfee5e, 0xe11938f3, 0x3dd82121, - 0xee87ca53, 0x3246d381, 0x48381cb6, 0x94f90564, 0x47a6ee16, 0x9b67f7c4, 0xb35a7079, 0x6f9b69ab, 0xbcc482d9, 0x60059b0b, 0x1a7b543c, - 0xc6ba4dee, 0x15e5a69c, 0xc924bf4e}, - {0x00000000, 0x87acd801, 0x0e59b103, 0x89f56902, 0x1cb26207, 0x9b1eba06, 0x12ebd304, 0x95470b05, 0x3864c50e, 0xbfc81d0f, 0x363d740d, - 0xb191ac0c, 0x24d6a709, 0xa37a7f08, 0x2a8f160a, 0xad23ce0b, 0x70c88a1d, 0xf764521c, 0x7e913b1e, 0xf93de31f, 0x6c7ae81a, 0xebd6301b, - 0x62235919, 0xe58f8118, 0x48ac4f13, 0xcf009712, 0x46f5fe10, 0xc1592611, 0x541e2d14, 0xd3b2f515, 0x5a479c17, 0xddeb4416, 0xe090153b, - 0x673ccd3a, 0xeec9a438, 0x69657c39, 0xfc22773c, 0x7b8eaf3d, 0xf27bc63f, 0x75d71e3e, 0xd8f4d035, 0x5f580834, 0xd6ad6136, 0x5101b937, - 0xc446b232, 0x43ea6a33, 0xca1f0331, 0x4db3db30, 0x90589f26, 0x17f44727, 0x9e012e25, 0x19adf624, 0x8ceafd21, 0x0b462520, 0x82b34c22, - 0x051f9423, 0xa83c5a28, 0x2f908229, 0xa665eb2b, 0x21c9332a, 0xb48e382f, 0x3322e02e, 0xbad7892c, 0x3d7b512d, 0xc0212b76, 0x478df377, - 0xce789a75, 0x49d44274, 0xdc934971, 0x5b3f9170, 0xd2caf872, 0x55662073, 0xf845ee78, 0x7fe93679, 0xf61c5f7b, 0x71b0877a, 0xe4f78c7f, - 0x635b547e, 0xeaae3d7c, 0x6d02e57d, 0xb0e9a16b, 0x3745796a, 0xbeb01068, 0x391cc869, 0xac5bc36c, 0x2bf71b6d, 0xa202726f, 0x25aeaa6e, - 0x888d6465, 0x0f21bc64, 0x86d4d566, 0x01780d67, 0x943f0662, 0x1393de63, 0x9a66b761, 0x1dca6f60, 0x20b13e4d, 0xa71de64c, 0x2ee88f4e, - 0xa944574f, 0x3c035c4a, 0xbbaf844b, 0x325aed49, 0xb5f63548, 0x18d5fb43, 0x9f792342, 0x168c4a40, 0x91209241, 0x04679944, 0x83cb4145, - 0x0a3e2847, 0x8d92f046, 0x5079b450, 0xd7d56c51, 0x5e200553, 0xd98cdd52, 0x4ccbd657, 0xcb670e56, 0x42926754, 0xc53ebf55, 0x681d715e, - 0xefb1a95f, 0x6644c05d, 0xe1e8185c, 0x74af1359, 0xf303cb58, 0x7af6a25a, 0xfd5a7a5b, 0x804356ec, 0x07ef8eed, 0x8e1ae7ef, 0x09b63fee, - 0x9cf134eb, 0x1b5decea, 0x92a885e8, 0x15045de9, 0xb82793e2, 0x3f8b4be3, 0xb67e22e1, 0x31d2fae0, 0xa495f1e5, 0x233929e4, 0xaacc40e6, - 0x2d6098e7, 0xf08bdcf1, 0x772704f0, 0xfed26df2, 0x797eb5f3, 0xec39bef6, 0x6b9566f7, 0xe2600ff5, 0x65ccd7f4, 0xc8ef19ff, 0x4f43c1fe, - 0xc6b6a8fc, 0x411a70fd, 0xd45d7bf8, 0x53f1a3f9, 0xda04cafb, 0x5da812fa, 0x60d343d7, 0xe77f9bd6, 0x6e8af2d4, 0xe9262ad5, 0x7c6121d0, - 0xfbcdf9d1, 0x723890d3, 0xf59448d2, 0x58b786d9, 0xdf1b5ed8, 0x56ee37da, 0xd142efdb, 0x4405e4de, 0xc3a93cdf, 0x4a5c55dd, 0xcdf08ddc, - 0x101bc9ca, 0x97b711cb, 0x1e4278c9, 0x99eea0c8, 0x0ca9abcd, 0x8b0573cc, 0x02f01ace, 0x855cc2cf, 0x287f0cc4, 0xafd3d4c5, 0x2626bdc7, - 0xa18a65c6, 0x34cd6ec3, 0xb361b6c2, 0x3a94dfc0, 0xbd3807c1, 0x40627d9a, 0xc7cea59b, 0x4e3bcc99, 0xc9971498, 0x5cd01f9d, 0xdb7cc79c, - 0x5289ae9e, 0xd525769f, 0x7806b894, 0xffaa6095, 0x765f0997, 0xf1f3d196, 0x64b4da93, 0xe3180292, 0x6aed6b90, 0xed41b391, 0x30aaf787, - 0xb7062f86, 0x3ef34684, 0xb95f9e85, 0x2c189580, 0xabb44d81, 0x22412483, 0xa5edfc82, 0x08ce3289, 0x8f62ea88, 0x0697838a, 0x813b5b8b, - 0x147c508e, 0x93d0888f, 0x1a25e18d, 0x9d89398c, 0xa0f268a1, 0x275eb0a0, 0xaeabd9a2, 0x290701a3, 0xbc400aa6, 0x3becd2a7, 0xb219bba5, - 0x35b563a4, 0x9896adaf, 0x1f3a75ae, 0x96cf1cac, 0x1163c4ad, 0x8424cfa8, 0x038817a9, 0x8a7d7eab, 0x0dd1a6aa, 0xd03ae2bc, 0x57963abd, - 0xde6353bf, 0x59cf8bbe, 0xcc8880bb, 0x4b2458ba, 0xc2d131b8, 0x457de9b9, 0xe85e27b2, 0x6ff2ffb3, 0xe60796b1, 0x61ab4eb0, 0xf4ec45b5, - 0x73409db4, 0xfab5f4b6, 0x7d192cb7}, - {0x00000000, 0xb79a6ddc, 0xd9281abc, 0x6eb27760, 0x054cf57c, 0xb2d698a0, 0xdc64efc0, 0x6bfe821c, 0x0a98eaf9, 0xbd028725, 0xd3b0f045, - 0x642a9d99, 0x0fd41f85, 0xb84e7259, 0xd6fc0539, 0x616668e5, 0xa32d14f7, 0x14b7792b, 0x7a050e4b, 0xcd9f6397, 0xa661e18b, 0x11fb8c57, - 0x7f49fb37, 0xc8d396eb, 0xa9b5fe0e, 0x1e2f93d2, 0x709de4b2, 0xc707896e, 0xacf90b72, 0x1b6366ae, 0x75d111ce, 0xc24b7c12, 0xf146e9ea, - 0x46dc8436, 0x286ef356, 0x9ff49e8a, 0xf40a1c96, 0x4390714a, 0x2d22062a, 0x9ab86bf6, 0xfbde0313, 0x4c446ecf, 0x22f619af, 0x956c7473, - 0xfe92f66f, 0x49089bb3, 0x27baecd3, 0x9020810f, 0x526bfd1d, 0xe5f190c1, 0x8b43e7a1, 0x3cd98a7d, 0x57270861, 0xe0bd65bd, 0x8e0f12dd, - 0x39957f01, 0x58f317e4, 0xef697a38, 0x81db0d58, 0x36416084, 0x5dbfe298, 0xea258f44, 0x8497f824, 0x330d95f8, 0x559013d1, 0xe20a7e0d, - 0x8cb8096d, 0x3b2264b1, 0x50dce6ad, 0xe7468b71, 0x89f4fc11, 0x3e6e91cd, 0x5f08f928, 0xe89294f4, 0x8620e394, 0x31ba8e48, 0x5a440c54, - 0xedde6188, 0x836c16e8, 0x34f67b34, 0xf6bd0726, 0x41276afa, 0x2f951d9a, 0x980f7046, 0xf3f1f25a, 0x446b9f86, 0x2ad9e8e6, 0x9d43853a, - 0xfc25eddf, 0x4bbf8003, 0x250df763, 0x92979abf, 0xf96918a3, 0x4ef3757f, 0x2041021f, 0x97db6fc3, 0xa4d6fa3b, 0x134c97e7, 0x7dfee087, - 0xca648d5b, 0xa19a0f47, 0x1600629b, 0x78b215fb, 0xcf287827, 0xae4e10c2, 0x19d47d1e, 0x77660a7e, 0xc0fc67a2, 0xab02e5be, 0x1c988862, - 0x722aff02, 0xc5b092de, 0x07fbeecc, 0xb0618310, 0xded3f470, 0x694999ac, 0x02b71bb0, 0xb52d766c, 0xdb9f010c, 0x6c056cd0, 0x0d630435, - 0xbaf969e9, 0xd44b1e89, 0x63d17355, 0x082ff149, 0xbfb59c95, 0xd107ebf5, 0x669d8629, 0x1d3de6a6, 0xaaa78b7a, 0xc415fc1a, 0x738f91c6, - 0x187113da, 0xafeb7e06, 0xc1590966, 0x76c364ba, 0x17a50c5f, 0xa03f6183, 0xce8d16e3, 0x79177b3f, 0x12e9f923, 0xa57394ff, 0xcbc1e39f, - 0x7c5b8e43, 0xbe10f251, 0x098a9f8d, 0x6738e8ed, 0xd0a28531, 0xbb5c072d, 0x0cc66af1, 0x62741d91, 0xd5ee704d, 0xb48818a8, 0x03127574, - 0x6da00214, 0xda3a6fc8, 0xb1c4edd4, 0x065e8008, 0x68ecf768, 0xdf769ab4, 0xec7b0f4c, 0x5be16290, 0x355315f0, 0x82c9782c, 0xe937fa30, - 0x5ead97ec, 0x301fe08c, 0x87858d50, 0xe6e3e5b5, 0x51798869, 0x3fcbff09, 0x885192d5, 0xe3af10c9, 0x54357d15, 0x3a870a75, 0x8d1d67a9, - 0x4f561bbb, 0xf8cc7667, 0x967e0107, 0x21e46cdb, 0x4a1aeec7, 0xfd80831b, 0x9332f47b, 0x24a899a7, 0x45cef142, 0xf2549c9e, 0x9ce6ebfe, - 0x2b7c8622, 0x4082043e, 0xf71869e2, 0x99aa1e82, 0x2e30735e, 0x48adf577, 0xff3798ab, 0x9185efcb, 0x261f8217, 0x4de1000b, 0xfa7b6dd7, - 0x94c91ab7, 0x2353776b, 0x42351f8e, 0xf5af7252, 0x9b1d0532, 0x2c8768ee, 0x4779eaf2, 0xf0e3872e, 0x9e51f04e, 0x29cb9d92, 0xeb80e180, - 0x5c1a8c5c, 0x32a8fb3c, 0x853296e0, 0xeecc14fc, 0x59567920, 0x37e40e40, 0x807e639c, 0xe1180b79, 0x568266a5, 0x383011c5, 0x8faa7c19, - 0xe454fe05, 0x53ce93d9, 0x3d7ce4b9, 0x8ae68965, 0xb9eb1c9d, 0x0e717141, 0x60c30621, 0xd7596bfd, 0xbca7e9e1, 0x0b3d843d, 0x658ff35d, - 0xd2159e81, 0xb373f664, 0x04e99bb8, 0x6a5becd8, 0xddc18104, 0xb63f0318, 0x01a56ec4, 0x6f1719a4, 0xd88d7478, 0x1ac6086a, 0xad5c65b6, - 0xc3ee12d6, 0x74747f0a, 0x1f8afd16, 0xa81090ca, 0xc6a2e7aa, 0x71388a76, 0x105ee293, 0xa7c48f4f, 0xc976f82f, 0x7eec95f3, 0x151217ef, - 0xa2887a33, 0xcc3a0d53, 0x7ba0608f}, - {0x00000000, 0x8d670d49, 0x1acf1a92, 0x97a817db, 0x8383f420, 0x0ee4f969, 0x994ceeb2, 0x142be3fb, 0x0607e941, 0x8b60e408, 0x1cc8f3d3, - 0x91affe9a, 0x85841d61, 0x08e31028, 0x9f4b07f3, 0x122c0aba, 0x0c0ed283, 0x8169dfca, 0x16c1c811, 0x9ba6c558, 0x8f8d26a3, 0x02ea2bea, - 0x95423c31, 0x18253178, 0x0a093bc2, 0x876e368b, 0x10c62150, 0x9da12c19, 0x898acfe2, 0x04edc2ab, 0x9345d570, 0x1e22d839, 0xaf016503, - 0x2266684a, 0xb5ce7f91, 0x38a972d8, 0x2c829123, 0xa1e59c6a, 0x364d8bb1, 0xbb2a86f8, 0xa9068c42, 0x2461810b, 0xb3c996d0, 0x3eae9b99, - 0x2a857862, 0xa7e2752b, 0x304a62f0, 0xbd2d6fb9, 0xa30fb780, 0x2e68bac9, 0xb9c0ad12, 0x34a7a05b, 0x208c43a0, 0xadeb4ee9, 0x3a435932, - 0xb724547b, 0xa5085ec1, 0x286f5388, 0xbfc74453, 0x32a0491a, 0x268baae1, 0xabeca7a8, 0x3c44b073, 0xb123bd3a, 0x5e03ca06, 0xd364c74f, - 0x44ccd094, 0xc9abdddd, 0xdd803e26, 0x50e7336f, 0xc74f24b4, 0x4a2829fd, 0x58042347, 0xd5632e0e, 0x42cb39d5, 0xcfac349c, 0xdb87d767, - 0x56e0da2e, 0xc148cdf5, 0x4c2fc0bc, 0x520d1885, 0xdf6a15cc, 0x48c20217, 0xc5a50f5e, 0xd18eeca5, 0x5ce9e1ec, 0xcb41f637, 0x4626fb7e, - 0x540af1c4, 0xd96dfc8d, 0x4ec5eb56, 0xc3a2e61f, 0xd78905e4, 0x5aee08ad, 0xcd461f76, 0x4021123f, 0xf102af05, 0x7c65a24c, 0xebcdb597, - 0x66aab8de, 0x72815b25, 0xffe6566c, 0x684e41b7, 0xe5294cfe, 0xf7054644, 0x7a624b0d, 0xedca5cd6, 0x60ad519f, 0x7486b264, 0xf9e1bf2d, - 0x6e49a8f6, 0xe32ea5bf, 0xfd0c7d86, 0x706b70cf, 0xe7c36714, 0x6aa46a5d, 0x7e8f89a6, 0xf3e884ef, 0x64409334, 0xe9279e7d, 0xfb0b94c7, - 0x766c998e, 0xe1c48e55, 0x6ca3831c, 0x788860e7, 0xf5ef6dae, 0x62477a75, 0xef20773c, 0xbc06940d, 0x31619944, 0xa6c98e9f, 0x2bae83d6, - 0x3f85602d, 0xb2e26d64, 0x254a7abf, 0xa82d77f6, 0xba017d4c, 0x37667005, 0xa0ce67de, 0x2da96a97, 0x3982896c, 0xb4e58425, 0x234d93fe, - 0xae2a9eb7, 0xb008468e, 0x3d6f4bc7, 0xaac75c1c, 0x27a05155, 0x338bb2ae, 0xbeecbfe7, 0x2944a83c, 0xa423a575, 0xb60fafcf, 0x3b68a286, - 0xacc0b55d, 0x21a7b814, 0x358c5bef, 0xb8eb56a6, 0x2f43417d, 0xa2244c34, 0x1307f10e, 0x9e60fc47, 0x09c8eb9c, 0x84afe6d5, 0x9084052e, - 0x1de30867, 0x8a4b1fbc, 0x072c12f5, 0x1500184f, 0x98671506, 0x0fcf02dd, 0x82a80f94, 0x9683ec6f, 0x1be4e126, 0x8c4cf6fd, 0x012bfbb4, - 0x1f09238d, 0x926e2ec4, 0x05c6391f, 0x88a13456, 0x9c8ad7ad, 0x11eddae4, 0x8645cd3f, 0x0b22c076, 0x190ecacc, 0x9469c785, 0x03c1d05e, - 0x8ea6dd17, 0x9a8d3eec, 0x17ea33a5, 0x8042247e, 0x0d252937, 0xe2055e0b, 0x6f625342, 0xf8ca4499, 0x75ad49d0, 0x6186aa2b, 0xece1a762, - 0x7b49b0b9, 0xf62ebdf0, 0xe402b74a, 0x6965ba03, 0xfecdadd8, 0x73aaa091, 0x6781436a, 0xeae64e23, 0x7d4e59f8, 0xf02954b1, 0xee0b8c88, - 0x636c81c1, 0xf4c4961a, 0x79a39b53, 0x6d8878a8, 0xe0ef75e1, 0x7747623a, 0xfa206f73, 0xe80c65c9, 0x656b6880, 0xf2c37f5b, 0x7fa47212, - 0x6b8f91e9, 0xe6e89ca0, 0x71408b7b, 0xfc278632, 0x4d043b08, 0xc0633641, 0x57cb219a, 0xdaac2cd3, 0xce87cf28, 0x43e0c261, 0xd448d5ba, - 0x592fd8f3, 0x4b03d249, 0xc664df00, 0x51ccc8db, 0xdcabc592, 0xc8802669, 0x45e72b20, 0xd24f3cfb, 0x5f2831b2, 0x410ae98b, 0xcc6de4c2, - 0x5bc5f319, 0xd6a2fe50, 0xc2891dab, 0x4fee10e2, 0xd8460739, 0x55210a70, 0x470d00ca, 0xca6a0d83, 0x5dc21a58, 0xd0a51711, 0xc48ef4ea, - 0x49e9f9a3, 0xde41ee78, 0x5326e331}, - {0x00000000, 0x780d281b, 0xf01a5036, 0x8817782d, 0xe035a06c, 0x98388877, 0x102ff05a, 0x6822d841, 0xc06b40d9, 0xb86668c2, 0x307110ef, - 0x487c38f4, 0x205ee0b5, 0x5853c8ae, 0xd044b083, 0xa8499898, 0x37ca41b6, 0x4fc769ad, 0xc7d01180, 0xbfdd399b, 0xd7ffe1da, 0xaff2c9c1, - 0x27e5b1ec, 0x5fe899f7, 0xf7a1016f, 0x8fac2974, 0x07bb5159, 0x7fb67942, 0x1794a103, 0x6f998918, 0xe78ef135, 0x9f83d92e, 0xd9894268, - 0xa1846a73, 0x2993125e, 0x519e3a45, 0x39bce204, 0x41b1ca1f, 0xc9a6b232, 0xb1ab9a29, 0x19e202b1, 0x61ef2aaa, 0xe9f85287, 0x91f57a9c, - 0xf9d7a2dd, 0x81da8ac6, 0x09cdf2eb, 0x71c0daf0, 0xee4303de, 0x964e2bc5, 0x1e5953e8, 0x66547bf3, 0x0e76a3b2, 0x767b8ba9, 0xfe6cf384, - 0x8661db9f, 0x2e284307, 0x56256b1c, 0xde321331, 0xa63f3b2a, 0xce1de36b, 0xb610cb70, 0x3e07b35d, 0x460a9b46, 0xb21385d0, 0xca1eadcb, - 0x4209d5e6, 0x3a04fdfd, 0x522625bc, 0x2a2b0da7, 0xa23c758a, 0xda315d91, 0x7278c509, 0x0a75ed12, 0x8262953f, 0xfa6fbd24, 0x924d6565, - 0xea404d7e, 0x62573553, 0x1a5a1d48, 0x85d9c466, 0xfdd4ec7d, 0x75c39450, 0x0dcebc4b, 0x65ec640a, 0x1de14c11, 0x95f6343c, 0xedfb1c27, - 0x45b284bf, 0x3dbfaca4, 0xb5a8d489, 0xcda5fc92, 0xa58724d3, 0xdd8a0cc8, 0x559d74e5, 0x2d905cfe, 0x6b9ac7b8, 0x1397efa3, 0x9b80978e, - 0xe38dbf95, 0x8baf67d4, 0xf3a24fcf, 0x7bb537e2, 0x03b81ff9, 0xabf18761, 0xd3fcaf7a, 0x5bebd757, 0x23e6ff4c, 0x4bc4270d, 0x33c90f16, - 0xbbde773b, 0xc3d35f20, 0x5c50860e, 0x245dae15, 0xac4ad638, 0xd447fe23, 0xbc652662, 0xc4680e79, 0x4c7f7654, 0x34725e4f, 0x9c3bc6d7, - 0xe436eecc, 0x6c2196e1, 0x142cbefa, 0x7c0e66bb, 0x04034ea0, 0x8c14368d, 0xf4191e96, 0xd33acba5, 0xab37e3be, 0x23209b93, 0x5b2db388, - 0x330f6bc9, 0x4b0243d2, 0xc3153bff, 0xbb1813e4, 0x13518b7c, 0x6b5ca367, 0xe34bdb4a, 0x9b46f351, 0xf3642b10, 0x8b69030b, 0x037e7b26, - 0x7b73533d, 0xe4f08a13, 0x9cfda208, 0x14eada25, 0x6ce7f23e, 0x04c52a7f, 0x7cc80264, 0xf4df7a49, 0x8cd25252, 0x249bcaca, 0x5c96e2d1, - 0xd4819afc, 0xac8cb2e7, 0xc4ae6aa6, 0xbca342bd, 0x34b43a90, 0x4cb9128b, 0x0ab389cd, 0x72bea1d6, 0xfaa9d9fb, 0x82a4f1e0, 0xea8629a1, - 0x928b01ba, 0x1a9c7997, 0x6291518c, 0xcad8c914, 0xb2d5e10f, 0x3ac29922, 0x42cfb139, 0x2aed6978, 0x52e04163, 0xdaf7394e, 0xa2fa1155, - 0x3d79c87b, 0x4574e060, 0xcd63984d, 0xb56eb056, 0xdd4c6817, 0xa541400c, 0x2d563821, 0x555b103a, 0xfd1288a2, 0x851fa0b9, 0x0d08d894, - 0x7505f08f, 0x1d2728ce, 0x652a00d5, 0xed3d78f8, 0x953050e3, 0x61294e75, 0x1924666e, 0x91331e43, 0xe93e3658, 0x811cee19, 0xf911c602, - 0x7106be2f, 0x090b9634, 0xa1420eac, 0xd94f26b7, 0x51585e9a, 0x29557681, 0x4177aec0, 0x397a86db, 0xb16dfef6, 0xc960d6ed, 0x56e30fc3, - 0x2eee27d8, 0xa6f95ff5, 0xdef477ee, 0xb6d6afaf, 0xcedb87b4, 0x46ccff99, 0x3ec1d782, 0x96884f1a, 0xee856701, 0x66921f2c, 0x1e9f3737, - 0x76bdef76, 0x0eb0c76d, 0x86a7bf40, 0xfeaa975b, 0xb8a00c1d, 0xc0ad2406, 0x48ba5c2b, 0x30b77430, 0x5895ac71, 0x2098846a, 0xa88ffc47, - 0xd082d45c, 0x78cb4cc4, 0x00c664df, 0x88d11cf2, 0xf0dc34e9, 0x98feeca8, 0xe0f3c4b3, 0x68e4bc9e, 0x10e99485, 0x8f6a4dab, 0xf76765b0, - 0x7f701d9d, 0x077d3586, 0x6f5fedc7, 0x1752c5dc, 0x9f45bdf1, 0xe74895ea, 0x4f010d72, 0x370c2569, 0xbf1b5d44, 0xc716755f, 0xaf34ad1e, - 0xd7398505, 0x5f2efd28, 0x2723d533}, - {0x00000000, 0x1168574f, 0x22d0ae9e, 0x33b8f9d1, 0xf3bd9c39, 0xe2d5cb76, 0xd16d32a7, 0xc00565e8, 0xe67b3973, 0xf7136e3c, 0xc4ab97ed, - 0xd5c3c0a2, 0x15c6a54a, 0x04aef205, 0x37160bd4, 0x267e5c9b, 0xccf772e6, 0xdd9f25a9, 0xee27dc78, 0xff4f8b37, 0x3f4aeedf, 0x2e22b990, - 0x1d9a4041, 0x0cf2170e, 0x2a8c4b95, 0x3be41cda, 0x085ce50b, 0x1934b244, 0xd931d7ac, 0xc85980e3, 0xfbe17932, 0xea892e7d, 0x2ff224c8, - 0x3e9a7387, 0x0d228a56, 0x1c4add19, 0xdc4fb8f1, 0xcd27efbe, 0xfe9f166f, 0xeff74120, 0xc9891dbb, 0xd8e14af4, 0xeb59b325, 0xfa31e46a, - 0x3a348182, 0x2b5cd6cd, 0x18e42f1c, 0x098c7853, 0xe305562e, 0xf26d0161, 0xc1d5f8b0, 0xd0bdafff, 0x10b8ca17, 0x01d09d58, 0x32686489, - 0x230033c6, 0x057e6f5d, 0x14163812, 0x27aec1c3, 0x36c6968c, 0xf6c3f364, 0xe7aba42b, 0xd4135dfa, 0xc57b0ab5, 0xe9f98894, 0xf891dfdb, - 0xcb29260a, 0xda417145, 0x1a4414ad, 0x0b2c43e2, 0x3894ba33, 0x29fced7c, 0x0f82b1e7, 0x1eeae6a8, 0x2d521f79, 0x3c3a4836, 0xfc3f2dde, - 0xed577a91, 0xdeef8340, 0xcf87d40f, 0x250efa72, 0x3466ad3d, 0x07de54ec, 0x16b603a3, 0xd6b3664b, 0xc7db3104, 0xf463c8d5, 0xe50b9f9a, - 0xc375c301, 0xd21d944e, 0xe1a56d9f, 0xf0cd3ad0, 0x30c85f38, 0x21a00877, 0x1218f1a6, 0x0370a6e9, 0xc60bac5c, 0xd763fb13, 0xe4db02c2, - 0xf5b3558d, 0x35b63065, 0x24de672a, 0x17669efb, 0x060ec9b4, 0x2070952f, 0x3118c260, 0x02a03bb1, 0x13c86cfe, 0xd3cd0916, 0xc2a55e59, - 0xf11da788, 0xe075f0c7, 0x0afcdeba, 0x1b9489f5, 0x282c7024, 0x3944276b, 0xf9414283, 0xe82915cc, 0xdb91ec1d, 0xcaf9bb52, 0xec87e7c9, - 0xfdefb086, 0xce574957, 0xdf3f1e18, 0x1f3a7bf0, 0x0e522cbf, 0x3dead56e, 0x2c828221, 0x65eed02d, 0x74868762, 0x473e7eb3, 0x565629fc, - 0x96534c14, 0x873b1b5b, 0xb483e28a, 0xa5ebb5c5, 0x8395e95e, 0x92fdbe11, 0xa14547c0, 0xb02d108f, 0x70287567, 0x61402228, 0x52f8dbf9, - 0x43908cb6, 0xa919a2cb, 0xb871f584, 0x8bc90c55, 0x9aa15b1a, 0x5aa43ef2, 0x4bcc69bd, 0x7874906c, 0x691cc723, 0x4f629bb8, 0x5e0accf7, - 0x6db23526, 0x7cda6269, 0xbcdf0781, 0xadb750ce, 0x9e0fa91f, 0x8f67fe50, 0x4a1cf4e5, 0x5b74a3aa, 0x68cc5a7b, 0x79a40d34, 0xb9a168dc, - 0xa8c93f93, 0x9b71c642, 0x8a19910d, 0xac67cd96, 0xbd0f9ad9, 0x8eb76308, 0x9fdf3447, 0x5fda51af, 0x4eb206e0, 0x7d0aff31, 0x6c62a87e, - 0x86eb8603, 0x9783d14c, 0xa43b289d, 0xb5537fd2, 0x75561a3a, 0x643e4d75, 0x5786b4a4, 0x46eee3eb, 0x6090bf70, 0x71f8e83f, 0x424011ee, - 0x532846a1, 0x932d2349, 0x82457406, 0xb1fd8dd7, 0xa095da98, 0x8c1758b9, 0x9d7f0ff6, 0xaec7f627, 0xbfafa168, 0x7faac480, 0x6ec293cf, - 0x5d7a6a1e, 0x4c123d51, 0x6a6c61ca, 0x7b043685, 0x48bccf54, 0x59d4981b, 0x99d1fdf3, 0x88b9aabc, 0xbb01536d, 0xaa690422, 0x40e02a5f, - 0x51887d10, 0x623084c1, 0x7358d38e, 0xb35db666, 0xa235e129, 0x918d18f8, 0x80e54fb7, 0xa69b132c, 0xb7f34463, 0x844bbdb2, 0x9523eafd, - 0x55268f15, 0x444ed85a, 0x77f6218b, 0x669e76c4, 0xa3e57c71, 0xb28d2b3e, 0x8135d2ef, 0x905d85a0, 0x5058e048, 0x4130b707, 0x72884ed6, - 0x63e01999, 0x459e4502, 0x54f6124d, 0x674eeb9c, 0x7626bcd3, 0xb623d93b, 0xa74b8e74, 0x94f377a5, 0x859b20ea, 0x6f120e97, 0x7e7a59d8, - 0x4dc2a009, 0x5caaf746, 0x9caf92ae, 0x8dc7c5e1, 0xbe7f3c30, 0xaf176b7f, 0x896937e4, 0x980160ab, 0xabb9997a, 0xbad1ce35, 0x7ad4abdd, - 0x6bbcfc92, 0x58040543, 0x496c520c}, - {0x00000000, 0xcadca15b, 0x94b943b7, 0x5e65e2ec, 0x9f6e466a, 0x55b2e731, 0x0bd705dd, 0xc10ba486, 0x3edd8cd4, 0xf4012d8f, 0xaa64cf63, - 0x60b86e38, 0xa1b3cabe, 0x6b6f6be5, 0x350a8909, 0xffd62852, 0xcba7d8ad, 0x017b79f6, 0x5f1e9b1a, 0x95c23a41, 0x54c99ec7, 0x9e153f9c, - 0xc070dd70, 0x0aac7c2b, 0xf57a5479, 0x3fa6f522, 0x61c317ce, 0xab1fb695, 0x6a141213, 0xa0c8b348, 0xfead51a4, 0x3471f0ff, 0x2152705f, - 0xeb8ed104, 0xb5eb33e8, 0x7f3792b3, 0xbe3c3635, 0x74e0976e, 0x2a857582, 0xe059d4d9, 0x1f8ffc8b, 0xd5535dd0, 0x8b36bf3c, 0x41ea1e67, - 0x80e1bae1, 0x4a3d1bba, 0x1458f956, 0xde84580d, 0xeaf5a8f2, 0x202909a9, 0x7e4ceb45, 0xb4904a1e, 0x759bee98, 0xbf474fc3, 0xe122ad2f, - 0x2bfe0c74, 0xd4282426, 0x1ef4857d, 0x40916791, 0x8a4dc6ca, 0x4b46624c, 0x819ac317, 0xdfff21fb, 0x152380a0, 0x42a4e0be, 0x887841e5, - 0xd61da309, 0x1cc10252, 0xddcaa6d4, 0x1716078f, 0x4973e563, 0x83af4438, 0x7c796c6a, 0xb6a5cd31, 0xe8c02fdd, 0x221c8e86, 0xe3172a00, - 0x29cb8b5b, 0x77ae69b7, 0xbd72c8ec, 0x89033813, 0x43df9948, 0x1dba7ba4, 0xd766daff, 0x166d7e79, 0xdcb1df22, 0x82d43dce, 0x48089c95, - 0xb7deb4c7, 0x7d02159c, 0x2367f770, 0xe9bb562b, 0x28b0f2ad, 0xe26c53f6, 0xbc09b11a, 0x76d51041, 0x63f690e1, 0xa92a31ba, 0xf74fd356, - 0x3d93720d, 0xfc98d68b, 0x364477d0, 0x6821953c, 0xa2fd3467, 0x5d2b1c35, 0x97f7bd6e, 0xc9925f82, 0x034efed9, 0xc2455a5f, 0x0899fb04, - 0x56fc19e8, 0x9c20b8b3, 0xa851484c, 0x628de917, 0x3ce80bfb, 0xf634aaa0, 0x373f0e26, 0xfde3af7d, 0xa3864d91, 0x695aecca, 0x968cc498, - 0x5c5065c3, 0x0235872f, 0xc8e92674, 0x09e282f2, 0xc33e23a9, 0x9d5bc145, 0x5787601e, 0x33550079, 0xf989a122, 0xa7ec43ce, 0x6d30e295, - 0xac3b4613, 0x66e7e748, 0x388205a4, 0xf25ea4ff, 0x0d888cad, 0xc7542df6, 0x9931cf1a, 0x53ed6e41, 0x92e6cac7, 0x583a6b9c, 0x065f8970, - 0xcc83282b, 0xf8f2d8d4, 0x322e798f, 0x6c4b9b63, 0xa6973a38, 0x679c9ebe, 0xad403fe5, 0xf325dd09, 0x39f97c52, 0xc62f5400, 0x0cf3f55b, - 0x529617b7, 0x984ab6ec, 0x5941126a, 0x939db331, 0xcdf851dd, 0x0724f086, 0x12077026, 0xd8dbd17d, 0x86be3391, 0x4c6292ca, 0x8d69364c, - 0x47b59717, 0x19d075fb, 0xd30cd4a0, 0x2cdafcf2, 0xe6065da9, 0xb863bf45, 0x72bf1e1e, 0xb3b4ba98, 0x79681bc3, 0x270df92f, 0xedd15874, - 0xd9a0a88b, 0x137c09d0, 0x4d19eb3c, 0x87c54a67, 0x46ceeee1, 0x8c124fba, 0xd277ad56, 0x18ab0c0d, 0xe77d245f, 0x2da18504, 0x73c467e8, - 0xb918c6b3, 0x78136235, 0xb2cfc36e, 0xecaa2182, 0x267680d9, 0x71f1e0c7, 0xbb2d419c, 0xe548a370, 0x2f94022b, 0xee9fa6ad, 0x244307f6, - 0x7a26e51a, 0xb0fa4441, 0x4f2c6c13, 0x85f0cd48, 0xdb952fa4, 0x11498eff, 0xd0422a79, 0x1a9e8b22, 0x44fb69ce, 0x8e27c895, 0xba56386a, - 0x708a9931, 0x2eef7bdd, 0xe433da86, 0x25387e00, 0xefe4df5b, 0xb1813db7, 0x7b5d9cec, 0x848bb4be, 0x4e5715e5, 0x1032f709, 0xdaee5652, - 0x1be5f2d4, 0xd139538f, 0x8f5cb163, 0x45801038, 0x50a39098, 0x9a7f31c3, 0xc41ad32f, 0x0ec67274, 0xcfcdd6f2, 0x051177a9, 0x5b749545, - 0x91a8341e, 0x6e7e1c4c, 0xa4a2bd17, 0xfac75ffb, 0x301bfea0, 0xf1105a26, 0x3bccfb7d, 0x65a91991, 0xaf75b8ca, 0x9b044835, 0x51d8e96e, - 0x0fbd0b82, 0xc561aad9, 0x046a0e5f, 0xceb6af04, 0x90d34de8, 0x5a0fecb3, 0xa5d9c4e1, 0x6f0565ba, 0x31608756, 0xfbbc260d, 0x3ab7828b, - 0xf06b23d0, 0xae0ec13c, 0x64d26067}}; - -static inline uint32_t -StrCrc(const char* Data) -{ - uint32_t CRC = 0xFFFFFFFF; - while (*Data) - { - char16_t C = *Data++; - int32_t CL = (C & 255); - CRC = (CRC << 8) ^ CRCTable[(CRC >> 24) ^ CL]; - int32_t CH = (C >> 8) & 255; - CRC = (CRC << 8) ^ CRCTable[(CRC >> 24) ^ CH]; - } - return ~CRC; -} - -#define BYTESWAP_ORDER32(x) (((x) >> 24) + (((x) >> 8) & 0xff00) + (((x) << 8) & 0xff0000) + ((x) << 24)) -#define UE_PTRDIFF_TO_INT32(argument) static_cast<int32_t>(argument) - -template<typename T> -constexpr T -Align(T Val, uint64_t Alignment) -{ - return (T)(((uint64_t)Val + Alignment - 1) & ~(Alignment - 1)); -} - -static uint32_t -MemCRC(const void* InData, size_t Length, uint32_t CRC = 0) -{ - // Based on the Slicing-by-8 implementation found here: - // http://slicing-by-8.sourceforge.net/ - - CRC = ~BYTESWAP_ORDER32(CRC); - - const uint8_t* __restrict Data = (uint8_t*)InData; - - // First we need to align to 32-bits - int32_t InitBytes = UE_PTRDIFF_TO_INT32(Align(Data, 4) - Data); - - if (Length > InitBytes) - { - Length -= InitBytes; - - for (; InitBytes; --InitBytes) - { - CRC = (CRC >> 8) ^ CRCTablesSB8[0][(CRC & 0xFF) ^ *Data++]; - } - - auto Data4 = (const uint32_t*)Data; - for (size_t Repeat = Length / 8; Repeat; --Repeat) - { - uint32_t V1 = *Data4++ ^ CRC; - uint32_t V2 = *Data4++; - CRC = CRCTablesSB8[7][V1 & 0xFF] ^ CRCTablesSB8[6][(V1 >> 8) & 0xFF] ^ CRCTablesSB8[5][(V1 >> 16) & 0xFF] ^ - CRCTablesSB8[4][V1 >> 24] ^ CRCTablesSB8[3][V2 & 0xFF] ^ CRCTablesSB8[2][(V2 >> 8) & 0xFF] ^ - CRCTablesSB8[1][(V2 >> 16) & 0xFF] ^ CRCTablesSB8[0][V2 >> 24]; - } - Data = (const uint8_t*)Data4; - - Length %= 8; - } - - for (; Length; --Length) - { - CRC = (CRC >> 8) ^ CRCTablesSB8[0][(CRC & 0xFF) ^ *Data++]; - } - - return BYTESWAP_ORDER32(~CRC); -} - struct CorruptionTrailer { enum @@ -327,12 +44,12 @@ struct CorruptionTrailer void Initialize(const void* Data, size_t Size) { - CRCofPayload = MemCRC(Data, Size); + CRCofPayload = zen::MemCrc32_Deprecated(Data, Size); SizeOfPayload = (uint32_t)Size; } }; -std::wstring +std::filesystem::path GenerateDdcPath(std::string_view Key, std::filesystem::path& rootDir) { std::filesystem::path FilePath = rootDir; @@ -341,7 +58,7 @@ GenerateDdcPath(std::string_view Key, std::filesystem::path& rootDir) for (auto& c : k8) c = (char)toupper(c); - const uint32_t Hash = StrCrc(k8.c_str()); + const uint32_t Hash = zen::StrCrc_Deprecated(k8.c_str()); std::wstring DirName; @@ -408,22 +125,22 @@ FileCacheStore::Get(std::string_view Key, CacheValue& OutValue) { CAtlFile File; - std::wstring nativePath; + std::filesystem::path NativePath; HRESULT hRes = E_FAIL; if (m_ReadRootDir.empty() == false) { - nativePath = UE::GenerateDdcPath(Key, m_ReadRootDir); + NativePath = UE::GenerateDdcPath(Key, m_ReadRootDir); - hRes = File.Create(nativePath.c_str(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING); + hRes = File.Create(NativePath.c_str(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING); } if (FAILED(hRes)) { - nativePath = UE::GenerateDdcPath(Key, m_RootDir); + NativePath = UE::GenerateDdcPath(Key, m_RootDir); - hRes = File.Create(nativePath.c_str(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING); + hRes = File.Create(NativePath.c_str(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING); } if (FAILED(hRes)) @@ -443,28 +160,7 @@ FileCacheStore::Get(std::string_view Key, CacheValue& OutValue) FileSize -= 16; // CorruptionWrapper trailer - IoBuffer Value(FileSize); - - uint8_t* ReadPointer = (uint8_t*)Value.Data(); - - while (FileSize) - { - const int MaxChunkSize = 16 * 1024 * 1024; - const int ChunkSize = gsl::narrow_cast<int>((FileSize > MaxChunkSize) ? MaxChunkSize : FileSize); - - DWORD BytesRead = 0; - hRes = File.Read(ReadPointer, ChunkSize, BytesRead); - - if (FAILED(hRes)) - { - return false; - } - - ReadPointer += BytesRead; - FileSize -= BytesRead; - } - - OutValue.Value = std::move(Value); + OutValue.Value = IoBuffer(IoBuffer::File, File.Detach(), 0, FileSize); spdlog::debug("GET HIT {}", Key); @@ -480,7 +176,7 @@ FileCacheStore::Put(std::string_view Key, const CacheValue& Value) UE::CorruptionTrailer Trailer; Trailer.Initialize(Data, Size); - std::wstring nativePath = UE::GenerateDdcPath(Key, m_RootDir); + std::filesystem::path NativePath = UE::GenerateDdcPath(Key, m_RootDir); CAtlTemporaryFile File; @@ -490,7 +186,7 @@ FileCacheStore::Put(std::string_view Key, const CacheValue& Value) if (SUCCEEDED(hRes)) { - const uint8_t* WritePointer = (const uint8_t*)Data; + const uint8_t* WritePointer = reinterpret_cast<const uint8_t*>(Data); while (Size) { @@ -505,7 +201,7 @@ FileCacheStore::Put(std::string_view Key, const CacheValue& Value) } File.Write(&Trailer, sizeof Trailer); - hRes = File.Close(nativePath.c_str()); // This renames the file to its final name + hRes = File.Close(NativePath.c_str()); // This renames the file to its final name if (FAILED(hRes)) { @@ -554,686 +250,3 @@ MemoryCacheStore::Put(std::string_view Key, const CacheValue& Value) RwLock::ExclusiveLockScope _(m_Lock); m_CacheMap[std::string(Key)] = Value.Value; } - -////////////////////////////////////////////////////////////////////////// - -ZenCacheStore::ZenCacheStore(zen::CasStore& Cas, const std::filesystem::path& RootDir) : m_DiskLayer{Cas, RootDir} -{ - zen::CreateDirectories(RootDir); -} - -ZenCacheStore::~ZenCacheStore() -{ -} - -bool -ZenCacheStore::Get(std::string_view InBucket, const zen::IoHash& HashKey, CacheValue& OutValue) -{ - bool Ok = m_MemLayer.Get(InBucket, HashKey, OutValue); - - if (!Ok) - { - Ok = m_DiskLayer.Get(InBucket, HashKey, OutValue); - -#if 0 // This would keep file handles open - if (ok) - { - m_memLayer.Put(InBucket, HashKey, OutValue); - } -#endif - } - - return Ok; -} - -void -ZenCacheStore::Put(std::string_view InBucket, const zen::IoHash& HashKey, const CacheValue& Value) -{ - m_MemLayer.Put(InBucket, HashKey, Value); - m_DiskLayer.Put(InBucket, HashKey, Value); -} - -////////////////////////////////////////////////////////////////////////// - -ZenCacheMemoryLayer::ZenCacheMemoryLayer() -{ -} - -ZenCacheMemoryLayer::~ZenCacheMemoryLayer() -{ -} - -bool -ZenCacheMemoryLayer::Get(std::string_view InBucket, const zen::IoHash& HashKey, CacheValue& OutValue) -{ - CacheBucket* Bucket = nullptr; - - { - RwLock::SharedLockScope _(m_Lock); - - auto it = m_Buckets.find(std::string(InBucket)); - - if (it != m_Buckets.end()) - { - Bucket = &it->second; - } - } - - if (Bucket == nullptr) - return false; - - ZEN_ASSERT(Bucket != nullptr); - - return Bucket->Get(HashKey, OutValue); -} - -void -ZenCacheMemoryLayer::Put(std::string_view InBucket, const zen::IoHash& HashKey, const CacheValue& Value) -{ - CacheBucket* Bucket = nullptr; - - { - RwLock::SharedLockScope _(m_Lock); - - auto it = m_Buckets.find(std::string(InBucket)); - - if (it != m_Buckets.end()) - { - Bucket = &it->second; - } - } - - if (Bucket == nullptr) - { - // New bucket - - RwLock::ExclusiveLockScope _(m_Lock); - - Bucket = &m_Buckets[std::string(InBucket)]; - } - - ZEN_ASSERT(Bucket != nullptr); - - Bucket->Put(HashKey, Value); -} - -bool -ZenCacheMemoryLayer::CacheBucket::Get(const zen::IoHash& HashKey, CacheValue& OutValue) -{ - RwLock::SharedLockScope _(m_bucketLock); - - auto bucketIt = m_cacheMap.find(HashKey); - - if (bucketIt == m_cacheMap.end()) - { - return false; - } - - OutValue.Value = bucketIt->second; - - return true; -} - -void -ZenCacheMemoryLayer::CacheBucket::Put(const zen::IoHash& HashKey, const CacheValue& Value) -{ - RwLock::ExclusiveLockScope _(m_bucketLock); - - m_cacheMap[HashKey] = Value.Value; -} - -////////////////////////////////////////////////////////////////////////// - -class ZenFile -{ -public: - void Open(std::filesystem::path FileName, bool IsCreate); - void Read(void* Data, uint64_t Size, uint64_t Offset); - void Write(const void* Data, uint64_t Size, uint64_t Offset); - void Flush(); - void* Handle() { return m_File; } - -private: - CAtlFile m_File; -}; - -void -ZenFile::Open(std::filesystem::path FileName, bool isCreate) -{ - const DWORD dwCreationDisposition = isCreate ? CREATE_ALWAYS : OPEN_EXISTING; - - HRESULT hRes = m_File.Create(FileName.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, dwCreationDisposition); - - if (FAILED(hRes)) - { - throw std::system_error(GetLastError(), std::system_category(), "Failed to open bucket sobs file"); - } -} - -void -ZenFile::Read(void* Data, uint64_t Size, uint64_t Offset) -{ - OVERLAPPED Ovl{}; - - Ovl.Offset = DWORD(Offset & 0xffff'ffffu); - Ovl.OffsetHigh = DWORD(Offset >> 32); - - HRESULT hRes = m_File.Read(Data, gsl::narrow<DWORD>(Size), &Ovl); - - if (FAILED(hRes)) - { - throw std::system_error(GetLastError(), - std::system_category(), - "Failed to read from file '{}'"_format(zen::PathFromHandle(m_File))); - } -} - -void -ZenFile::Write(const void* Data, uint64_t Size, uint64_t Offset) -{ - OVERLAPPED Ovl{}; - - Ovl.Offset = DWORD(Offset & 0xffff'ffffu); - Ovl.OffsetHigh = DWORD(Offset >> 32); - - HRESULT hRes = m_File.Write(Data, gsl::narrow<DWORD>(Size), &Ovl); - - if (FAILED(hRes)) - { - throw std::system_error(GetLastError(), std::system_category(), "Failed to write to file '{}'"_format(zen::PathFromHandle(m_File))); - } -} - -void -ZenFile::Flush() -{ - m_File.Flush(); -} - -////////////////////////////////////////////////////////////////////////// - -class ZenLogFile -{ -public: - ZenLogFile(); - ~ZenLogFile(); - - void Open(std::filesystem::path FileName, size_t RecordSize, bool isCreate); - void Append(const void* DataPointer, uint64_t DataSize); - void Replay(std::function<void(const void*)>&& Handler); - void Flush(); - -private: - CAtlFile m_File; - size_t m_RecordSize = 1; - uint64_t m_AppendOffset = 0; -}; - -ZenLogFile::ZenLogFile() -{ -} - -ZenLogFile::~ZenLogFile() -{ -} - -void -ZenLogFile::Open(std::filesystem::path FileName, size_t RecordSize, bool IsCreate) -{ - m_RecordSize = RecordSize; - - const DWORD dwCreationDisposition = IsCreate ? CREATE_ALWAYS : OPEN_EXISTING; - - HRESULT hRes = m_File.Create(FileName.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, dwCreationDisposition); - - if (FAILED(hRes)) - { - throw std::system_error(GetLastError(), std::system_category(), "Failed to open log file" /* TODO: add path */); - } - - // TODO: write/validate header and log contents and prepare for appending/replay -} - -void -ZenLogFile::Replay(std::function<void(const void*)>&& Handler) -{ - std::vector<uint8_t> ReadBuffer; - - ULONGLONG LogFileSize; - m_File.GetSize(LogFileSize); - - // Ensure we end up on a clean boundary - LogFileSize -= LogFileSize % m_RecordSize; - - ReadBuffer.resize(LogFileSize); - - HRESULT hRes = m_File.Read(ReadBuffer.data(), gsl::narrow<DWORD>(ReadBuffer.size())); - - if (FAILED(hRes)) - { - throw std::system_error(GetLastError(), std::system_category(), "Failed to read log file" /* TODO: add context */); - } - - const size_t EntryCount = LogFileSize / m_RecordSize; - - for (int i = 0; i < EntryCount; ++i) - { - Handler(ReadBuffer.data() + (i * m_RecordSize)); - } -} - -void -ZenLogFile::Append(const void* DataPointer, uint64_t DataSize) -{ - HRESULT hRes = m_File.Write(DataPointer, gsl::narrow<DWORD>(DataSize)); - - if (FAILED(hRes)) - { - throw std::system_error(GetLastError(), std::system_category(), "Failed to write to log file" /* TODO: add context */); - } -} - -void -ZenLogFile::Flush() -{ -} - -template<typename T> -class TZenLogFile : public ZenLogFile -{ -public: - void Replay(std::function<void(const T&)>&& Handler) - { - ZenLogFile::Replay([&](const void* VoidPtr) { - const T& Record = *reinterpret_cast<const T*>(VoidPtr); - - Handler(Record); - }); - } - - void Append(const T& Record) { ZenLogFile::Append(&Record, sizeof Record); } -}; - -////////////////////////////////////////////////////////////////////////// - -#pragma pack(push) -#pragma pack(1) - -struct DiskLocation -{ - uint64_t Offset; - uint32_t Size; -}; - -struct DiskIndexEntry -{ - zen::IoHash Key; - DiskLocation Location; -}; - -#pragma pack(pop) - -static_assert(sizeof(DiskIndexEntry) == 32); - -struct ZenCacheDiskLayer::CacheBucket -{ - CacheBucket(CasStore& Cas); - ~CacheBucket(); - - void OpenOrCreate(std::filesystem::path BucketDir); - - bool Get(const zen::IoHash& HashKey, CacheValue& OutValue); - void Put(const zen::IoHash& HashKey, const CacheValue& Value); - void Flush(); - - void PutLargeObject(const zen::IoHash& HashKey, const CacheValue& Value); - - inline bool IsOk() const { return m_Ok; } - - CasStore& m_CasStore; - std::filesystem::path m_BucketDir; - Oid m_BucketId; - bool m_Ok = false; - uint64_t m_LargeObjectThreshold = 1024; - - ZenFile m_SobsFile; - TZenLogFile<DiskIndexEntry> m_SlogFile; - ZenFile m_SidxFile; - - void BuildPath(zen::WideStringBuilderBase& Path, const zen::IoHash& HashKey); - - RwLock m_IndexLock; - std::unordered_map<zen::IoHash, DiskLocation, zen::IoHash::Hasher> m_Index; - uint64_t m_WriteCursor = 0; -}; - -ZenCacheDiskLayer::CacheBucket::CacheBucket(CasStore& Cas) : m_CasStore(Cas) -{ -} - -ZenCacheDiskLayer::CacheBucket::~CacheBucket() -{ -} - -void -ZenCacheDiskLayer::CacheBucket::OpenOrCreate(std::filesystem::path BucketDir) -{ - std::filesystem::create_directories(BucketDir); - - m_BucketDir = BucketDir; - - std::wstring ManifestPath{(m_BucketDir / "zen_manifest").c_str()}; - std::wstring SobsPath{(m_BucketDir / "zen.sobs").c_str()}; - std::wstring SlogPath{(m_BucketDir / "zen.slog").c_str()}; - std::wstring SidxPath{(m_BucketDir / "zen.sidx").c_str()}; - - CAtlFile ManifestFile; - - // Try opening existing file first - - bool IsNew = false; - - HRESULT hRes = ManifestFile.Create(ManifestPath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, OPEN_EXISTING); - - if (SUCCEEDED(hRes)) - { - ULONGLONG FileSize; - ManifestFile.GetSize(FileSize); - - if (FileSize == sizeof(Oid)) - { - hRes = ManifestFile.Read(&m_BucketId, sizeof(Oid)); - - if (SUCCEEDED(hRes)) - { - m_Ok = true; - } - } - - if (!m_Ok) - ManifestFile.Close(); - } - - if (!m_Ok) - { - // This is a new bucket - - hRes = ManifestFile.Create(ManifestPath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS); - - if (FAILED(hRes)) - { - throw std::system_error(GetLastError(), std::system_category(), "Failed to create bucket manifest"); - } - - m_BucketId.Generate(); - - hRes = ManifestFile.Write(&m_BucketId, sizeof(Oid)); - - IsNew = true; - } - - // Initialize small object storage related files - - m_SobsFile.Open(SobsPath, IsNew); - m_SidxFile.Open(SidxPath, IsNew); - - // Open and replay log - - m_SlogFile.Open(SlogPath, sizeof(DiskIndexEntry), IsNew); - - uint64_t maxFileOffset = 0; - - { - // This is not technically necessary but may help future static analysis - zen::RwLock::ExclusiveLockScope _(m_IndexLock); - - m_SlogFile.Replay([&](const DiskIndexEntry& Record) { - m_Index[Record.Key] = Record.Location; - - maxFileOffset = std::max<uint64_t>(maxFileOffset, Record.Location.Offset + Record.Location.Size); - }); - } - - m_WriteCursor = (maxFileOffset + 15) & ~15; - - m_Ok = true; -} - -void -ZenCacheDiskLayer::CacheBucket::BuildPath(zen::WideStringBuilderBase& Path, const zen::IoHash& HashKey) -{ - char hex[sizeof(HashKey.Hash) * 2]; - ToHexBytes(HashKey.Hash, sizeof HashKey.Hash, hex); - - Path.Append(m_BucketDir.c_str()); - Path.Append(L"/"); - Path.AppendAsciiRange(hex, hex + sizeof(hex)); -} - -bool -ZenCacheDiskLayer::CacheBucket::Get(const zen::IoHash& HashKey, CacheValue& OutValue) -{ - if (!m_Ok) - return false; - - { - zen::RwLock::SharedLockScope _(m_IndexLock); - - auto it = m_Index.find(HashKey); - - if (it != m_Index.end()) - { - OutValue.Value = IoBufferBuilder::MakeFromFileHandle(m_SobsFile.Handle(), it->second.Offset, it->second.Size); - - return true; - } - } - - WideStringBuilder<128> dataFilePath; - BuildPath(dataFilePath, HashKey); - - zen::IoBuffer data = IoBufferBuilder::MakeFromFile(dataFilePath.c_str()); - - if (!data) - { - return false; - } - - OutValue.Value = data; - - // TODO: should populate index? - - return true; -} - -void -ZenCacheDiskLayer::CacheBucket::Put(const zen::IoHash& HashKey, const CacheValue& Value) -{ - if (!m_Ok) - return; - - if (Value.Value.Size() >= m_LargeObjectThreshold) - PutLargeObject(HashKey, Value); - - // Small object put - - zen::RwLock::ExclusiveLockScope _(m_IndexLock); - - auto it = m_Index.find(HashKey); - - DiskLocation loc{.Offset = m_WriteCursor, .Size = gsl::narrow<uint32_t>(Value.Value.Size())}; - - m_WriteCursor = (m_WriteCursor + loc.Size + 15) & ~15; - - if (it == m_Index.end()) - { - m_Index.insert({HashKey, loc}); - } - else - { - // TODO: should check if write is idempotent and bail out if it is? - - it->second = loc; - } - - DiskIndexEntry indexEntry{.Key = HashKey, .Location = loc}; - - m_SlogFile.Append(indexEntry); - - m_SobsFile.Write(Value.Value.Data(), loc.Size, loc.Offset); - - return; -} - -void -ZenCacheDiskLayer::CacheBucket::Flush() -{ - m_SobsFile.Flush(); - m_SidxFile.Flush(); - m_SlogFile.Flush(); -} - -void -ZenCacheDiskLayer::CacheBucket::PutLargeObject(const zen::IoHash& HashKey, const CacheValue& Value) -{ - zen::WideStringBuilder<128> dataFilePath; - - BuildPath(dataFilePath, HashKey); - - CAtlTemporaryFile dataFile; - - HRESULT hRes = dataFile.Create(m_BucketDir.c_str()); - - hRes = dataFile.Write(Value.Value.Data(), gsl::narrow<DWORD>(Value.Value.Size())); - - if (FAILED(hRes)) - { - // TODO: report error! and delete temp file - - return; - } - - hRes = dataFile.Close(dataFilePath.c_str()); -} - -////////////////////////////////////////////////////////////////////////// - -ZenCacheDiskLayer::ZenCacheDiskLayer(CasStore& Cas, const std::filesystem::path& RootDir) : m_RootDir(RootDir), m_CasStore(Cas) -{ -} - -ZenCacheDiskLayer::~ZenCacheDiskLayer() = default; - -bool -ZenCacheDiskLayer::Get(std::string_view InBucket, const zen::IoHash& HashKey, CacheValue& OutValue) -{ - CacheBucket* Bucket = nullptr; - - { - zen::RwLock::SharedLockScope _(m_Lock); - - auto it = m_Buckets.find(std::string(InBucket)); - - if (it != m_Buckets.end()) - { - Bucket = &it->second; - } - } - - if (Bucket == nullptr) - { - // Bucket needs to be opened/created - - zen::RwLock::ExclusiveLockScope _(m_Lock); - - auto It = m_Buckets.try_emplace(std::string(InBucket), m_CasStore); - Bucket = &It.first->second; - - std::filesystem::path BucketPath = m_RootDir; - BucketPath /= std::string(InBucket); - - Bucket->OpenOrCreate(BucketPath.c_str()); - } - - ZEN_ASSERT(Bucket != nullptr); - - return Bucket->Get(HashKey, OutValue); -} - -void -ZenCacheDiskLayer::Put(std::string_view InBucket, const zen::IoHash& HashKey, const CacheValue& Value) -{ - CacheBucket* Bucket = nullptr; - - { - zen::RwLock::SharedLockScope _(m_Lock); - - auto it = m_Buckets.find(std::string(InBucket)); - - if (it != m_Buckets.end()) - { - Bucket = &it->second; - } - } - - if (Bucket == nullptr) - { - // New bucket needs to be created - - zen::RwLock::ExclusiveLockScope _(m_Lock); - - auto It = m_Buckets.try_emplace(std::string(InBucket), m_CasStore); - Bucket = &It.first->second; - - std::filesystem::path bucketPath = m_RootDir; - bucketPath /= std::string(InBucket); - - Bucket->OpenOrCreate(bucketPath.c_str()); - } - - ZEN_ASSERT(Bucket != nullptr); - - if (Bucket->IsOk()) - { - Bucket->Put(HashKey, Value); - } -} - -void -ZenCacheDiskLayer::Flush() -{ - std::vector<CacheBucket*> Buckets; - Buckets.reserve(m_Buckets.size()); - - { - zen::RwLock::SharedLockScope _(m_Lock); - - for (auto& Kv : m_Buckets) - { - Buckets.push_back(&Kv.second); - } - } - - for (auto& Bucket : Buckets) - { - Bucket->Flush(); - } -} - -////////////////////////////////////////////////////////////////////////// - -ZenCacheTracker::ZenCacheTracker(ZenCacheStore& CacheStore) -{ - ZEN_UNUSED(CacheStore); -} - -ZenCacheTracker::~ZenCacheTracker() -{ -} - -void -ZenCacheTracker::TrackAccess(std::string_view Bucket, const zen::IoHash& HashKey) -{ - ZEN_UNUSED(Bucket); - ZEN_UNUSED(HashKey); -} diff --git a/zenserver/cache/cachestore.h b/zenserver/cache/cachestore.h index 1ac01279b..89c6396b8 100644 --- a/zenserver/cache/cachestore.h +++ b/zenserver/cache/cachestore.h @@ -82,94 +82,3 @@ private: zen::RwLock m_Lock; std::unordered_map<std::string, zen::IoBuffer> m_CacheMap; }; - -/****************************************************************************** - - /$$$$$$$$ /$$$$$$ /$$ - |_____ $$ /$$__ $$ | $$ - /$$/ /$$$$$$ /$$$$$$$ | $$ \__/ /$$$$$$ /$$$$$$| $$$$$$$ /$$$$$$ - /$$/ /$$__ $| $$__ $$ | $$ |____ $$/$$_____| $$__ $$/$$__ $$ - /$$/ | $$$$$$$| $$ \ $$ | $$ /$$$$$$| $$ | $$ \ $| $$$$$$$$ - /$$/ | $$_____| $$ | $$ | $$ $$/$$__ $| $$ | $$ | $| $$_____/ - /$$$$$$$| $$$$$$| $$ | $$ | $$$$$$| $$$$$$| $$$$$$| $$ | $| $$$$$$$ - |________/\_______|__/ |__/ \______/ \_______/\_______|__/ |__/\_______/ - - Cache store for UE5. Restricts keys to "{bucket}/{hash}" pairs where the hash - is 40 (hex) chars in size. Values may be opaque blobs or structured objects - which can in turn contain references to other objects. - -******************************************************************************/ - -class ZenCacheMemoryLayer -{ -public: - ZenCacheMemoryLayer(); - ~ZenCacheMemoryLayer(); - - bool Get(std::string_view Bucket, const zen::IoHash& HashKey, CacheValue& OutValue); - void Put(std::string_view Bucket, const zen::IoHash& HashKey, const CacheValue& Value); - -private: - struct CacheBucket - { - zen::RwLock m_bucketLock; - std::unordered_map<zen::IoHash, zen::IoBuffer, zen::IoHash::Hasher> m_cacheMap; - - bool Get(const zen::IoHash& HashKey, CacheValue& OutValue); - void Put(const zen::IoHash& HashKey, const CacheValue& Value); - }; - - zen::RwLock m_Lock; - std::unordered_map<std::string, CacheBucket> m_Buckets; -}; - -class ZenCacheDiskLayer -{ -public: - ZenCacheDiskLayer(zen::CasStore& Cas, const std::filesystem::path& RootDir); - ~ZenCacheDiskLayer(); - - bool Get(std::string_view Bucket, const zen::IoHash& HashKey, CacheValue& OutValue); - void Put(std::string_view Bucket, const zen::IoHash& HashKey, const CacheValue& Value); - - void Flush(); - -private: - /** A cache bucket manages a single directory containing - metadata and data for that bucket - */ - struct CacheBucket; - - zen::CasStore& m_CasStore; - std::filesystem::path m_RootDir; - zen::RwLock m_Lock; - std::unordered_map<std::string, CacheBucket> m_Buckets; // TODO: make this case insensitive -}; - -class ZenCacheStore -{ -public: - ZenCacheStore(zen::CasStore& Cas, const std::filesystem::path& RootDir); - ~ZenCacheStore(); - - virtual bool Get(std::string_view Bucket, const zen::IoHash& HashKey, CacheValue& OutValue); - virtual void Put(std::string_view Bucket, const zen::IoHash& HashKey, const CacheValue& Value); - -private: - std::filesystem::path m_RootDir; - ZenCacheMemoryLayer m_MemLayer; - ZenCacheDiskLayer m_DiskLayer; -}; - -/** Tracks cache entry access, stats and orchestrates cleanup activities - */ -class ZenCacheTracker -{ -public: - ZenCacheTracker(ZenCacheStore& CacheStore); - ~ZenCacheTracker(); - - void TrackAccess(std::string_view Bucket, const zen::IoHash& HashKey); - -private: -}; diff --git a/zenserver/cache/kvcache.cpp b/zenserver/cache/kvcache.cpp index 404b17e5a..4c3b1e33d 100644 --- a/zenserver/cache/kvcache.cpp +++ b/zenserver/cache/kvcache.cpp @@ -109,7 +109,7 @@ HttpKvCacheService::HandleRequest(zen::HttpServerRequest& Request) if (!Success) { - // Success = m_cache_.Get(Key, Value); + Success = m_cache_.Get(Key, Value); if (!Success) { diff --git a/zenserver/cache/structuredcache.cpp b/zenserver/cache/structuredcache.cpp index 0d62f297c..792d764cb 100644 --- a/zenserver/cache/structuredcache.cpp +++ b/zenserver/cache/structuredcache.cpp @@ -2,23 +2,39 @@ #pragma once +#include <zencore/compactbinarybuilder.h> +#include <zencore/compactbinaryvalidation.h> +#include <zencore/compress.h> #include <zencore/fmtutils.h> #include <zencore/httpserver.h> +#include <zencore/timer.h> -#include "cachestore.h" #include "structuredcache.h" +#include "structuredcachestore.h" #include "upstream/jupiter.h" +#include "zenstore/cidstore.h" #include <spdlog/spdlog.h> #include <filesystem> namespace zen { -HttpStructuredCacheService::HttpStructuredCacheService(std::filesystem::path RootPath, zen::CasStore& InStore) +using namespace std::literals; + +HttpStructuredCacheService::HttpStructuredCacheService(std::filesystem::path RootPath, zen::CasStore& InStore, zen::CidStore& InCidStore) : m_CasStore(InStore) , m_CacheStore(InStore, RootPath) +, m_CidStore(InCidStore) { spdlog::info("initializing structured cache at '{}'", RootPath); + +#if 0 + m_Cloud = new CloudCacheClient("https://jupiter.devtools.epicgames.com"sv, + "ue4.ddc"sv /* namespace */, + "https://epicgames.okta.com/oauth2/auso645ojjWVdRI3d0x7/v1/token"sv /* provider */, + "0oao91lrhqPiAlaGD0x7"sv /* client id */, + "-GBWjjenhCgOwhxL5yBKNJECVIoDPH0MK4RDuN7d"sv /* oauth secret */); +#endif } HttpStructuredCacheService::~HttpStructuredCacheService() @@ -42,6 +58,21 @@ HttpStructuredCacheService::HandleRequest(zen::HttpServerRequest& Request) return Request.WriteResponse(zen::HttpResponse::BadRequest); // invalid URL } + if (Ref.PayloadId == IoHash::Zero) + { + return HandleCacheRecordRequest(Request, Ref); + } + else + { + return HandleCachePayloadRequest(Request, Ref); + } + + return; +} + +void +HttpStructuredCacheService::HandleCacheRecordRequest(zen::HttpServerRequest& Request, CacheRef& Ref) +{ switch (auto Verb = Request.RequestVerb()) { using enum zen::HttpVerb; @@ -49,25 +80,20 @@ HttpStructuredCacheService::HandleRequest(zen::HttpServerRequest& Request) case kHead: case kGet: { - CacheValue Value; - bool Success = m_CacheStore.Get(Ref.BucketSegment, Ref.HashKey, /* out */ Value); + ZenCacheValue Value; + bool Success = m_CacheStore.Get(Ref.BucketSegment, Ref.HashKey, /* out */ Value); if (!Success) { - Request.WriteResponse(zen::HttpResponse::NotFound); + return Request.WriteResponse(zen::HttpResponse::NotFound); } - else + + if (Verb == kHead) { - if (Verb == kHead) - { - Request.SetSuppressResponseBody(); - Request.WriteResponse(zen::HttpResponse::OK, zen::HttpContentType::kBinary, Value.Value); - } - else - { - Request.WriteResponse(zen::HttpResponse::OK, zen::HttpContentType::kBinary, Value.Value); - } + Request.SetSuppressResponseBody(); } + + return Request.WriteResponse(zen::HttpResponse::OK, zen::HttpContentType::kBinary, Value.Value); } break; @@ -75,12 +101,99 @@ HttpStructuredCacheService::HandleRequest(zen::HttpServerRequest& Request) { if (zen::IoBuffer Body = Request.ReadPayload()) { - CacheValue Value; + if (Body.Size() == 0) + { + return Request.WriteResponse(zen::HttpResponse::BadRequest); + } + + ZenCacheValue Value; Value.Value = Body; + HttpContentType ContentType = Request.RequestContentType(); + + bool IsCompactBinary; + + switch (ContentType) + { + case HttpContentType::kUnknownContentType: + case HttpContentType::kBinary: + IsCompactBinary = false; + break; + + case HttpContentType::kCbObject: + IsCompactBinary = true; + break; + + default: + return Request.WriteResponse(zen::HttpResponse::BadRequest); + } + + // Compute index data + + if (IsCompactBinary) + { + // Validate payload before accessing it + zen::CbValidateError ValidationResult = + zen::ValidateCompactBinary(MemoryView(Body.Data(), Body.Size()), zen::CbValidateMode::All); + + if (ValidationResult != CbValidateError::None) + { + // TODO: add details in response + return Request.WriteResponse(HttpResponse::BadRequest); + } + + // Extract data for index + zen::CbObjectView Cbo(Body.Data()); + + std::vector<IoHash> References; + Cbo.IterateAttachments([&](CbFieldView AttachmentView) { References.push_back(AttachmentView.AsHash()); }); + + if (!References.empty()) + { + zen::CbObjectWriter Idx; + Idx.BeginArray("r"); + + for (const IoHash& Hash : References) + { + Idx.AddHash(Hash); + } + + Idx.EndArray(); + } + + // TODO: store references in index + } + m_CacheStore.Put(Ref.BucketSegment, Ref.HashKey, Value); - Request.WriteResponse(zen::HttpResponse::Created); + // This is currently synchronous for simplicity and debuggability but should be + // made asynchronous + + if (m_Cloud) + { + CloudCacheSession Session(m_Cloud); + + zen::Stopwatch Timer; + + try + { + Session.Put(Ref.BucketSegment, Ref.HashKey, Value); + spdlog::debug("upstream PUT ({}) succeeded after {:5}!", + Ref.HashKey, + zen::NiceTimeSpanMs(Timer.getElapsedTimeMs())); + } + catch (std::exception& e) + { + spdlog::debug("upstream PUT ({}) failed after {:5}: '{}'", + Ref.HashKey, + zen::NiceTimeSpanMs(Timer.getElapsedTimeMs()), + e.what()); + + throw; + } + } + + return Request.WriteResponse(zen::HttpResponse::Created); } else { @@ -97,26 +210,136 @@ HttpStructuredCacheService::HandleRequest(zen::HttpServerRequest& Request) } } -[[nodiscard]] bool +void +HttpStructuredCacheService::HandleCachePayloadRequest(zen::HttpServerRequest& Request, CacheRef& Ref) +{ + // Note: the URL references the uncompressed payload hash - so this maintains the mapping + // from uncompressed CAS identity to the stored payload hash + // + // this is a PITA but a consequence of the fact that the client side code is not able to + // address data by compressed hash + + switch (auto Verb = Request.RequestVerb()) + { + using enum zen::HttpVerb; + + case kHead: + case kGet: + { + // TODO: need to map from uncompressed content address into the storage + // (compressed) content address + + zen::IoBuffer Payload = m_CidStore.FindChunkByCid(Ref.PayloadId); + + if (!Payload) + { + return Request.WriteResponse(zen::HttpResponse::NotFound); + } + + if (Verb == kHead) + { + Request.SetSuppressResponseBody(); + } + + return Request.WriteResponse(zen::HttpResponse::OK, zen::HttpContentType::kBinary, Payload); + } + break; + + case kPut: + { + if (zen::IoBuffer Body = Request.ReadPayload()) + { + if (Body.Size() == 0) + { + return Request.WriteResponse(zen::HttpResponse::BadRequest); + } + + zen::IoHash ChunkHash = zen::IoHash::HashMemory(Body); + + zen::CompressedBuffer Compressed = zen::CompressedBuffer::FromCompressed(SharedBuffer(Body)); + + if (IoHash::FromBLAKE3(Compressed.GetRawHash()) != Ref.PayloadId) + { + // the URL specified content id and content hashes don't match! + return Request.WriteResponse(HttpResponse::BadRequest); + } + + zen::CasStore::InsertResult Result = m_CasStore.InsertChunk(Body, ChunkHash); + + m_CidStore.AddCompressedCid(Ref.PayloadId, ChunkHash); + + if (Result.New) + { + return Request.WriteResponse(zen::HttpResponse::Created); + } + else + { + return Request.WriteResponse(zen::HttpResponse::OK); + } + } + } + break; + + case kPost: + break; + + default: + break; + } +} + +bool HttpStructuredCacheService::ValidateUri(zen::HttpServerRequest& Request, CacheRef& OutRef) { std::string_view Key = Request.RelativeUri(); - std::string_view::size_type BucketSplitOffset = Key.find_last_of('/'); + std::string_view::size_type BucketSplitOffset = Key.find_first_of('/'); if (BucketSplitOffset == std::string_view::npos) { return false; } - OutRef.BucketSegment = Key.substr(0, BucketSplitOffset); - std::string_view HashSegment = Key.substr(BucketSplitOffset + 1); + OutRef.BucketSegment = Key.substr(0, BucketSplitOffset); + + std::string_view HashSegment; + std::string_view PayloadSegment; - if (HashSegment.size() != (2 * sizeof OutRef.HashKey.Hash)) + std::string_view::size_type PayloadSplitOffset = Key.find_last_of('/'); + + // We know there is a slash so no need to check for npos return + + if (PayloadSplitOffset == BucketSplitOffset) + { + // Basic cache record lookup + HashSegment = Key.substr(BucketSplitOffset + 1); + } + else + { + // Cache record + payload lookup + HashSegment = Key.substr(BucketSplitOffset + 1, PayloadSplitOffset - BucketSplitOffset - 1); + PayloadSegment = Key.substr(PayloadSplitOffset + 1); + } + + if (HashSegment.size() != zen::IoHash::StringLength) { return false; } - bool IsOk = zen::ParseHexBytes(HashSegment.data(), HashSegment.size(), OutRef.HashKey.Hash); + if (!PayloadSegment.empty() && PayloadSegment.size() == zen::IoHash::StringLength) + { + const bool IsOk = zen::ParseHexBytes(PayloadSegment.data(), PayloadSegment.size(), OutRef.PayloadId.Hash); + + if (!IsOk) + { + return false; + } + } + else + { + OutRef.PayloadId = zen::IoHash::Zero; + } + + const bool IsOk = zen::ParseHexBytes(HashSegment.data(), HashSegment.size(), OutRef.HashKey.Hash); if (!IsOk) { @@ -125,5 +348,4 @@ HttpStructuredCacheService::ValidateUri(zen::HttpServerRequest& Request, CacheRe return true; } - } // namespace zen diff --git a/zenserver/cache/structuredcache.h b/zenserver/cache/structuredcache.h index e2ab60928..72aeecfd5 100644 --- a/zenserver/cache/structuredcache.h +++ b/zenserver/cache/structuredcache.h @@ -3,12 +3,16 @@ #pragma once #include <zencore/httpserver.h> +#include <zencore/refcount.h> -#include "cachestore.h" +#include "structuredcachestore.h" #include "upstream/jupiter.h" namespace zen { +class CloudCacheClient; +class CidStore; + /** * New-style cache service. Imposes constraints on keys, supports blobs and * structured values @@ -40,7 +44,7 @@ namespace zen { class HttpStructuredCacheService : public zen::HttpService { public: - HttpStructuredCacheService(std::filesystem::path RootPath, zen::CasStore& InStore); + HttpStructuredCacheService(std::filesystem::path RootPath, zen::CasStore& InStore, zen::CidStore& InCidStore); ~HttpStructuredCacheService(); virtual const char* BaseUri() const override; @@ -52,12 +56,17 @@ private: { std::string BucketSegment; IoHash HashKey; + IoHash PayloadId; }; [[nodiscard]] bool ValidateUri(zen::HttpServerRequest& Request, CacheRef& OutRef); + void HandleCacheRecordRequest(zen::HttpServerRequest& Request, CacheRef& Ref); + void HandleCachePayloadRequest(zen::HttpServerRequest& Request, CacheRef& Ref); - zen::CasStore& m_CasStore; - ZenCacheStore m_CacheStore; + zen::CasStore& m_CasStore; + zen::CidStore& m_CidStore; + ZenCacheStore m_CacheStore; + RefPtr<CloudCacheClient> m_Cloud; }; } // namespace zen diff --git a/zenserver/cache/structuredcachestore.cpp b/zenserver/cache/structuredcachestore.cpp new file mode 100644 index 000000000..764bce93e --- /dev/null +++ b/zenserver/cache/structuredcachestore.cpp @@ -0,0 +1,625 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "structuredcachestore.h" + +#include <zencore/except.h> +#include <zencore/windows.h> + +#include <zencore/compactbinary.h> +#include <zencore/filesystem.h> +#include <zencore/fmtutils.h> +#include <zencore/iobuffer.h> +#include <zencore/string.h> +#include <zencore/thread.h> +#include <zenstore/basicfile.h> +#include <zenstore/cas.h> +#include <zenstore/caslog.h> + +#include <fmt/core.h> +#include <spdlog/spdlog.h> +#include <concepts> +#include <filesystem> +#include <gsl/gsl-lite.hpp> +#include <unordered_map> + +#include <atlfile.h> + +using namespace zen; +using namespace fmt::literals; + +////////////////////////////////////////////////////////////////////////// + +ZenCacheStore::ZenCacheStore(zen::CasStore& Cas, const std::filesystem::path& RootDir) : m_DiskLayer{Cas, RootDir} +{ + zen::CreateDirectories(RootDir); +} + +ZenCacheStore::~ZenCacheStore() +{ +} + +bool +ZenCacheStore::Get(std::string_view InBucket, const zen::IoHash& HashKey, ZenCacheValue& OutValue) +{ + bool Ok = m_MemLayer.Get(InBucket, HashKey, OutValue); + + if (Ok) + { + ZEN_ASSERT(OutValue.Value.Size()); + } + + if (!Ok) + { + Ok = m_DiskLayer.Get(InBucket, HashKey, OutValue); + + if (Ok) + { + ZEN_ASSERT(OutValue.Value.Size()); + } + + if (Ok && (OutValue.Value.Size() <= m_DiskLayerSizeThreshold)) + { + m_MemLayer.Put(InBucket, HashKey, OutValue); + } + } + + return Ok; +} + +void +ZenCacheStore::Put(std::string_view InBucket, const zen::IoHash& HashKey, const ZenCacheValue& Value) +{ + // Store value and index + + ZEN_ASSERT(Value.Value.Size()); + + m_DiskLayer.Put(InBucket, HashKey, Value); + + if (Value.Value.Size() <= m_DiskLayerSizeThreshold) + { + m_MemLayer.Put(InBucket, HashKey, Value); + } +} + +////////////////////////////////////////////////////////////////////////// + +ZenCacheMemoryLayer::ZenCacheMemoryLayer() +{ +} + +ZenCacheMemoryLayer::~ZenCacheMemoryLayer() +{ +} + +bool +ZenCacheMemoryLayer::Get(std::string_view InBucket, const zen::IoHash& HashKey, ZenCacheValue& OutValue) +{ + CacheBucket* Bucket = nullptr; + + { + RwLock::SharedLockScope _(m_Lock); + + auto it = m_Buckets.find(std::string(InBucket)); + + if (it != m_Buckets.end()) + { + Bucket = &it->second; + } + } + + if (Bucket == nullptr) + return false; + + ZEN_ASSERT(Bucket != nullptr); + + return Bucket->Get(HashKey, OutValue); +} + +void +ZenCacheMemoryLayer::Put(std::string_view InBucket, const zen::IoHash& HashKey, const ZenCacheValue& Value) +{ + CacheBucket* Bucket = nullptr; + + { + RwLock::SharedLockScope _(m_Lock); + + auto it = m_Buckets.find(std::string(InBucket)); + + if (it != m_Buckets.end()) + { + Bucket = &it->second; + } + } + + if (Bucket == nullptr) + { + // New bucket + + RwLock::ExclusiveLockScope _(m_Lock); + + Bucket = &m_Buckets[std::string(InBucket)]; + } + + ZEN_ASSERT(Bucket != nullptr); + + // Note that since the underlying IoBuffer is retained, the content type is also + + Bucket->Put(HashKey, Value); +} + +bool +ZenCacheMemoryLayer::CacheBucket::Get(const zen::IoHash& HashKey, ZenCacheValue& OutValue) +{ + RwLock::SharedLockScope _(m_bucketLock); + + auto bucketIt = m_cacheMap.find(HashKey); + + if (bucketIt == m_cacheMap.end()) + { + return false; + } + + OutValue.Value = bucketIt->second; + + return true; +} + +void +ZenCacheMemoryLayer::CacheBucket::Put(const zen::IoHash& HashKey, const ZenCacheValue& Value) +{ + RwLock::ExclusiveLockScope _(m_bucketLock); + + m_cacheMap[HashKey] = Value.Value; +} + +////////////////////////////////////////////////////////////////////////// + +#pragma pack(push) +#pragma pack(1) + +struct DiskLocation +{ + uint64_t OffsetAndFlags; + uint32_t Size; + uint32_t IndexDataSize; + + static const uint64_t kOffsetMask = 0x00FF'ffFF'ffFF'ffFFull; + static const uint64_t kFlagsMask = 0xff00'0000'0000'0000ull; + static const uint64_t kStandaloneFile = 0x8000'0000'0000'0000ull; + static const uint64_t kStructured = 0x4000'0000'0000'0000ull; + + static uint64_t CombineOffsetAndFlags(uint64_t Offset, uint64_t Flags) { return Offset | Flags; } + + inline uint64_t Offset() const { return OffsetAndFlags & kOffsetMask; } + inline uint64_t IsFlagSet(uint64_t Flag) const { return OffsetAndFlags & Flag; } +}; + +struct DiskIndexEntry +{ + zen::IoHash Key; + DiskLocation Location; +}; + +#pragma pack(pop) + +static_assert(sizeof(DiskIndexEntry) == 36); + +struct ZenCacheDiskLayer::CacheBucket +{ + CacheBucket(CasStore& Cas); + ~CacheBucket(); + + void OpenOrCreate(std::filesystem::path BucketDir); + + bool Get(const zen::IoHash& HashKey, ZenCacheValue& OutValue); + void Put(const zen::IoHash& HashKey, const ZenCacheValue& Value); + void Flush(); + + inline bool IsOk() const { return m_Ok; } + +private: + CasStore& m_CasStore; + std::filesystem::path m_BucketDir; + Oid m_BucketId; + bool m_Ok = false; + uint64_t m_LargeObjectThreshold = 1024; + + BasicFile m_SobsFile; + TCasLogFile<DiskIndexEntry> m_SlogFile; + + void BuildPath(zen::WideStringBuilderBase& Path, const zen::IoHash& HashKey); + void PutLargeObject(const zen::IoHash& HashKey, const ZenCacheValue& Value); + + RwLock m_IndexLock; + tsl::robin_map<zen::IoHash, DiskLocation, zen::IoHash::Hasher> m_Index; + uint64_t m_WriteCursor = 0; +}; + +ZenCacheDiskLayer::CacheBucket::CacheBucket(CasStore& Cas) : m_CasStore(Cas) +{ +} + +ZenCacheDiskLayer::CacheBucket::~CacheBucket() +{ +} + +void +ZenCacheDiskLayer::CacheBucket::OpenOrCreate(std::filesystem::path BucketDir) +{ + zen::CreateDirectories(BucketDir); + + m_BucketDir = BucketDir; + + std::filesystem::path ManifestPath{m_BucketDir / "zen_manifest"}; + std::filesystem::path SobsPath{m_BucketDir / "zen.sobs"}; + std::filesystem::path SlogPath{m_BucketDir / "zen.slog"}; + + CAtlFile ManifestFile; + + // Try opening existing file first + + bool IsNew = false; + + HRESULT hRes = ManifestFile.Create(ManifestPath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, OPEN_EXISTING); + + if (SUCCEEDED(hRes)) + { + ULONGLONG FileSize; + ManifestFile.GetSize(FileSize); + + if (FileSize == sizeof(Oid)) + { + hRes = ManifestFile.Read(&m_BucketId, sizeof(Oid)); + + if (SUCCEEDED(hRes)) + { + m_Ok = true; + } + } + + if (!m_Ok) + { + ManifestFile.Close(); + } + } + + if (!m_Ok) + { + // This is a new bucket + + hRes = ManifestFile.Create(ManifestPath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS); + + if (FAILED(hRes)) + { + ThrowLastError("Failed to create bucket manifest '{}'"_format(ManifestPath)); + } + + m_BucketId.Generate(); + + hRes = ManifestFile.Write(&m_BucketId, sizeof(Oid)); + + IsNew = true; + } + + // Initialize small object storage related files + + m_SobsFile.Open(SobsPath, IsNew); + + // Open and replay log + + m_SlogFile.Open(SlogPath, IsNew); + + uint64_t MaxFileOffset = 0; + + { + // This is not technically necessary but may help future static analysis + zen::RwLock::ExclusiveLockScope _(m_IndexLock); + + m_SlogFile.Replay([&](const DiskIndexEntry& Record) { + m_Index[Record.Key] = Record.Location; + + MaxFileOffset = std::max<uint64_t>(MaxFileOffset, Record.Location.Offset() + Record.Location.Size); + }); + } + + m_WriteCursor = (MaxFileOffset + 15) & ~15; + + m_Ok = true; +} + +void +ZenCacheDiskLayer::CacheBucket::BuildPath(zen::WideStringBuilderBase& Path, const zen::IoHash& HashKey) +{ + char hex[sizeof(HashKey.Hash) * 2]; + ToHexBytes(HashKey.Hash, sizeof HashKey.Hash, hex); + + Path.Append(m_BucketDir.c_str()); + Path.Append(L"/"); + Path.AppendAsciiRange(hex, hex + sizeof(hex)); +} + +bool +ZenCacheDiskLayer::CacheBucket::Get(const zen::IoHash& HashKey, ZenCacheValue& OutValue) +{ + if (!m_Ok) + { + return false; + } + + zen::RwLock::SharedLockScope _(m_IndexLock); + + if (auto it = m_Index.find(HashKey); it != m_Index.end()) + { + const DiskLocation& Loc = it->second; + + ZenContentType ContentType = ZenContentType::kBinary; + + if (Loc.IsFlagSet(DiskLocation::kStructured)) + { + ContentType = ZenContentType::kCbObject; + } + + if (!Loc.IsFlagSet(DiskLocation::kStandaloneFile)) + { + OutValue.Value = IoBufferBuilder::MakeFromFileHandle(m_SobsFile.Handle(), Loc.Offset(), Loc.Size); + OutValue.Value.SetContentType(ContentType); + + return true; + } + else + { + _.ReleaseNow(); + + WideStringBuilder<128> DataFilePath; + BuildPath(DataFilePath, HashKey); + + if (zen::IoBuffer Data = IoBufferBuilder::MakeFromFile(DataFilePath.c_str())) + { + OutValue.Value = Data; + OutValue.Value.SetContentType(ContentType); + + return true; + } + } + } + + return false; +} + +void +ZenCacheDiskLayer::CacheBucket::Put(const zen::IoHash& HashKey, const ZenCacheValue& Value) +{ + if (!m_Ok) + { + return; + } + + if (Value.Value.Size() >= m_LargeObjectThreshold) + { + return PutLargeObject(HashKey, Value); + } + else + { + // Small object put + + uint64_t EntryFlags = 0; + + if (Value.Value.GetContentType() == ZenContentType::kCbObject) + { + EntryFlags |= DiskLocation::kStructured; + } + + zen::RwLock::ExclusiveLockScope _(m_IndexLock); + + DiskLocation Loc{.OffsetAndFlags = DiskLocation::CombineOffsetAndFlags(m_WriteCursor, EntryFlags), + .Size = gsl::narrow<uint32_t>(Value.Value.Size())}; + + m_WriteCursor = zen::RoundUp(m_WriteCursor + Loc.Size, 16); + + if (auto it = m_Index.find(HashKey); it == m_Index.end()) + { + // Previously unknown object + m_Index.insert({HashKey, Loc}); + } + else + { + // TODO: should check if write is idempotent and bail out if it is? + it.value() = Loc; + } + + m_SlogFile.Append({.Key = HashKey, .Location = Loc}); + m_SobsFile.Write(Value.Value.Data(), Loc.Size, Loc.Offset()); + } +} + +void +ZenCacheDiskLayer::CacheBucket::Flush() +{ + m_SobsFile.Flush(); + m_SlogFile.Flush(); +} + +void +ZenCacheDiskLayer::CacheBucket::PutLargeObject(const zen::IoHash& HashKey, const ZenCacheValue& Value) +{ + zen::WideStringBuilder<128> DataFilePath; + BuildPath(DataFilePath, HashKey); + + // TODO: replace this with a more efficient implementation with proper atomic rename + + CAtlTemporaryFile DataFile; + + HRESULT hRes = DataFile.Create(m_BucketDir.c_str()); + + if (FAILED(hRes)) + { + zen::ThrowSystemException(hRes, "Failed to open temporary file for put at '{}'"_format(m_BucketDir)); + } + + hRes = DataFile.Write(Value.Value.Data(), gsl::narrow<DWORD>(Value.Value.Size())); + + if (FAILED(hRes)) + { + zen::ThrowSystemException(hRes, "Failed to write payload ({} bytes) to file"_format(NiceBytes(Value.Value.Size()))); + } + + hRes = DataFile.Close(DataFilePath.c_str()); + + if (FAILED(hRes)) + { + zen::ThrowSystemException(hRes, "Failed to finalize file '{}'"_format(zen::WideToUtf8(DataFilePath))); + } + + // Update index + + uint64_t EntryFlags = DiskLocation::kStandaloneFile; + + if (Value.Value.GetContentType() == ZenContentType::kCbObject) + { + EntryFlags |= DiskLocation::kStructured; + } + + zen::RwLock::ExclusiveLockScope _(m_IndexLock); + + DiskLocation Loc{.OffsetAndFlags = DiskLocation::CombineOffsetAndFlags(0, EntryFlags), .Size = 0}; + + if (auto it = m_Index.find(HashKey); it == m_Index.end()) + { + // Previously unknown object + m_Index.insert({HashKey, Loc}); + } + else + { + // TODO: should check if write is idempotent and bail out if it is? + it.value() = Loc; + } + + m_SlogFile.Append({.Key = HashKey, .Location = Loc}); +} + +////////////////////////////////////////////////////////////////////////// + +ZenCacheDiskLayer::ZenCacheDiskLayer(CasStore& Cas, const std::filesystem::path& RootDir) : m_RootDir(RootDir), m_CasStore(Cas) +{ +} + +ZenCacheDiskLayer::~ZenCacheDiskLayer() = default; + +bool +ZenCacheDiskLayer::Get(std::string_view InBucket, const zen::IoHash& HashKey, ZenCacheValue& OutValue) +{ + CacheBucket* Bucket = nullptr; + + { + zen::RwLock::SharedLockScope _(m_Lock); + + auto it = m_Buckets.find(std::string(InBucket)); + + if (it != m_Buckets.end()) + { + Bucket = &it->second; + } + } + + if (Bucket == nullptr) + { + // Bucket needs to be opened/created + + zen::RwLock::ExclusiveLockScope _(m_Lock); + + auto It = m_Buckets.try_emplace(std::string(InBucket), m_CasStore); + Bucket = &It.first->second; + + std::filesystem::path BucketPath = m_RootDir; + BucketPath /= std::string(InBucket); + + Bucket->OpenOrCreate(BucketPath.c_str()); + } + + ZEN_ASSERT(Bucket != nullptr); + + return Bucket->Get(HashKey, OutValue); +} + +void +ZenCacheDiskLayer::Put(std::string_view InBucket, const zen::IoHash& HashKey, const ZenCacheValue& Value) +{ + CacheBucket* Bucket = nullptr; + + { + zen::RwLock::SharedLockScope _(m_Lock); + + auto it = m_Buckets.find(std::string(InBucket)); + + if (it != m_Buckets.end()) + { + Bucket = &it->second; + } + } + + if (Bucket == nullptr) + { + // New bucket needs to be created + + zen::RwLock::ExclusiveLockScope _(m_Lock); + + auto It = m_Buckets.try_emplace(std::string(InBucket), m_CasStore); + Bucket = &It.first->second; + + std::filesystem::path bucketPath = m_RootDir; + bucketPath /= std::string(InBucket); + + Bucket->OpenOrCreate(bucketPath.c_str()); + } + + ZEN_ASSERT(Bucket != nullptr); + + if (Bucket->IsOk()) + { + Bucket->Put(HashKey, Value); + } +} + +void +ZenCacheDiskLayer::Flush() +{ + std::vector<CacheBucket*> Buckets; + Buckets.reserve(m_Buckets.size()); + + { + zen::RwLock::SharedLockScope _(m_Lock); + + for (auto& Kv : m_Buckets) + { + Buckets.push_back(&Kv.second); + } + } + + for (auto& Bucket : Buckets) + { + Bucket->Flush(); + } +} + +////////////////////////////////////////////////////////////////////////// + +ZenCacheTracker::ZenCacheTracker(ZenCacheStore& CacheStore) +{ + ZEN_UNUSED(CacheStore); +} + +ZenCacheTracker::~ZenCacheTracker() +{ +} + +void +ZenCacheTracker::TrackAccess(std::string_view Bucket, const zen::IoHash& HashKey) +{ + ZEN_UNUSED(Bucket); + ZEN_UNUSED(HashKey); +} + +void +ZenCacheTracker::Flush() +{ +} diff --git a/zenserver/cache/structuredcachestore.h b/zenserver/cache/structuredcachestore.h new file mode 100644 index 000000000..81425a29e --- /dev/null +++ b/zenserver/cache/structuredcachestore.h @@ -0,0 +1,125 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include <zencore/compactbinary.h> +#include <zencore/iobuffer.h> +#include <zencore/iohash.h> +#include <zencore/thread.h> +#include <zencore/uid.h> +#include <zenstore/cas.h> + +#pragma warning(push) +#pragma warning(disable : 4127) +#include <tsl/robin_map.h> +#pragma warning(pop) + +#include <compare> +#include <filesystem> +#include <unordered_map> + +namespace zen { + +class WideStringBuilderBase; +class CasStore; + +} // namespace zen + +/****************************************************************************** + + /$$$$$$$$ /$$$$$$ /$$ + |_____ $$ /$$__ $$ | $$ + /$$/ /$$$$$$ /$$$$$$$ | $$ \__/ /$$$$$$ /$$$$$$| $$$$$$$ /$$$$$$ + /$$/ /$$__ $| $$__ $$ | $$ |____ $$/$$_____| $$__ $$/$$__ $$ + /$$/ | $$$$$$$| $$ \ $$ | $$ /$$$$$$| $$ | $$ \ $| $$$$$$$$ + /$$/ | $$_____| $$ | $$ | $$ $$/$$__ $| $$ | $$ | $| $$_____/ + /$$$$$$$| $$$$$$| $$ | $$ | $$$$$$| $$$$$$| $$$$$$| $$ | $| $$$$$$$ + |________/\_______|__/ |__/ \______/ \_______/\_______|__/ |__/\_______/ + + Cache store for UE5. Restricts keys to "{bucket}/{hash}" pairs where the hash + is 40 (hex) chars in size. Values may be opaque blobs or structured objects + which can in turn contain references to other objects (or blobs). + +******************************************************************************/ + +struct ZenCacheValue +{ + zen::IoBuffer Value; + zen::CbObject IndexData; +}; + +class ZenCacheMemoryLayer +{ +public: + ZenCacheMemoryLayer(); + ~ZenCacheMemoryLayer(); + + bool Get(std::string_view Bucket, const zen::IoHash& HashKey, ZenCacheValue& OutValue); + void Put(std::string_view Bucket, const zen::IoHash& HashKey, const ZenCacheValue& Value); + +private: + struct CacheBucket + { + zen::RwLock m_bucketLock; + tsl::robin_map<zen::IoHash, zen::IoBuffer> m_cacheMap; + + bool Get(const zen::IoHash& HashKey, ZenCacheValue& OutValue); + void Put(const zen::IoHash& HashKey, const ZenCacheValue& Value); + }; + + zen::RwLock m_Lock; + std::unordered_map<std::string, CacheBucket> m_Buckets; +}; + +class ZenCacheDiskLayer +{ +public: + ZenCacheDiskLayer(zen::CasStore& Cas, const std::filesystem::path& RootDir); + ~ZenCacheDiskLayer(); + + bool Get(std::string_view Bucket, const zen::IoHash& HashKey, ZenCacheValue& OutValue); + void Put(std::string_view Bucket, const zen::IoHash& HashKey, const ZenCacheValue& Value); + + void Flush(); + +private: + /** A cache bucket manages a single directory containing + metadata and data for that bucket + */ + struct CacheBucket; + + zen::CasStore& m_CasStore; + std::filesystem::path m_RootDir; + zen::RwLock m_Lock; + std::unordered_map<std::string, CacheBucket> m_Buckets; // TODO: make this case insensitive +}; + +class ZenCacheStore +{ +public: + ZenCacheStore(zen::CasStore& Cas, const std::filesystem::path& RootDir); + ~ZenCacheStore(); + + bool Get(std::string_view Bucket, const zen::IoHash& HashKey, ZenCacheValue& OutValue); + void Put(std::string_view Bucket, const zen::IoHash& HashKey, const ZenCacheValue& Value); + +private: + std::filesystem::path m_RootDir; + ZenCacheMemoryLayer m_MemLayer; + ZenCacheDiskLayer m_DiskLayer; + uint64_t m_DiskLayerSizeThreshold = 4 * 1024; +}; + +/** Tracks cache entry access, stats and orchestrates cleanup activities + */ +class ZenCacheTracker +{ +public: + ZenCacheTracker(ZenCacheStore& CacheStore); + ~ZenCacheTracker(); + + void TrackAccess(std::string_view Bucket, const zen::IoHash& HashKey); + void Flush(); + +private: +}; diff --git a/zenserver/experimental/usnjournal.cpp b/zenserver/experimental/usnjournal.cpp index f44e50945..a266c2338 100644 --- a/zenserver/experimental/usnjournal.cpp +++ b/zenserver/experimental/usnjournal.cpp @@ -164,7 +164,10 @@ UsnJournalReader::Initialize(std::filesystem::path VolumePath) HRESULT hRes = VolumeRootDir.Create(VolumePathName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS); - ThrowIfFailed(hRes, "Failed to open handle to volume root"); + if (FAILED(hRes)) + { + ThrowSystemException(hRes, "Failed to open handle to volume root"); + } FILE_ID_INFO FileInformation{}; BOOL Success = GetFileInformationByHandleEx(VolumeRootDir, FileIdInfo, &FileInformation, sizeof FileInformation); diff --git a/zenserver/projectstore.cpp b/zenserver/projectstore.cpp index 0d7ad0f16..c94a8fe35 100644 --- a/zenserver/projectstore.cpp +++ b/zenserver/projectstore.cpp @@ -11,13 +11,17 @@ #include <zencore/string.h> #include <zencore/timer.h> #include <zencore/windows.h> +#include <zenstore/basicfile.h> #include <zenstore/cas.h> #include <zenstore/caslog.h> -#pragma comment(lib, "Rpcrt4.lib") // RocksDB made me do this -#include <rocksdb/db.h> +#define USE_ROCKSDB 0 + +#if USE_ROCKSDB +# pragma comment(lib, "Rpcrt4.lib") // RocksDB made me do this +# include <rocksdb/db.h> +#endif -#include <lmdb.h> #include <ppl.h> #include <spdlog/spdlog.h> #include <xxh3.h> @@ -27,9 +31,12 @@ namespace zen { -namespace rocksdb = ROCKSDB_NAMESPACE; using namespace fmt::literals; +#if USE_ROCKSDB +namespace rocksdb = ROCKSDB_NAMESPACE; +#endif + ////////////////////////////////////////////////////////////////////////// struct ProjectStore::OplogStorage : public RefCounted @@ -43,12 +50,7 @@ struct ProjectStore::OplogStorage : public RefCounted Log().info("closing oplog storage at {}", m_OplogStoragePath); Flush(); - if (m_LmdbEnv) - { - mdb_env_close(m_LmdbEnv); - m_LmdbEnv = nullptr; - } - +#if USE_ROCKSDB if (m_RocksDb) { // Column families must be torn down before database is closed @@ -64,6 +66,7 @@ struct ProjectStore::OplogStorage : public RefCounted Log().warn("db close error reported for '{}' : '{}'", m_OplogStoragePath, Status.getState()); } } +#endif } [[nodiscard]] bool Exists() { return Exists(m_OplogStoragePath); } @@ -93,15 +96,7 @@ struct ProjectStore::OplogStorage : public RefCounted ZEN_ASSERT(IsPow2(m_OpsAlign)); ZEN_ASSERT(!(m_NextOpsOffset & (m_OpsAlign - 1))); - { - std::string LmdbPath = WideToUtf8((m_OplogStoragePath / "ops.zdb").native().c_str()); - - int rc = mdb_env_create(&m_LmdbEnv); - rc = mdb_env_set_mapsize(m_LmdbEnv, 8 * 1024 * 1024); - rc = mdb_env_set_maxreaders(m_LmdbEnv, 256); - rc = mdb_env_open(m_LmdbEnv, LmdbPath.c_str(), MDB_NOSUBDIR | MDB_WRITEMAP | MDB_NOMETASYNC | MDB_NOSYNC, 0666); - } - +#if USE_ROCKSDB { std::string RocksdbPath = WideToUtf8((m_OplogStoragePath / "ops.rdb").native().c_str()); @@ -143,6 +138,7 @@ struct ProjectStore::OplogStorage : public RefCounted m_RocksDb.reset(Db); } +#endif } void ReplayLog(std::function<void(CbObject, const OplogEntry&)>&& Handler) @@ -228,13 +224,15 @@ private: std::filesystem::path m_OplogStoragePath; RwLock m_RwLock; TCasLogFile<OplogEntry> m_Oplog; - CasBlobFile m_OpBlobs; + BasicFile m_OpBlobs; std::atomic<uint64_t> m_NextOpsOffset{0}; uint64_t m_OpsAlign = 32; std::atomic<uint32_t> m_MaxLsn{0}; - MDB_env* m_LmdbEnv = nullptr; + +#if USE_ROCKSDB std::unique_ptr<rocksdb::DB> m_RocksDb; std::vector<rocksdb::ColumnFamilyHandle*> m_RocksDbColumnHandles; +#endif }; ////////////////////////////////////////////////////////////////////////// @@ -276,9 +274,9 @@ ProjectStore::Oplog::FindChunk(Oid ChunkId) return m_CasStore.FindChunk(ChunkIt->second); } - if (auto FileIt = m_ServerFileMap.find(ChunkId); FileIt != m_ServerFileMap.end()) + if (auto FileIt = m_FileMap.find(ChunkId); FileIt != m_FileMap.end()) { - std::filesystem::path FilePath = m_OuterProject->RootDir / FileIt->second; + std::filesystem::path FilePath = m_OuterProject->RootDir / FileIt->second.ServerPath; return IoBufferBuilder::MakeFromFile(FilePath.native().c_str()); } @@ -296,20 +294,35 @@ ProjectStore::Oplog::IterateFileMap(std::function<void(const Oid&, const std::st { for (const auto& Kv : m_FileMap) { - Fn(Kv.first, Kv.second); + Fn(Kv.first, Kv.second.ClientPath); } } -void -ProjectStore::Oplog::AddFileMapping(Oid FileId, std::string_view Path) +bool +ProjectStore::Oplog::AddFileMapping(Oid FileId, IoHash Hash, std::string_view ServerPath, std::string_view ClientPath) { - m_FileMap.emplace(FileId, Path); -} + if (ServerPath.empty() || ClientPath.empty()) + { + return false; + } -void -ProjectStore::Oplog::AddServerFileMapping(Oid FileId, std::string_view Path) -{ - m_ServerFileMap.emplace(FileId, Path); + if (ServerPath[0] == '/') + { + ServerPath = ServerPath.substr(1); + } + + FileMapEntry Entry; + Entry.ServerPath = ServerPath; + Entry.ClientPath = ClientPath; + + m_FileMap.emplace(FileId, std::move(Entry)); + + if (Hash != IoHash::Zero) + { + m_ChunkMap.emplace(FileId, Hash); + } + + return true; } void @@ -356,93 +369,30 @@ ProjectStore::Oplog::RegisterOplogEntry(CbObject Core, const OplogEntry& OpEntry Log().debug("bulkdata {} -> {}", BulkDataId, BulkDataHash); } - if (CbFieldView FilesArray = Core["files"sv]) + if (Core["files"sv]) { - int FileCount = 0; - int ServerFileCount = 0; - - std::atomic<bool> InvalidOp{false}; - Stopwatch Timer; + int32_t FileCount = 0; - std::future<void> f0 = std::async(std::launch::async, [&] { - for (CbFieldView& Entry : FilesArray) - { - CbObjectView FileObj = Entry.AsObjectView(); - const Oid FileId = FileObj["id"sv].AsObjectId(); - - if (auto PathField = FileObj["path"sv]) - { - AddFileMapping(FileId, PathField.AsString()); - - // Log().debug("file {} -> {}", FileId, PathString); - - ++FileCount; - } - else - { - // Every file entry needs to specify a path - InvalidOp = true; - break; - } - - if (InvalidOp.load(std::memory_order::relaxed)) - { - break; - } - } - }); - - std::future<void> f1 = std::async(std::launch::async, [&] { - CbArrayView ServerFilesArray = Core["serverfiles"sv].AsArrayView(); + for (CbFieldView& Entry : Core["files"sv]) + { + CbObjectView FileObj = Entry.AsObjectView(); + const Oid FileId = FileObj["id"sv].AsObjectId(); + IoHash FileDataHash = FileObj["data"sv].AsBinaryAttachment(); + std::string_view ServerPath = FileObj["serverpath"sv].AsString(); + std::string_view ClientPath = FileObj["clientpath"sv].AsString(); - for (CbFieldView& Entry : ServerFilesArray) + if (AddFileMapping(FileId, FileDataHash, ServerPath, ClientPath)) { - CbObjectView FileObj = Entry.AsObjectView(); - const Oid FileId = FileObj["id"sv].AsObjectId(); - - if (auto PathField = FileObj["path"sv]) - { - AddServerFileMapping(FileId, PathField.AsString()); - - // m_log.debug("file {} -> {}", FileId, PathString); - - ++ServerFileCount; - } - else - { - // Every file entry needs to specify a path - InvalidOp = true; - break; - } - - if (InvalidOp.load(std::memory_order::relaxed)) - { - break; - } + ++FileCount; } - }); - - f0.wait(); - f1.wait(); - - if (InvalidOp) - { - return kInvalidOp; - } - - if (FileCount || ServerFileCount) - { - Log().debug("{} files registered, {} server files (took {})", - FileCount, - ServerFileCount, - NiceTimeSpanMs(Timer.getElapsedTimeMs())); - - if (FileCount != ServerFileCount) + else { - Log().warn("client/server file list mismatch: {} vs {}", FileCount, ServerFileCount); + Log().warn("invalid file"); } } + + Log().debug("added {} file(s) in {}", FileCount, NiceTimeSpanMs(Timer.getElapsedTimeMs())); } for (CbFieldView& Entry : Core["meta"sv]) @@ -510,7 +460,7 @@ ProjectStore::Project::Read() spdlog::info("reading config for project '{}' from {}", Identifier, ProjectStateFilePath); - CasBlobFile Blob; + BasicFile Blob; Blob.Open(ProjectStateFilePath, false); IoBuffer Obj = Blob.ReadAll(); @@ -551,7 +501,7 @@ ProjectStore::Project::Write() spdlog::info("persisting config for project '{}' to {}", Identifier, ProjectStateFilePath); - CasBlobFile Blob; + BasicFile Blob; Blob.Open(ProjectStateFilePath, true); Blob.Write(Mem.Data(), Mem.Size(), 0); Blob.Flush(); @@ -1114,7 +1064,7 @@ HttpProjectService::HttpProjectService(CasStore& Store, ProjectStore* Projects) // the prep step rejected the chunk. This should be fixed since there's // a performance cost associated with any file system activity - bool IsValid = true; + bool IsValid = true; std::vector<IoHash> MissingChunks; CbPackage::AttachmentResolver Resolver = [&](const IoHash& Hash) -> SharedBuffer { diff --git a/zenserver/projectstore.h b/zenserver/projectstore.h index 38c53ea6e..72b8a1cd6 100644 --- a/zenserver/projectstore.h +++ b/zenserver/projectstore.h @@ -99,6 +99,12 @@ public: uint64_t Size; }; + struct FileMapEntry + { + std::string ServerPath; + std::string ClientPath; + }; + template<class V> using OidMap = tsl::robin_map<Oid, V, Oid::Hasher>; @@ -108,18 +114,17 @@ public: std::filesystem::path m_BasePath; std::filesystem::path m_TempPath; - OidMap<IoHash> m_ChunkMap; // output data chunk id -> CAS address - OidMap<IoHash> m_MetaMap; // meta chunk id -> CAS address - OidMap<std::string> m_FileMap; // file id -> client file - OidMap<std::string> m_ServerFileMap; // file id -> server file - std::map<int, OplogEntryAddress> m_OpAddressMap; // Index LSN -> op data in ops blob file - OidMap<int> m_LatestOpMap; // op key -> latest op LSN for key + OidMap<IoHash> m_ChunkMap; // output data chunk id -> CAS address + OidMap<IoHash> m_MetaMap; // meta chunk id -> CAS address + OidMap<FileMapEntry> m_FileMap; // file id -> file map entry + int32_t m_ManifestVersion; // File system manifest version + std::map<int, OplogEntryAddress> m_OpAddressMap; // Index LSN -> op data in ops blob file + OidMap<int> m_LatestOpMap; // op key -> latest op LSN for key RefPtr<OplogStorage> m_Storage; std::string m_OplogId; - void AddFileMapping(Oid FileId, std::string_view Path); - void AddServerFileMapping(Oid FileId, std::string_view Path); + bool AddFileMapping(Oid FileId, IoHash Hash, std::string_view ServerPath, std::string_view ClientPath); void AddChunkMapping(Oid ChunkId, IoHash Hash); void AddMetaMapping(Oid ChunkId, IoHash Hash); }; diff --git a/zenserver/upstream/jupiter.cpp b/zenserver/upstream/jupiter.cpp index 363ebcd61..977bcc712 100644 --- a/zenserver/upstream/jupiter.cpp +++ b/zenserver/upstream/jupiter.cpp @@ -2,6 +2,8 @@ #include "jupiter.h" +#include "cache/structuredcachestore.h" + #include <fmt/format.h> #include <zencore/compactbinary.h> #include <zencore/iobuffer.h> @@ -110,23 +112,38 @@ CloudCacheSession::Put(std::string_view BucketId, std::string_view Key, IoBuffer } void -CloudCacheSession::Put(std::string_view BucketId, std::string_view Key, CbObjectView Data) +CloudCacheSession::Put(std::string_view BucketId, const IoHash& Key, ZenCacheValue Data) { ExtendableStringBuilder<256> Uri; Uri << m_CacheClient->ServiceUrl(); - Uri << "/api/v1/c/ddc/" << m_CacheClient->Namespace() << "/" << BucketId << "/" TESTING_PREFIX << Key; + Uri << "/api/v1/c/ddc/" << m_CacheClient->Namespace() << "/" << BucketId << "/" TESTING_PREFIX << Key.ToHexString(); auto& Session = m_SessionState->Session; - IoHash Hash = Data.GetHash(); - MemoryView DataView = Data.GetView(); - std::string Auth; m_CacheClient->AcquireAccessToken(Auth); Session.SetOption(cpr::Url{Uri.c_str()}); - Session.SetOption( - cpr::Header{{"Authorization", Auth}, {"X-Jupiter-IoHash", Hash.ToHexString()}, {"Content-Type", "application/x-ue-cb"}}); - Session.SetOption(cpr::Body{(const char*)DataView.GetData(), DataView.GetSize()}); + + if (Data.Value.GetContentType() == ZenContentType::kCbObject) + { + CbObjectView Cbo(Data.Value.Data()); + const IoHash Hash = Cbo.GetHash(); + const MemoryView DataView = Cbo.GetView(); + + Session.SetOption( + cpr::Header{{"Authorization", Auth}, {"X-Jupiter-IoHash", Hash.ToHexString()}, {"Content-Type", "application/x-ue-cb"}}); + + Session.SetOption(cpr::Body{reinterpret_cast<const char*>(DataView.GetData()), DataView.GetSize()}); + } + else + { + const IoHash Hash = IoHash::HashMemory(Data.Value.Data(), Data.Value.Size()); + + Session.SetOption( + cpr::Header{{"Authorization", Auth}, {"X-Jupiter-IoHash", Hash.ToHexString()}, {"Content-Type", "application/x-ue-cb"}}); + + Session.SetOption(cpr::Body{reinterpret_cast<const char*>(Data.Value.Data()), Data.Value.Size()}); + } cpr::Response Response = Session.Put(); diff --git a/zenserver/upstream/jupiter.h b/zenserver/upstream/jupiter.h index a0dbefa3c..2ed458142 100644 --- a/zenserver/upstream/jupiter.h +++ b/zenserver/upstream/jupiter.h @@ -11,6 +11,8 @@ #include <memory> #include <vector> +struct ZenCacheValue; + namespace zen { namespace detail { struct CloudCacheSessionState; @@ -56,7 +58,7 @@ public: // Structured cache operations IoBuffer Get(std::string_view BucketId, const IoHash& Key); - void Put(std::string_view BucketId, std::string_view Key, CbObjectView Data); + void Put(std::string_view BucketId, const IoHash& Key, ZenCacheValue Data); std::vector<IoHash> Filter(std::string_view BucketId, const std::vector<IoHash>& ChunkHashes); diff --git a/zenserver/zenserver.cpp b/zenserver/zenserver.cpp index 934fd95bc..e57a6142d 100644 --- a/zenserver/zenserver.cpp +++ b/zenserver/zenserver.cpp @@ -10,6 +10,7 @@ #include <zencore/timer.h> #include <zencore/windows.h> #include <zenstore/cas.h> +#include <zenstore/cidstore.h> #include <fmt/format.h> #include <mimalloc-new-delete.h> @@ -76,18 +77,20 @@ public: // Ok so now we're configured, let's kick things off zen::CasStoreConfiguration Config; - Config.RootDirectory = m_DataRoot / "CAS"; + Config.RootDirectory = m_DataRoot / "cas"; m_CasStore->Initialize(Config); spdlog::info("instantiating project service"); - m_ProjectStore = new zen::ProjectStore(*m_CasStore, m_DataRoot / "Builds"); + m_ProjectStore = new zen::ProjectStore(*m_CasStore, m_DataRoot / "projects"); m_HttpProjectService.reset(new zen::HttpProjectService{*m_CasStore, m_ProjectStore}); m_LocalProjectService = zen::LocalProjectService::New(*m_CasStore, m_ProjectStore); m_HttpLaunchService = std::make_unique<zen::HttpLaunchService>(*m_CasStore); + m_CidStore = std::make_unique<zen::CidStore>(*m_CasStore, m_DataRoot / "cid"); + if (ServiceConfig.LegacyCacheEnabled) { spdlog::info("instantiating legacy cache service"); @@ -101,7 +104,7 @@ public: if (ServiceConfig.StructuredCacheEnabled) { spdlog::info("instantiating structured cache service"); - m_StructuredCacheService.reset(new zen::HttpStructuredCacheService(m_DataRoot / "cache", *m_CasStore)); + m_StructuredCacheService.reset(new zen::HttpStructuredCacheService(m_DataRoot / "cache", *m_CasStore, *m_CidStore)); } else { @@ -207,6 +210,7 @@ private: zen::HttpServer m_Http; std::unique_ptr<zen::CasStore> m_CasStore{zen::CreateCasStore()}; + std::unique_ptr<zen::CidStore> m_CidStore; zen::CasGc m_Gc{*m_CasStore}; zen::CasScrubber m_Scrubber{*m_CasStore}; HttpTestService m_TestService; diff --git a/zenserver/zenserver.vcxproj b/zenserver/zenserver.vcxproj index 2ba6bd551..449603334 100644 --- a/zenserver/zenserver.vcxproj +++ b/zenserver/zenserver.vcxproj @@ -105,6 +105,7 @@ <ItemGroup> <ClInclude Include="admin\admin.h" /> <ClInclude Include="cache\structuredcache.h" /> + <ClInclude Include="cache\structuredcachestore.h" /> <ClInclude Include="config.h" /> <ClInclude Include="diag\crashreport.h" /> <ClInclude Include="diag\logging.h" /> @@ -124,6 +125,7 @@ <ItemGroup> <ClCompile Include="cache\kvcache.cpp" /> <ClCompile Include="cache\structuredcache.cpp" /> + <ClCompile Include="cache\structuredcachestore.cpp" /> <ClCompile Include="config.cpp" /> <ClCompile Include="diag\crashreport.cpp" /> <ClCompile Include="diag\logging.cpp" /> diff --git a/zenserver/zenserver.vcxproj.filters b/zenserver/zenserver.vcxproj.filters index fcf869e19..a8375b0b4 100644 --- a/zenserver/zenserver.vcxproj.filters +++ b/zenserver/zenserver.vcxproj.filters @@ -36,6 +36,7 @@ <ClInclude Include="config.h" /> <ClInclude Include="diag\logging.h" /> <ClInclude Include="diag\crashreport.h" /> + <ClInclude Include="cache\structuredcachestore.h" /> </ItemGroup> <ItemGroup> <ClCompile Include="zenserver.cpp" /> @@ -67,6 +68,7 @@ <ClCompile Include="config.cpp" /> <ClCompile Include="diag\logging.cpp" /> <ClCompile Include="diag\crashreport.cpp" /> + <ClCompile Include="cache\structuredcachestore.cpp" /> </ItemGroup> <ItemGroup> <Filter Include="cache"> diff --git a/zenstore/basicfile.cpp b/zenstore/basicfile.cpp new file mode 100644 index 000000000..7f10fc5e6 --- /dev/null +++ b/zenstore/basicfile.cpp @@ -0,0 +1,86 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "zenstore/basicfile.h" + +#include <zencore/except.h> +#include <zencore/filesystem.h> +#include <zencore/fmtutils.h> + +#include <fmt/format.h> +#include <gsl/gsl-lite.hpp> + +namespace zen { + +using namespace fmt::literals; + +void +BasicFile::Open(std::filesystem::path FileName, bool isCreate) +{ + const DWORD dwCreationDisposition = isCreate ? CREATE_ALWAYS : OPEN_EXISTING; + + HRESULT hRes = m_File.Create(FileName.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, dwCreationDisposition); + + if (FAILED(hRes)) + { + ThrowSystemException(hRes, "Failed to open bucket sobs file '{}'"_format(FileName)); + } +} + +void +BasicFile::Read(void* Data, uint64_t Size, uint64_t Offset) +{ + OVERLAPPED Ovl{}; + + Ovl.Offset = DWORD(Offset & 0xffff'ffffu); + Ovl.OffsetHigh = DWORD(Offset >> 32); + + HRESULT hRes = m_File.Read(Data, gsl::narrow<DWORD>(Size), &Ovl); + + if (FAILED(hRes)) + { + ThrowSystemException(hRes, "Failed to read from file '{}'"_format(zen::PathFromHandle(m_File))); + } +} + +IoBuffer +BasicFile::ReadAll() +{ + IoBuffer Buffer(FileSize()); + + Read((void*)Buffer.Data(), Buffer.Size(), 0); + + return Buffer; +} + +void +BasicFile::Write(const void* Data, uint64_t Size, uint64_t Offset) +{ + OVERLAPPED Ovl{}; + + Ovl.Offset = DWORD(Offset & 0xffff'ffffu); + Ovl.OffsetHigh = DWORD(Offset >> 32); + + HRESULT hRes = m_File.Write(Data, gsl::narrow<DWORD>(Size), &Ovl); + + if (FAILED(hRes)) + { + ThrowSystemException(hRes, "Failed to write to file '{}'"_format(zen::PathFromHandle(m_File))); + } +} + +void +BasicFile::Flush() +{ + m_File.Flush(); +} + +uint64_t +BasicFile::FileSize() +{ + ULONGLONG Sz; + m_File.GetSize(Sz); + + return uint64_t(Sz); +} + +} // namespace zen diff --git a/zenstore/caslog.cpp b/zenstore/caslog.cpp index c648cea5e..0ef3ed1bd 100644 --- a/zenstore/caslog.cpp +++ b/zenstore/caslog.cpp @@ -125,7 +125,10 @@ CasLogFile::Replay(std::function<void(const void*)>&& Handler) m_File.Seek(LogBaseOffset, FILE_BEGIN); HRESULT hRes = m_File.Read(ReadBuffer.data(), gsl::narrow<DWORD>(LogDataSize)); - zen::ThrowIfFailed(hRes, "Failed to read log file"); + if (FAILED(hRes)) + { + zen::ThrowSystemException(hRes, "Failed to read log file"); + } for (int i = 0; i < LogEntryCount; ++i) { @@ -140,9 +143,7 @@ CasLogFile::Append(const void* DataPointer, uint64_t DataSize) if (FAILED(hRes)) { - throw std::system_error(GetLastError(), - std::system_category(), - "Failed to write to log file '{}'"_format(zen::PathFromHandle(m_File))); + zen::ThrowSystemException(hRes, "Failed to write to log file '{}'"_format(zen::PathFromHandle(m_File))); } } @@ -152,78 +153,4 @@ CasLogFile::Flush() m_File.Flush(); } -////////////////////////////////////////////////////////////////////////// - -void -CasBlobFile::Open(std::filesystem::path FileName, bool isCreate) -{ - const DWORD dwCreationDisposition = isCreate ? CREATE_ALWAYS : OPEN_EXISTING; - - HRESULT hRes = m_File.Create(FileName.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, dwCreationDisposition); - - if (FAILED(hRes)) - { - throw std::system_error(GetLastError(), std::system_category(), "Failed to open bucket sobs file '{}'"_format(FileName)); - } -} - -void -CasBlobFile::Read(void* Data, uint64_t Size, uint64_t Offset) -{ - OVERLAPPED Ovl{}; - - Ovl.Offset = DWORD(Offset & 0xffff'ffffu); - Ovl.OffsetHigh = DWORD(Offset >> 32); - - HRESULT hRes = m_File.Read(Data, gsl::narrow<DWORD>(Size), &Ovl); - - if (FAILED(hRes)) - { - throw std::system_error(GetLastError(), - std::system_category(), - "Failed to read from file '{}'"_format(zen::PathFromHandle(m_File))); - } -} - -IoBuffer -CasBlobFile::ReadAll() -{ - IoBuffer Buffer(FileSize()); - - Read((void*)Buffer.Data(), Buffer.Size(), 0); - - return Buffer; -} - -void -CasBlobFile::Write(const void* Data, uint64_t Size, uint64_t Offset) -{ - OVERLAPPED Ovl{}; - - Ovl.Offset = DWORD(Offset & 0xffff'ffffu); - Ovl.OffsetHigh = DWORD(Offset >> 32); - - HRESULT hRes = m_File.Write(Data, gsl::narrow<DWORD>(Size), &Ovl); - - if (FAILED(hRes)) - { - throw std::system_error(GetLastError(), std::system_category(), "Failed to write to file '{}'"_format(zen::PathFromHandle(m_File))); - } -} - -void -CasBlobFile::Flush() -{ - m_File.Flush(); -} - -uint64_t -CasBlobFile::FileSize() -{ - ULONGLONG Sz; - m_File.GetSize(Sz); - - return uint64_t(Sz); -} - } // namespace zen diff --git a/zenstore/cidstore.cpp b/zenstore/cidstore.cpp new file mode 100644 index 000000000..e041574a4 --- /dev/null +++ b/zenstore/cidstore.cpp @@ -0,0 +1,85 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "zenstore/cidstore.h" + +#include <zencore/filesystem.h> +#include <zenstore/CAS.h> +#include <zenstore/caslog.h> + +#include <spdlog/spdlog.h> +#include <filesystem> + +namespace zen { + +struct CidStore::CidState +{ + CidState(CasStore& InCasStore) : m_CasStore(InCasStore) {} + + struct IndexEntry + { + IoHash Uncompressed; + IoHash Compressed; + }; + + CasStore& m_CasStore; + TCasLogFile<IndexEntry> m_LogFile; + + RwLock m_Lock; + tsl::robin_map<IoHash, IoHash> m_CidMap; + + void AddCompressedCid(const IoHash& DecompressedId, const IoHash& Compressed) + { + RwLock::ExclusiveLockScope _(m_Lock); + m_CidMap.insert_or_assign(DecompressedId, Compressed); + m_LogFile.Append({.Uncompressed = DecompressedId, .Compressed = Compressed}); + } + + IoBuffer FindChunkByCid(const IoHash& DecompressedId) + { + if (auto It = m_CidMap.find(DecompressedId); It != m_CidMap.end()) + { + return m_CasStore.FindChunk(It->second); + } + + return IoBuffer(); + } + + void InitializeIndex(const std::filesystem::path& RootDir) + { + zen::CreateDirectories(RootDir); + std::filesystem::path SlogPath{RootDir / "cid.slog"}; + + bool IsNew = !std::filesystem::exists(SlogPath); + + m_LogFile.Open(SlogPath, IsNew); + + m_LogFile.Replay([&](const IndexEntry& Ie) { m_CidMap.insert_or_assign(Ie.Uncompressed, Ie.Compressed); }); + + spdlog::debug("CID index initialized: {} entries found", m_CidMap.size()); + } +}; + +////////////////////////////////////////////////////////////////////////// + +CidStore::CidStore(CasStore& InCasStore, const std::filesystem::path& RootDir) : m_Impl(std::make_unique<CidState>(InCasStore)) +{ + m_Impl->InitializeIndex(RootDir); +} + +CidStore::~CidStore() +{ +} + +void +CidStore::AddCompressedCid(const IoHash& DecompressedId, const IoHash& Compressed) +{ + m_Impl->AddCompressedCid(DecompressedId, Compressed); +} + +IoBuffer +CidStore::FindChunkByCid(const IoHash& DecompressedId) +{ + return m_Impl->FindChunkByCid(DecompressedId); +} + +} // namespace zen diff --git a/zenstore/compactcas.h b/zenstore/compactcas.h index 4d318c2e2..db115c85d 100644 --- a/zenstore/compactcas.h +++ b/zenstore/compactcas.h @@ -10,6 +10,7 @@ #include <zencore/thread.h> #include <zencore/uid.h> #include <zencore/windows.h> +#include <zenstore/basicfile.h> #include <zenstore/cas.h> #include <zenstore/caslog.h> @@ -52,8 +53,8 @@ private: CasStore::Stats& m_Stats; uint64_t m_PayloadAlignment = 1 << 4; bool m_IsInitialized = false; - CasBlobFile m_SmallObjectFile; - CasBlobFile m_SmallObjectIndex; + BasicFile m_SmallObjectFile; + BasicFile m_SmallObjectIndex; TCasLogFile<CasDiskIndexEntry> m_CasLog; RwLock m_LocationMapLock; diff --git a/zenstore/include/zenstore/CAS.h b/zenstore/include/zenstore/CAS.h index 8b9a66e3f..9c569f2aa 100644 --- a/zenstore/include/zenstore/CAS.h +++ b/zenstore/include/zenstore/CAS.h @@ -23,7 +23,7 @@ struct CasStoreConfiguration // Threshold below which values are considered 'tiny' and managed using the 'tiny values' strategy uint64_t TinyValueThreshold = 1024; - // Threshold above which values are considered 'tiny' and managed using the 'huge values' strategy + // Threshold above which values are considered 'huge' and managed using the 'huge values' strategy uint64_t HugeValueThreshold = 1024 * 1024; }; diff --git a/zenstore/include/zenstore/basicfile.h b/zenstore/include/zenstore/basicfile.h new file mode 100644 index 000000000..b38feb3da --- /dev/null +++ b/zenstore/include/zenstore/basicfile.h @@ -0,0 +1,34 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include <zencore/iobuffer.h> +#include <zencore/zencore.h> + +#include <zencore/windows.h> + +#include <atlfile.h> +#include <filesystem> + +namespace zen { + +/** + * Probably the most basic file abstraction in the universe + */ + +class BasicFile +{ +public: + void Open(std::filesystem::path FileName, bool IsCreate); + void Read(void* Data, uint64_t Size, uint64_t Offset); + void Write(const void* Data, uint64_t Size, uint64_t Offset); + void Flush(); + uint64_t FileSize(); + void* Handle() { return m_File; } + IoBuffer ReadAll(); + +private: + CAtlFile m_File; +}; + +} // namespace zen diff --git a/zenstore/include/zenstore/caslog.h b/zenstore/include/zenstore/caslog.h index b318577d7..aea855e4c 100644 --- a/zenstore/include/zenstore/caslog.h +++ b/zenstore/include/zenstore/caslog.h @@ -69,28 +69,14 @@ public: }); } - void Append(const T& Record) { CasLogFile::Append(&Record, sizeof Record); } - void Open(std::filesystem::path FileName, bool IsCreate) { CasLogFile::Open(FileName, sizeof(T), IsCreate); } -}; - -////////////////////////////////////////////////////////////////////////// -// -// This should go in its own header -// - -class CasBlobFile -{ -public: - void Open(std::filesystem::path FileName, bool IsCreate); - void Read(void* Data, uint64_t Size, uint64_t Offset); - void Write(const void* Data, uint64_t Size, uint64_t Offset); - void Flush(); - uint64_t FileSize(); - void* Handle() { return m_File; } - IoBuffer ReadAll(); + void Append(const T& Record) + { + // TODO: implement some more efficent path here so we don't end up with + // a syscall per append -private: - CAtlFile m_File; + CasLogFile::Append(&Record, sizeof Record); + } + void Open(std::filesystem::path FileName, bool IsCreate) { CasLogFile::Open(FileName, sizeof(T), IsCreate); } }; } // namespace zen diff --git a/zenstore/include/zenstore/cidstore.h b/zenstore/include/zenstore/cidstore.h new file mode 100644 index 000000000..e365b198e --- /dev/null +++ b/zenstore/include/zenstore/cidstore.h @@ -0,0 +1,38 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include <tsl/robin_map.h> +#include <zencore/iohash.h> + +namespace std::filesystem { +class path; +} + +namespace zen { + +class CasStore; +class IoBuffer; + +/** Content Store + * + * Data in the content store is referenced by content identifiers (CIDs), rather than their + * literal hash. This is required to map uncompressed hashes to compressed hashes and may + * be used to deal with other indirections in the future. + * + */ +class CidStore +{ +public: + CidStore(CasStore& InCasStore, const std::filesystem::path& RootDir); + ~CidStore(); + + void AddCompressedCid(const IoHash& DecompressedId, const IoHash& Compressed); + IoBuffer FindChunkByCid(const IoHash& DecompressedId); + +private: + struct CidState; + std::unique_ptr<CidState> m_Impl; +}; + +} // namespace zen diff --git a/zenstore/zenstore.vcxproj b/zenstore/zenstore.vcxproj index 06cb9db32..0e4caa92c 100644 --- a/zenstore/zenstore.vcxproj +++ b/zenstore/zenstore.vcxproj @@ -11,8 +11,10 @@ </ProjectConfiguration> </ItemGroup> <ItemGroup> + <ClCompile Include="basicfile.cpp" /> <ClCompile Include="CAS.cpp" /> <ClCompile Include="caslog.cpp" /> + <ClCompile Include="cidstore.cpp" /> <ClCompile Include="compactcas.cpp" /> <ClCompile Include="filecas.cpp" /> <ClCompile Include="gc.cpp" /> @@ -21,6 +23,8 @@ <ItemGroup> <ClInclude Include="compactcas.h" /> <ClInclude Include="filecas.h" /> + <ClInclude Include="include\zenstore\basicfile.h" /> + <ClInclude Include="include\zenstore\cidstore.h" /> <ClInclude Include="include\zenstore\gc.h" /> <ClInclude Include="include\zenstore\scrub.h" /> <ClInclude Include="include\zenstore\CAS.h" /> diff --git a/zenstore/zenstore.vcxproj.filters b/zenstore/zenstore.vcxproj.filters index 6ab5a7cb2..3dfb89dbf 100644 --- a/zenstore/zenstore.vcxproj.filters +++ b/zenstore/zenstore.vcxproj.filters @@ -5,11 +5,19 @@ <ClCompile Include="caslog.cpp" /> <ClCompile Include="compactcas.cpp" /> <ClCompile Include="filecas.cpp" /> + <ClCompile Include="gc.cpp" /> + <ClCompile Include="scrub.cpp" /> + <ClCompile Include="basicfile.cpp" /> + <ClCompile Include="cidstore.cpp" /> </ItemGroup> <ItemGroup> <ClInclude Include="compactcas.h" /> <ClInclude Include="filecas.h" /> <ClInclude Include="include\zenstore\CAS.h" /> <ClInclude Include="include\zenstore\caslog.h" /> + <ClInclude Include="include\zenstore\gc.h" /> + <ClInclude Include="include\zenstore\scrub.h" /> + <ClInclude Include="include\zenstore\basicfile.h" /> + <ClInclude Include="include\zenstore\cidstore.h" /> </ItemGroup> </Project>
\ No newline at end of file |