os_win32.c (6621B)
1 /* See LICENSE for license details. */ 2 #define OS_PATH_SEPARATOR_CHAR '\\' 3 #define OS_PATH_SEPARATOR "\\" 4 5 #include "util.h" 6 7 #define STD_INPUT_HANDLE -10 8 #define STD_OUTPUT_HANDLE -11 9 #define STD_ERROR_HANDLE -12 10 11 #define PAGE_READWRITE 0x04 12 #define MEM_COMMIT 0x1000 13 #define MEM_RESERVE 0x2000 14 #define MEM_RELEASE 0x8000 15 16 #define GENERIC_WRITE 0x40000000 17 #define GENERIC_READ 0x80000000 18 19 #define FILE_SHARE_READ 0x00000001 20 #define FILE_MAP_ALL_ACCESS 0x000F001F 21 #define FILE_FLAG_BACKUP_SEMANTICS 0x02000000 22 #define FILE_FLAG_OVERLAPPED 0x40000000 23 24 #define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 25 26 #define FILE_ACTION_MODIFIED 0x00000003 27 28 #define CREATE_ALWAYS 2 29 #define OPEN_EXISTING 3 30 31 #define THREAD_SET_LIMITED_INFORMATION 0x0400 32 33 /* NOTE: this is packed because the w32 api designers are dumb and ordered the members 34 * incorrectly. They worked around it be making the ft* members a struct {u32, u32} which 35 * is aligned on a 4-byte boundary. Then in their documentation they explicitly tell you not 36 * to cast to u64 because "it can cause alignment faults on 64-bit Windows" - go figure */ 37 typedef struct __attribute__((packed)) { 38 u32 dwFileAttributes; 39 u64 ftCreationTime; 40 u64 ftLastAccessTime; 41 u64 ftLastWriteTime; 42 u32 dwVolumeSerialNumber; 43 u32 nFileSizeHigh; 44 u32 nFileSizeLow; 45 u32 nNumberOfLinks; 46 u32 nFileIndexHigh; 47 u32 nFileIndexLow; 48 } w32_file_info; 49 50 typedef struct { 51 u32 next_entry_offset; 52 u32 action; 53 u32 filename_size; 54 u16 filename[]; 55 } w32_file_notify_info; 56 57 typedef struct { 58 uptr internal, internal_high; 59 union { 60 struct {u32 off, off_high;}; 61 sptr pointer; 62 }; 63 sptr event_handle; 64 } w32_overlapped; 65 66 typedef struct { 67 sptr io_completion_handle; 68 u64 timer_start_time; 69 u64 timer_frequency; 70 } w32_context; 71 72 typedef enum { 73 W32_IO_FILE_WATCH, 74 W32_IO_PIPE, 75 } W32_IO_Event; 76 77 typedef struct { 78 u64 tag; 79 sptr context; 80 } w32_io_completion_event; 81 82 #define W32(r) __declspec(dllimport) r __stdcall 83 W32(b32) CloseHandle(sptr); 84 W32(sptr) CreateFileA(c8 *, u32, u32, void *, u32, u32, void *); 85 W32(sptr) CreateFileMappingA(sptr, void *, u32, u32, u32, c8 *); 86 W32(sptr) CreateIoCompletionPort(sptr, sptr, uptr, u32); 87 W32(sptr) CreateThread(sptr, uz, sptr, sptr, u32, u32 *); 88 W32(void) ExitProcess(s32); 89 W32(b32) GetFileInformationByHandle(sptr, void *); 90 W32(s32) GetLastError(void); 91 W32(b32) GetQueuedCompletionStatus(sptr, u32 *, uptr *, w32_overlapped **, u32); 92 W32(sptr) GetStdHandle(s32); 93 W32(void) GetSystemInfo(void *); 94 W32(void *) MapViewOfFile(sptr, u32, u32, u32, u64); 95 W32(b32) ReadDirectoryChangesW(sptr, u8 *, u32, b32, u32, u32 *, void *, void *); 96 W32(b32) ReadFile(sptr, u8 *, s32, s32 *, void *); 97 W32(b32) ReleaseSemaphore(sptr, s64, s64 *); 98 W32(s32) SetThreadDescription(sptr, u16 *); 99 W32(b32) WaitOnAddress(void *, void *, uz, u32); 100 W32(s32) WakeByAddressAll(void *); 101 W32(b32) WriteFile(sptr, u8 *, s32, s32 *, void *); 102 W32(void *) VirtualAlloc(u8 *, sz, u32, u32); 103 W32(b32) VirtualFree(u8 *, sz, u32); 104 105 function OS_WRITE_FILE_FN(os_write_file) 106 { 107 s32 wlen = 0; 108 if (raw.len > 0 && raw.len <= U32_MAX) WriteFile(file, raw.data, raw.len, &wlen, 0); 109 return raw.len == wlen; 110 } 111 112 function void __attribute__((noreturn)) 113 os_exit(s32 code) 114 { 115 ExitProcess(code); 116 unreachable(); 117 } 118 119 function void __attribute__((noreturn)) 120 os_fatal(str8 msg) 121 { 122 os_write_file(GetStdHandle(STD_ERROR_HANDLE), msg); 123 os_exit(1); 124 unreachable(); 125 } 126 127 function OS_ALLOC_ARENA_FN(os_alloc_arena) 128 { 129 Arena result = {0}; 130 131 struct { 132 u16 architecture; 133 u16 _pad1; 134 u32 page_size; 135 sz minimum_application_address; 136 sz maximum_application_address; 137 u64 active_processor_mask; 138 u32 number_of_processors; 139 u32 processor_type; 140 u32 allocation_granularity; 141 u16 processor_level; 142 u16 processor_revision; 143 } info; 144 145 GetSystemInfo(&info); 146 147 if (capacity % info.page_size != 0) 148 capacity += (info.page_size - capacity % info.page_size); 149 150 void *beg = VirtualAlloc(0, capacity, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); 151 if (beg) { 152 result.beg = beg; 153 result.end = result.beg + capacity; 154 } 155 return result; 156 } 157 158 function OS_READ_WHOLE_FILE_FN(os_read_whole_file) 159 { 160 str8 result = str8(""); 161 162 w32_file_info fileinfo; 163 sptr h = CreateFileA(file, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); 164 if (h >= 0 && GetFileInformationByHandle(h, &fileinfo)) { 165 sz filesize = (sz)fileinfo.nFileSizeHigh << 32; 166 filesize |= (sz)fileinfo.nFileSizeLow; 167 result = str8_alloc(arena, filesize); 168 169 assert(filesize <= (sz)U32_MAX); 170 171 s32 rlen; 172 if (!ReadFile(h, result.data, result.len, &rlen, 0) || rlen != result.len) 173 result = str8(""); 174 } 175 if (h >= 0) CloseHandle(h); 176 177 return result; 178 } 179 180 function OS_WRITE_NEW_FILE_FN(os_write_new_file) 181 { 182 enum { CHUNK_SIZE = GB(2) }; 183 184 b32 result = 0; 185 sptr h = CreateFileA(fname, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); 186 if (h >= 0) { 187 while (raw.len > 0) { 188 str8 chunk = raw; 189 chunk.len = MIN(chunk.len, CHUNK_SIZE); 190 result = os_write_file(h, chunk); 191 if (!result) break; 192 raw = str8_cut_head(raw, chunk.len); 193 } 194 CloseHandle(h); 195 } 196 return result; 197 } 198 199 function OS_ADD_FILE_WATCH_FN(os_add_file_watch) 200 { 201 str8 directory = path; 202 directory.len = str8_scan_backwards(path, '\\'); 203 if (directory.len < 0) { 204 directory = str8("."); 205 } else { 206 path = str8_cut_head(path, directory.len + 1); 207 } 208 209 u64 hash = str8_hash(directory); 210 FileWatchContext *fwctx = &os->file_watch_context; 211 FileWatchDirectory *dir = lookup_file_watch_directory(fwctx, hash); 212 if (!dir) { 213 dir = da_push(a, fwctx); 214 dir->hash = hash; 215 dir->name = push_str8_zero(a, directory); 216 dir->handle = CreateFileA((c8 *)dir->name.data, GENERIC_READ, FILE_SHARE_READ, 0, 217 OPEN_EXISTING, 218 FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, 0); 219 220 w32_context *ctx = (w32_context *)os->context; 221 w32_io_completion_event *event = push_struct(a, typeof(*event)); 222 event->tag = W32_IO_FILE_WATCH; 223 event->context = (sptr)dir; 224 CreateIoCompletionPort(dir->handle, ctx->io_completion_handle, (uptr)event, 0); 225 226 dir->buffer.beg = arena_alloc(a, 4096 + sizeof(w32_overlapped), 64, 1); 227 dir->buffer.end = dir->buffer.beg + 4096 + sizeof(w32_overlapped); 228 w32_overlapped *overlapped = (w32_overlapped *)(dir->buffer.beg + 4096); 229 zero_struct(overlapped); 230 231 ReadDirectoryChangesW(dir->handle, dir->buffer.beg, 4096, 0, 232 FILE_NOTIFY_CHANGE_LAST_WRITE, 0, overlapped, 0); 233 } 234 235 FileWatch *fw = da_push(a, dir); 236 fw->user_data = user_data; 237 fw->callback = callback; 238 fw->hash = str8_hash(path); 239 }