From 8b5213bd050634e17b6215cbe25228b4cc6128ef Mon Sep 17 00:00:00 2001 From: Stefan Boberg Date: Sun, 19 Sep 2021 18:35:56 +0200 Subject: Changed BasicFile implementation * No longer uses ATL on Windows (we just use raw Win32 API) * Added non-throwing Open() implementation * Added beginnings of a test suite, for verifying cross-platform implementation --- zenstore/basicfile.cpp | 90 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 17 deletions(-) (limited to 'zenstore/basicfile.cpp') diff --git a/zenstore/basicfile.cpp b/zenstore/basicfile.cpp index 35ccdd042..75de638cf 100644 --- a/zenstore/basicfile.cpp +++ b/zenstore/basicfile.cpp @@ -5,7 +5,9 @@ #include #include #include +#include +#include #include #include @@ -13,16 +15,54 @@ namespace zen { using namespace fmt::literals; +BasicFile::~BasicFile() +{ + Close(); +} + void -BasicFile::Open(std::filesystem::path FileName, bool isCreate) +BasicFile::Open(std::filesystem::path FileName, bool IsCreate) { - const DWORD dwCreationDisposition = isCreate ? CREATE_ALWAYS : OPEN_EXISTING; + std::error_code Ec; + Open(FileName, IsCreate, Ec); - HRESULT hRes = m_File.Create(FileName.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, dwCreationDisposition); + if (Ec) + { + throw std::system_error(Ec, "failed to open file '{}'"_format(FileName)); + } +} - if (FAILED(hRes)) +void +BasicFile::Open(std::filesystem::path FileName, bool IsCreate, std::error_code& Ec) +{ + const DWORD dwCreationDisposition = IsCreate ? CREATE_ALWAYS : OPEN_EXISTING; + const DWORD dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; + const DWORD dwShareMode = FILE_SHARE_READ; + const DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; + HANDLE hTemplateFile = nullptr; + + HANDLE FileHandle = CreateFile(FileName.c_str(), + dwDesiredAccess, + dwShareMode, + /* lpSecurityAttributes */ nullptr, + dwCreationDisposition, + dwFlagsAndAttributes, + hTemplateFile); + + if (FileHandle == INVALID_HANDLE_VALUE) { - ThrowSystemException(hRes, "Failed to open bucket sobs file '{}'"_format(FileName)); + Ec = zen::MakeErrorCodeFromLastError(); + } + + m_FileHandle = FileHandle; +} + +void +BasicFile::Close() +{ + if (m_FileHandle) + { + ::CloseHandle(m_FileHandle); } } @@ -34,11 +74,14 @@ BasicFile::Read(void* Data, uint64_t Size, uint64_t Offset) Ovl.Offset = DWORD(Offset & 0xffff'ffffu); Ovl.OffsetHigh = DWORD(Offset >> 32); - HRESULT hRes = m_File.Read(Data, gsl::narrow(Size), &Ovl); + DWORD dwNumberOfBytesToRead = gsl::narrow(Size); + DWORD dwNumberOfBytesRead = 0; + + BOOL Success = ::ReadFile(m_FileHandle, Data, dwNumberOfBytesToRead, &dwNumberOfBytesRead, &Ovl); - if (FAILED(hRes)) + if (!Success) { - ThrowSystemException(hRes, "Failed to read from file '{}'"_format(zen::PathFromHandle(m_File))); + ThrowLastError("Failed to read from file '{}'"_format(zen::PathFromHandle(m_FileHandle))); } } @@ -60,33 +103,46 @@ BasicFile::Write(const void* Data, uint64_t Size, uint64_t Offset) Ovl.Offset = DWORD(Offset & 0xffff'ffffu); Ovl.OffsetHigh = DWORD(Offset >> 32); - HRESULT hRes = m_File.Write(Data, gsl::narrow(Size), &Ovl); + DWORD dwNumberOfBytesToWrite = gsl::narrow(Size); + DWORD dwNumberOfBytesWritten = 0; + + BOOL Success = ::WriteFile(m_FileHandle, Data, dwNumberOfBytesToWrite, &dwNumberOfBytesWritten, &Ovl); - if (FAILED(hRes)) + if (!Success) { - ThrowSystemException(hRes, "Failed to write to file '{}'"_format(zen::PathFromHandle(m_File))); + ThrowLastError("Failed to write to file '{}'"_format(zen::PathFromHandle(m_FileHandle))); } } void BasicFile::Flush() { - m_File.Flush(); + FlushFileBuffers(m_FileHandle); } uint64_t BasicFile::FileSize() { - ULONGLONG Sz; - m_File.GetSize(Sz); + ULARGE_INTEGER liFileSize; + liFileSize.LowPart = ::GetFileSize(m_FileHandle, &liFileSize.HighPart); - return uint64_t(Sz); + return uint64_t(liFileSize.QuadPart); +} + +TEST_CASE("BasicFile") +{ + ScopedCurrentDirectoryChange _; + + BasicFile File1; + CHECK_THROWS(File1.Open("zonk", false)); + CHECK_NOTHROW(File1.Open("zonk", true)); + CHECK_NOTHROW(File1.Write("abcd", 4, 0)); + CHECK(File1.FileSize() == 4); } void -BasicFile::Close() +basicfile_forcelink() { - m_File.Close(); } } // namespace zen -- cgit v1.2.3 From fe068c76e0f38dabf80bff2730ff9c713763d707 Mon Sep 17 00:00:00 2001 From: Stefan Boberg Date: Sun, 19 Sep 2021 23:17:44 +0200 Subject: Added BasicFile::StreamFile helper function to support reading large files in a chunked fashion (will be using memory-mapped strategy in the future where it makes sense) --- zenstore/basicfile.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'zenstore/basicfile.cpp') diff --git a/zenstore/basicfile.cpp b/zenstore/basicfile.cpp index 75de638cf..126f2a8b3 100644 --- a/zenstore/basicfile.cpp +++ b/zenstore/basicfile.cpp @@ -95,6 +95,29 @@ BasicFile::ReadAll() return Buffer; } +void +BasicFile::StreamFile(std::function&& ChunkFun) +{ + const uint64_t ChunkSize = 128 * 1024; + IoBuffer ReadBuffer{ChunkSize}; + void* BufferPtr = ReadBuffer.MutableData(); + + uint64_t RemainBytes = FileSize(); + uint64_t CurrentOffset = 0; + + while (RemainBytes) + { + const uint64_t ThisChunkBytes = zen::Min(ChunkSize, RemainBytes); + + Read(BufferPtr, ThisChunkBytes, CurrentOffset); + + ChunkFun(BufferPtr, ThisChunkBytes); + + CurrentOffset += ThisChunkBytes; + RemainBytes -= ThisChunkBytes; + } +} + void BasicFile::Write(const void* Data, uint64_t Size, uint64_t Offset) { -- cgit v1.2.3 From 6240dbc8aac714998e38b92850c3bff3d579f2ab Mon Sep 17 00:00:00 2001 From: Stefan Boberg Date: Sun, 19 Sep 2021 23:30:03 +0200 Subject: Implemented BasicFile::StreamByteRange --- zenstore/basicfile.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'zenstore/basicfile.cpp') diff --git a/zenstore/basicfile.cpp b/zenstore/basicfile.cpp index 126f2a8b3..0b92a8979 100644 --- a/zenstore/basicfile.cpp +++ b/zenstore/basicfile.cpp @@ -97,13 +97,19 @@ BasicFile::ReadAll() void BasicFile::StreamFile(std::function&& ChunkFun) +{ + StreamByteRange(0, FileSize(), std::move(ChunkFun)); +} + +void +BasicFile::StreamByteRange(uint64_t FileOffset, uint64_t Size, std::function&& ChunkFun) { const uint64_t ChunkSize = 128 * 1024; IoBuffer ReadBuffer{ChunkSize}; void* BufferPtr = ReadBuffer.MutableData(); - uint64_t RemainBytes = FileSize(); - uint64_t CurrentOffset = 0; + uint64_t RemainBytes = Size; + uint64_t CurrentOffset = FileOffset; while (RemainBytes) { -- cgit v1.2.3