aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2021-05-25 09:54:09 +0200
committerStefan Boberg <[email protected]>2021-05-25 09:54:09 +0200
commit882e93e4786f9e67e0edf6c276b16bb40848bae9 (patch)
treec5d4c45679c676c6aeb804c7601f43340b78ea0b
parentUpdated structured cache description (diff)
parentCompile out all rocksdb code for a smaller binary (diff)
downloadzen-882e93e4786f9e67e0edf6c276b16bb40848bae9.tar.xz
zen-882e93e4786f9e67e0edf6c276b16bb40848bae9.zip
Merged from origin/main
-rw-r--r--zencore/blake3.cpp21
-rw-r--r--zencore/compositebuffer.cpp341
-rw-r--r--zencore/compress.cpp758
-rw-r--r--zencore/crc32.cpp546
-rw-r--r--zencore/except.cpp20
-rw-r--r--zencore/filesystem.cpp6
-rw-r--r--zencore/httpserver.cpp16
-rw-r--r--zencore/include/zencore/blake3.h21
-rw-r--r--zencore/include/zencore/compositebuffer.h133
-rw-r--r--zencore/include/zencore/compress.h131
-rw-r--r--zencore/include/zencore/crc32.h13
-rw-r--r--zencore/include/zencore/except.h18
-rw-r--r--zencore/include/zencore/httpserver.h13
-rw-r--r--zencore/include/zencore/intmath.h4
-rw-r--r--zencore/include/zencore/iobuffer.h56
-rw-r--r--zencore/include/zencore/iohash.h16
-rw-r--r--zencore/include/zencore/memory.h188
-rw-r--r--zencore/include/zencore/refcount.h48
-rw-r--r--zencore/include/zencore/sharedbuffer.h52
-rw-r--r--zencore/include/zencore/thread.h30
-rw-r--r--zencore/include/zencore/uid.h3
-rw-r--r--zencore/iobuffer.cpp2
-rw-r--r--zencore/sharedbuffer.cpp26
-rw-r--r--zencore/thread.cpp4
-rw-r--r--zencore/uid.cpp11
-rw-r--r--zencore/zencore.cpp22
-rw-r--r--zencore/zencore.vcxproj6
-rw-r--r--zencore/zencore.vcxproj.filters6
-rw-r--r--zenserver-test/zenserver-test.cpp73
-rw-r--r--zenserver/cache/cachestore.cpp1027
-rw-r--r--zenserver/cache/cachestore.h91
-rw-r--r--zenserver/cache/kvcache.cpp2
-rw-r--r--zenserver/cache/structuredcache.cpp270
-rw-r--r--zenserver/cache/structuredcache.h17
-rw-r--r--zenserver/cache/structuredcachestore.cpp625
-rw-r--r--zenserver/cache/structuredcachestore.h125
-rw-r--r--zenserver/experimental/usnjournal.cpp5
-rw-r--r--zenserver/projectstore.cpp176
-rw-r--r--zenserver/projectstore.h21
-rw-r--r--zenserver/upstream/jupiter.cpp33
-rw-r--r--zenserver/upstream/jupiter.h4
-rw-r--r--zenserver/zenserver.cpp10
-rw-r--r--zenserver/zenserver.vcxproj2
-rw-r--r--zenserver/zenserver.vcxproj.filters2
-rw-r--r--zenstore/basicfile.cpp86
-rw-r--r--zenstore/caslog.cpp83
-rw-r--r--zenstore/cidstore.cpp85
-rw-r--r--zenstore/compactcas.h5
-rw-r--r--zenstore/include/zenstore/CAS.h2
-rw-r--r--zenstore/include/zenstore/basicfile.h34
-rw-r--r--zenstore/include/zenstore/caslog.h28
-rw-r--r--zenstore/include/zenstore/cidstore.h38
-rw-r--r--zenstore/zenstore.vcxproj4
-rw-r--r--zenstore/zenstore.vcxproj.filters8
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