Windows is a nightmare.

This commit is contained in:
Kyle Isom 2023-10-10 16:44:29 -07:00
parent 138bf8267b
commit 67d4a1ebce
8 changed files with 266 additions and 137 deletions

144
Arena.cc
View File

@ -13,10 +13,8 @@
#define PROT_RW (PROT_WRITE|PROT_READ)
#elif defined(__WIN64__) || defined(__WIN32__) || defined(WIN32)
#include <Windows.h>
#include <winbase.h>
#include <fileapi.h>
#include <strsafe.h>
#include "WinHelpers.h"
#pragma comment(lib, "advapi32.lib")
#endif
#include <ios>
@ -136,73 +134,35 @@ Arena::Create(const char *path, size_t fileSize)
return this->Open(path);
}
#elif defined(__WIN64__) || defined(__WIN32__) || defined(WIN32)
static void
displayWinErr(LPTSTR lpszFunction)
{
// Retrieve the system error message for the last-error code
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
// Display the error message and exit the process
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
}
int
Arena::Open(const char *path)
{
HANDLE fHandle;
DWORD fRead = 0;
size_t fSize;
size_t fRemaining;
auto *cursor = this->store;
OVERLAPPED overlap = {0};
HANDLE fHandle;
DWORD fRead = 0;
size_t fSize;
size_t fRemaining;
auto *cursor = this->store;
OVERLAPPED overlap = {0};
fHandle = CreateFileA(
(LPSTR)path,
(LPSTR) path,
GENERIC_READ,
(FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE),
(FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE),
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (fHandle == INVALID_HANDLE_VALUE) {
displayWinErr("CreateFileA");
return -1;
return Windows::DisplayWinError("CreateFileA", NULL);
}
if (SetFilePointer(fHandle, 0, 0, FILE_BEGIN) != 0) {
displayWinErr("SetFilePointer");
CloseHandle(fHandle);
return -1;
return Windows::DisplayWinError("SetFilePointer", fHandle);
}
if (GetFileSizeEx(fHandle, reinterpret_cast<PLARGE_INTEGER>(&fSize)) != TRUE) {
displayWinErr("GetFileSizeEx");
CloseHandle(fHandle);
return -1;
if (GetFileSizeEx(fHandle, reinterpret_cast<PLARGE_INTEGER>(&fSize)) !=
TRUE) {
return Windows::DisplayWinError("GetFileSizeEx", fHandle);
}
this->SetAlloc(fSize);
@ -212,15 +172,14 @@ Arena::Open(const char *path)
fRemaining = fSize;
while (fRemaining != 0) {
overlap.Offset = (fSize - fRemaining);
if (ReadFile(fHandle, cursor, fSize-1,
if (ReadFile(fHandle, cursor, fSize - 1,
&fRead,
&overlap) != TRUE) {
auto errorCode = GetLastError();
if (errorCode != ERROR_HANDLE_EOF) {
displayWinErr("ReadFile");
CloseHandle(fHandle);
this->Destroy();
return -1;
return Windows::DisplayWinError("ReadFile", fHandle);
}
break;
}
@ -235,32 +194,15 @@ Arena::Open(const char *path)
int
Arena::Create(const char *path, size_t fileSize, DWORD mode)
Arena::Create(const char *path, size_t fileSize)
{
HANDLE fHandle;
fHandle = CreateFileA(
(LPSTR)path,
GENERIC_READ|GENERIC_WRITE,
mode,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (fHandle == INVALID_HANDLE_VALUE) {
displayWinErr("Create::CreateFileA");
return -1;
auto errorCode = Windows::CreateFixedSizeFile(path, fileSize);
if (errorCode != 0) {
return errorCode;
}
if (SetFileValidData(fHandle, fileSize) != fileSize) {
displayWinErr("SetFileValidData");
CloseHandle(fHandle);
return -1;
}
CloseHandle(fHandle);
return this->Open(path);
}
#endif
bool
@ -308,18 +250,18 @@ Arena::Destroy()
delete this->store;
break;
#if defined(__linux__)
case ArenaType::MemoryMapped:
if (munmap(this->store, this->size) == -1) {
abort();
return;
}
case ArenaType::MemoryMapped:
if (munmap(this->store, this->size) == -1) {
abort();
return;
}
if (close(this->fd) == -1) {
abort();
}
if (close(this->fd) == -1) {
abort();
}
this->fd = 0;
break;
this->fd = 0;
break;
#endif
default:
#if defined(NDEBUG)
@ -342,7 +284,7 @@ operator<<(std::ostream &os, Arena &arena)
auto cursor = arena.NewCursor();
char cursorString[33] = {0};
snprintf(cursorString, 32, "%#016llx",
(long long unsigned int)cursor);
(long long unsigned int) cursor);
os << "Arena<";
switch (arena.Type()) {
@ -356,9 +298,9 @@ operator<<(std::ostream &os, Arena &arena)
os << "allocated";
break;
#if defined(__linux__)
case ArenaType::MemoryMapped:
os << "mmap/file";
break;
case ArenaType::MemoryMapped:
os << "mmap/file";
break;
#endif
default:
os << "unknown (this is a bug)";
@ -384,7 +326,7 @@ Arena::Write(const char *path)
arenaFile = fopen(path, "w");
if (arenaFile == nullptr) {
#else
if (fopen_s(&arenaFile, path, "w") != 0) {
if (fopen_s(&arenaFile, path, "w") != 0) {
#endif
return -1;
}
@ -404,14 +346,14 @@ Arena::Write(const char *path)
uint8_t &
Arena::operator[](size_t index)
{
if (index > this->size) {
if (index > this->size) {
#if defined(DESKTOP_BUILD) and !defined(KLIB_NO_ASSERT)
throw std::range_error("index out of range");
throw std::range_error("index out of range");
#else
abort();
abort();
#endif
}
return this->store[index];
}
return this->store[index];
}

View File

@ -116,7 +116,7 @@ public:
int Create(const char *path, size_t fileSize);
#elif defined(__WIN64__) || defined(__WIN32__) || defined(WIN32)
int Create(const char *path, size_t fileSize, DWORD mode);
int Create(const char *path, size_t fileSize);
#endif
/// Open reads a file into the arena; the file must already exist. On

View File

@ -26,7 +26,8 @@ set(HEADER_FILES
Dictionary.h
Exceptions.h
Test.h
TLV.h)
TLV.h
WinHelpers.h)
set(SOURCE_FILES
Arena.cc
@ -36,7 +37,8 @@ set(SOURCE_FILES
Test.cc
TLV.cc
Commander.cpp
Commander.h)
Commander.h
WinHelpers.cpp)
add_library(klib STATIC ${SOURCE_FILES} ${HEADER_FILES})

142
WinHelpers.cpp Normal file
View File

@ -0,0 +1,142 @@
//
// Created by kyle on 2023-10-10.
//
#if defined(__WIN64__) || defined(__WIN32__) || defined(WIN32)
#include "WinHelpers.h"
namespace klib {
namespace Windows {
int
DisplayWinError(LPTSTR lpszFunction, HANDLE handle)
{
// Retrieve the system error message for the last-error code
DWORD dw = GetLastError();
#ifndef NDEBUG
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL);
// Display the error message and exit the process
lpDisplayBuf = (LPVOID) LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR) lpMsgBuf) +
lstrlen((LPCTSTR) lpszFunction) +
40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR) lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR) lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
#endif
if ((handle != NULL) && (handle != INVALID_HANDLE_VALUE)) {
CloseHandle(handle);
}
return dw;
}
BOOL SetPrivilege(
HANDLE hToken, // access token handle
LPCTSTR lpszPrivilege, // name of privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
)
{
TOKEN_PRIVILEGES tp;
LUID luid;
if (!LookupPrivilegeValue(
NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid)) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %u\n", GetLastError());
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if (bEnablePrivilege)
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;
// Enable the privilege or disable all privileges.
if (!AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL)) {
printf("AdjustTokenPrivileges error: %u\n", GetLastError());
return FALSE;
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) {
printf("The token does not have the specified privilege. \n");
return FALSE;
}
return TRUE;
}
HANDLE
CreateFileWindows(const char *path)
{
HANDLE fHandle;
return CreateFileA(
(LPSTR) path,
(GENERIC_READ | GENERIC_WRITE),
(FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE),
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
}
int
CreateFixedSizeFile(const char *path, size_t size)
{
_LARGE_INTEGER fileSize;
fileSize.QuadPart = size;
HANDLE fHandle = CreateFileWindows(path);
if (SetFilePointerEx(fHandle, fileSize, nullptr, FILE_BEGIN) != TRUE) {
return DisplayWinError("SetFilePointerEx", fHandle);
}
if (SetEndOfFile(fHandle) != TRUE) {
return DisplayWinError("SetEndOfFile", fHandle);
}
CloseHandle(fHandle);
return 0;
}
} // namespace Windows
} // namespace klib
#endif

39
WinHelpers.h Normal file
View File

@ -0,0 +1,39 @@
//
// Created by kyle on 2023-10-10.
//
#ifndef KLIB_WINHELPERS_H
#define KLIB_WINHELPERS_H
#if defined(__WIN64__) || defined(__WIN32__) || defined(WIN32)
#include <Windows.h>
#include <winbase.h>
#include <fileapi.h>
#include <strsafe.h>
namespace klib {
namespace Windows {
int DisplayWinError(LPTSTR lpszFunction, HANDLE handle);
BOOL SetPrivilege(
HANDLE hToken, // access token handle
LPCTSTR lpszPrivilege, // name of privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
);
HANDLE CreateFileWindows(const char *path);
int CreateFixedSizeFile(const char *path, size_t size);
} // namespace Windows
} // namespace klib
#endif // Windows-only guards.
#endif //KLIB_WINHELPERS_H

View File

@ -16,43 +16,43 @@ main(int argc, char *argv[])
Buffer goodbyeWorld("goodbye, world");
Buffer goodbyeCruelWorld("goodbye, cruel world");
std::cout << buffer << std::endl;
std::cout << buffer << "\n";
buffer.Insert(1, (uint8_t *) "el", 2);
buffer.Append('!');
assert(buffer == helloWorld);
std::cout << buffer << std::endl;
std::cout << buffer << "\n";
buffer.Remove(buffer.Length() - 1);
std::cout << buffer << std::endl;
std::cout << buffer << "\n";
buffer.Remove(0, 5);
std::cout << buffer << std::endl;
std::cout << buffer << "\n";
buffer.Insert(0, 'g');
std::cout << buffer << std::endl;
std::cout << buffer << "\n";
buffer.Insert(1, (uint8_t *) "oodbye", 6);
std::cout << buffer << std::endl;
std::cout << buffer << "\n";
assert(buffer == goodbyeWorld);
buffer.Insert(9, (uint8_t *)"cruel ", 6);
std::cout << buffer << std::endl;
std::cout << buffer << "\n";
buffer.HexDump(std::cout);
buffer.Reclaim();
buffer.Append("and now for something completely different...");
std::cout << buffer.Contents() << std::endl;
std::cout << "Length: " << buffer.Length() << ", capacity " << buffer.Capacity() << std::endl;
std::cout << buffer.Contents() << "\n";
std::cout << "Length: " << buffer.Length() << ", capacity " << buffer.Capacity() << "\n";
buffer.Resize(128);
std::cout << "Length: " << buffer.Length() << ", capacity " << buffer.Capacity() << std::endl;
std::cout << "Length: " << buffer.Length() << ", capacity " << buffer.Capacity() << "\n";
buffer.Trim();
std::cout << "Length: " << buffer.Length() << ", capacity " << buffer.Capacity() << std::endl;
std::cout << "Length: " << buffer.Length() << ", capacity " << buffer.Capacity() << "\n";
Buffer buffer2("and now for something completely different...");
assert(buffer == buffer2);
buffer2.Remove(buffer2.Length()-3, 3);
std::cout << buffer << std::endl;
std::cout << buffer << "\n";
assert(buffer != buffer2);
return 0;

View File

@ -26,7 +26,7 @@ testSetKV(Dictionary &pb, const char *k, uint8_t kl, const char *v,
uint8_t vl)
{
bool ok;
std::cout << "test Set " << k << "->" << v << std::endl;
std::cout << "test Set " << k << "->" << v << "\n";
ok = pb.Set(k, kl, v, vl) == 0;
std::cout << "\tSet complete\n";
return ok;
@ -42,7 +42,7 @@ main(int argc, const char *argv[])
TLV::Record value;
TLV::Record expect;
std::cout << "TESTPROG: " << argv[0] << std::endl;
std::cout << "TESTPROG: " << argv[0] << "\n";
#if defined(__linux__)
if (arena.Create(ARENA_FILE, ARENA_SIZE) == -1) {
@ -53,7 +53,7 @@ main(int argc, const char *argv[])
abort();
}
#endif
std::cout << arena << std::endl;
std::cout << arena << "\n";
TLV::SetRecord(expect, DICTIONARY_TAG_VAL, TEST_KVSTRLEN3, TEST_KVSTR3);
Dictionary dict(arena);
@ -73,17 +73,17 @@ main(int argc, const char *argv[])
assert(cmpRecord(value, expect));
std::cout << "test overwriting key" << std::endl;
std::cout << "test overwriting key" << "\n";
assert(testSetKV(dict, TEST_KVSTR2, TEST_KVSTRLEN2, TEST_KVSTR6,
TEST_KVSTRLEN6));
std::cout << dict;
TLV::SetRecord(expect, DICTIONARY_TAG_VAL, TEST_KVSTRLEN6, TEST_KVSTR6);
std::cout << "\tlookup" << std::endl;
std::cout << "\tlookup" << "\n";
assert(dict.Lookup(TEST_KVSTR2, TEST_KVSTRLEN2, value));
std::cout << "\tcompare records" << std::endl;
std::cout << "\tcompare records" << "\n";
assert(cmpRecord(value, expect));
std::cout << "\tadd new key to dictionary" << std::endl;
std::cout << "\tadd new key to dictionary" << "\n";
assert(testSetKV(dict, TEST_KVSTR3, TEST_KVSTRLEN3, TEST_KVSTR5,
TEST_KVSTRLEN5));
std::cout << dict;
@ -92,7 +92,7 @@ main(int argc, const char *argv[])
assert(dict.Lookup(TEST_KVSTR4, TEST_KVSTRLEN4, value));
assert(cmpRecord(value, expect));
std::cout << "OK" <<std::endl;
std::cout << "OK" <<"\n";
// Dump the generated arena for inspection later.
#if defined(__linux__)

View File

@ -21,29 +21,29 @@ tlvTestSuite(Arena &backend)
TLV::Record rec1, rec2, rec3, rec4;
uint8_t *cursor = nullptr;
std::cout << "\tSetting first three records." << std::endl;
std::cout << "\tSetting first three records." << "\n";
TLV::SetRecord(rec1, 1, TEST_STRLEN1, TEST_STR1);
TLV::SetRecord(rec2, 2, TEST_STRLEN2, TEST_STR2);
TLV::SetRecord(rec3, 1, TEST_STRLEN4, TEST_STR4);
rec4.Tag = 1;
std::cout << "\twriting new rec1" << std::endl;
std::cout << "\twriting new rec1" << "\n";
assert(TLV::WriteToMemory(backend, cursor, rec1) != nullptr);
std::cout << "\twriting new rec2" << std::endl;
std::cout << "\twriting new rec2" << "\n";
assert((cursor = TLV::WriteToMemory(backend, cursor, rec2)) != nullptr);
std::cout << "\twriting new rec3" << std::endl;
std::cout << "\twriting new rec3" << "\n";
assert(TLV::WriteToMemory(backend, cursor, rec3) != nullptr);
cursor = nullptr;
// the cursor should point at the next record,
// and rec4 should contain the same data as rec1.
std::cout << "\tFindTag 1" << std::endl;
std::cout << "\tFindTag 1" << "\n";
cursor = TLV::FindTag(backend, cursor, rec4);
assert(cursor != nullptr);
assert(cursor != backend.NewCursor());
assert(cmpRecord(rec1, rec4));
std::cout << "\tFindTag 2" << std::endl;
std::cout << "\tFindTag 2" << "\n";
cursor = TLV::FindTag(backend, cursor, rec4);
assert(cursor != nullptr);
assert(cmpRecord(rec3, rec4));
@ -62,20 +62,22 @@ tlvTestSuite(Arena &backend)
bool
runSuite(Arena &backend, const char *label)
{
std::cout << backend << std::endl;
std::exception exc;
std::cout << backend << "\n";
std::cout << "running test suite " << label << ": ";
try {
tlvTestSuite(backend);
} catch (std::exception &exc){
std::cout << "FAILED" << std::endl;
std::cout << "FAILED: " << exc.what() << "\n";
return false;
}
std::cout << "OK" << std::endl;
std::cout << "OK" << "\n";
std::cout << "\tdestroying arena: ";
backend.Destroy();
std::cout << "OK" << std::endl;
std::cout << "OK" << "\n";
return true;
}
@ -88,7 +90,7 @@ main(int argc, const char *argv[])
Arena arenaStatic;
Arena arenaMem;
std::cout << "TESTPROG: " << argv[0] << std::endl;
std::cout << "TESTPROG: " << argv[0] << "\n";
if (-1 == arenaStatic.SetStatic(arenaBuffer, ARENA_SIZE)) {
abort();
@ -98,8 +100,10 @@ main(int argc, const char *argv[])
arenaStatic.Clear();
Arena arenaFile;
auto status = arenaFile.Create(ARENA_FILE, ARENA_SIZE);
if (-1 == arenaFile.Create(ARENA_FILE, ARENA_SIZE)) {
if (status != 0) {
std::cerr << "Create failed with error " << status << "\n";
abort();
} else if (!runSuite(arenaFile, "arenaFile")) {
abort();
@ -112,6 +116,6 @@ main(int argc, const char *argv[])
}
arenaMem.Clear();
std::cout << "OK" << std::endl;
std::cout << "OK" << "\n";
return 0;
}