diff options
author | crupest <crupest@outlook.com> | 2024-10-06 13:57:39 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2024-10-06 13:57:39 +0800 |
commit | dfe62dcf8bcefc523b466e127c3edc4dc2756629 (patch) | |
tree | 1c751a14ba0da07ca2ff805633f97568060aa4c9 /src/base/platform | |
parent | f51eb955e188858272230a990565931e7403f23b (diff) | |
download | cru-dfe62dcf8bcefc523b466e127c3edc4dc2756629.tar.gz cru-dfe62dcf8bcefc523b466e127c3edc4dc2756629.tar.bz2 cru-dfe62dcf8bcefc523b466e127c3edc4dc2756629.zip |
Rename common to base.
Diffstat (limited to 'src/base/platform')
-rw-r--r-- | src/base/platform/Exception.cpp | 1 | ||||
-rw-r--r-- | src/base/platform/osx/Convert.cpp | 29 | ||||
-rw-r--r-- | src/base/platform/osx/Exception.cpp | 1 | ||||
-rw-r--r-- | src/base/platform/unix/PosixSpawnSubProcess.cpp | 204 | ||||
-rw-r--r-- | src/base/platform/unix/UnixFileStream.cpp | 106 | ||||
-rw-r--r-- | src/base/platform/unix/UnixPipe.cpp | 51 | ||||
-rw-r--r-- | src/base/platform/web/WebException.cpp | 1 | ||||
-rw-r--r-- | src/base/platform/win/BridgeComStream.cpp | 106 | ||||
-rw-r--r-- | src/base/platform/win/BrigdeComStream.h | 43 | ||||
-rw-r--r-- | src/base/platform/win/ComAutoInit.cpp | 15 | ||||
-rw-r--r-- | src/base/platform/win/DebugLogTarget.cpp | 10 | ||||
-rw-r--r-- | src/base/platform/win/Exception.cpp | 38 | ||||
-rw-r--r-- | src/base/platform/win/StreamConvert.cpp | 28 | ||||
-rw-r--r-- | src/base/platform/win/Win32FileStream.cpp | 122 | ||||
-rw-r--r-- | src/base/platform/win/Win32FileStreamPrivate.h | 13 |
15 files changed, 768 insertions, 0 deletions
diff --git a/src/base/platform/Exception.cpp b/src/base/platform/Exception.cpp new file mode 100644 index 00000000..b5e8c5b9 --- /dev/null +++ b/src/base/platform/Exception.cpp @@ -0,0 +1 @@ +#include "cru/base/platform/Exception.h" diff --git a/src/base/platform/osx/Convert.cpp b/src/base/platform/osx/Convert.cpp new file mode 100644 index 00000000..1a6deb8f --- /dev/null +++ b/src/base/platform/osx/Convert.cpp @@ -0,0 +1,29 @@ +#include "cru/base/platform/osx/Convert.h" + +namespace cru::platform::osx { +CFStringRef Convert(const String& string) { + return CFStringCreateWithBytes( + nullptr, reinterpret_cast<const UInt8*>(string.data()), + string.size() * sizeof(std::uint16_t), kCFStringEncodingUTF16, false); +} + +String Convert(CFStringRef string) { + auto length = CFStringGetLength(string); + + String result; + + for (int i = 0; i < length; i++) { + result.AppendCodePoint(CFStringGetCharacterAtIndex(string, i)); + } + + return result; +} + +CFRange Convert(const Range& range) { + return CFRangeMake(range.position, range.count); +} + +Range Convert(const CFRange& range) { + return Range(range.location, range.length); +} +} // namespace cru::platform::osx diff --git a/src/base/platform/osx/Exception.cpp b/src/base/platform/osx/Exception.cpp new file mode 100644 index 00000000..a60448dd --- /dev/null +++ b/src/base/platform/osx/Exception.cpp @@ -0,0 +1 @@ +#include "cru/base/platform//osx/Exception.h" diff --git a/src/base/platform/unix/PosixSpawnSubProcess.cpp b/src/base/platform/unix/PosixSpawnSubProcess.cpp new file mode 100644 index 00000000..75d48cc2 --- /dev/null +++ b/src/base/platform/unix/PosixSpawnSubProcess.cpp @@ -0,0 +1,204 @@ +#include "cru/base/platform/unix/PosixSpawnSubProcess.h" +#include "cru/base/Exception.h" +#include "cru/base/Format.h" +#include "cru/base/Guard.h" +#include "cru/base/String.h" +#include "cru/base/SubProcess.h" +#include "cru/base/log/Logger.h" + +#include <signal.h> +#include <spawn.h> +#include <sys/wait.h> +#include <unistd.h> +#include <memory> +#include <unordered_map> + +namespace cru::platform::unix { +PosixSpawnSubProcessImpl::PosixSpawnSubProcessImpl() + : pid_(0), + exit_code_(0), + stdin_pipe_(UnixPipe::Usage::Send, false), + stdout_pipe_(UnixPipe::Usage::Receive, false), + stderr_pipe_(UnixPipe::Usage::Receive, false) { + stdin_stream_ = std::make_unique<UnixFileStream>( + stdin_pipe_.GetSelfFileDescriptor(), false, false, true, true); + stdout_stream_ = std::make_unique<UnixFileStream>( + stdout_pipe_.GetSelfFileDescriptor(), false, true, false, true); + stderr_stream_ = std::make_unique<UnixFileStream>( + stderr_pipe_.GetSelfFileDescriptor(), false, true, false, true); + + stdout_buffer_stream_ = + std::make_unique<io::AutoReadStream>(stdout_stream_.get(), false); + stderr_buffer_stream_ = + std::make_unique<io::AutoReadStream>(stderr_stream_.get(), false); +} + +PosixSpawnSubProcessImpl::~PosixSpawnSubProcessImpl() {} + +namespace { +char** CreateCstrArray(const std::vector<String>& argv) { + std::vector<Buffer> utf8_argv; + for (const auto& arg : argv) { + utf8_argv.push_back(arg.ToUtf8Buffer()); + } + char** result = new char*[utf8_argv.size() + 1]; + for (int i = 0; i < utf8_argv.size(); i++) { + result[i] = reinterpret_cast<char*>(utf8_argv[i].Detach()); + } + result[utf8_argv.size()] = nullptr; + return result; +} + +char** CreateCstrArray(const std::unordered_map<String, String>& envp) { + std::vector<String> str_array; + for (auto& [key, value] : envp) { + str_array.push_back(cru::Format(u"{}={}", key, value)); + } + return CreateCstrArray(str_array); +} + +void DestroyCstrArray(char** argv) { + int index = 0; + char* arg = argv[index]; + while (arg) { + delete[] arg; + index++; + arg = argv[index]; + } + delete[] argv; +} +} // namespace + +void PosixSpawnSubProcessImpl::PlatformCreateProcess( + const SubProcessStartInfo& start_info) { + int error; + auto check_error = [&error](String message) { + if (error == 0) return; + std::unique_ptr<ErrnoException> inner(new ErrnoException({}, error)); + throw SubProcessFailedToStartException(std::move(message), + std::move(inner)); + }; + + posix_spawn_file_actions_t file_actions; + error = posix_spawn_file_actions_init(&file_actions); + check_error(u"Failed to call posix_spawn_file_actions_init."); + Guard file_actions_guard( + [&file_actions] { posix_spawn_file_actions_destroy(&file_actions); }); + + // dup2 stdin/stdout/stderr + error = posix_spawn_file_actions_adddup2( + &file_actions, stdin_pipe_.GetOtherFileDescriptor(), STDIN_FILENO); + check_error(u"Failed to call posix_spawn_file_actions_adddup2 on stdin."); + error = posix_spawn_file_actions_adddup2( + &file_actions, stdout_pipe_.GetOtherFileDescriptor(), STDOUT_FILENO); + check_error(u"Failed to call posix_spawn_file_actions_adddup2 on stdout."); + error = posix_spawn_file_actions_adddup2( + &file_actions, stderr_pipe_.GetOtherFileDescriptor(), STDERR_FILENO); + check_error(u"Failed to call posix_spawn_file_actions_adddup2 on stderr."); + + error = posix_spawn_file_actions_addclose( + &file_actions, stdin_pipe_.GetOtherFileDescriptor()); + check_error( + u"Failed to call posix_spawn_file_actions_addclose on self fd of stdin."); + error = posix_spawn_file_actions_addclose( + &file_actions, stdout_pipe_.GetOtherFileDescriptor()); + check_error( + u"Failed to call posix_spawn_file_actions_addclose on self fd stdout."); + error = posix_spawn_file_actions_addclose( + &file_actions, stderr_pipe_.GetOtherFileDescriptor()); + check_error( + u"Failed to call posix_spawn_file_actions_addclose on self fd stderr."); + + error = posix_spawn_file_actions_addclose( + &file_actions, stdin_pipe_.GetSelfFileDescriptor()); + check_error( + u"Failed to call posix_spawn_file_actions_addclose on parent fd of " + u"stdin."); + error = posix_spawn_file_actions_addclose( + &file_actions, stdout_pipe_.GetSelfFileDescriptor()); + check_error( + u"Failed to call posix_spawn_file_actions_addclose on parent fd of " + u"stdout."); + error = posix_spawn_file_actions_addclose( + &file_actions, stderr_pipe_.GetSelfFileDescriptor()); + check_error( + u"Failed to call posix_spawn_file_actions_addclose on parent fd of " + u"stderr."); + + posix_spawnattr_t attr; + error = posix_spawnattr_init(&attr); + check_error(u"Failed to call posix_spawnattr_init."); + Guard attr_guard([&attr] { posix_spawnattr_destroy(&attr); }); + +#ifdef CRU_PLATFORM_OSX + error = posix_spawnattr_setflags(&attr, POSIX_SPAWN_CLOEXEC_DEFAULT); + check_error(u"Failed to set flag POSIX_SPAWN_CLOEXEC_DEFAULT (osx)."); +#endif + + auto exe = start_info.program.ToUtf8(); + std::vector<String> arguments{start_info.program}; + arguments.insert(arguments.cend(), start_info.arguments.cbegin(), + start_info.arguments.cend()); + + auto argv = CreateCstrArray(arguments); + Guard argv_guard([argv] { DestroyCstrArray(argv); }); + + auto envp = CreateCstrArray(start_info.environments); + Guard envp_guard([envp] { DestroyCstrArray(envp); }); + + error = posix_spawnp(&pid_, exe.c_str(), &file_actions, &attr, argv, envp); + check_error(u"Failed to call posix_spawnp."); + + error = ::close(stdin_pipe_.GetOtherFileDescriptor()); + check_error(u"Failed to close child stdin."); + error = ::close(stdout_pipe_.GetOtherFileDescriptor()); + check_error(u"Failed to close child stdout."); + error = ::close(stderr_pipe_.GetOtherFileDescriptor()); + check_error(u"Failed to close child stderr."); +} + +SubProcessExitResult PosixSpawnSubProcessImpl::PlatformWaitForProcess() { + int wstatus; + + while (waitpid(pid_, &wstatus, 0) == -1) { + if (errno == EINTR) { + CRU_LOG_INFO(u"Waitpid is interrupted by a signal. Call it again."); + continue; + } + + std::unique_ptr<ErrnoException> inner(new ErrnoException({}, errno)); + + throw SubProcessInternalException( + u"Failed to call waitpid on a subprocess.", std::move(inner)); + } + + if (WIFEXITED(wstatus)) { + return SubProcessExitResult::Normal(WEXITSTATUS(wstatus)); + } else if (WIFEXITED(wstatus)) { + return SubProcessExitResult::Signal(WTERMSIG(wstatus), WCOREDUMP(wstatus)); + } else { + return SubProcessExitResult::Unknown(); + } +} + +void PosixSpawnSubProcessImpl::PlatformKillProcess() { + int error = kill(pid_, SIGKILL); + if (error != 0) { + std::unique_ptr<ErrnoException> inner(new ErrnoException({}, errno)); + throw SubProcessInternalException(u"Failed to call kill on a subprocess.", + std::move(inner)); + } +} + +io::Stream* PosixSpawnSubProcessImpl::GetStdinStream() { + return stdin_stream_.get(); +} + +io::Stream* PosixSpawnSubProcessImpl::GetStdoutStream() { + return stdout_buffer_stream_.get(); +} + +io::Stream* PosixSpawnSubProcessImpl::GetStderrStream() { + return stderr_buffer_stream_.get(); +} +} // namespace cru::platform::unix diff --git a/src/base/platform/unix/UnixFileStream.cpp b/src/base/platform/unix/UnixFileStream.cpp new file mode 100644 index 00000000..c53bbbaa --- /dev/null +++ b/src/base/platform/unix/UnixFileStream.cpp @@ -0,0 +1,106 @@ +#include "cru/base/platform/unix/UnixFileStream.h" +#include "cru/base/Exception.h" +#include "cru/base/Format.h" +#include "cru/base/io/Stream.h" +#include "cru/base/log/Logger.h" + +#include <fcntl.h> +#include <sys/fcntl.h> +#include <unistd.h> +#include <cerrno> + +namespace cru::platform::unix { +using namespace cru::io; + +namespace { +bool OflagCanSeek([[maybe_unused]] int oflag) { + // Treat every file seekable. + return true; +} + +bool OflagCanRead(int oflag) { + // There is a problem: O_RDONLY is 0. So we must test it specially. + // If it is not write-only. Then it can read. + return !(oflag & O_WRONLY); +} + +bool OflagCanWrite(int oflag) { return oflag & O_WRONLY || oflag & O_RDWR; } + +int MapSeekOrigin(Stream::SeekOrigin origin) { + switch (origin) { + case Stream::SeekOrigin::Begin: + return SEEK_SET; + case Stream::SeekOrigin::Current: + return SEEK_CUR; + case Stream::SeekOrigin::End: + return SEEK_END; + default: + throw Exception(u"Invalid seek origin."); + } +} +} // namespace + +UnixFileStream::UnixFileStream(const char *path, int oflag, mode_t mode) { + file_descriptor_ = ::open(path, oflag, mode); + if (file_descriptor_ == -1) { + throw ErrnoException( + Format(u"Failed to open file {} with oflag {}, mode {}.", + String::FromUtf8(path), oflag, mode)); + } + + SetSupportedOperations( + {OflagCanSeek(oflag), OflagCanRead(oflag), OflagCanWrite(oflag)}); + + auto_close_ = true; +} + +UnixFileStream::UnixFileStream(int fd, bool can_seek, bool can_read, + bool can_write, bool auto_close) + : Stream(can_seek, can_read, can_write) { + file_descriptor_ = fd; + auto_close_ = auto_close; +} + +UnixFileStream::~UnixFileStream() { + if (auto_close_ && file_descriptor_ >= 0) { + if (::close(file_descriptor_) == -1) { + // We are in destructor, so we can not throw. + CRU_LOG_WARN(u"Failed to close file descriptor {}, errno {}.", + file_descriptor_, errno); + } + } +} + +Index UnixFileStream::DoSeek(Index offset, SeekOrigin origin) { + off_t result = ::lseek(file_descriptor_, offset, MapSeekOrigin(origin)); + if (result == -1) { + throw ErrnoException(u"Failed to seek file."); + } + return result; +} + +Index UnixFileStream::DoRead(std::byte *buffer, Index offset, Index size) { + auto result = ::read(file_descriptor_, buffer + offset, size); + if (result == -1) { + throw ErrnoException(u"Failed to read file."); + } + return result; +} + +Index UnixFileStream::DoWrite(const std::byte *buffer, Index offset, + Index size) { + auto result = ::write(file_descriptor_, buffer + offset, size); + if (result == -1) { + throw ErrnoException(u"Failed to write file."); + } + return result; +} + +void UnixFileStream::DoClose() { + CRU_STREAM_BEGIN_CLOSE + if (auto_close_ && ::close(file_descriptor_) == -1) { + throw ErrnoException(u"Failed to close file."); + } + file_descriptor_ = -1; +} +} // namespace cru::platform::unix diff --git a/src/base/platform/unix/UnixPipe.cpp b/src/base/platform/unix/UnixPipe.cpp new file mode 100644 index 00000000..747efe94 --- /dev/null +++ b/src/base/platform/unix/UnixPipe.cpp @@ -0,0 +1,51 @@ +#include "cru/base/platform/unix/UnixPipe.h" +#include "cru/base/Exception.h" +#include "cru/base/log/Logger.h" + +#include <fcntl.h> +#include <sys/fcntl.h> +#include <unistd.h> + +namespace cru::platform::unix { +UnixPipe::UnixPipe(Usage usage, bool auto_close, UnixPipeFlag flags) + : usage_(usage), auto_close_(auto_close), flags_(flags) { + int fds[2]; + if (pipe(fds) != 0) { + throw ErrnoException(u"Failed to create unix pipe."); + } + + if (flags & UnixPipeFlags::NonBlock) { + fcntl(fds[0], F_SETFL, O_NONBLOCK); + fcntl(fds[1], F_SETFL, O_NONBLOCK); + } + + read_fd_ = fds[0]; + write_fd_ = fds[1]; +} + +int UnixPipe::GetSelfFileDescriptor() { + if (usage_ == Usage::Send) { + return write_fd_; + } else { + return read_fd_; + } +} + +int UnixPipe::GetOtherFileDescriptor() { + if (usage_ == Usage::Send) { + return read_fd_; + } else { + return write_fd_; + } +} + +UnixPipe::~UnixPipe() { + if (auto_close_) { + auto self_fd = GetSelfFileDescriptor(); + if (::close(self_fd) != 0) { + CRU_LOG_ERROR(u"Failed to close unix pipe file descriptor {}, errno {}.", + self_fd, errno); + } + } +} +} // namespace cru::platform::unix diff --git a/src/base/platform/web/WebException.cpp b/src/base/platform/web/WebException.cpp new file mode 100644 index 00000000..f473bb5b --- /dev/null +++ b/src/base/platform/web/WebException.cpp @@ -0,0 +1 @@ +#include "cru/base/platform/web/WebException.h" diff --git a/src/base/platform/win/BridgeComStream.cpp b/src/base/platform/win/BridgeComStream.cpp new file mode 100644 index 00000000..c6987ab2 --- /dev/null +++ b/src/base/platform/win/BridgeComStream.cpp @@ -0,0 +1,106 @@ +#include "BrigdeComStream.h" +#include "cru/base/io/Stream.h" + +namespace cru::platform::win { +BridgeComStream::BridgeComStream(io::Stream *stream) + : stream_(stream), ref_count_(1) {} + +BridgeComStream::~BridgeComStream() {} + +ULONG BridgeComStream::AddRef() { return ++ref_count_; } + +ULONG BridgeComStream::Release() { + --ref_count_; + + if (ref_count_ == 0) { + delete this; + return 0; + } + + return ref_count_; +} + +HRESULT BridgeComStream::QueryInterface(const IID &riid, void **ppvObject) { + if (riid == IID_IStream) { + *ppvObject = static_cast<IStream *>(this); + AddRef(); + return S_OK; + } else if (riid == IID_ISequentialStream) { + *ppvObject = static_cast<ISequentialStream *>(this); + AddRef(); + return S_OK; + } else if (riid == IID_IUnknown) { + *ppvObject = static_cast<IUnknown *>(this); + AddRef(); + return S_OK; + } else { + return E_NOINTERFACE; + } +} + +HRESULT BridgeComStream::Read(void *pv, ULONG cb, ULONG *pcbRead) { + *pcbRead = stream_->Read(static_cast<std::byte *>(pv), cb); + return S_OK; +} + +HRESULT BridgeComStream::Write(const void *pv, ULONG cb, ULONG *pcbWritten) { + *pcbWritten = stream_->Write(static_cast<const std::byte *>(pv), cb); + return S_OK; +} + +HRESULT BridgeComStream::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, + ULARGE_INTEGER *plibNewPosition) { + io::Stream::SeekOrigin so; + + switch (dwOrigin) { + case STREAM_SEEK_SET: + so = io::Stream::SeekOrigin::Begin; + break; + case STREAM_SEEK_CUR: + so = io::Stream::SeekOrigin::Current; + break; + case STREAM_SEEK_END: + so = io::Stream::SeekOrigin::End; + break; + default: + return STG_E_INVALIDFUNCTION; + }; + + plibNewPosition->QuadPart = stream_->Seek(dlibMove.QuadPart, so); + return S_OK; +} + +HRESULT BridgeComStream::SetSize(ULARGE_INTEGER libNewSize) { + return E_NOTIMPL; +} + +HRESULT BridgeComStream::CopyTo(IStream *pstm, ULARGE_INTEGER cb, + ULARGE_INTEGER *pcbRead, + ULARGE_INTEGER *pcbWritten) { + return E_NOTIMPL; +} + +HRESULT BridgeComStream::Commit(DWORD grfCommitFlags) { return S_OK; } + +HRESULT BridgeComStream::Revert() { return S_OK; } + +HRESULT BridgeComStream::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, + DWORD dwLockType) { + return S_OK; +} + +HRESULT BridgeComStream::UnlockRegion(ULARGE_INTEGER libOffset, + ULARGE_INTEGER cb, DWORD dwLockType) { + return S_OK; +} + +HRESULT BridgeComStream::Stat(STATSTG *pstatstg, DWORD grfStatFlag) { + return E_NOTIMPL; +} + +HRESULT BridgeComStream::Clone(IStream **ppstm) { + *ppstm = new BridgeComStream(stream_); + return S_OK; +} + +} // namespace cru::platform::win diff --git a/src/base/platform/win/BrigdeComStream.h b/src/base/platform/win/BrigdeComStream.h new file mode 100644 index 00000000..1621b567 --- /dev/null +++ b/src/base/platform/win/BrigdeComStream.h @@ -0,0 +1,43 @@ +#pragma once +#include "cru/base/platform/win/WinPreConfig.h" + +#include "cru/base/io/Stream.h" + +#include <objidlbase.h> + +namespace cru::platform::win { +class BridgeComStream : public IStream { + public: + explicit BridgeComStream(io::Stream* stream); + + CRU_DELETE_COPY(BridgeComStream) + CRU_DELETE_MOVE(BridgeComStream) + + ~BridgeComStream(); + + public: + ULONG AddRef() override; + ULONG Release() override; + HRESULT QueryInterface(REFIID riid, void** ppvObject) override; + + HRESULT Read(void* pv, ULONG cb, ULONG* pcbRead) override; + HRESULT Write(const void* pv, ULONG cb, ULONG* pcbWritten) override; + HRESULT Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, + ULARGE_INTEGER* plibNewPosition) override; + HRESULT SetSize(ULARGE_INTEGER libNewSize) override; + HRESULT CopyTo(IStream* pstm, ULARGE_INTEGER cb, ULARGE_INTEGER* pcbRead, + ULARGE_INTEGER* pcbWritten) override; + HRESULT Commit(DWORD grfCommitFlags) override; + HRESULT Revert() override; + HRESULT LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, + DWORD dwLockType) override; + HRESULT UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, + DWORD dwLockType) override; + HRESULT Stat(STATSTG* pstatstg, DWORD grfStatFlag) override; + HRESULT Clone(IStream** ppstm) override; + + private: + io::Stream* stream_; + ULONG ref_count_; +}; +} // namespace cru::platform::win diff --git a/src/base/platform/win/ComAutoInit.cpp b/src/base/platform/win/ComAutoInit.cpp new file mode 100644 index 00000000..548a7bea --- /dev/null +++ b/src/base/platform/win/ComAutoInit.cpp @@ -0,0 +1,15 @@ +#include "cru/base/platform/win/ComAutoInit.h" +#include "cru/base/platform/win/Exception.h" + +#include <combaseapi.h> + +namespace cru::platform::win { +ComAutoInit::ComAutoInit() { + const auto hresult = ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + if (FAILED(hresult)) { + throw HResultError(hresult, "Failed to call CoInitializeEx."); + } +} + +ComAutoInit::~ComAutoInit() { ::CoUninitialize(); } +} // namespace cru::platform::win diff --git a/src/base/platform/win/DebugLogTarget.cpp b/src/base/platform/win/DebugLogTarget.cpp new file mode 100644 index 00000000..89bd3d19 --- /dev/null +++ b/src/base/platform/win/DebugLogTarget.cpp @@ -0,0 +1,10 @@ +#include "cru/base/platform/win/DebugLogTarget.h" + +namespace cru::platform::win { +void WinDebugLogTarget::Write(::cru::log::LogLevel level, StringView s) { + CRU_UNUSED(level) + + String m = s.ToString(); + ::OutputDebugStringW(reinterpret_cast<const wchar_t*>(m.c_str())); +} +} // namespace cru::platform::win diff --git a/src/base/platform/win/Exception.cpp b/src/base/platform/win/Exception.cpp new file mode 100644 index 00000000..5ff6146b --- /dev/null +++ b/src/base/platform/win/Exception.cpp @@ -0,0 +1,38 @@ +#include "cru/base/platform/win/Exception.h" +#include "cru/base/Format.h" + +#include <optional> + +namespace cru::platform::win { + +inline String HResultMakeMessage(HRESULT h_result, + std::optional<String> message) { + if (message.has_value()) + return Format(u"HRESULT: {}. Message: {}", h_result, message->WinCStr()); + else + return Format(u"HRESULT: {}.", h_result); +} + +HResultError::HResultError(HRESULT h_result) + : PlatformException(HResultMakeMessage(h_result, std::nullopt)), + h_result_(h_result) {} + +HResultError::HResultError(HRESULT h_result, + std::string_view additional_message) + : PlatformException(HResultMakeMessage( + h_result, String::FromUtf8(additional_message.data(), + additional_message.size()))), + h_result_(h_result) {} + +inline String Win32MakeMessage(DWORD error_code, String message) { + return Format(u"Last error code: {}.\nMessage: {}\n", error_code, + message.WinCStr()); +} + +Win32Error::Win32Error(String message) + : Win32Error(::GetLastError(), message) {} + +Win32Error::Win32Error(DWORD error_code, String message) + : PlatformException(Win32MakeMessage(error_code, message)), + error_code_(error_code) {} +} // namespace cru::platform::win diff --git a/src/base/platform/win/StreamConvert.cpp b/src/base/platform/win/StreamConvert.cpp new file mode 100644 index 00000000..f7a0537c --- /dev/null +++ b/src/base/platform/win/StreamConvert.cpp @@ -0,0 +1,28 @@ +#include "cru/base/platform/win/StreamConvert.h" +#include "BrigdeComStream.h" +#include "Win32FileStreamPrivate.h" +#include "cru/base/Exception.h" +#include "cru/base/io/MemoryStream.h" +#include "cru/base/io/OpenFileFlag.h" +#include "cru/base/platform/win/ComAutoInit.h" +#include "cru/base/platform/win/Exception.h" +#include "cru/base/platform/win/Win32FileStream.h" + +#include <shlwapi.h> +#include <winnt.h> + +namespace cru::platform::win { +IStream* ConvertStreamToComStream(io::Stream* stream) { + static ComAutoInit com_auto_init; + + if (auto memory_stream = dynamic_cast<io::MemoryStream*>(stream)) { + return SHCreateMemStream( + reinterpret_cast<const BYTE*>(memory_stream->GetBuffer()), + memory_stream->GetSize()); + } else if (auto file_stream = dynamic_cast<Win32FileStream*>(stream)) { + return file_stream->GetPrivate_()->stream_; + } else { + return new BridgeComStream(stream); + } +} +} // namespace cru::platform::win diff --git a/src/base/platform/win/Win32FileStream.cpp b/src/base/platform/win/Win32FileStream.cpp new file mode 100644 index 00000000..54e6ae45 --- /dev/null +++ b/src/base/platform/win/Win32FileStream.cpp @@ -0,0 +1,122 @@ +#include "cru/base/platform/win/Win32FileStream.h" + +#include "Win32FileStreamPrivate.h" +#include "cru/base/io/OpenFileFlag.h" +#include "cru/base/platform/win/Exception.h" + +#include <Windows.h> +#include <coml2api.h> +#include <shlwapi.h> +#include <winnt.h> +#include <filesystem> + +namespace cru::platform::win { +using namespace cru::io; + +Win32FileStream::Win32FileStream(String path, OpenFileFlag flags) + : path_(std::move(path)), + flags_(flags), + p_(new details::Win32FileStreamPrivate()) { + DWORD grfMode = STGM_SHARE_DENY_NONE; + if (flags & io::OpenFileFlags::Read) { + if (flags & io::OpenFileFlags::Write) { + grfMode |= STGM_READWRITE; + } else { + grfMode |= STGM_READ; + } + } else { + if (flags & io::OpenFileFlags::Write) { + grfMode |= STGM_WRITE; + } else { + throw Exception(u"Stream must be readable or writable."); + } + } + + if (flags & io::OpenFileFlags::Truncate) { + grfMode |= STGM_CREATE; + } + + IStream* stream; + + ThrowIfFailed(SHCreateStreamOnFileEx( + path_.WinCStr(), grfMode, FILE_ATTRIBUTE_NORMAL, + flags & io::OpenFileFlags::Create ? TRUE : FALSE, NULL, &stream)); + + p_->stream_ = stream; +} + +Win32FileStream::~Win32FileStream() { + Close(); + delete p_; +} + +bool Win32FileStream::CanSeek() { return true; } + +Index Win32FileStream::Seek(Index offset, SeekOrigin origin) { + CheckClosed(); + + DWORD dwOrigin = 0; + + if (origin == SeekOrigin::Current) { + dwOrigin = STREAM_SEEK_CUR; + } else if (origin == SeekOrigin::End) { + dwOrigin = STREAM_SEEK_END; + } else { + dwOrigin = STREAM_SEEK_SET; + } + + LARGE_INTEGER n_offset; + n_offset.QuadPart = offset; + ULARGE_INTEGER n_new_offset; + + ThrowIfFailed(p_->stream_->Seek(n_offset, dwOrigin, &n_new_offset)); + + return n_new_offset.QuadPart; +} + +bool Win32FileStream::CanRead() { return true; } + +Index Win32FileStream::Read(std::byte* buffer, Index offset, Index size) { + if (size < 0) { + throw Exception(u"Size must be greater than 0."); + } + + CheckClosed(); + + ULONG n_read; + ThrowIfFailed(p_->stream_->Read(buffer + offset, size, &n_read)); + return n_read; +} + +bool Win32FileStream::CanWrite() { return true; } + +Index Win32FileStream::Write(const std::byte* buffer, Index offset, + Index size) { + if (size < 0) { + throw Exception(u"Size must be greater than 0."); + } + + CheckClosed(); + + ULONG n_written; + ThrowIfFailed(p_->stream_->Write(buffer + offset, size, &n_written)); + + return n_written; +} + +void Win32FileStream::Close() { + if (closed_) return; + + if (p_->stream_) { + p_->stream_->Release(); + p_->stream_ = nullptr; + } + + closed_ = true; +} + +void Win32FileStream::CheckClosed() { + if (closed_) throw Exception(u"Stream is closed."); +} + +} // namespace cru::platform::win diff --git a/src/base/platform/win/Win32FileStreamPrivate.h b/src/base/platform/win/Win32FileStreamPrivate.h new file mode 100644 index 00000000..718f8d9a --- /dev/null +++ b/src/base/platform/win/Win32FileStreamPrivate.h @@ -0,0 +1,13 @@ +#include "cru/base/platform/win/WinPreConfig.h" + +#include <objidl.h> + +namespace cru::platform::win { + +namespace details { +struct Win32FileStreamPrivate { + IStream* stream_ = nullptr; +}; +} // namespace details + +} // namespace cru::platform::win |