From 90efe911c462b19c5f8f8525eb75fc1ff51fd502 Mon Sep 17 00:00:00 2001 From: nmlgc Date: Sat, 31 Dec 2022 20:53:12 +0100 Subject: [PATCH] [Platform] [Files] Add a Windows Unicode implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Locale independence for file names achieved! Was hoping that it would remove more bloat from the binary, but it seems as if printf() still comes with a narrow↔wide converter for its wide character support. Part of P0226, funded by alp-bib and Arandui. --- .vscode/c_cpp_properties.json | 5 +- Tupfile.lua | 1 + platform/c/file.cpp | 2 +- platform/unicode.h | 4 + platform/windows/file.cpp | 177 +++++++++++++++++++++++++++++++++- platform/windows/unicode.h | 12 +++ 6 files changed, 198 insertions(+), 3 deletions(-) create mode 100644 platform/windows/unicode.h diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index a31b92ad..55859024 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -6,7 +6,10 @@ "${workspaceFolder}/**" ], "cppStandard": "c++23", - "intelliSenseMode": "windows-msvc-x86" + "intelliSenseMode": "windows-msvc-x86", + "defines": [ + "WIN32" + ] } ], "version": 4 diff --git a/Tupfile.lua b/Tupfile.lua index db3080be..b1ef66a1 100644 --- a/Tupfile.lua +++ b/Tupfile.lua @@ -1,6 +1,7 @@ BASE = { cflags = ( "/std:c++latest " .. + "/DWIN32 " .. "/I. " .. "/EHsc " .. "/source-charset:utf-8 " .. diff --git a/platform/c/file.cpp b/platform/c/file.cpp index 56dc69ae..f411dcb4 100644 --- a/platform/c/file.cpp +++ b/platform/c/file.cpp @@ -1,5 +1,5 @@ /* - * File loading + * File loading (libc implementation) * */ diff --git a/platform/unicode.h b/platform/unicode.h index d17ce781..1c391aec 100644 --- a/platform/unicode.h +++ b/platform/unicode.h @@ -5,7 +5,11 @@ #pragma once +#ifdef WIN32 + #include "platform/windows/unicode.h" +#else #include "platform/c/unicode.h" +#endif using UNICODE_CODEPOINT = UNICODE_STRING::value_type; diff --git a/platform/windows/file.cpp b/platform/windows/file.cpp index 646edd19..32cde8ed 100644 --- a/platform/windows/file.cpp +++ b/platform/windows/file.cpp @@ -1 +1,176 @@ -#include "platform/c/file.cpp" +/* + * File loading (Win32 implementation) + * + */ + +#define WIN32_LEAN_AND_MEAN + +#include "platform/file.h" +#include + +static HANDLE OpenRead(const PATH_LITERAL s) +{ + return CreateFileW( + s, + GENERIC_READ, + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + nullptr + ); +} + +static HANDLE OpenWrite(const PATH_LITERAL s, DWORD disposition) +{ + return CreateFileW(s, GENERIC_WRITE, 0, nullptr, disposition, 0, nullptr); +} + +static size_t HandleRead(std::span buf, HANDLE handle) +{ + DWORD bytes_read; + if((handle == INVALID_HANDLE_VALUE) || !ReadFile( + handle, buf.data(), buf.size_bytes(), &bytes_read, nullptr + )) { + return 0; + } + return bytes_read; +} + +static bool HandleWrite(HANDLE handle, const BYTE_BUFFER_BORROWED buf) +{ + DWORD bytes_written; + if((handle == INVALID_HANDLE_VALUE) || !WriteFile( + handle, buf.data(), buf.size_bytes(), &bytes_written, nullptr + )) { + return false; + } + return (buf.size_bytes() == bytes_written); +} + +static size_t LoadInplace(std::span buf, HANDLE&& handle) +{ + auto ret = HandleRead(buf, handle); + CloseHandle(handle); + return ret; +} + +static bool WriteAndClose( + HANDLE&& handle, std::span bufs +) +{ + auto ret = [&]() { + for(const auto& buf : bufs) { + if(!HandleWrite(handle, buf)) { + return false; + } + } + return true; + }(); + return (CloseHandle(handle) && ret); +} + +size_t FileLoadInplace(std::span buf, const PATH_LITERAL s) +{ + return LoadInplace(buf, OpenRead(s)); +} + +BYTE_BUFFER_OWNED FileLoad(const PATH_LITERAL s, size_t size_limit) +{ + auto handle = OpenRead(s); + if(handle == INVALID_HANDLE_VALUE) { + return {}; + } + + auto fail = [&handle]() -> BYTE_BUFFER_OWNED { + CloseHandle(handle); + return {}; + }; + + LARGE_INTEGER size64; + if(!GetFileSizeEx(handle, &size64) || (size64.QuadPart > size_limit)) { + return fail(); + } + const size_t size = size64.QuadPart; + + auto buf = BYTE_BUFFER_OWNED{ size }; + if(!buf) { + return fail(); + } + + if(LoadInplace({ buf.get(), size }, std::move(handle)) != size) { + return {}; + } + + return std::move(buf); +} + +bool FileWrite(const PATH_LITERAL s, std::span bufs) +{ + return WriteAndClose(OpenWrite(s, CREATE_ALWAYS), bufs); +} + +bool FileAppend( + const PATH_LITERAL s, std::span bufs +) +{ + auto handle = OpenWrite(s, OPEN_ALWAYS); + return ( + handle && + (SetFilePointer(handle, 0, 0, FILE_END) != INVALID_SET_FILE_POINTER) && + WriteAndClose(std::move(handle), bufs) + ); +} + +// Streams +// ------- + +struct FILE_STREAM_WIN32 : public FILE_STREAM_WRITE { + HANDLE handle; + +public: + FILE_STREAM_WIN32(HANDLE handle) : + handle(handle) { + } + + ~FILE_STREAM_WIN32() { + if(*this) { + CloseHandle(handle); + } + } + + explicit operator bool() override { + return (handle != INVALID_HANDLE_VALUE); + }; + + bool Seek(int64_t offset, SEEK_WHENCE whence) override { + DWORD origin; + LARGE_INTEGER offset_large = { .QuadPart = offset }; + switch(whence) { + case SEEK_WHENCE::BEGIN: origin = FILE_BEGIN; break; + case SEEK_WHENCE::CURRENT: origin = FILE_CURRENT; break; + case SEEK_WHENCE::END: origin = FILE_END; break; + } + return SetFilePointerEx(handle, offset_large, nullptr, origin); + }; + + std::optional Tell() override { + LARGE_INTEGER ret; + if(!SetFilePointerEx(handle, { 0 }, &ret, FILE_CURRENT)) { + return std::nullopt; + } + return ret.QuadPart; + }; + + bool Write(BYTE_BUFFER_BORROWED buf) override { + return HandleWrite(handle, buf); + }; +}; + +std::unique_ptr FileStreamWrite(const PATH_LITERAL s) +{ + return std::unique_ptr( + new (std::nothrow) FILE_STREAM_WIN32(OpenWrite(s, CREATE_ALWAYS)) + ); +} +// ------- diff --git a/platform/windows/unicode.h b/platform/windows/unicode.h new file mode 100644 index 00000000..bf494570 --- /dev/null +++ b/platform/windows/unicode.h @@ -0,0 +1,12 @@ +/* + * Unicode types for native system APIs (Win32 Unicode version) + * + */ + +#pragma once + +#include + +using UNICODE_STRING = std::wstring; +using UNICODE_LITERAL = const wchar_t *; +#define _UNICODE(str) L##str