ALOT OF CHANGES

This commit is contained in:
krzosa
2025-04-29 16:22:26 +02:00
parent ed9ec06eee
commit 92139cf799
38 changed files with 2230 additions and 1967 deletions

View File

@@ -228,7 +228,7 @@ inline int64_t StringLen(char *string) {
return i;
}
inline int64_t WideLength(wchar_t *string) {
inline int64_t WideLength(char16_t *string) {
if (!string) return 0;
int64_t len = 0;
while (*string++ != 0)
@@ -248,9 +248,9 @@ struct Slice {
Slice(const char *s) : data((char *)s), len(StringLen((char *)s)) {}
Slice(const char *s, int64_t l) : data((char *)s), len(l) {}
Slice(wchar_t *s) : data(s), len(WideLength(s)) {}
Slice(const wchar_t *s) : data((wchar_t *)s), len(WideLength((wchar_t *)s)) {}
Slice(const wchar_t *s, int64_t l) : data((wchar_t *)s), len(l) {}
Slice(char16_t *s) : data(s), len(WideLength(s)) {}
Slice(const char16_t *s) : data((char16_t *)s), len(WideLength((char16_t *)s)) {}
Slice(const char16_t *s, int64_t l) : data((char16_t *)s), len(l) {}
T &operator[](int64_t index) {
Assert(index < len);
@@ -782,7 +782,7 @@ struct UTF8Iter {
};
using String = Slice<char>;
using String16 = Slice<wchar_t>;
using String16 = Slice<char16_t>;
bool IsValid(UTF8Iter &iter);
void Advance(UTF8Iter *iter);
UTF8Iter IterateUTF8Ex(char *data, int64_t len);
@@ -792,7 +792,7 @@ UTF8Iter IterateUTF8(String string);
bool IsAlphabetic(char a);
#define FmtString(string) (int)(string).len, (string).data
bool AreEqual(String a, String b, unsigned ignore_case = false);
int64_t CreateWidecharFromChar(wchar_t *buffer, int64_t buffer_size, char *in, int64_t inlen);
int64_t CreateWidecharFromChar(char16_t *buffer, int64_t buffer_size, char *in, int64_t inlen);
inline bool operator==(String a, String b) { return AreEqual(a, b); }
inline bool operator!=(String a, String b) { return !AreEqual(a, b); }
@@ -805,16 +805,17 @@ inline bool operator!=(String a, String b) { return !AreEqual(a, b); }
String Format(Allocator allocator, const char *data, ...);
String FormatV(Allocator allocator, const char *data, va_list args1);
String ToString(Allocator allocator, String16 string);
String ToString(Allocator allocator, wchar_t *string, int64_t len);
String ToString(Allocator allocator, wchar_t *wstring);
String ToString(Allocator allocator, char16_t *string, int64_t len);
String ToString(Allocator allocator, char16_t *wstring);
String16 ToString16(Allocator allocator, String string);
wchar_t *ToWidechar(Allocator allocator, String string);
char16_t *ToWidechar(Allocator allocator, String string);
void NormalizePathInPlace(String s);
String ChopLastSlash(String s);
String ChopLastPeriod(String s);
String SkipToLastSlash(String s);
String SkipToLastPeriod(String s);
String Copy(Allocator allocator, String string);
Int GetSize(Array<String> array);
/*
Hash table implementation:
Pointers to values
@@ -1212,13 +1213,13 @@ UTF16Result UTF32ToUTF16(uint32_t codepoint) {
return result;
}
#define UTF__HANDLE_DECODE_ERROR(question_mark) \
#define UTF__HANDLE_DECODE_ERROR(question_mark, I) \
{ \
if (outlen < buffer_size - 1) buffer[outlen++] = (question_mark); \
break; \
i += I; \
}
int64_t CreateCharFromWidechar(char *buffer, int64_t buffer_size, wchar_t *in, int64_t inlen) {
int64_t CreateCharFromWidechar(char *buffer, int64_t buffer_size, char16_t *in, int64_t inlen) {
int64_t outlen = 0;
for (int64_t i = 0; i < inlen && in[i];) {
UTF32Result decode = UTF16ToUTF32((uint16_t *)(in + i), (int64_t)(inlen - i));
@@ -1231,15 +1232,15 @@ int64_t CreateCharFromWidechar(char *buffer, int64_t buffer_size, wchar_t *in, i
buffer[outlen++] = encode.out_str[j];
}
}
} else UTF__HANDLE_DECODE_ERROR('?');
} else UTF__HANDLE_DECODE_ERROR('?');
} else UTF__HANDLE_DECODE_ERROR('?', 0);
} else UTF__HANDLE_DECODE_ERROR('?', 1);
}
buffer[outlen] = 0;
return outlen;
}
int64_t CreateWidecharFromChar(wchar_t *buffer, int64_t buffer_size, char *in, int64_t inlen) {
int64_t CreateWidecharFromChar(char16_t *buffer, int64_t buffer_size, char *in, int64_t inlen) {
int64_t outlen = 0;
for (int64_t i = 0; i < inlen;) {
UTF32Result decode = UTF8ToUTF32((uint8_t *)(in + i), (int64_t)(inlen - i));
@@ -1252,8 +1253,8 @@ int64_t CreateWidecharFromChar(wchar_t *buffer, int64_t buffer_size, char *in, i
buffer[outlen++] = encode.out_str[j];
}
}
} else UTF__HANDLE_DECODE_ERROR(0x003f);
} else UTF__HANDLE_DECODE_ERROR(0x003f);
} else UTF__HANDLE_DECODE_ERROR(0x003f, 0);
} else UTF__HANDLE_DECODE_ERROR(0x003f, 1);
}
buffer[outlen] = 0;
@@ -1546,6 +1547,12 @@ String Merge(Allocator allocator, Array<String> list, String separator = " ") {
return string;
}
Int GetSize(Array<String> array) {
Int result = 0;
For (array) result += it.len;
return result;
}
#include <stdio.h>
String FormatV(Allocator allocator, const char *data, va_list args1) {
va_list args2;
@@ -1565,20 +1572,20 @@ String Format(Allocator allocator, const char *data, ...) {
}
String16 ToString16(Allocator allocator, String string) {
Assert(sizeof(wchar_t) == 2);
wchar_t *buffer = (wchar_t *)AllocSize(allocator, sizeof(wchar_t) * (string.len + 1));
Assert(sizeof(char16_t) == 2);
char16_t *buffer = (char16_t *)AllocSize(allocator, sizeof(char16_t) * (string.len + 1));
int64_t size = CreateWidecharFromChar(buffer, string.len + 1, string.data, string.len);
String16 result = {buffer, size};
return result;
}
wchar_t *ToWidechar(Allocator allocator, String string) {
char16_t *ToWidechar(Allocator allocator, String string) {
String16 result = ToString16(allocator, string);
return result.data;
}
String ToString(Allocator allocator, String16 string) {
Assert(sizeof(wchar_t) == 2);
Assert(sizeof(char16_t) == 2);
int64_t buffer_size = (string.len + 1) * 2;
char *buffer = (char *)AllocSize(allocator, buffer_size);
@@ -1589,11 +1596,11 @@ String ToString(Allocator allocator, String16 string) {
return result;
}
String ToString(Allocator allocator, wchar_t *string, int64_t len) {
String ToString(Allocator allocator, char16_t *string, int64_t len) {
return ToString(allocator, {string, len});
}
String ToString(Allocator allocator, wchar_t *wstring) {
String ToString(Allocator allocator, char16_t *wstring) {
int64_t size = WideLength(wstring);
String result = ToString(allocator, {wstring, size});
return result;

View File

@@ -1,52 +1,52 @@
#pragma once
#include "../basic/basic.h"
struct FileIter {
bool is_valid;
bool is_directory;
String absolute_path;
String relative_path;
String filename;
String path;
Allocator allocator;
Arena *arena;
TempArena temp;
union {
struct Win32_FileIter *w32;
void *dir;
};
};
String ReadFile(Allocator arena, String path);
bool WriteFile(String path, String data);
String GetAbsolutePath(Allocator arena, String relative);
bool IsValid(const FileIter &it);
void Advance(FileIter *it);
FileIter IterateFiles(Allocator allocator, String path);
void InitOS(void (*error_proc)(const char *, ...));
String GetExePath(Allocator allocator);
String GetExeDir(Allocator allocator);
bool FileExists(String path);
bool IsDir(String path);
bool IsFile(String path);
String GetWorkingDir(Allocator arena);
bool IsAbsolute(String path);
int64_t GetFileModTime(String file);
struct Process {
bool is_valid;
int exit_code;
char platform[6 * 8];
int64_t view_id; // text editor view
bool scroll_to_end;
};
Process SpawnProcess(String command_line, String working_dir, String write_stdin = {});
bool IsValid(Process *process);
void KillProcess(Process *process);
String PollStdout(Allocator allocator, Process *process);
void WriteStdin(Process *process, String string);
#pragma once
#include "../basic/basic.h"
struct FileIter {
bool is_valid;
bool is_directory;
String absolute_path;
String relative_path;
String filename;
String path;
Allocator allocator;
Arena *arena;
TempArena temp;
union {
struct Win32_FileIter *w32;
void *dir;
};
};
String ReadFile(Allocator arena, String path);
bool WriteFile(String path, String data);
String GetAbsolutePath(Allocator arena, String relative);
bool IsValid(const FileIter &it);
void Advance(FileIter *it);
FileIter IterateFiles(Allocator allocator, String path);
void InitOS(void (*error_proc)(const char *, ...));
String GetExePath(Allocator allocator);
String GetExeDir(Allocator allocator);
bool FileExists(String path);
bool IsDir(String path);
bool IsFile(String path);
String GetWorkingDir(Allocator arena);
bool IsAbsolute(String path);
int64_t GetFileModTime(String file);
struct Process {
bool is_valid;
int exit_code;
char platform[6 * 8];
int64_t view_id; // text editor view
bool scroll_to_end;
};
Process SpawnProcess(String command_line, String working_dir, String write_stdin = {}, Array<String> enviroment = {});
bool IsValid(Process *process);
void KillProcess(Process *process);
String PollStdout(Allocator allocator, Process *process);
void WriteStdin(Process *process, String string);
void CloseStdin(Process *process);

View File

@@ -1,68 +1,60 @@
wchar_t ToLowerCase(wchar_t a) {
char16_t ToLowerCase(char16_t a) {
if (a >= 'A' && a <= 'Z') a += 32;
return a;
}
wchar_t ToUpperCase(wchar_t a) {
char16_t ToUpperCase(char16_t a) {
if (a >= 'a' && a <= 'z') a -= 32;
return a;
}
bool IsWhitespace(wchar_t w) {
bool IsWhitespace(char16_t w) {
bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r';
return result;
}
bool IsSymbol(wchar_t w) {
bool IsSymbol(char16_t w) {
bool result = (w >= '!' && w <= '/') || (w >= ':' && w <= '@') || (w >= '[' && w <= '`') || (w >= '{' && w <= '~');
return result;
}
bool IsNonWord(wchar_t w) {
bool IsNonWord(char16_t w) {
if (w == '_') return false;
bool result = IsSymbol(w) || IsWhitespace(w);
return result;
}
bool IsWord(wchar_t w) {
bool IsWord(char16_t w) {
bool result = !IsNonWord(w);
return result;
}
bool IsLoadWord(wchar_t w) {
bool result = w == L'(' || w == L')' || w == L'/' || w == L'\\' || w == L':' || w == L'+' || w == L'_' || w == L'.' || w == L'-' || w == L',';
if (!result) {
result = !(IsSymbol(w) || IsWhitespace(w));
}
return result;
}
bool IsBrace(wchar_t c) {
bool IsBrace(char16_t c) {
bool result = c == '{' || c == '}';
return result;
}
bool IsParen(wchar_t c) {
bool IsParen(char16_t c) {
bool result = c == '(' || c == ')';
return result;
}
bool IsAlphabetic(wchar_t a) {
bool IsAlphabetic(char16_t a) {
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
return result;
}
bool IsIdent(wchar_t a) {
bool IsIdent(char16_t a) {
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || a == '_';
return result;
}
bool IsDigit(wchar_t a) {
bool IsDigit(char16_t a) {
bool result = a >= '0' && a <= '9';
return result;
}
bool IsAlphanumeric(wchar_t a) {
bool IsAlphanumeric(char16_t a) {
bool result = IsDigit(a) || IsAlphabetic(a);
return result;
}
@@ -70,8 +62,8 @@ bool IsAlphanumeric(wchar_t a) {
bool AreEqual(String16 a, String16 b, unsigned ignore_case = false) {
if (a.len != b.len) return false;
for (int64_t i = 0; i < a.len; i++) {
wchar_t A = a.data[i];
wchar_t B = b.data[i];
char16_t A = a.data[i];
char16_t B = b.data[i];
if (ignore_case) {
A = ToLowerCase(A);
B = ToLowerCase(B);
@@ -84,8 +76,8 @@ bool AreEqual(String16 a, String16 b, unsigned ignore_case = false) {
inline bool operator==(String16 a, String16 b) { return AreEqual(a, b); }
inline bool operator!=(String16 a, String16 b) { return !AreEqual(a, b); }
wchar_t GetChar(String16 string, int64_t i) {
wchar_t result = 0;
char16_t GetChar(String16 string, int64_t i) {
char16_t result = 0;
if (i < string.len) result = string.data[i];
return result;
}
@@ -99,14 +91,14 @@ String16 Merge(Allocator allocator, Array<String16> list, String16 separator = "
int64_t base_size = (char_count + 1);
int64_t sep_size = (node_count - 1) * separator.len;
int64_t size = base_size + sep_size;
wchar_t *buff = (wchar_t *)AllocSize(allocator, sizeof(wchar_t) * (size + 1));
char16_t *buff = (char16_t *)AllocSize(allocator, sizeof(char16_t) * (size + 1));
String16 string = {buff, 0};
For(list) {
Assert(string.len + it.len <= size);
memcpy(string.data + string.len, it.data, it.len * sizeof(wchar_t));
memcpy(string.data + string.len, it.data, it.len * sizeof(char16_t));
string.len += it.len;
if (!IsLast(list, it)) {
memcpy(string.data + string.len, separator.data, separator.len * sizeof(wchar_t));
memcpy(string.data + string.len, separator.data, separator.len * sizeof(char16_t));
string.len += separator.len;
}
}
@@ -146,20 +138,20 @@ bool Seek(String16 string, String16 find, int64_t *index_out = NULL, SeekFlag fl
String16 ChopLastSlash(String16 s) {
String16 result = s;
Seek(s, L"/", &result.len, SeekFlag_MatchFindLast);
Seek(s, u"/", &result.len, SeekFlag_MatchFindLast);
return result;
}
String16 ChopLastPeriod(String16 s) {
String16 result = s;
Seek(s, L".", &result.len, SeekFlag_MatchFindLast);
Seek(s, u".", &result.len, SeekFlag_MatchFindLast);
return result;
}
String16 SkipToLastSlash(String16 s) {
int64_t pos;
String16 result = s;
if (Seek(s, L"/", &pos, SeekFlag_MatchFindLast)) {
if (Seek(s, u"/", &pos, SeekFlag_MatchFindLast)) {
result = Skip(result, pos + 1);
}
return result;
@@ -168,7 +160,7 @@ String16 SkipToLastSlash(String16 s) {
String16 SkipToLastPeriod(String16 s) {
int64_t pos;
String16 result = s;
if (Seek(s, L".", &pos, SeekFlag_MatchFindLast)) {
if (Seek(s, u".", &pos, SeekFlag_MatchFindLast)) {
result = Skip(result, pos + 1);
}
return result;
@@ -238,22 +230,22 @@ bool StartsWith(String16 a, String16 start, unsigned ignore_case = false) {
}
String16 Copy16(Allocator allocator, String16 string) {
wchar_t *copy = (wchar_t *)AllocSize(allocator, sizeof(wchar_t) * (string.len + 1));
char16_t *copy = (char16_t *)AllocSize(allocator, sizeof(char16_t) * (string.len + 1));
memcpy(copy, string.data, string.len);
copy[string.len] = 0;
String16 result = {copy, string.len};
return result;
}
String16 Copy16(Allocator allocator, wchar_t *string) {
String16 Copy16(Allocator allocator, char16_t *string) {
String16 s = {string, (int64_t)WideLength(string)};
return Copy(allocator, s);
}
void NormalizePathInPlace(String16 s) {
for (int64_t i = 0; i < s.len; i++) {
if (s.data[i] == L'\\')
s.data[i] = L'/';
if (s.data[i] == u'\\')
s.data[i] = u'/';
}
}
@@ -306,14 +298,14 @@ String16 SkipWhitespace(String16 *string) {
return begin;
}
String16 Format16V(Allocator allocator, const wchar_t *data, va_list args1) {
String16 Format16V(Allocator allocator, const char16_t *data, va_list args1) {
va_list args2;
va_copy(args2, args1);
int64_t len = vswprintf(0, 0, data, args2);
int64_t len = vswprintf(0, 0, (wchar_t *)data, args2); // @todo: check this type
va_end(args2);
wchar_t *result = (wchar_t *)AllocSize(allocator, sizeof(wchar_t) * (len + 1));
vswprintf(result, (int)(len + 1), data, args1);
char16_t *result = (char16_t *)AllocSize(allocator, sizeof(char16_t) * (len + 1));
vswprintf((wchar_t *)result, (int)(len + 1), (wchar_t *)data, args1); // @todo: check this type
String16 res = {result, len};
return res;
}
@@ -324,7 +316,7 @@ String16 Format16V(Allocator allocator, const wchar_t *data, va_list args1) {
String16 result = Format16V(allocator, data, args1); \
va_end(args1)
String16 Format16(Allocator allocator, const wchar_t *data, ...) {
String16 Format16(Allocator allocator, const char16_t *data, ...) {
STRING16_FORMAT(allocator, data, result);
return result;
}

View File

@@ -1,469 +1,485 @@
#include "filesystem.h"
#include "thread_queue.h"
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <stdio.h>
#include <intrin.h>
#include "win32_thread.cpp"
void (*Error)(const char *, ...);
// Basic begin
void *VReserve(size_t size) {
void *result = (uint8_t *)VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE);
return result;
}
bool VCommit(void *p, size_t size) {
void *result = VirtualAlloc(p, size, MEM_COMMIT, PAGE_READWRITE);
return result ? true : false;
}
bool VRelease(void *p) {
BOOL result = VirtualFree(p, 0, MEM_RELEASE);
return result ? true : false;
}
bool VDecommit(void *p, size_t size) {
BOOL result = VirtualFree(p, size, MEM_DECOMMIT);
return result ? true : false;
}
// Basic end
void InitOS(void (*error_proc)(const char *, ...)) {
Error = error_proc;
}
String ReadFile(Allocator arena, String path) {
bool success = false;
String result = {};
Scratch scratch(arena);
String16 string16 = ToString16(scratch, path);
HANDLE handle = CreateFileW(string16.data, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle != INVALID_HANDLE_VALUE) {
LARGE_INTEGER file_size;
if (GetFileSizeEx(handle, &file_size)) {
if (file_size.QuadPart != 0) {
result.len = (int64_t)file_size.QuadPart;
result.data = (char *)AllocSize(arena, result.len + 1);
DWORD read;
if (ReadFile(handle, result.data, (DWORD)result.len, &read, NULL)) { // @todo: can only read 32 byte size files?
if (read == result.len) {
success = true;
result.data[result.len] = 0;
}
}
}
}
CloseHandle(handle);
}
if (!success) {
Dealloc(arena, &result.data);
result = {};
}
return result;
}
typedef struct Win32_FileIter {
HANDLE handle;
WIN32_FIND_DATAW data;
} Win32_FileIter;
String GetAbsolutePath(Allocator arena, String relative) {
Scratch scratch(arena);
String16 wpath = ToString16(scratch, relative);
wchar_t *wpath_abs = AllocArray(scratch, wchar_t, 4096);
DWORD written = GetFullPathNameW(wpath.data, 4096, wpath_abs, 0);
if (written == 0)
return {};
String path = ToString(arena, {(wchar_t *)wpath_abs, written});
NormalizePathInPlace(path);
return path;
}
bool IsValid(const FileIter &it) {
return it.is_valid;
}
void Advance(FileIter *it) {
if (it->temp.arena) EndTemp(it->temp);
it->temp = BeginTemp(it->arena);
while (FindNextFileW(it->w32->handle, &it->w32->data) != 0) {
WIN32_FIND_DATAW *data = &it->w32->data;
// Skip '.' and '..'
if (data->cFileName[0] == '.' && data->cFileName[1] == '.' && data->cFileName[2] == 0) continue;
if (data->cFileName[0] == '.' && data->cFileName[1] == 0) continue;
it->is_directory = data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
it->filename = ToString(*it->arena, (wchar_t *)data->cFileName, WideLength(data->cFileName));
const char *is_dir = it->is_directory ? "/" : "";
const char *separator = it->path.data[it->path.len - 1] == '/' ? "" : "/";
it->relative_path = Format(*it->arena, "%.*s%s%.*s%s", FmtString(it->path), separator, FmtString(it->filename), is_dir);
it->absolute_path = GetAbsolutePath(*it->arena, it->relative_path);
it->is_valid = true;
if (it->is_directory) {
Assert(it->relative_path.data[it->relative_path.len - 1] == '/');
Assert(it->absolute_path.data[it->absolute_path.len - 1] == '/');
}
return;
}
it->is_valid = false;
DWORD error = GetLastError();
Assert(error == ERROR_NO_MORE_FILES);
FindClose(it->w32->handle);
Dealloc(it->allocator, &it->arena);
}
FileIter IterateFiles(Allocator alo, String path) {
FileIter it = {0};
it.allocator = alo;
it.arena = AllocArena(alo, MiB(2));
it.path = path;
String modified_path = Format(*it.arena, "%.*s\\*", FmtString(path));
String16 modified_path16 = ToString16(*it.arena, modified_path);
it.w32 = AllocType(*it.arena, Win32_FileIter);
it.w32->handle = FindFirstFileW(modified_path16.data, &it.w32->data);
if (it.w32->handle == INVALID_HANDLE_VALUE) {
it.is_valid = false;
return it;
}
Advance(&it);
return it;
}
#if _WIN32
#include <Windows.h>
double get_time_in_micros(void) {
static double invfreq;
if (!invfreq) {
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
invfreq = 1000000.0 / frequency.QuadPart;
}
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
return counter.QuadPart * invfreq;
}
#else
#include <unistd.h>
double get_time_in_micros(void) {
struct timespec spec;
clock_gettime(CLOCK_MONOTONIC, &spec);
return (((double)spec.tv_sec) * 1000000) + (((double)spec.tv_nsec) / 1000);
}
#endif
bool WriteFile(String path, String data) {
bool result = false;
Scratch scratch;
String16 wpath = ToString16(scratch, path);
DWORD access = GENERIC_WRITE;
DWORD creation_disposition = CREATE_ALWAYS;
HANDLE handle = CreateFileW(wpath.data, access, 0, NULL, creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle != INVALID_HANDLE_VALUE) {
DWORD bytes_written = 0;
Assert(data.len == (DWORD)data.len); // @Todo: can only read 32 byte size files?
BOOL error = WriteFile(handle, data.data, (DWORD)data.len, &bytes_written, NULL);
if (error == TRUE) {
if (bytes_written == data.len) {
result = true;
}
}
CloseHandle(handle);
}
return result;
}
String GetExePath(Allocator allocator) {
Scratch scratch(allocator);
wchar_t *wbuffer = AllocArray(scratch, wchar_t, 4096);
DWORD wsize = GetModuleFileNameW(0, wbuffer, 4096);
Assert(wsize != 0);
String path = ToString(allocator, wbuffer, wsize);
NormalizePathInPlace(path);
return path;
}
String GetExeDir(Allocator allocator) {
Scratch scratch((Arena *)allocator.object);
String path = GetExePath(scratch);
path = ChopLastSlash(path);
path = Copy(allocator, path);
return path;
}
bool FileExists(String path) {
Scratch scratch;
String16 wbuff = ToString16(scratch, path);
DWORD attribs = GetFileAttributesW(wbuff.data);
bool result = attribs == INVALID_FILE_ATTRIBUTES ? false : true;
return result;
}
bool IsDir(String path) {
Scratch scratch;
String16 wbuff = ToString16(scratch, path);
DWORD dwAttrib = GetFileAttributesW(wbuff.data);
return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
}
bool IsFile(String path) {
Scratch scratch;
String16 wbuff = ToString16(scratch, path);
DWORD dwAttrib = GetFileAttributesW(wbuff.data);
bool is_file = (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0;
return dwAttrib != INVALID_FILE_ATTRIBUTES && is_file;
}
String GetWorkingDir(Allocator arena) {
Scratch scratch(arena);
wchar_t *wbuffer = AllocArray(scratch, wchar_t, 4096);
DWORD wsize = GetCurrentDirectoryW(4096, wbuffer);
Assert(wsize != 0);
wbuffer[wsize] = 0;
String path = ToString(arena, wbuffer, wsize);
NormalizePathInPlace(path);
return path;
}
bool IsAbsolute(String path) {
bool result = path.len > 3 && IsAlphabetic(path.data[0]) && path.data[1] == ':' && (path.data[2] == '/' || path.data[2] == '\\');
return result;
}
bool DeleteFile(String path) {
Scratch scratch;
String16 wpath = ToString16(scratch, path);
BOOL success = DeleteFileW(wpath.data);
bool result = true;
if (success == 0) result = false;
return result;
}
int64_t GetFileModTime(String file) {
Scratch scratch;
String16 string16 = ToString16(scratch, file);
WIN32_FIND_DATAW data;
HANDLE handle = FindFirstFileW(string16.data, &data);
if (handle != INVALID_HANDLE_VALUE) {
FindClose(handle);
FILETIME time = data.ftLastWriteTime;
int64_t result = (int64_t)time.dwHighDateTime << 32 | time.dwLowDateTime;
return result;
} else {
return -1;
}
}
struct Win32Process {
HANDLE handle;
HANDLE child_stdout_read;
HANDLE child_stdout_write;
HANDLE child_stdin_read;
HANDLE child_stdin_write;
};
static_assert(sizeof(Win32Process) < sizeof(Process::platform));
void Win32ReportError(String msg, String cmd) {
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);
defer { LocalFree(lpMsgBuf); };
char *buff = (char *)lpMsgBuf;
size_t buffLen = strlen((const char *)buff);
if (buffLen > 0 && buff[buffLen - 1] == '\n') buff[buffLen - 1] = 0;
Error("%.*s: %.*s! %s", FmtString(msg), FmtString(cmd), (char *)lpMsgBuf);
}
void Win32CloseProcess(Process *process) {
Win32Process *p = (Win32Process *)process->platform;
if (p->handle != INVALID_HANDLE_VALUE) CloseHandle(p->handle);
if (p->child_stdout_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_write);
if (p->child_stdout_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_read);
if (p->child_stdin_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_read);
if (p->child_stdin_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_write);
process->is_valid = false;
}
static void Win32ProcessError(Process *process, String msg, String cmd) {
Win32Process *p = (Win32Process *)process->platform;
Win32ReportError(msg, cmd);
Win32CloseProcess(process);
}
void WriteStdin(Process *process, String string) {
if (string.len == 0) return;
Assert(process->is_valid);
Win32Process *p = (Win32Process *)process->platform;
Assert(p->child_stdin_write != INVALID_HANDLE_VALUE);
DWORD written = 0;
bool write_error = WriteFile(p->child_stdin_write, string.data, (DWORD)string.len, &written, NULL) == 0;
Assert(write_error == false);
Assert(written == string.len);
}
void CloseStdin(Process *process) {
Win32Process *p = (Win32Process *)process->platform;
CloseHandle(p->child_stdin_write);
p->child_stdin_write = INVALID_HANDLE_VALUE;
}
Process SpawnProcess(String command_line, String working_dir, String write_stdin) {
Process process = {};
Win32Process *p = (Win32Process *)process.platform;
Scratch scratch;
command_line = Format(scratch, "/C %.*s", FmtString(command_line));
p->handle = INVALID_HANDLE_VALUE;
p->child_stdout_write = INVALID_HANDLE_VALUE;
p->child_stdout_read = INVALID_HANDLE_VALUE;
p->child_stdin_read = INVALID_HANDLE_VALUE;
p->child_stdin_write = INVALID_HANDLE_VALUE;
SECURITY_ATTRIBUTES security_atrb = {};
security_atrb.nLength = sizeof(SECURITY_ATTRIBUTES);
security_atrb.bInheritHandle = TRUE;
if (!CreatePipe(&p->child_stdout_read, &p->child_stdout_write, &security_atrb, 0)) {
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
return process;
}
if (!SetHandleInformation(p->child_stdout_read, HANDLE_FLAG_INHERIT, 0)) {
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
return process;
}
if (!CreatePipe(&p->child_stdin_read, &p->child_stdin_write, &security_atrb, 0)) {
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
return process;
}
if (!SetHandleInformation(p->child_stdin_write, HANDLE_FLAG_INHERIT, 0)) {
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
return process;
}
STARTUPINFOW startup = {};
startup.cb = sizeof(STARTUPINFO);
startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
startup.hStdInput = p->child_stdin_read;
startup.hStdOutput = p->child_stdout_write;
startup.hStdError = p->child_stdout_write;
startup.wShowWindow = SW_HIDE;
String16 cwd = ToString16(scratch, working_dir);
String16 cmd = ToString16(scratch, command_line);
void *env = NULL;
DWORD dwCreationFlags = 0;
BOOL bInheritHandles = TRUE;
PROCESS_INFORMATION info = {};
if (!CreateProcessW(L"c:\\windows\\system32\\cmd.exe", cmd.data, 0, 0, bInheritHandles, dwCreationFlags, env, cwd.data, &startup, &info)) {
Win32ProcessError(&process, "failed to create process", command_line);
return process;
}
// Close handles to the stdin and stdout pipes no longer needed by the child process.
// If they are not explicitly closed, there is no way to recognize that the child process has ended.
CloseHandle(info.hThread);
CloseHandle(p->child_stdin_read);
CloseHandle(p->child_stdout_write);
p->child_stdin_read = INVALID_HANDLE_VALUE;
p->child_stdout_write = INVALID_HANDLE_VALUE;
p->handle = info.hProcess;
process.is_valid = true;
if (write_stdin.len) {
WriteStdin(&process, write_stdin);
CloseStdin(&process);
}
return process;
}
bool IsValid(Process *process) {
if (process->is_valid == false) return false;
Win32Process *p = (Win32Process *)process->platform;
if (WaitForSingleObject(p->handle, 0) != WAIT_OBJECT_0) {
return true;
}
DWORD exit_code;
bool get_exit_code_failed = GetExitCodeProcess(p->handle, &exit_code) == 0;
if (get_exit_code_failed) {
exit_code = -1;
}
process->exit_code = (int)exit_code;
Win32CloseProcess(process);
return false;
}
void KillProcess(Process *process) {
Assert(process->is_valid);
Win32Process *p = (Win32Process *)process->platform;
bool terminate_process_error = TerminateProcess(p->handle, -1) == 0;
if (terminate_process_error) {
Assert(0);
}
Win32CloseProcess(process);
process->exit_code = -1;
}
String PollStdout(Allocator allocator, Process *process) {
Assert(process->is_valid);
Win32Process *p = (Win32Process *)process->platform;
DWORD bytes_avail = 0;
bool peek_error = PeekNamedPipe(p->child_stdout_read, NULL, 0, NULL, &bytes_avail, NULL) == 0;
if (peek_error) {
return {};
} else if (bytes_avail == 0) {
return {};
}
size_t buffer_size = ClampTop(bytes_avail, (DWORD)(4096 * 16));
char *buffer = AllocArray(allocator, char, buffer_size);
DWORD bytes_read = 0;
bool read_error = ReadFile(p->child_stdout_read, buffer, (DWORD)buffer_size, &bytes_read, 0) == 0;
if (read_error) {
Win32ReportError("Failed to read the stdout of child process", "ReadFile");
Dealloc(allocator, &buffer);
Win32CloseProcess(process);
return {};
}
String result = {buffer, bytes_read};
return result;
}
#include "filesystem.h"
#include "thread_queue.h"
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <stdio.h>
#include <intrin.h>
#include "win32_thread.cpp"
void (*Error)(const char *, ...);
// Basic begin
void *VReserve(size_t size) {
void *result = (uint8_t *)VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE);
return result;
}
bool VCommit(void *p, size_t size) {
void *result = VirtualAlloc(p, size, MEM_COMMIT, PAGE_READWRITE);
return result ? true : false;
}
bool VRelease(void *p) {
BOOL result = VirtualFree(p, 0, MEM_RELEASE);
return result ? true : false;
}
bool VDecommit(void *p, size_t size) {
BOOL result = VirtualFree(p, size, MEM_DECOMMIT);
return result ? true : false;
}
// Basic end
void InitOS(void (*error_proc)(const char *, ...)) {
Error = error_proc;
SetConsoleOutputCP(65001);
SetConsoleCP(65001);
}
String ReadFile(Allocator arena, String path) {
bool success = false;
String result = {};
Scratch scratch(arena);
String16 string16 = ToString16(scratch, path);
HANDLE handle = CreateFileW((wchar_t *)string16.data, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle != INVALID_HANDLE_VALUE) {
LARGE_INTEGER file_size;
if (GetFileSizeEx(handle, &file_size)) {
if (file_size.QuadPart != 0) {
result.len = (int64_t)file_size.QuadPart;
result.data = (char *)AllocSize(arena, result.len + 1);
DWORD read;
if (ReadFile(handle, result.data, (DWORD)result.len, &read, NULL)) { // @todo: can only read 32 byte size files?
if (read == result.len) {
success = true;
result.data[result.len] = 0;
}
}
}
}
CloseHandle(handle);
}
if (!success) {
Dealloc(arena, &result.data);
result = {};
}
return result;
}
typedef struct Win32_FileIter {
HANDLE handle;
WIN32_FIND_DATAW data;
} Win32_FileIter;
String GetAbsolutePath(Allocator arena, String relative) {
Scratch scratch(arena);
String16 wpath = ToString16(scratch, relative);
wchar_t *wpath_abs = AllocArray(scratch, wchar_t, 4096);
DWORD written = GetFullPathNameW((wchar_t *)wpath.data, 4096, wpath_abs, 0);
if (written == 0)
return {};
String path = ToString(arena, {(char16_t *)wpath_abs, written});
NormalizePathInPlace(path);
return path;
}
bool IsValid(const FileIter &it) {
return it.is_valid;
}
void Advance(FileIter *it) {
if (it->temp.arena) EndTemp(it->temp);
it->temp = BeginTemp(it->arena);
while (FindNextFileW(it->w32->handle, &it->w32->data) != 0) {
WIN32_FIND_DATAW *data = &it->w32->data;
// Skip '.' and '..'
if (data->cFileName[0] == '.' && data->cFileName[1] == '.' && data->cFileName[2] == 0) continue;
if (data->cFileName[0] == '.' && data->cFileName[1] == 0) continue;
it->is_directory = data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
it->filename = ToString(*it->arena, (char16_t *)data->cFileName, WideLength((char16_t *)data->cFileName));
const char *is_dir = it->is_directory ? "/" : "";
const char *separator = it->path.data[it->path.len - 1] == '/' ? "" : "/";
it->relative_path = Format(*it->arena, "%.*s%s%.*s%s", FmtString(it->path), separator, FmtString(it->filename), is_dir);
it->absolute_path = GetAbsolutePath(*it->arena, it->relative_path);
it->is_valid = true;
if (it->is_directory) {
Assert(it->relative_path.data[it->relative_path.len - 1] == '/');
Assert(it->absolute_path.data[it->absolute_path.len - 1] == '/');
}
return;
}
it->is_valid = false;
DWORD error = GetLastError();
Assert(error == ERROR_NO_MORE_FILES);
FindClose(it->w32->handle);
Dealloc(it->allocator, &it->arena);
}
FileIter IterateFiles(Allocator alo, String path) {
FileIter it = {0};
it.allocator = alo;
it.arena = AllocArena(alo, MiB(2));
it.path = path;
String modified_path = Format(*it.arena, "%.*s\\*", FmtString(path));
String16 modified_path16 = ToString16(*it.arena, modified_path);
it.w32 = AllocType(*it.arena, Win32_FileIter);
it.w32->handle = FindFirstFileW((wchar_t *)modified_path16.data, &it.w32->data);
if (it.w32->handle == INVALID_HANDLE_VALUE) {
it.is_valid = false;
return it;
}
Advance(&it);
return it;
}
#if _WIN32
#include <Windows.h>
double get_time_in_micros(void) {
static double invfreq;
if (!invfreq) {
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
invfreq = 1000000.0 / frequency.QuadPart;
}
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
return counter.QuadPart * invfreq;
}
#else
#include <unistd.h>
double get_time_in_micros(void) {
struct timespec spec;
clock_gettime(CLOCK_MONOTONIC, &spec);
return (((double)spec.tv_sec) * 1000000) + (((double)spec.tv_nsec) / 1000);
}
#endif
bool WriteFile(String path, String data) {
bool result = false;
Scratch scratch;
String16 wpath = ToString16(scratch, path);
DWORD access = GENERIC_WRITE;
DWORD creation_disposition = CREATE_ALWAYS;
HANDLE handle = CreateFileW((wchar_t *)wpath.data, access, 0, NULL, creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle != INVALID_HANDLE_VALUE) {
DWORD bytes_written = 0;
Assert(data.len == (DWORD)data.len); // @Todo: can only read 32 byte size files?
BOOL error = WriteFile(handle, data.data, (DWORD)data.len, &bytes_written, NULL);
if (error == TRUE) {
if (bytes_written == data.len) {
result = true;
}
}
CloseHandle(handle);
}
return result;
}
String GetExePath(Allocator allocator) {
Scratch scratch(allocator);
wchar_t *wbuffer = AllocArray(scratch, wchar_t, 4096);
DWORD wsize = GetModuleFileNameW(0, wbuffer, 4096);
Assert(wsize != 0);
String path = ToString(allocator, (char16_t *)wbuffer, wsize);
NormalizePathInPlace(path);
return path;
}
String GetExeDir(Allocator allocator) {
Scratch scratch((Arena *)allocator.object);
String path = GetExePath(scratch);
path = ChopLastSlash(path);
path = Copy(allocator, path);
return path;
}
bool FileExists(String path) {
Scratch scratch;
String16 wbuff = ToString16(scratch, path);
DWORD attribs = GetFileAttributesW((wchar_t *)wbuff.data);
bool result = attribs == INVALID_FILE_ATTRIBUTES ? false : true;
return result;
}
bool IsDir(String path) {
Scratch scratch;
String16 wbuff = ToString16(scratch, path);
DWORD dwAttrib = GetFileAttributesW((wchar_t *)wbuff.data);
return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
}
bool IsFile(String path) {
Scratch scratch;
String16 wbuff = ToString16(scratch, path);
DWORD dwAttrib = GetFileAttributesW((wchar_t *)wbuff.data);
bool is_file = (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0;
return dwAttrib != INVALID_FILE_ATTRIBUTES && is_file;
}
String GetWorkingDir(Allocator arena) {
Scratch scratch(arena);
wchar_t *wbuffer = AllocArray(scratch, wchar_t, 4096);
DWORD wsize = GetCurrentDirectoryW(4096, wbuffer);
Assert(wsize != 0);
wbuffer[wsize] = 0;
String path = ToString(arena, (char16_t *)wbuffer, wsize);
NormalizePathInPlace(path);
return path;
}
bool IsAbsolute(String path) {
bool result = path.len > 3 && IsAlphabetic(path.data[0]) && path.data[1] == ':' && (path.data[2] == '/' || path.data[2] == '\\');
return result;
}
bool DeleteFile(String path) {
Scratch scratch;
String16 wpath = ToString16(scratch, path);
BOOL success = DeleteFileW((wchar_t *)wpath.data);
bool result = true;
if (success == 0) result = false;
return result;
}
int64_t GetFileModTime(String file) {
Scratch scratch;
String16 string16 = ToString16(scratch, file);
WIN32_FIND_DATAW data;
HANDLE handle = FindFirstFileW((wchar_t *)string16.data, &data);
if (handle != INVALID_HANDLE_VALUE) {
FindClose(handle);
FILETIME time = data.ftLastWriteTime;
int64_t result = (int64_t)time.dwHighDateTime << 32 | time.dwLowDateTime;
return result;
} else {
return -1;
}
}
struct Win32Process {
HANDLE handle;
HANDLE child_stdout_read;
HANDLE child_stdout_write;
HANDLE child_stdin_read;
HANDLE child_stdin_write;
};
// static_assert(sizeof(Win32Process) < sizeof(Process::platform));
void Win32ReportError(String msg, String cmd) {
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);
defer { LocalFree(lpMsgBuf); };
char *buff = (char *)lpMsgBuf;
size_t buffLen = strlen((const char *)buff);
if (buffLen > 0 && buff[buffLen - 1] == '\n') buff[buffLen - 1] = 0;
Error("%.*s: %.*s! %s", FmtString(msg), FmtString(cmd), (char *)lpMsgBuf);
}
void Win32CloseProcess(Process *process) {
Win32Process *p = (Win32Process *)process->platform;
if (p->handle != INVALID_HANDLE_VALUE) CloseHandle(p->handle);
if (p->child_stdout_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_write);
if (p->child_stdout_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_read);
if (p->child_stdin_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_read);
if (p->child_stdin_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_write);
process->is_valid = false;
}
static void Win32ProcessError(Process *process, String msg, String cmd) {
Win32Process *p = (Win32Process *)process->platform;
Win32ReportError(msg, cmd);
Win32CloseProcess(process);
}
void WriteStdin(Process *process, String string) {
if (string.len == 0) return;
Assert(process->is_valid);
Win32Process *p = (Win32Process *)process->platform;
Assert(p->child_stdin_write != INVALID_HANDLE_VALUE);
DWORD written = 0;
bool write_error = WriteFile(p->child_stdin_write, string.data, (DWORD)string.len, &written, NULL) == 0;
Assert(write_error == false);
Assert(written == string.len);
}
void CloseStdin(Process *process) {
Win32Process *p = (Win32Process *)process->platform;
CloseHandle(p->child_stdin_write);
p->child_stdin_write = INVALID_HANDLE_VALUE;
}
Process SpawnProcess(String command_line, String working_dir, String write_stdin, Array<String> enviroment) {
Process process = {};
Win32Process *p = (Win32Process *)process.platform;
Scratch scratch;
command_line = Format(scratch, "/C %.*s", FmtString(command_line));
p->handle = INVALID_HANDLE_VALUE;
p->child_stdout_write = INVALID_HANDLE_VALUE;
p->child_stdout_read = INVALID_HANDLE_VALUE;
p->child_stdin_read = INVALID_HANDLE_VALUE;
p->child_stdin_write = INVALID_HANDLE_VALUE;
SECURITY_ATTRIBUTES security_atrb = {};
security_atrb.nLength = sizeof(SECURITY_ATTRIBUTES);
security_atrb.bInheritHandle = TRUE;
if (!CreatePipe(&p->child_stdout_read, &p->child_stdout_write, &security_atrb, 0)) {
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
return process;
}
if (!SetHandleInformation(p->child_stdout_read, HANDLE_FLAG_INHERIT, 0)) {
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
return process;
}
if (!CreatePipe(&p->child_stdin_read, &p->child_stdin_write, &security_atrb, 0)) {
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
return process;
}
if (!SetHandleInformation(p->child_stdin_write, HANDLE_FLAG_INHERIT, 0)) {
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
return process;
}
STARTUPINFOW startup = {};
startup.cb = sizeof(STARTUPINFO);
startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
startup.hStdInput = p->child_stdin_read;
startup.hStdOutput = p->child_stdout_write;
startup.hStdError = p->child_stdout_write;
startup.wShowWindow = SW_HIDE;
String16 cwd = ToString16(scratch, working_dir);
String16 cmd = ToString16(scratch, command_line);
char *env = NULL;
if (enviroment.len) {
Int size = GetSize(enviroment) + enviroment.len + 1;
env = (char *)PushSize(scratch, size);
Int i = 0;
For(enviroment) {
MemoryCopy(env + i, it.data, it.len);
i += it.len;
env[i++] = 0;
}
env[i++] = 0;
}
DWORD dwCreationFlags = 0;
BOOL bInheritHandles = TRUE;
PROCESS_INFORMATION info = {};
if (!CreateProcessW(L"c:\\windows\\system32\\cmd.exe", (wchar_t *)cmd.data, 0, 0, bInheritHandles, dwCreationFlags, env, (wchar_t *)cwd.data, &startup, &info)) {
Win32ProcessError(&process, "failed to create process", command_line);
return process;
}
// Close handles to the stdin and stdout pipes no longer needed by the child process.
// If they are not explicitly closed, there is no way to recognize that the child process has ended.
CloseHandle(info.hThread);
CloseHandle(p->child_stdin_read);
CloseHandle(p->child_stdout_write);
p->child_stdin_read = INVALID_HANDLE_VALUE;
p->child_stdout_write = INVALID_HANDLE_VALUE;
p->handle = info.hProcess;
process.is_valid = true;
if (write_stdin.len) {
WriteStdin(&process, write_stdin);
CloseStdin(&process);
}
return process;
}
bool IsValid(Process *process) {
if (process->is_valid == false) return false;
Win32Process *p = (Win32Process *)process->platform;
if (WaitForSingleObject(p->handle, 0) != WAIT_OBJECT_0) {
return true;
}
DWORD exit_code;
bool get_exit_code_failed = GetExitCodeProcess(p->handle, &exit_code) == 0;
if (get_exit_code_failed) {
exit_code = -1;
}
process->exit_code = (int)exit_code;
Win32CloseProcess(process);
return false;
}
void KillProcess(Process *process) {
Assert(process->is_valid);
Win32Process *p = (Win32Process *)process->platform;
bool terminate_process_error = TerminateProcess(p->handle, -1) == 0;
if (terminate_process_error) {
Assert(0);
}
Win32CloseProcess(process);
process->exit_code = -1;
}
String PollStdout(Allocator allocator, Process *process) {
Assert(process->is_valid);
Win32Process *p = (Win32Process *)process->platform;
DWORD bytes_avail = 0;
bool peek_error = PeekNamedPipe(p->child_stdout_read, NULL, 0, NULL, &bytes_avail, NULL) == 0;
if (peek_error) {
return {};
} else if (bytes_avail == 0) {
return {};
}
size_t buffer_size = ClampTop(bytes_avail, (DWORD)(4096 * 16));
char *buffer = AllocArray(allocator, char, buffer_size);
DWORD bytes_read = 0;
bool read_error = ReadFile(p->child_stdout_read, buffer, (DWORD)buffer_size, &bytes_read, 0) == 0;
if (read_error) {
Win32ReportError("Failed to read the stdout of child process", "ReadFile");
Dealloc(allocator, &buffer);
Win32CloseProcess(process);
return {};
}
String result = {buffer, bytes_read};
return result;
}