os_win32.c (8744B)
1 /* See LICENSE for license details. */ 2 #include "util.h" 3 4 #define STD_OUTPUT_HANDLE -11 5 #define STD_ERROR_HANDLE -12 6 7 #define PAGE_READWRITE 0x04 8 #define MEM_COMMIT 0x1000 9 #define MEM_RESERVE 0x2000 10 #define MEM_RELEASE 0x8000 11 12 #define GENERIC_WRITE 0x40000000 13 #define GENERIC_READ 0x80000000 14 15 #define PIPE_TYPE_BYTE 0x00 16 #define PIPE_ACCESS_INBOUND 0x01 17 18 #define FILE_SHARE_READ 0x00000001 19 #define FILE_MAP_ALL_ACCESS 0x000F001F 20 #define FILE_FLAG_BACKUP_SEMANTICS 0x02000000 21 #define FILE_FLAG_OVERLAPPED 0x40000000 22 23 #define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 24 25 #define FILE_ACTION_MODIFIED 0x00000003 26 27 #define CREATE_ALWAYS 2 28 #define OPEN_EXISTING 3 29 30 typedef struct { 31 u16 wProcessorArchitecture; 32 u16 _pad1; 33 u32 dwPageSize; 34 size lpMinimumApplicationAddress; 35 size lpMaximumApplicationAddress; 36 u64 dwActiveProcessorMask; 37 u32 dwNumberOfProcessors; 38 u32 dwProcessorType; 39 u32 dwAllocationGranularity; 40 u16 wProcessorLevel; 41 u16 wProcessorRevision; 42 } w32_sys_info; 43 44 /* NOTE: this is packed because the w32 api designers are dumb and ordered the members 45 * incorrectly. They worked around it be making the ft* members a struct {u32, u32} which 46 * is aligned on a 4-byte boundary. Then in their documentation they explicitly tell you not 47 * to cast to u64 because "it can cause alignment faults on 64-bit Windows" - go figure */ 48 typedef struct __attribute__((packed)) { 49 u32 dwFileAttributes; 50 u64 ftCreationTime; 51 u64 ftLastAccessTime; 52 u64 ftLastWriteTime; 53 u32 dwVolumeSerialNumber; 54 u32 nFileSizeHigh; 55 u32 nFileSizeLow; 56 u32 nNumberOfLinks; 57 u32 nFileIndexHigh; 58 u32 nFileIndexLow; 59 } w32_file_info; 60 61 typedef struct { 62 u32 next_entry_offset; 63 u32 action; 64 u32 filename_size; 65 u16 filename[]; 66 } w32_file_notify_info; 67 68 typedef struct { 69 uptr internal, internal_high; 70 union { 71 struct {u32 off, off_high;}; 72 iptr pointer; 73 }; 74 iptr event_handle; 75 } w32_overlapped; 76 77 #define W32(r) __declspec(dllimport) r __stdcall 78 W32(b32) CloseHandle(iptr); 79 W32(b32) CopyFileA(c8 *, c8 *, b32); 80 W32(iptr) CreateFileA(c8 *, u32, u32, void *, u32, u32, void *); 81 W32(iptr) CreateFileMappingA(iptr, void *, u32, u32, u32, c8 *); 82 W32(iptr) CreateIoCompletionPort(iptr, iptr, uptr, u32); 83 W32(iptr) CreateNamedPipeA(c8 *, u32, u32, u32, u32, u32, u32, void *); 84 W32(b32) DeleteFileA(c8 *); 85 W32(void) ExitProcess(i32); 86 W32(b32) FreeLibrary(void *); 87 W32(b32) GetFileInformationByHandle(iptr, w32_file_info *); 88 W32(i32) GetLastError(void); 89 W32(void *) GetProcAddress(void *, c8 *); 90 W32(b32) GetQueuedCompletionStatus(iptr, u32 *, uptr *, w32_overlapped **, u32); 91 W32(iptr) GetStdHandle(i32); 92 W32(void) GetSystemInfo(void *); 93 W32(void *) LoadLibraryA(c8 *); 94 W32(void *) MapViewOfFile(iptr, u32, u32, u32, u64); 95 W32(b32) PeekNamedPipe(iptr, u8 *, i32, i32 *, i32 *, i32 *); 96 W32(b32) ReadDirectoryChangesW(iptr, u8 *, u32, b32, u32, u32 *, void *, void *); 97 W32(b32) ReadFile(iptr, u8 *, i32, i32 *, void *); 98 W32(b32) WriteFile(iptr, u8 *, i32, i32 *, void *); 99 W32(void *) VirtualAlloc(u8 *, size, u32, u32); 100 W32(b32) VirtualFree(u8 *, size, u32); 101 102 static iptr win32_stderr_handle; 103 104 static PLATFORM_WRITE_FILE_FN(os_write_file) 105 { 106 i32 wlen; 107 WriteFile(file, raw.data, raw.len, &wlen, 0); 108 return raw.len == wlen; 109 } 110 111 static void 112 os_write_err_msg(s8 msg) 113 { 114 if (!win32_stderr_handle) 115 win32_stderr_handle = GetStdHandle(STD_ERROR_HANDLE); 116 os_write_file(win32_stderr_handle, msg); 117 } 118 119 static void __attribute__((noreturn)) 120 os_fatal(s8 msg) 121 { 122 os_write_err_msg(msg); 123 ExitProcess(1); 124 unreachable(); 125 } 126 127 static PLATFORM_ALLOC_ARENA_FN(os_alloc_arena) 128 { 129 Arena result; 130 w32_sys_info Info; 131 GetSystemInfo(&Info); 132 133 if (capacity % Info.dwPageSize != 0) 134 capacity += (Info.dwPageSize - capacity % Info.dwPageSize); 135 136 size oldsize = old.end - old.beg; 137 if (oldsize > capacity) 138 return old; 139 140 if (old.beg) 141 VirtualFree(old.beg, oldsize, MEM_RELEASE); 142 143 result.beg = VirtualAlloc(0, capacity, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); 144 if (result.beg == NULL) 145 os_fatal(s8("os_alloc_arena: couldn't allocate memory\n")); 146 result.end = result.beg + capacity; 147 return result; 148 } 149 150 static PLATFORM_CLOSE_FN(os_close) 151 { 152 CloseHandle(file); 153 } 154 155 static PLATFORM_OPEN_FOR_WRITE_FN(os_open_for_write) 156 { 157 iptr result = CreateFileA(fname, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); 158 return result; 159 } 160 161 static s8 162 os_read_file(Arena *a, char *file, size filesize) 163 { 164 s8 result = {0}; 165 166 if (filesize > 0 && filesize <= (size)U32_MAX) { 167 result = s8alloc(a, filesize); 168 iptr h = CreateFileA(file, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); 169 if (h >= 0) { 170 i32 rlen; 171 if (!ReadFile(h, result.data, result.len, &rlen, 0) || rlen != result.len) { 172 result = (s8){0}; 173 } 174 CloseHandle(h); 175 } 176 } 177 178 return result; 179 } 180 181 static PLATFORM_WRITE_NEW_FILE_FN(os_write_new_file) 182 { 183 if (raw.len > (size)U32_MAX) { 184 os_write_err_msg(s8("os_write_file: files >4GB are not yet handled on win32\n")); 185 return 0; 186 } 187 188 iptr h = CreateFileA(fname, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); 189 if (h == INVALID_FILE) 190 return 0; 191 192 b32 ret = os_write_file(h, raw); 193 CloseHandle(h); 194 195 return ret; 196 } 197 198 static FileStats 199 os_get_file_stats(char *fname) 200 { 201 iptr h = CreateFileA(fname, 0, 0, 0, OPEN_EXISTING, 0, 0); 202 if (h == INVALID_FILE) 203 return ERROR_FILE_STATS; 204 205 w32_file_info fileinfo; 206 if (!GetFileInformationByHandle(h, &fileinfo)) { 207 os_write_err_msg(s8("os_get_file_stats: couldn't get file info\n")); 208 CloseHandle(h); 209 return ERROR_FILE_STATS; 210 } 211 CloseHandle(h); 212 213 size filesize = (size)fileinfo.nFileSizeHigh << 32; 214 filesize |= (size)fileinfo.nFileSizeLow; 215 216 return (FileStats){.filesize = filesize, .timestamp = fileinfo.ftLastWriteTime}; 217 } 218 219 static Pipe 220 os_open_named_pipe(char *name) 221 { 222 iptr h = CreateNamedPipeA(name, PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE, 1, 223 0, 1 * MEGABYTE, 0, 0); 224 return (Pipe){.file = h, .name = name}; 225 } 226 227 static PLATFORM_READ_PIPE_FN(os_read_pipe) 228 { 229 i32 total_read = 0; 230 ReadFile(pipe, buf, len, &total_read, 0); 231 return total_read; 232 } 233 234 static void * 235 os_open_shared_memory_area(char *name, size cap) 236 { 237 iptr h = CreateFileMappingA(-1, 0, PAGE_READWRITE, 0, cap, name); 238 if (h == INVALID_FILE) 239 return NULL; 240 241 return MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, cap); 242 } 243 244 static void * 245 os_load_library(char *name, char *temp_name, Stream *e) 246 { 247 if (temp_name) { 248 if (CopyFileA(name, temp_name, 0)) 249 name = temp_name; 250 } 251 252 void *res = LoadLibraryA(name); 253 if (!res && e) { 254 s8 errs[] = {s8("WARNING: os_load_library("), cstr_to_s8(name), s8("): ")}; 255 stream_append_s8_array(e, errs, ARRAY_COUNT(errs)); 256 stream_append_i64(e, GetLastError()); 257 stream_append_byte(e, '\n'); 258 os_write_err_msg(stream_to_s8(e)); 259 e->widx = 0; 260 } 261 262 if (temp_name) 263 DeleteFileA(temp_name); 264 265 return res; 266 } 267 268 static void * 269 os_lookup_dynamic_symbol(void *h, char *name, Stream *e) 270 { 271 if (!h) 272 return 0; 273 void *res = GetProcAddress(h, name); 274 if (!res && e) { 275 s8 errs[] = {s8("WARNING: os_lookup_dynamic_symbol("), cstr_to_s8(name), s8("): ")}; 276 stream_append_s8_array(e, errs, ARRAY_COUNT(errs)); 277 stream_append_i64(e, GetLastError()); 278 stream_append_byte(e, '\n'); 279 os_write_err_msg(stream_to_s8(e)); 280 e->widx = 0; 281 } 282 return res; 283 } 284 285 static void 286 os_unload_library(void *h) 287 { 288 FreeLibrary(h); 289 } 290 291 static PLATFORM_ADD_FILE_WATCH_FN(os_add_file_watch) 292 { 293 s8 directory = path; 294 directory.len = s8_scan_backwards(path, '\\'); 295 ASSERT(directory.len > 0); 296 297 u64 hash = s8_hash(directory); 298 FileWatchContext *fwctx = &platform->file_watch_context; 299 FileWatchDirectory *dir = lookup_file_watch_directory(fwctx, hash); 300 if (!dir) { 301 ASSERT(path.data[directory.len] == '\\'); 302 303 dir = fwctx->directory_watches + fwctx->directory_watch_count++; 304 dir->hash = hash; 305 306 dir->name = push_s8(a, directory); 307 arena_commit(a, 1); 308 dir->name.data[dir->name.len] = 0; 309 310 dir->handle = CreateFileA((c8 *)dir->name.data, GENERIC_READ, FILE_SHARE_READ, 0, 311 OPEN_EXISTING, 312 FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, 0); 313 314 w32_context *ctx = (w32_context *)platform->os_context; 315 w32_io_completion_event *event = push_struct(a, typeof(*event)); 316 event->tag = W32_IO_FILE_WATCH; 317 event->context = (iptr)dir; 318 CreateIoCompletionPort(dir->handle, ctx->io_completion_handle, (uptr)event, 0); 319 320 dir->buffer = sub_arena(a, 4096 + sizeof(w32_overlapped), 64); 321 w32_overlapped *overlapped = (w32_overlapped *)(dir->buffer.beg + 4096); 322 zero_struct(overlapped); 323 324 ReadDirectoryChangesW(dir->handle, dir->buffer.beg, 4096, 0, 325 FILE_NOTIFY_CHANGE_LAST_WRITE, 0, overlapped, 0); 326 } 327 328 insert_file_watch(dir, s8_cut_head(path, dir->name.len + 1), user_data, callback); 329 }