// Copyright Epic Games, Inc. All Rights Reserved. #include "compactcas.h" #include #if ZEN_WITH_TESTS # include # include # include # include # include #endif ////////////////////////////////////////////////////////////////////////// namespace zen { ////////////////////////////////////////////////////////////////////////// BlockStoreFile::BlockStoreFile(const std::filesystem::path& BlockPath) : m_Path(BlockPath) { } BlockStoreFile::~BlockStoreFile() { m_IoBuffer = IoBuffer(); m_File.Detach(); } const std::filesystem::path& BlockStoreFile::GetPath() const { return m_Path; } void BlockStoreFile::Open() { m_File.Open(m_Path, BasicFile::Mode::kDelete); void* FileHandle = m_File.Handle(); m_IoBuffer = IoBuffer(IoBuffer::File, FileHandle, 0, m_File.FileSize()); } void BlockStoreFile::Create(uint64_t InitialSize) { auto ParentPath = m_Path.parent_path(); if (!std::filesystem::is_directory(ParentPath)) { CreateDirectories(ParentPath); } m_File.Open(m_Path, BasicFile::Mode::kTruncateDelete); if (InitialSize > 0) { m_File.SetFileSize(InitialSize); } void* FileHandle = m_File.Handle(); m_IoBuffer = IoBuffer(IoBuffer::File, FileHandle, 0, InitialSize); } uint64_t BlockStoreFile::FileSize() { return m_File.FileSize(); } void BlockStoreFile::MarkAsDeleteOnClose(std::error_code& Ec) { m_File.MarkAsDeleteOnClose(Ec); } IoBuffer BlockStoreFile::GetChunk(uint64_t Offset, uint64_t Size) { return IoBuffer(m_IoBuffer, Offset, Size); } void BlockStoreFile::Read(void* Data, uint64_t Size, uint64_t FileOffset) { m_File.Read(Data, Size, FileOffset); } void BlockStoreFile::Write(const void* Data, uint64_t Size, uint64_t FileOffset) { m_File.Write(Data, Size, FileOffset); } void BlockStoreFile::Truncate(uint64_t Size) { m_File.SetFileSize(Size); } void BlockStoreFile::Flush() { m_File.Flush(); } void BlockStoreFile::StreamByteRange(uint64_t FileOffset, uint64_t Size, std::function&& ChunkFun) { m_File.StreamByteRange(FileOffset, Size, std::move(ChunkFun)); } #if ZEN_WITH_TESTS static bool operator==(const BlockStoreLocation& Lhs, const BlockStoreLocation& Rhs) { return Lhs.BlockIndex == Rhs.BlockIndex && Lhs.Offset == Rhs.Offset && Lhs.Size == Rhs.Size; } TEST_CASE("blockstore.blockstoredisklocation") { BlockStoreLocation Zero = BlockStoreLocation{.BlockIndex = 0, .Offset = 0, .Size = 0}; CHECK(Zero == BlockStoreDiskLocation(Zero, 4).Get(4)); BlockStoreLocation MaxBlockIndex = BlockStoreLocation{.BlockIndex = BlockStoreDiskLocation::MaxBlockIndex, .Offset = 0, .Size = 0}; CHECK(MaxBlockIndex == BlockStoreDiskLocation(MaxBlockIndex, 4).Get(4)); BlockStoreLocation MaxOffset = BlockStoreLocation{.BlockIndex = 0, .Offset = BlockStoreDiskLocation::MaxOffset * 4, .Size = 0}; CHECK(MaxOffset == BlockStoreDiskLocation(MaxOffset, 4).Get(4)); BlockStoreLocation MaxSize = BlockStoreLocation{.BlockIndex = 0, .Offset = 0, .Size = std::numeric_limits::max()}; CHECK(MaxSize == BlockStoreDiskLocation(MaxSize, 4).Get(4)); BlockStoreLocation MaxBlockIndexAndOffset = BlockStoreLocation{.BlockIndex = BlockStoreDiskLocation::MaxBlockIndex, .Offset = BlockStoreDiskLocation::MaxOffset * 4, .Size = 0}; CHECK(MaxBlockIndexAndOffset == BlockStoreDiskLocation(MaxBlockIndexAndOffset, 4).Get(4)); BlockStoreLocation MaxAll = BlockStoreLocation{.BlockIndex = BlockStoreDiskLocation::MaxBlockIndex, .Offset = BlockStoreDiskLocation::MaxOffset * 4, .Size = std::numeric_limits::max()}; CHECK(MaxAll == BlockStoreDiskLocation(MaxAll, 4).Get(4)); BlockStoreLocation MaxAll4096 = BlockStoreLocation{.BlockIndex = BlockStoreDiskLocation::MaxBlockIndex, .Offset = BlockStoreDiskLocation::MaxOffset * 4096, .Size = std::numeric_limits::max()}; CHECK(MaxAll4096 == BlockStoreDiskLocation(MaxAll4096, 4096).Get(4096)); BlockStoreLocation Middle = BlockStoreLocation{.BlockIndex = (BlockStoreDiskLocation::MaxBlockIndex) / 2, .Offset = ((BlockStoreDiskLocation::MaxOffset) / 2) * 4, .Size = std::numeric_limits::max() / 2}; CHECK(Middle == BlockStoreDiskLocation(Middle, 4).Get(4)); } TEST_CASE("blockstore.blockfile") { ScopedTemporaryDirectory TempDir; auto RootDirectory = TempDir.Path() / "blocks"; CreateDirectories(RootDirectory); { BlockStoreFile File1(RootDirectory / "1"); File1.Create(16384); CHECK(File1.FileSize() == 16384); File1.Write("data", 5, 0); IoBuffer DataChunk = File1.GetChunk(0, 5); File1.Write("boop", 5, 5); IoBuffer BoopChunk = File1.GetChunk(5, 5); const char* Data = static_cast(DataChunk.GetData()); CHECK(std::string(Data) == "data"); const char* Boop = static_cast(BoopChunk.GetData()); CHECK(std::string(Boop) == "boop"); File1.Flush(); } { BlockStoreFile File1(RootDirectory / "1"); File1.Open(); char DataRaw[5]; File1.Read(DataRaw, 5, 0); CHECK(std::string(DataRaw) == "data"); IoBuffer DataChunk = File1.GetChunk(0, 5); char BoopRaw[5]; File1.Read(BoopRaw, 5, 5); CHECK(std::string(BoopRaw) == "boop"); IoBuffer BoopChunk = File1.GetChunk(5, 5); const char* Data = static_cast(DataChunk.GetData()); CHECK(std::string(Data) == "data"); const char* Boop = static_cast(BoopChunk.GetData()); CHECK(std::string(Boop) == "boop"); } { IoBuffer DataChunk; IoBuffer BoopChunk; { BlockStoreFile File1(RootDirectory / "1"); File1.Open(); DataChunk = File1.GetChunk(0, 5); BoopChunk = File1.GetChunk(5, 5); } CHECK(std::filesystem::exists(RootDirectory / "1")); const char* Data = static_cast(DataChunk.GetData()); CHECK(std::string(Data) == "data"); const char* Boop = static_cast(BoopChunk.GetData()); CHECK(std::string(Boop) == "boop"); } CHECK(std::filesystem::exists(RootDirectory / "1")); { IoBuffer DataChunk; IoBuffer BoopChunk; { BlockStoreFile File1(RootDirectory / "1"); File1.Open(); std::error_code Ec; File1.MarkAsDeleteOnClose(Ec); CHECK(!Ec); DataChunk = File1.GetChunk(0, 5); BoopChunk = File1.GetChunk(5, 5); } const char* Data = static_cast(DataChunk.GetData()); CHECK(std::string(Data) == "data"); const char* Boop = static_cast(BoopChunk.GetData()); CHECK(std::string(Boop) == "boop"); } CHECK(!std::filesystem::exists(RootDirectory / "1")); } #endif void blockstore_forcelink() { } } // namespace zen