diff options
-rw-r--r-- | include/cru/common/io/FileStream.hpp | 5 | ||||
-rw-r--r-- | include/cru/common/io/Win32FileStream.hpp | 53 | ||||
-rw-r--r-- | src/common/CMakeLists.txt | 6 | ||||
-rw-r--r-- | src/common/io/Win32FileStream.cpp | 127 | ||||
-rw-r--r-- | test/common/CMakeLists.txt | 4 | ||||
-rw-r--r-- | test/common/io/Win32FileStreamTest.cpp | 32 |
6 files changed, 227 insertions, 0 deletions
diff --git a/include/cru/common/io/FileStream.hpp b/include/cru/common/io/FileStream.hpp index da7849cd..fdde13fa 100644 --- a/include/cru/common/io/FileStream.hpp +++ b/include/cru/common/io/FileStream.hpp @@ -5,4 +5,9 @@ namespace cru::io { using FileStream = UnixFileStream; } +#elif CRU_PLATFORM_WINDOWS +#include "Win32FileStream.hpp" +namespace cru::io { +using FileStream = Win32FileStream; +} #endif diff --git a/include/cru/common/io/Win32FileStream.hpp b/include/cru/common/io/Win32FileStream.hpp new file mode 100644 index 00000000..08600a8e --- /dev/null +++ b/include/cru/common/io/Win32FileStream.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include "../PreConfig.hpp" + +#ifdef CRU_PLATFORM_WINDOWS + +#include "../String.hpp" +#include "OpenFileFlag.hpp" +#include "Stream.hpp" +#include "cru/common/Base.hpp" + +namespace cru::io { +namespace details { +class Win32FileStreamPrivate; +} + +class CRU_BASE_API Win32FileStream : public Stream { + public: + Win32FileStream(String path, OpenFileFlag flags); + + CRU_DELETE_COPY(Win32FileStream) + CRU_DELETE_MOVE(Win32FileStream) + + ~Win32FileStream() override; + + public: + bool CanSeek() override; + Index Tell() override; + void Seek(Index offset, SeekOrigin origin = SeekOrigin::Current) override; + + bool CanRead() override; + Index Read(std::byte* buffer, Index offset, Index size) override; + using Stream::Read; + + bool CanWrite() override; + Index Write(const std::byte* buffer, Index offset, Index size) override; + using Stream::Write; + + void Close() override; + + private: + void CheckClosed(); + + private: + String path_; + OpenFileFlag flags_; + bool closed_ = false; + + details::Win32FileStreamPrivate* p_; +}; +} // namespace cru::io + +#endif diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 40c85ef0..d8462abf 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -21,6 +21,12 @@ if (UNIX) endif() if (WIN32) + target_sources(cru_base PRIVATE + io/Win32FileStream.cpp + ) +endif() + +if (WIN32) target_compile_definitions(cru_base PUBLIC CRU_PLATFORM_WINDOWS) elseif(APPLE) target_compile_definitions(cru_base PUBLIC CRU_PLATFORM_OSX) diff --git a/src/common/io/Win32FileStream.cpp b/src/common/io/Win32FileStream.cpp new file mode 100644 index 00000000..e7ee0e6a --- /dev/null +++ b/src/common/io/Win32FileStream.cpp @@ -0,0 +1,127 @@ +#include "cru/common/io/Win32FileStream.hpp" + +#include "cru/common/Exception.hpp" +#include "cru/common/io/OpenFileFlag.hpp" + +#include <Windows.h> + +namespace cru::io { +namespace details { +struct Win32FileStreamPrivate { + HANDLE handle; +}; +} // namespace details + +Win32FileStream::Win32FileStream(String path, OpenFileFlag flags) + : path_(std::move(path)), flags_(flags) { + p_ = new details::Win32FileStreamPrivate(); + + DWORD dwDesiredAccess = 0; + if (flags & OpenFileFlags::Read) { + dwDesiredAccess |= GENERIC_READ; + } + if (flags & OpenFileFlags::Write) { + dwDesiredAccess |= GENERIC_WRITE; + } + + DWORD dwCreationDisposition = 0; + if (flags & OpenFileFlags::Create) { + if (flags & OpenFileFlags::ThrowOnExist) { + dwCreationDisposition = CREATE_NEW; + } else { + dwCreationDisposition = OPEN_ALWAYS; + } + } else { + dwCreationDisposition = OPEN_EXISTING; + } + + p_->handle = + ::CreateFileW(path_.WinCStr(), dwDesiredAccess, 0, nullptr, + dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, nullptr); + + if (p_->handle == INVALID_HANDLE_VALUE) { + throw Exception(u"Failed to call CreateFileW."); + } +} + +Win32FileStream::~Win32FileStream() { + Close(); + delete p_; +} + +bool Win32FileStream::CanSeek() { return true; } + +Index Win32FileStream::Tell() { + CheckClosed(); + + LARGE_INTEGER offset; + offset.QuadPart = 0; + LARGE_INTEGER file_pointer; + if (::SetFilePointerEx(p_->handle, offset, &file_pointer, FILE_CURRENT) == + 0) { + throw Exception(u"Failed to call SetFilePointerEx."); + } + + return file_pointer.QuadPart; +} + +void Win32FileStream::Seek(Index offset, SeekOrigin origin) { + CheckClosed(); + + DWORD dwMoveMethod = 0; + + if (origin == SeekOrigin::Current) { + dwMoveMethod = FILE_CURRENT; + } else if (origin == SeekOrigin::End) { + dwMoveMethod = FILE_END; + } else { + dwMoveMethod = FILE_BEGIN; + } + + LARGE_INTEGER n_offset; + n_offset.QuadPart = offset; + if (::SetFilePointerEx(p_->handle, n_offset, nullptr, dwMoveMethod) == 0) { + throw Exception(u"Failed to call SetFilePointerEx."); + } +} + +bool Win32FileStream::CanRead() { return true; } + +Index Win32FileStream::Read(std::byte* buffer, Index offset, Index size) { + CheckClosed(); + + DWORD dwRead; + if (::ReadFile(p_->handle, buffer + offset, size, &dwRead, nullptr) == 0) { + throw Exception(u"Failed to call ReadFile."); + } + return dwRead; +} + +bool Win32FileStream::CanWrite() { return true; } + +Index Win32FileStream::Write(const std::byte* buffer, Index offset, + Index size) { + CheckClosed(); + + DWORD dwWritten; + if (::WriteFile(p_->handle, buffer + offset, size, &dwWritten, nullptr) == + 0) { + throw Exception(u"Failed to call WriteFile."); + } + + return dwWritten; +} + +void Win32FileStream::Close() { + if (closed_) return; + + ::CloseHandle(p_->handle); + + closed_ = true; +} + +void Win32FileStream::CheckClosed() { + if (closed_) throw Exception(u"Stream is closed."); +} + +} // namespace cru::io diff --git a/test/common/CMakeLists.txt b/test/common/CMakeLists.txt index 7926437c..38c15000 100644 --- a/test/common/CMakeLists.txt +++ b/test/common/CMakeLists.txt @@ -13,6 +13,10 @@ if (UNIX) endif() if (WIN32) + target_sources(cru_base_test PRIVATE + io/Win32FileStreamTest.cpp + ) + add_custom_command(TARGET cru_base_test POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:cru_base_test> $<TARGET_FILE_DIR:cru_base_test> COMMAND_EXPAND_LISTS diff --git a/test/common/io/Win32FileStreamTest.cpp b/test/common/io/Win32FileStreamTest.cpp new file mode 100644 index 00000000..6c3edb6f --- /dev/null +++ b/test/common/io/Win32FileStreamTest.cpp @@ -0,0 +1,32 @@ +#include "cru/common/io/OpenFileFlag.hpp" +#include "cru/common/io/Win32FileStream.hpp" + +#include <gtest/gtest.h> + +#include <cstdio> +#include <filesystem> + +TEST(UnixFileStream, Work) { + using namespace cru; + using namespace cru::io; + + auto temp_file_path = + (std::filesystem::temp_directory_path() / "cru_test_temp.XXXXXX") + .native(); + _wmktemp(temp_file_path.data()); + + String path = temp_file_path; + + Win32FileStream file(path, OpenFileFlags::Write | OpenFileFlags::Create); + file.Write("abc", 3); + file.Close(); + + Win32FileStream file2(path, OpenFileFlags::Read); + auto buffer = std::make_unique<std::byte[]>(3); + file2.Read(buffer.get(), 3); + ASSERT_EQ(std::string_view(reinterpret_cast<const char*>(buffer.get()), 3), + "abc"); + file2.Close(); + + std::filesystem::remove(temp_file_path); +} |