[file io, wip] open/close file API
This commit is contained in:
parent
0fab5635e2
commit
4ae51d7a23
9
build.sh
9
build.sh
|
@ -29,7 +29,7 @@ elif [ $target = wasm3 ] ; then
|
|||
for file in ./ext/wasm3/source/*.c ; do
|
||||
name=$(basename $file)
|
||||
name=${name/.c/.o}
|
||||
clang -c -g -Wno-extern-initializer -Dd_m3VerboseErrorMessages -o ./bin/obj/$name -I./ext/wasm3/source $file
|
||||
clang -c -g -foptimize-sibling-calls -Wno-extern-initializer -Dd_m3VerboseErrorMessages -o ./bin/obj/$name -I./ext/wasm3/source $file
|
||||
done
|
||||
ar -rcs ./bin/libwasm3.a ./bin/obj/*.o
|
||||
rm -rf ./bin/obj
|
||||
|
@ -43,7 +43,7 @@ elif [ $target = orca ] ; then
|
|||
cp milepost/bin/libGLESv2.dylib bin/
|
||||
cp milepost/bin/libEGL.dylib bin/
|
||||
|
||||
INCLUDES="-Imilepost/src -Imilepost/src/util -Imilepost/src/platform -Iext/wasm3/source -Imilepost/ext/"
|
||||
INCLUDES="-Isrc -Isdk -Imilepost/src -Imilepost/src/util -Imilepost/src/platform -Iext/wasm3/source -Imilepost/ext/"
|
||||
LIBS="-Lbin -lmilepost -lwasm3"
|
||||
FLAGS="-g -DLOG_COMPILE_DEBUG -mmacos-version-min=10.15.4 -maes"
|
||||
|
||||
|
@ -57,6 +57,11 @@ elif [ $target = orca ] ; then
|
|||
--guest-include graphics.h \
|
||||
--wasm3-bindings ./src/canvas_api_bind_gen.c
|
||||
|
||||
python3 ./scripts/bindgen2.py io \
|
||||
src/io_api.json \
|
||||
--guest-stubs sdk/io_stubs.c \
|
||||
--wasm3-bindings ./src/io_api_bind_gen.c
|
||||
|
||||
# compile orca
|
||||
clang $FLAGS $INCLUDES $LIBS -o bin/orca src/main.c
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ wasmFlags="--target=wasm32 \
|
|||
-Wl,--allow-undefined \
|
||||
-g \
|
||||
-D__ORCA__ \
|
||||
-I ../../sdk -I../../milepost/ext -I ../../milepost -I ../../milepost/src -I ../../milepost/src/util -I ../../milepost/src/platform -I../.."
|
||||
-I ../../src -I ../../sdk -I../../milepost/ext -I ../../milepost -I ../../milepost/src -I ../../milepost/src/util -I ../../milepost/src/platform -I../.."
|
||||
|
||||
/usr/local/opt/llvm/bin/clang $wasmFlags -o ./module.wasm ../../sdk/orca.c src/main.c
|
||||
|
||||
|
|
|
@ -40,6 +40,9 @@ void OnInit(void)
|
|||
//TODO create surface for main window
|
||||
surface = mg_surface_main();
|
||||
canvas = mg_canvas_create();
|
||||
|
||||
file_handle file = file_open(STR8("test_file.txt") , IO_OPEN_CREATE | IO_OPEN_WRITE);
|
||||
file_close(file);
|
||||
}
|
||||
|
||||
void OnFrameResize(u32 width, u32 height)
|
||||
|
|
|
@ -39,6 +39,7 @@ contents_dir = bundle_path + '/Contents'
|
|||
exe_dir = contents_dir + '/MacOS'
|
||||
res_dir = contents_dir + '/resources'
|
||||
wasm_dir = contents_dir + '/wasm'
|
||||
data_dir = contents_dir + '/data'
|
||||
|
||||
if os.path.exists(bundle_path):
|
||||
shutil.rmtree(bundle_path)
|
||||
|
@ -47,6 +48,7 @@ os.mkdir(contents_dir)
|
|||
os.mkdir(exe_dir)
|
||||
os.mkdir(res_dir)
|
||||
os.mkdir(wasm_dir)
|
||||
os.mkdir(data_dir)
|
||||
|
||||
#-----------------------------------------------------------
|
||||
#NOTE: copy orca runtime executable and libraries
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: io.c
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 09/05/2023
|
||||
*
|
||||
*****************************************************************/
|
||||
#include"io.h"
|
||||
#include"io_stubs.c"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// File stream read/write API
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
file_handle file_open(str8 path, file_open_flags flags)
|
||||
{
|
||||
io_req req = {.op = IO_OP_OPEN,
|
||||
.size = path.len,
|
||||
.buffer = path.ptr,
|
||||
.openFlags = flags};
|
||||
|
||||
io_cmp cmp = io_wait_single_req(&req);
|
||||
|
||||
//WARN: we always return a handle that can be queried for errors. Handles must be closed
|
||||
// even if there was an error when opening
|
||||
file_handle handle = { cmp.result };
|
||||
return(handle);
|
||||
}
|
||||
|
||||
void file_close(file_handle file)
|
||||
{
|
||||
io_req req = {.op = IO_OP_CLOSE,
|
||||
.handle = file};
|
||||
io_wait_single_req(&req);
|
||||
}
|
||||
|
||||
size_t file_size(file_handle file)
|
||||
{
|
||||
io_req req = {.op = IO_OP_SIZE,
|
||||
.handle = file};
|
||||
|
||||
io_cmp cmp = io_wait_single_req(&req);
|
||||
return((size_t)cmp.result);
|
||||
}
|
||||
|
||||
size_t file_pos(file_handle file)
|
||||
{
|
||||
io_req req = {.op = IO_OP_POS,
|
||||
.handle = file};
|
||||
|
||||
io_cmp cmp = io_wait_single_req(&req);
|
||||
return((size_t)cmp.result);
|
||||
}
|
||||
|
||||
size_t file_seek(file_handle file, long offset, file_whence whence)
|
||||
{
|
||||
io_req req = {.op = IO_OP_SEEK,
|
||||
.handle = file,
|
||||
.size = offset,
|
||||
.whence = whence};
|
||||
|
||||
io_cmp cmp = io_wait_single_req(&req);
|
||||
return((size_t)cmp.result);
|
||||
}
|
||||
|
||||
size_t file_write(file_handle file, size_t size, char* buffer)
|
||||
{
|
||||
io_req req = {.op = IO_OP_WRITE,
|
||||
.handle = file,
|
||||
.size = size,
|
||||
.buffer = buffer};
|
||||
|
||||
io_cmp cmp = io_wait_single_req(&req);
|
||||
return((size_t)cmp.result);
|
||||
}
|
||||
|
||||
size_t file_read(file_handle file, size_t size, char* buffer)
|
||||
{
|
||||
io_req req = {.op = IO_OP_READ,
|
||||
.handle = file,
|
||||
.size = size,
|
||||
.buffer = buffer};
|
||||
|
||||
io_cmp cmp = io_wait_single_req(&req);
|
||||
return((size_t)cmp.result);
|
||||
}
|
||||
|
||||
int file_error(file_handle file)
|
||||
{
|
||||
io_req req = {.op = IO_OP_ERROR,
|
||||
.handle = file};
|
||||
|
||||
io_cmp cmp = io_wait_single_req(&req);
|
||||
return((int)cmp.result);
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: io.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 09/05/2023
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __IO_H_
|
||||
#define __IO_H_
|
||||
|
||||
#include"util/typedefs.h"
|
||||
#include"util/strings.h"
|
||||
|
||||
#include"io_common.h"
|
||||
|
||||
file_handle file_open(str8 path, file_open_flags flags);
|
||||
void file_close(file_handle file);
|
||||
|
||||
size_t file_size(file_handle file);
|
||||
size_t file_pos(file_handle file);
|
||||
size_t file_seek(file_handle file, long offset, file_whence whence);
|
||||
|
||||
size_t file_write(file_handle file, size_t size, char* buffer);
|
||||
size_t file_read(file_handle file, size_t size, char* buffer);
|
||||
|
||||
int file_error(file_handle file);
|
||||
|
||||
#endif //__IO_H_
|
|
@ -16,3 +16,5 @@
|
|||
|
||||
#include"graphics_common.c"
|
||||
#include"orca_surface.c"
|
||||
|
||||
#include"io.c"
|
||||
|
|
|
@ -16,5 +16,6 @@
|
|||
#include"platform/platform_log.h"
|
||||
#include"platform/platform_assert.h"
|
||||
|
||||
#include"io.h"
|
||||
|
||||
#endif //__ORCA_H_
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
[
|
||||
{
|
||||
"name": "io_wait_single_req",
|
||||
"cname": "io_wait_single_req",
|
||||
"ret": {"name": "io_cmp", "tag": "S"},
|
||||
"args": [ {"name": "req",
|
||||
"type": {"name": "io_req*", "tag": "p"}}]
|
||||
}
|
||||
]
|
|
@ -0,0 +1,88 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: io_common.h
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 09/05/2023
|
||||
*
|
||||
*****************************************************************/
|
||||
#ifndef __IO_COMMON_H_
|
||||
#define __IO_COMMON_H_
|
||||
|
||||
#include"util/typedefs.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Host IO interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
typedef struct { u64 h; } file_handle;
|
||||
|
||||
typedef u64 io_req_id;
|
||||
|
||||
typedef u32 io_op;
|
||||
enum
|
||||
{
|
||||
IO_OP_OPEN,
|
||||
IO_OP_CLOSE,
|
||||
IO_OP_SIZE,
|
||||
IO_OP_POS,
|
||||
IO_OP_SEEK,
|
||||
IO_OP_READ,
|
||||
IO_OP_WRITE,
|
||||
IO_OP_ERROR,
|
||||
//...
|
||||
};
|
||||
|
||||
typedef enum { FILE_SET, FILE_END, FILE_CURR } file_whence;
|
||||
|
||||
typedef u32 file_open_flags;
|
||||
enum {
|
||||
IO_OPEN_READ,
|
||||
IO_OPEN_WRITE,
|
||||
IO_OPEN_APPEND,
|
||||
IO_OPEN_TRUNCATE,
|
||||
IO_OPEN_CREATE,
|
||||
//...
|
||||
};
|
||||
|
||||
typedef struct io_req
|
||||
{
|
||||
io_req_id id;
|
||||
io_op op;
|
||||
file_handle handle;
|
||||
u64 offset;
|
||||
u64 size;
|
||||
|
||||
union
|
||||
{
|
||||
char* buffer;
|
||||
u64 shadow; // This is a horrible hack to get the same layout on wasm and on host
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
file_open_flags openFlags;
|
||||
file_whence whence;
|
||||
};
|
||||
|
||||
} io_req;
|
||||
|
||||
typedef i32 io_error;
|
||||
enum {
|
||||
IO_OK = 0,
|
||||
IO_ERR_INVALID, // invalid argument or argument combination
|
||||
IO_ERR_PERM, // access denied
|
||||
IO_ERR_PATH, // path does not exist
|
||||
IO_ERR_EXISTS, // file already exists
|
||||
IO_ERR_HANDLE, // invalid handle
|
||||
IO_ERR_MAX_FILES,
|
||||
//...
|
||||
};
|
||||
|
||||
typedef struct io_cmp
|
||||
{
|
||||
io_req id;
|
||||
io_error error;
|
||||
u64 result;
|
||||
} io_cmp;
|
||||
|
||||
#endif //__IO_COMMON_H_
|
|
@ -0,0 +1,247 @@
|
|||
/************************************************************//**
|
||||
*
|
||||
* @file: io_impl.c
|
||||
* @author: Martin Fouilleul
|
||||
* @date: 09/05/2023
|
||||
*
|
||||
*****************************************************************/
|
||||
|
||||
#include<fcntl.h>
|
||||
#include<unistd.h>
|
||||
#include"io_common.h"
|
||||
|
||||
//TODO: - file_handle to FILE* association
|
||||
// - open / close handles
|
||||
// - read / write
|
||||
// - file positioning & size
|
||||
|
||||
typedef struct file_slot
|
||||
{
|
||||
u32 generation;
|
||||
int fd;
|
||||
io_error error;
|
||||
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;
|
||||
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 < ORCA_MAX_FILE_SLOTS)
|
||||
{
|
||||
file_slot* candidate = &table->slots[index];
|
||||
if(candidate->generation == generation)
|
||||
{
|
||||
slot = candidate;
|
||||
}
|
||||
}
|
||||
return(slot);
|
||||
}
|
||||
|
||||
io_cmp io_open(io_req* req)
|
||||
{
|
||||
io_cmp cmp = {0};
|
||||
|
||||
int flags = 0;
|
||||
if(req->openFlags & IO_OPEN_READ)
|
||||
{
|
||||
if(req->openFlags & IO_OPEN_WRITE)
|
||||
{
|
||||
flags = O_RDWR;
|
||||
}
|
||||
else
|
||||
{
|
||||
flags = O_RDONLY;
|
||||
}
|
||||
}
|
||||
else if(req->openFlags & IO_OPEN_WRITE)
|
||||
{
|
||||
flags = O_WRONLY;
|
||||
}
|
||||
|
||||
if(req->openFlags & IO_OPEN_TRUNCATE)
|
||||
{
|
||||
flags |= O_TRUNC;
|
||||
}
|
||||
if(req->openFlags & IO_OPEN_APPEND)
|
||||
{
|
||||
flags |= O_APPEND;
|
||||
}
|
||||
if(req->openFlags & IO_OPEN_CREATE)
|
||||
{
|
||||
flags |= O_CREAT;
|
||||
}
|
||||
|
||||
mode_t mode = S_IRUSR
|
||||
| S_IWUSR
|
||||
| S_IRGRP
|
||||
| S_IWGRP
|
||||
| S_IROTH
|
||||
| S_IWOTH;
|
||||
|
||||
//NOTE: build path
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
//TODO: canonicalize directory path & check that it's inside local app folder
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
mem_arena* scratch = mem_scratch();
|
||||
mem_arena_marker mark = mem_arena_mark(scratch);
|
||||
|
||||
str8_list list = {0};
|
||||
|
||||
str8 execPath = mp_app_get_executable_path(scratch);
|
||||
str8 execDir = mp_path_directory(execPath);
|
||||
|
||||
str8_list_push(scratch, &list, execDir);
|
||||
str8_list_push(scratch, &list, STR8("/../data/"));
|
||||
str8_list_push(scratch, &list, str8_from_buffer(req->size, req->buffer));
|
||||
|
||||
str8 absPath = str8_list_join(scratch, list);
|
||||
char* absCString = str8_to_cstring(scratch, absPath);
|
||||
|
||||
//NOTE: open
|
||||
int fd = open(absCString, flags, mode);
|
||||
|
||||
if(fd >= 0)
|
||||
{
|
||||
file_slot* slot = file_slot_alloc(&__globalFileTable);
|
||||
if(slot)
|
||||
{
|
||||
slot->fd = fd;
|
||||
file_handle handle = file_handle_from_slot(&__globalFileTable, slot);
|
||||
cmp.result = handle.h;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmp.error = IO_ERR_MAX_FILES;
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(errno)
|
||||
{
|
||||
case EACCES:
|
||||
cmp.error = IO_ERR_PERM;
|
||||
break;
|
||||
|
||||
//TODO: convert open error codes to io_error
|
||||
default:
|
||||
cmp.error = IO_ERR_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
mem_arena_clear_to(scratch, mark);
|
||||
|
||||
return(cmp);
|
||||
}
|
||||
|
||||
io_cmp io_close(io_req* req)
|
||||
{
|
||||
io_cmp cmp = {0};
|
||||
file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle);
|
||||
if(slot)
|
||||
{
|
||||
close(slot->fd);
|
||||
file_slot_recycle(&__globalFileTable, slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
cmp.error = IO_ERR_HANDLE;
|
||||
}
|
||||
return(cmp);
|
||||
}
|
||||
|
||||
|
||||
io_cmp io_wait_single_req(io_req* req)
|
||||
{
|
||||
mem_arena* scratch = mem_scratch();
|
||||
|
||||
io_cmp cmp = {0};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
//TODO: we need to convert the req->buffer wasm pointer to a native pointer here
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
//TODO: check that req->buffer lies in wasm memory with at least req->size space
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
//TODO: we can't read reqs from wasm memory since the pointers are not the same size
|
||||
// (hence the layout won't be the same...)
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
//NOTE: for some reason, wasm3 memory doesn't start at the beginning of the block we give it.
|
||||
u32 memSize = 0;
|
||||
char* memory = (char*)m3_GetMemory(__orcaApp.runtime.m3Runtime, &memSize, 0);
|
||||
|
||||
u64 bufferIndex = (u64)req->buffer & 0xffffff;
|
||||
|
||||
if(bufferIndex + req->size > memSize)
|
||||
{
|
||||
cmp.error = IO_ERR_INVALID;
|
||||
}
|
||||
else
|
||||
{
|
||||
req->buffer = memory + bufferIndex;
|
||||
|
||||
switch(req->op)
|
||||
{
|
||||
case IO_OP_OPEN:
|
||||
cmp = io_open(req);
|
||||
break;
|
||||
|
||||
case IO_OP_CLOSE:
|
||||
cmp = io_close(req);
|
||||
break;
|
||||
|
||||
default:
|
||||
cmp.error = IO_ERR_INVALID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return(cmp);
|
||||
}
|
|
@ -137,6 +137,8 @@ typedef struct orca_app
|
|||
|
||||
orca_app __orcaApp = {0};
|
||||
|
||||
#include"io_impl.c"
|
||||
|
||||
void orca_log(log_level level,
|
||||
int fileLen,
|
||||
char* file,
|
||||
|
@ -348,10 +350,11 @@ orca_runtime* orca_runtime_get()
|
|||
|
||||
#include"bindgen_core_api.c"
|
||||
#include"canvas_api_bind.c"
|
||||
#include"io_api_bind_gen.c"
|
||||
|
||||
#include"bindgen_gles_api.c"
|
||||
#include"manual_gles_api.c"
|
||||
|
||||
|
||||
void* orca_runloop(void* user)
|
||||
{
|
||||
orca_app* app = &__orcaApp;
|
||||
|
@ -396,6 +399,7 @@ void* orca_runloop(void* user)
|
|||
//NOTE: bind orca APIs
|
||||
bindgen_link_core_api(app->runtime.m3Module);
|
||||
bindgen_link_canvas_api(app->runtime.m3Module);
|
||||
bindgen_link_io_api(app->runtime.m3Module);
|
||||
bindgen_link_gles_api(app->runtime.m3Module);
|
||||
manual_link_gles_api(app->runtime.m3Module);
|
||||
|
||||
|
|
Loading…
Reference in New Issue