Skip to content

Commit

Permalink
[Platform] [Files] Add a Windows Unicode implementation
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nmlgc committed Dec 31, 2022
1 parent 55197ba commit 90efe91
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 3 deletions.
5 changes: 4 additions & 1 deletion .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
"${workspaceFolder}/**"
],
"cppStandard": "c++23",
"intelliSenseMode": "windows-msvc-x86"
"intelliSenseMode": "windows-msvc-x86",
"defines": [
"WIN32"
]
}
],
"version": 4
Expand Down
1 change: 1 addition & 0 deletions Tupfile.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
BASE = {
cflags = (
"/std:c++latest " ..
"/DWIN32 " ..
"/I. " ..
"/EHsc " ..
"/source-charset:utf-8 " ..
Expand Down
2 changes: 1 addition & 1 deletion platform/c/file.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* File loading
* File loading (libc implementation)
*
*/

Expand Down
4 changes: 4 additions & 0 deletions platform/unicode.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
177 changes: 176 additions & 1 deletion platform/windows/file.cpp
Original file line number Diff line number Diff line change
@@ -1 +1,176 @@
#include "platform/c/file.cpp"
/*
* File loading (Win32 implementation)
*
*/

#define WIN32_LEAN_AND_MEAN

#include "platform/file.h"
#include <windows.h>

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<uint8_t> 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<uint8_t> buf, HANDLE&& handle)
{
auto ret = HandleRead(buf, handle);
CloseHandle(handle);
return ret;
}

static bool WriteAndClose(
HANDLE&& handle, std::span<const BYTE_BUFFER_BORROWED> 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<uint8_t> 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<const BYTE_BUFFER_BORROWED> bufs)
{
return WriteAndClose(OpenWrite(s, CREATE_ALWAYS), bufs);
}

bool FileAppend(
const PATH_LITERAL s, std::span<const BYTE_BUFFER_BORROWED> 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<int64_t> 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<FILE_STREAM_WRITE> FileStreamWrite(const PATH_LITERAL s)
{
return std::unique_ptr<FILE_STREAM_WIN32>(
new (std::nothrow) FILE_STREAM_WIN32(OpenWrite(s, CREATE_ALWAYS))
);
}
// -------
12 changes: 12 additions & 0 deletions platform/windows/unicode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Unicode types for native system APIs (Win32 Unicode version)
*
*/

#pragma once

#include <string>

using UNICODE_STRING = std::wstring;
using UNICODE_LITERAL = const wchar_t *;
#define _UNICODE(str) L##str

0 comments on commit 90efe91

Please sign in to comment.