[win32, io, wip] add win32 io impl. for open/close/seek/read/write

This commit is contained in:
martinfouilleul 2023-05-26 16:03:23 +02:00
parent 41b6128a35
commit 3667ab30e0
10 changed files with 566 additions and 92 deletions

View File

@ -17,6 +17,8 @@
#if PLATFORM_WINDOWS
#include"platform/win32_memory.c"
#include"platform/win32_clock.c"
#include"platform/win32_io.c"
#include"platform/win32_path.c"
//TODO
#elif PLATFORM_MACOS
#include"platform/unix_memory.c"

View File

@ -121,58 +121,59 @@ typedef struct io_cmp
//----------------------------------------------------------------
//TODO: complete io queue api
//----------------------------------------------------------------
io_cmp io_wait_single_req(io_req* req);
MP_API io_cmp io_wait_single_req(io_req* req);
//----------------------------------------------------------------
// File IO wrapper API
//----------------------------------------------------------------
file_handle file_open(str8 path, file_open_flags flags);
file_handle file_open_relative(file_handle base, str8 path, file_open_flags flags);
void file_close(file_handle file);
MP_API file_handle file_open(str8 path, file_open_flags flags);
MP_API file_handle file_open_relative(file_handle base, str8 path, file_open_flags flags);
MP_API void file_close(file_handle file);
i64 file_pos(file_handle file);
i64 file_seek(file_handle file, long offset, file_whence whence);
MP_API i64 file_pos(file_handle file);
MP_API i64 file_seek(file_handle file, i64 offset, file_whence whence);
u64 file_write(file_handle file, u64 size, char* buffer);
u64 file_read(file_handle file, u64 size, char* buffer);
MP_API u64 file_write(file_handle file, u64 size, char* buffer);
MP_API u64 file_read(file_handle file, u64 size, char* buffer);
io_error file_last_error(file_handle handle);
MP_API io_error file_last_error(file_handle handle);
//----------------------------------------------------------------
// File System wrapper API
//----------------------------------------------------------------
typedef enum
typedef enum file_type
{
FILE_UNKNOWN,
FILE_REGULAR,
FILE_DIRECTORY,
FILE_SYMLINK,
FILE_BLOCK,
FILE_CHARACTER,
FILE_FIFO,
FILE_SOCKET,
MP_FILE_UNKNOWN,
MP_FILE_REGULAR,
MP_FILE_DIRECTORY,
MP_FILE_SYMLINK,
MP_FILE_BLOCK,
MP_FILE_CHARACTER,
MP_FILE_FIFO,
MP_FILE_SOCKET,
} file_type;
typedef u16 file_perm;
enum file_perm
{
FILE_OTHER_EXEC = 1<<0,
FILE_OTHER_WRITE = 1<<1,
FILE_OTHER_READ = 1<<2,
MP_FILE_OTHER_EXEC = 1<<0,
MP_FILE_OTHER_WRITE = 1<<1,
MP_FILE_OTHER_READ = 1<<2,
FILE_GROUP_EXEC = 1<<3,
FILE_GROUP_WRITE = 1<<4,
FILE_GROUP_READ = 1<<5,
MP_FILE_GROUP_EXEC = 1<<3,
MP_FILE_GROUP_WRITE = 1<<4,
MP_FILE_GROUP_READ = 1<<5,
FILE_OWNER_EXEC = 1<<6,
FILE_OWNER_WRITE = 1<<7,
FILE_OWNER_READ = 1<<8,
MP_FILE_OWNER_EXEC = 1<<6,
MP_FILE_OWNER_WRITE = 1<<7,
MP_FILE_OWNER_READ = 1<<8,
FILE_STICKY_BIT = 1<<9,
FILE_SET_GID = 1<<10,
FILE_SET_UID = 1<<11,
MP_FILE_STICKY_BIT = 1<<9,
MP_FILE_SET_GID = 1<<10,
MP_FILE_SET_UID = 1<<11,
};
typedef struct file_status
@ -185,8 +186,8 @@ typedef struct file_status
} file_status;
file_status file_get_status(file_handle file);
u64 file_size(file_handle file);
MP_API file_status file_get_status(file_handle file);
MP_API u64 file_size(file_handle file);
//TODO: Complete as needed...

View File

@ -42,11 +42,11 @@ i64 file_pos(file_handle file)
return(cmp.offset);
}
i64 file_seek(file_handle file, long offset, file_whence whence)
i64 file_seek(file_handle file, i64 offset, file_whence whence)
{
io_req req = {.op = IO_OP_SEEK,
.handle = file,
.size = offset,
.offset = offset,
.whence = whence};
io_cmp cmp = io_wait_single_req(&req);

View File

@ -92,7 +92,7 @@ str8 path_append(mem_arena* arena, str8 parent, str8 relPath)
str8 path_executable_relative(mem_arena* arena, str8 relPath)
{
str8_list list = {};
str8_list list = {0};
mem_arena_scope scratch = mem_scratch_begin_next(arena);
str8 executablePath = path_executable(scratch.arena);

View File

@ -302,35 +302,35 @@ file_type io_convert_type_from_stat(u16 mode)
switch(mode & S_IFMT)
{
case S_IFIFO:
type = FILE_FIFO;
type = MP_FILE_FIFO;
break;
case S_IFCHR:
type = FILE_CHARACTER;
type = MP_FILE_CHARACTER;
break;
case S_IFDIR:
type = FILE_DIRECTORY;
type = MP_FILE_DIRECTORY;
break;
case S_IFBLK:
type = FILE_BLOCK;
type = MP_FILE_BLOCK;
break;
case S_IFREG:
type = FILE_REGULAR;
type = MP_FILE_REGULAR;
break;
case S_IFLNK:
type = FILE_SYMLINK;
type = MP_FILE_SYMLINK;
break;
case S_IFSOCK:
type = FILE_SOCKET;
type = MP_FILE_SOCKET;
break;
default:
type = FILE_UNKNOWN;
type = MP_FILE_UNKNOWN;
break;
}
return(type);
@ -395,7 +395,7 @@ io_cmp io_seek(file_slot* slot, io_req* req)
case FILE_SEEK_END:
whence = SEEK_END;
}
cmp.result = lseek(slot->fd, req->size, whence);
cmp.result = lseek(slot->fd, req->offset, whence);
if(cmp.result < 0)
{

484
src/platform/win32_io.c Normal file
View File

@ -0,0 +1,484 @@
/************************************************************//**
*
* @file: win32_io.c
* @author: Martin Fouilleul
* @date: 25/05/2023
*
*****************************************************************/
#include<errno.h>
#include<limits.h>
#include"platform_io_common.c"
typedef struct file_slot
{
u32 generation;
HANDLE h;
io_error error;
bool fatal;
list_elt freeListElt;
} file_slot;
enum
{
ORCA_MAX_FILE_SLOTS = 256,
};
typedef struct file_table
{
file_slot slots[ORCA_MAX_FILE_SLOTS];
u32 nextSlot;
list_info freeList;
} file_table;
file_table __globalFileTable = {0};
file_slot* file_slot_alloc(file_table* table)
{
file_slot* slot = list_pop_entry(&table->freeList, file_slot, freeListElt);
if(!slot && table->nextSlot < ORCA_MAX_FILE_SLOTS)
{
slot = &table->slots[table->nextSlot];
slot->generation = 1;
slot->h = NULL;
table->nextSlot++;
}
return(slot);
}
void file_slot_recycle(file_table* table, file_slot* slot)
{
slot->generation++;
list_push(&table->freeList, &slot->freeListElt);
}
file_handle file_handle_from_slot(file_table* table, file_slot* slot)
{
u64 index = slot - table->slots;
u64 generation = slot->generation;
file_handle handle = {.h = (generation<<32) | index };
return(handle);
}
file_slot* file_slot_from_handle(file_table* table, file_handle handle)
{
file_slot* slot = 0;
u64 index = handle.h & 0xffffffff;
u64 generation = handle.h>>32;
if(index < table->nextSlot)
{
file_slot* candidate = &table->slots[index];
if(candidate->generation == generation)
{
slot = candidate;
}
}
return(slot);
}
io_error io_convert_win32_error(int winError)
{
io_error error = 0;
switch(winError)
{
case ERROR_SUCCESS:
error = IO_OK;
break;
case ERROR_ACCESS_DENIED:
error = IO_ERR_PERM;
break;
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
case ERROR_INVALID_DRIVE:
case ERROR_DIRECTORY:
error = IO_ERR_NO_ENTRY;
break;
case ERROR_TOO_MANY_OPEN_FILES:
error = IO_ERR_MAX_FILES;
break;
case ERROR_NOT_ENOUGH_MEMORY:
case ERROR_OUTOFMEMORY:
error = IO_ERR_MEM;
break;
case ERROR_DEV_NOT_EXIST:
error = IO_ERR_NO_DEVICE;
break;
case ERROR_FILE_EXISTS:
case ERROR_ALREADY_EXISTS:
error = IO_ERR_EXISTS;
break;
case ERROR_BUFFER_OVERFLOW:
case ERROR_FILENAME_EXCED_RANGE:
error = IO_ERR_PATH_LENGTH;
break;
case ERROR_FILE_TOO_LARGE:
error = IO_ERR_FILE_SIZE;
break;
//TODO: complete
default:
error = IO_ERR_UNKNOWN;
break;
}
return(error);
}
io_cmp io_open_at(file_slot* atSlot, io_req* req)
{
io_cmp cmp = {0};
file_slot* slot = file_slot_alloc(&__globalFileTable);
if(!slot)
{
cmp.error = IO_ERR_MAX_FILES;
cmp.result = 0;
}
else
{
cmp.handle = file_handle_from_slot(&__globalFileTable, slot);
//NOTE: open
mem_arena_scope scratch = mem_scratch_begin();
int sizeWide = 1 + MultiByteToWideChar(CP_UTF8, 0, req->buffer, req->size, NULL, 0);
LPWSTR pathWide = mem_arena_alloc_array(scratch.arena, wchar_t, sizeWide);
MultiByteToWideChar(CP_UTF8, 0, req->buffer, req->size, pathWide, sizeWide);
pathWide[sizeWide-1] = '\0';
DWORD accessFlags = 0;
DWORD createFlags = 0;
DWORD attributesFlags = FILE_ATTRIBUTE_NORMAL;
if(req->openFlags & FILE_OPEN_READ)
{
accessFlags |= GENERIC_READ;
}
if(req->openFlags & FILE_OPEN_WRITE)
{
if(req->openFlags & FILE_OPEN_APPEND)
{
accessFlags |= FILE_APPEND_DATA;
}
else
{
accessFlags |= GENERIC_WRITE;
}
}
if(req->openFlags & FILE_OPEN_TRUNCATE)
{
if(req->openFlags & FILE_OPEN_CREATE)
{
createFlags |= CREATE_ALWAYS;
}
else
{
createFlags |= TRUNCATE_EXISTING;
}
}
if(req->openFlags & FILE_OPEN_CREATE)
{
if(!createFlags & CREATE_ALWAYS)
{
createFlags |= OPEN_ALWAYS;
}
}
if( !(createFlags & OPEN_ALWAYS)
&& !(createFlags & CREATE_ALWAYS)
&& !(createFlags & TRUNCATE_EXISTING))
{
createFlags |= OPEN_EXISTING;
}
if(req->openFlags & FILE_OPEN_SYMLINK)
{
attributesFlags |= FILE_FLAG_OPEN_REPARSE_POINT;
}
if(req->openFlags & FILE_OPEN_RESTRICT)
{
//TODO: if FILE_OPEN_RESTRICT, do the file traversal to check that path is in the
// subtree rooted at atSlot->fd
}
//TODO: open at
//TODO: take care of share mode and security attributes, make it consistent with posix impl
slot->h = CreateFileW(pathWide, accessFlags, 0, NULL, createFlags, attributesFlags, NULL);
mem_scratch_end(scratch);
if(slot->h == INVALID_HANDLE_VALUE)
{
slot->fatal = true;
slot->error = io_convert_win32_error(GetLastError());
}
cmp.error = slot->error;
}
return(cmp);
}
io_cmp io_close(file_slot* slot, io_req* req)
{
io_cmp cmp = {0};
if(slot->h)
{
CloseHandle(slot->h);
}
file_slot_recycle(&__globalFileTable, slot);
return(cmp);
}
/*
file_perm io_convert_perm_from_stat(u16 mode)
{
file_perm perm = mode & 07777;
return(perm);
}
file_type io_convert_type_from_stat(u16 mode)
{
file_type type;
switch(mode & S_IFMT)
{
case S_IFIFO:
type = FILE_FIFO;
break;
case S_IFCHR:
type = FILE_CHARACTER;
break;
case S_IFDIR:
type = FILE_DIRECTORY;
break;
case S_IFBLK:
type = FILE_BLOCK;
break;
case S_IFREG:
type = FILE_REGULAR;
break;
case S_IFLNK:
type = FILE_SYMLINK;
break;
case S_IFSOCK:
type = FILE_SOCKET;
break;
default:
type = FILE_UNKNOWN;
break;
}
return(type);
}
io_cmp io_fstat(file_slot* slot, io_req* req)
{
io_cmp cmp = {0};
if(req->size < sizeof(file_status))
{
cmp.error = IO_ERR_ARG;
}
else
{
struct stat s;
if(fstat(slot->fd, &s))
{
slot->error = io_convert_errno(errno);
cmp.error = slot->error;
}
else
{
file_status* status = (file_status*)req->buffer;
status->perm = io_convert_perm_from_stat(s.st_mode);
status->type = io_convert_type_from_stat(s.st_mode);
status->size = s.st_size;
//TODO: times
}
}
return(cmp);
}
*/
io_cmp io_pos(file_slot* slot, io_req* req)
{
io_cmp cmp = {0};
LARGE_INTEGER off = {.QuadPart = req->offset};
LARGE_INTEGER newPos = {0};
if(!SetFilePointerEx(slot->h, off, &newPos, FILE_CURRENT))
{
slot->error = io_convert_win32_error(GetLastError());
cmp.error = slot->error;
}
else
{
cmp.result = newPos.QuadPart;
}
return(cmp);
}
io_cmp io_seek(file_slot* slot, io_req* req)
{
io_cmp cmp = {0};
DWORD whence;
switch(req->whence)
{
case FILE_SEEK_CURRENT:
whence = FILE_CURRENT;
break;
case FILE_SEEK_SET:
whence = FILE_BEGIN;
break;
case FILE_SEEK_END:
whence = FILE_END;
}
LARGE_INTEGER off = {.QuadPart = req->offset};
LARGE_INTEGER newPos = {0};
if(!SetFilePointerEx(slot->h, off, &newPos, whence))
{
slot->error = io_convert_win32_error(GetLastError());
cmp.error = slot->error;
}
else
{
cmp.result = newPos.QuadPart;
}
return(cmp);
}
io_cmp io_read(file_slot* slot, io_req* req)
{
io_cmp cmp = {0};
DWORD bytesRead = 0;
if(!ReadFile(slot->h, req->buffer, req->size, &bytesRead, NULL))
{
cmp.result = -1;
slot->error = io_convert_win32_error(GetLastError());
cmp.error = slot->error;
}
else
{
cmp.result = bytesRead;
}
return(cmp);
}
io_cmp io_write(file_slot* slot, io_req* req)
{
io_cmp cmp = {0};
DWORD bytesWritten = 0;
if(!WriteFile(slot->h, req->buffer, req->size, &bytesWritten, NULL))
{
cmp.result = -1;
slot->error = io_convert_win32_error(GetLastError());
cmp.error = slot->error;
}
else
{
cmp.result = bytesWritten;
}
return(cmp);
}
io_cmp io_get_error(file_slot* slot, io_req* req)
{
io_cmp cmp = {0};
cmp.result = slot->error;
return(cmp);
}
io_cmp io_wait_single_req(io_req* req)
{
io_cmp cmp = {0};
file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle);
if(!slot)
{
if(req->op != IO_OP_OPEN_AT)
{
cmp.error = IO_ERR_HANDLE;
}
}
else if(slot->fatal && req->op != IO_OP_CLOSE)
{
cmp.error = IO_ERR_PREV;
}
if(cmp.error == IO_OK)
{
switch(req->op)
{
case IO_OP_OPEN_AT:
cmp = io_open_at(slot, req);
break;
/*
case IO_OP_FSTAT:
cmp = io_fstat(slot, req);
break;
*/
case IO_OP_CLOSE:
cmp = io_close(slot, req);
break;
case IO_OP_READ:
cmp = io_read(slot, req);
break;
case IO_OP_WRITE:
cmp = io_write(slot, req);
break;
/*
case IO_OP_POS:
cmp = io_pos(slot, req);
break;
case IO_OP_SEEK:
cmp = io_seek(slot, req);
break;
*/
case IO_OP_ERROR:
cmp = io_get_error(slot, req);
break;
default:
cmp.error = IO_ERR_OP;
if(slot)
{
slot->error = cmp.error;
}
break;
}
}
return(cmp);
}

28
src/platform/win32_path.c Normal file
View File

@ -0,0 +1,28 @@
/************************************************************//**
*
* @file: win32_path.c
* @author: Martin Fouilleul
* @date: 24/05/2023
*
*****************************************************************/
#include"platform_path.c"
str8 path_executable(mem_arena* arena)
{
//TODO use wide chars
char* buffer = mem_arena_alloc_array(arena, char, MAX_PATH+1);
int size = GetModuleFileName(NULL, buffer, MAX_PATH+1);
//TODO: check for errors...
for(int i=0; i<size; i++)
{
if(buffer[i] == '\\')
{
buffer[i] = '/';
}
}
return(str8_from_buffer(size, buffer));
}
str8 path_canonical(mem_arena* arena, str8 path); //TODO

View File

@ -1036,51 +1036,6 @@ mg_surface_data* mg_win32_surface_create_host(mp_window window)
return(surface);
}
/////////////////////////////////////////// WIP ///////////////////////////////////////////////
//TODO: this is thrown here for a quick test. We should:
// - check for errors
// - use utf8 version of API
str8 mp_app_get_executable_path(mem_arena* arena)
{
char* buffer = mem_arena_alloc_array(arena, char, MAX_PATH+1);
int size = GetModuleFileName(NULL, buffer, MAX_PATH+1);
//TODO: check for errors...
return(str8_from_buffer(size, buffer));
}
str8 mp_app_get_resource_path(mem_arena* arena, const char* name)
{
str8_list list = {0};
mem_arena* scratch = mem_scratch();
str8 executablePath = mp_app_get_executable_path(scratch);
char* executablePathCString = str8_to_cstring(scratch, executablePath);
char* driveBuffer = mem_arena_alloc_array(scratch, char, MAX_PATH);
char* dirBuffer = mem_arena_alloc_array(scratch, char, MAX_PATH);
_splitpath_s(executablePathCString, driveBuffer, MAX_PATH, dirBuffer, MAX_PATH, 0, 0, 0, 0);
str8 drive = STR8(driveBuffer);
str8 dirPath = STR8(dirBuffer);
str8_list_push(scratch, &list, drive);
str8_list_push(scratch, &list, dirPath);
str8_list_push(scratch, &list, STR8("\\"));
str8_list_push(scratch, &list, str8_push_cstring(scratch, name));
str8 path = str8_list_join(scratch, list);
char* pathCString = str8_to_cstring(scratch, path);
char* buffer = mem_arena_alloc_array(arena, char, path.len+1);
char* filePart = 0;
int size = GetFullPathName(pathCString, MAX_PATH, buffer, &filePart);
str8 result = str8_from_buffer(size, buffer);
return(result);
}
//////////////////////////////////////////////////////////////////////////////////////////////////
//--------------------------------------------------------------------
// native open/save/alert windows
//--------------------------------------------------------------------

4
test/files/build.bat Normal file
View File

@ -0,0 +1,4 @@
set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext
cl /we4013 /Zi /Zc:preprocessor /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.dll.lib /out:../../bin/test_files.exe

View File

@ -115,7 +115,7 @@ int test_stat_type(mem_arena* arena, str8 dataDir)
log_error("Error while retrieving file status\n");
return(-1);
}
if(status.type != FILE_REGULAR)
if(status.type != MP_FILE_REGULAR)
{
log_error("file type doesn't match\n");
return(-1);
@ -131,7 +131,7 @@ int test_stat_type(mem_arena* arena, str8 dataDir)
log_error("Error while retrieving file status\n");
return(-1);
}
if(status.type != FILE_DIRECTORY)
if(status.type != MP_FILE_DIRECTORY)
{
log_error("file type doesn't match\n");
return(-1);
@ -147,7 +147,7 @@ int test_stat_type(mem_arena* arena, str8 dataDir)
log_error("Error while retrieving file status\n");
return(-1);
}
if(status.type != FILE_SYMLINK)
if(status.type != MP_FILE_SYMLINK)
{
log_error("file type doesn't match\n");
return(-1);