[io] testing abstracted io

This commit is contained in:
Martin Fouilleul 2023-05-26 11:40:00 +02:00
parent c75661cfc9
commit 41b6128a35
10 changed files with 246 additions and 38 deletions

2
.gitignore vendored
View File

@ -1,6 +1,6 @@
.DS_Store .DS_Store
*.dSYM *.dSYM
bin/* bin
*.metallib *.metallib
*.pdb *.pdb

View File

@ -76,6 +76,7 @@ if [ $target = 'lib' ] ; then
install_name_tool -change "./libGLESv2.dylib" '@rpath/libGLESv2.dylib' $BINDIR/libmilepost.dylib install_name_tool -change "./libGLESv2.dylib" '@rpath/libGLESv2.dylib' $BINDIR/libmilepost.dylib
# add executable path to rpath. Client executable can still add its own rpaths if needed, e.g. @executable_path/libs/ etc. # add executable path to rpath. Client executable can still add its own rpaths if needed, e.g. @executable_path/libs/ etc.
install_name_tool -id "@rpath/libmilepost.dylib" $BINDIR/libmilepost.dylib
install_name_tool -add_rpath "@executable_path" $BINDIR/libmilepost.dylib install_name_tool -add_rpath "@executable_path" $BINDIR/libmilepost.dylib
else else

View File

@ -25,8 +25,9 @@ enum {
FILE_OPEN_TRUNCATE = 1<<3, FILE_OPEN_TRUNCATE = 1<<3,
FILE_OPEN_CREATE = 1<<4, FILE_OPEN_CREATE = 1<<4,
FILE_OPEN_NO_FOLLOW = 1<<5, FILE_OPEN_SYMLINK = 1<<5,
FILE_OPEN_RESTRICT = 1<<6, FILE_OPEN_NO_FOLLOW = 1<<6,
FILE_OPEN_RESTRICT = 1<<7,
//... //...
}; };
@ -104,12 +105,12 @@ enum {
typedef struct io_cmp typedef struct io_cmp
{ {
io_req id; io_req_id id;
io_error error; io_error error;
union union
{ {
u64 result; i64 result;
u64 size; u64 size;
i64 offset; i64 offset;
file_handle handle; file_handle handle;
@ -130,13 +131,13 @@ file_handle file_open(str8 path, file_open_flags flags);
file_handle file_open_relative(file_handle base, 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); void file_close(file_handle file);
off_t file_pos(file_handle file); i64 file_pos(file_handle file);
off_t file_seek(file_handle file, long offset, file_whence whence); i64 file_seek(file_handle file, long offset, file_whence whence);
size_t file_write(file_handle file, size_t size, char* buffer); u64 file_write(file_handle file, u64 size, char* buffer);
size_t file_read(file_handle file, size_t size, char* buffer); u64 file_read(file_handle file, u64 size, char* buffer);
io_error io_last_error(file_handle handle); io_error file_last_error(file_handle handle);
//---------------------------------------------------------------- //----------------------------------------------------------------
// File System wrapper API // File System wrapper API
@ -185,7 +186,7 @@ typedef struct file_status
} file_status; } file_status;
file_status file_get_status(file_handle file); file_status file_get_status(file_handle file);
size_t file_size(file_handle file); u64 file_size(file_handle file);
//TODO: Complete as needed... //TODO: Complete as needed...

View File

@ -14,7 +14,7 @@
file_handle file_open(str8 path, file_open_flags flags) file_handle file_open(str8 path, file_open_flags flags)
{ {
io_req req = {.op = IO_OP_OPEN, io_req req = {.op = IO_OP_OPEN_AT,
.size = path.len, .size = path.len,
.buffer = path.ptr, .buffer = path.ptr,
.openFlags = flags}; .openFlags = flags};
@ -23,8 +23,7 @@ file_handle file_open(str8 path, file_open_flags flags)
//WARN: we always return a handle that can be queried for errors. Handles must be closed //WARN: we always return a handle that can be queried for errors. Handles must be closed
// even if there was an error when opening // even if there was an error when opening
file_handle handle = { cmp.result }; return(cmp.handle);
return(handle);
} }
void file_close(file_handle file) void file_close(file_handle file)
@ -34,16 +33,16 @@ void file_close(file_handle file)
io_wait_single_req(&req); io_wait_single_req(&req);
} }
off_t file_pos(file_handle file) i64 file_pos(file_handle file)
{ {
io_req req = {.op = IO_OP_POS, io_req req = {.op = IO_OP_POS,
.handle = file}; .handle = file};
io_cmp cmp = io_wait_single_req(&req); io_cmp cmp = io_wait_single_req(&req);
return((size_t)cmp.result); return(cmp.offset);
} }
off_t file_seek(file_handle file, long offset, file_whence whence) i64 file_seek(file_handle file, long offset, file_whence whence)
{ {
io_req req = {.op = IO_OP_SEEK, io_req req = {.op = IO_OP_SEEK,
.handle = file, .handle = file,
@ -51,10 +50,10 @@ off_t file_seek(file_handle file, long offset, file_whence whence)
.whence = whence}; .whence = whence};
io_cmp cmp = io_wait_single_req(&req); io_cmp cmp = io_wait_single_req(&req);
return((size_t)cmp.result); return(cmp.offset);
} }
size_t file_write(file_handle file, size_t size, char* buffer) u64 file_write(file_handle file, u64 size, char* buffer)
{ {
io_req req = {.op = IO_OP_WRITE, io_req req = {.op = IO_OP_WRITE,
.handle = file, .handle = file,
@ -62,10 +61,10 @@ size_t file_write(file_handle file, size_t size, char* buffer)
.buffer = buffer}; .buffer = buffer};
io_cmp cmp = io_wait_single_req(&req); io_cmp cmp = io_wait_single_req(&req);
return((size_t)cmp.result); return(cmp.size);
} }
size_t file_read(file_handle file, size_t size, char* buffer) u64 file_read(file_handle file, u64 size, char* buffer)
{ {
io_req req = {.op = IO_OP_READ, io_req req = {.op = IO_OP_READ,
.handle = file, .handle = file,
@ -73,16 +72,16 @@ size_t file_read(file_handle file, size_t size, char* buffer)
.buffer = buffer}; .buffer = buffer};
io_cmp cmp = io_wait_single_req(&req); io_cmp cmp = io_wait_single_req(&req);
return((size_t)cmp.result); return(cmp.size);
} }
io_error io_last_error(file_handle file) io_error file_last_error(file_handle file)
{ {
io_req req = {.op = IO_OP_ERROR, io_req req = {.op = IO_OP_ERROR,
.handle = file}; .handle = file};
io_cmp cmp = io_wait_single_req(&req); io_cmp cmp = io_wait_single_req(&req);
return((int)cmp.result); return((io_error)cmp.result);
} }
file_status file_get_status(file_handle file) file_status file_get_status(file_handle file)
@ -91,13 +90,13 @@ file_status file_get_status(file_handle file)
io_req req = {.op = IO_OP_FSTAT, io_req req = {.op = IO_OP_FSTAT,
.handle = file, .handle = file,
.size = sizeof(file_status), .size = sizeof(file_status),
.buffer = (char*)status}; .buffer = (char*)&status};
io_cmp cmp = io_wait_single_req(&req); io_cmp cmp = io_wait_single_req(&req);
return(status); return(status);
} }
size_t file_size(file_handle file) u64 file_size(file_handle file)
{ {
file_status status = file_get_status(file); file_status status = file_get_status(file);
return(status.size); return(status.size);

View File

@ -12,7 +12,7 @@
#include<unistd.h> #include<unistd.h>
#include<limits.h> #include<limits.h>
#include"platform_io.h" #include"platform_io_common.c"
typedef struct file_slot typedef struct file_slot
{ {
@ -45,6 +45,7 @@ file_slot* file_slot_alloc(file_table* table)
{ {
slot = &table->slots[table->nextSlot]; slot = &table->slots[table->nextSlot];
slot->generation = 1; slot->generation = 1;
slot->fd = -1;
table->nextSlot++; table->nextSlot++;
} }
return(slot); return(slot);
@ -71,7 +72,7 @@ file_slot* file_slot_from_handle(file_table* table, file_handle handle)
u64 index = handle.h & 0xffffffff; u64 index = handle.h & 0xffffffff;
u64 generation = handle.h>>32; u64 generation = handle.h>>32;
if(index < ORCA_MAX_FILE_SLOTS) if(index < table->nextSlot)
{ {
file_slot* candidate = &table->slots[index]; file_slot* candidate = &table->slots[index];
if(candidate->generation == generation) if(candidate->generation == generation)
@ -211,6 +212,10 @@ int io_convert_open_flags(file_open_flags flags)
{ {
oflags |= O_NOFOLLOW; oflags |= O_NOFOLLOW;
} }
if(flags & FILE_OPEN_SYMLINK)
{
oflags |= O_SYMLINK;
}
return(oflags); return(oflags);
} }
@ -226,18 +231,17 @@ io_cmp io_open_at(file_slot* atSlot, io_req* req)
} }
else else
{ {
file_handle handle = file_handle_from_slot(&__globalFileTable, slot); cmp.handle = file_handle_from_slot(&__globalFileTable, slot);
cmp.result = handle.h;
int flags = io_convert_open_flags(req->openFlags); int flags = io_convert_open_flags(req->openFlags);
//TODO: allow specifying access //TODO: allow specifying access
mode_t mode = S_IRUSR mode_t mode = S_IRUSR
| S_IWUSR | S_IWUSR
| S_IRGRP | S_IRGRP
| S_IWGRP | S_IWGRP
| S_IROTH | S_IROTH
| S_IWOTH; | S_IWOTH;
//NOTE: open //NOTE: open
mem_arena_scope scratch = mem_scratch_begin(); mem_arena_scope scratch = mem_scratch_begin();
@ -336,7 +340,7 @@ io_cmp io_fstat(file_slot* slot, io_req* req)
{ {
io_cmp cmp = {0}; io_cmp cmp = {0};
if(req->size <= sizeof(file_status)) if(req->size < sizeof(file_status))
{ {
cmp.error = IO_ERR_ARG; cmp.error = IO_ERR_ARG;
} }
@ -444,9 +448,12 @@ io_cmp io_wait_single_req(io_req* req)
io_cmp cmp = {0}; io_cmp cmp = {0};
file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle); file_slot* slot = file_slot_from_handle(&__globalFileTable, req->handle);
if(!slot && (req->op != IO_OP_OPEN_AT)) if(!slot)
{ {
cmp.error = IO_ERR_HANDLE; if(req->op != IO_OP_OPEN_AT)
{
cmp.error = IO_ERR_HANDLE;
}
} }
else if(slot->fatal && req->op != IO_OP_CLOSE) else if(slot->fatal && req->op != IO_OP_CLOSE)
{ {

17
test/files/build.sh Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
LIBDIR=../../bin
RESDIR=../../resources
SRCDIR=../../src
INCLUDES="-I$SRCDIR -I$SRCDIR/util -I$SRCDIR/platform -I$SRCDIR/app"
LIBS="-L$LIBDIR -lmilepost"
FLAGS="-mmacos-version-min=10.15.4 -DDEBUG -DLOG_COMPILE_DEBUG"
if [ ! \( -e bin \) ] ; then
mkdir ./bin
fi
clang -g $FLAGS $LIBS $INCLUDES -o ./bin/test_files main.c
install_name_tool -add_rpath "@executable_path/../../../bin" ./bin/test_files

View File

View File

@ -0,0 +1 @@
Hello, world!

1
test/files/data/symlink Symbolic link
View File

@ -0,0 +1 @@
regular.txt

181
test/files/main.c Normal file
View File

@ -0,0 +1,181 @@
/************************************************************//**
*
* @file: main.c
* @author: Martin Fouilleul
* @date: 26/05/2023
*
*****************************************************************/
#include<stdio.h>
#include"milepost.h"
int test_write(mem_arena* arena, str8 path, str8 test_string)
{
log_info("writing\n");
file_handle f = file_open(path, FILE_OPEN_CREATE|FILE_OPEN_WRITE|FILE_OPEN_TRUNCATE);
if(file_last_error(f))
{
log_error("Can't create/open file %.*s for writing\n", (int)path.len, path.ptr);
return(-1);
}
file_write(f, test_string.len, test_string.ptr);
if(file_last_error(f))
{
log_error("Error while writing %.*s\n", (int)path.len, path.ptr);
return(-1);
}
file_close(f);
// check
char* pathCStr = str8_to_cstring(arena, path);
FILE* file = fopen(pathCStr, "r");
if(!file)
{
log_error("File %.*s not found while checking\n", (int)path.len, path.ptr);
return(-1);
}
char buffer[256];
int n = fread(buffer, 1, 256, file);
if(n != test_string.len || strncmp(buffer, test_string.ptr, test_string.len))
{
log_error("Didn't recover test string\n");
return(-1);
}
fclose(file);
return(0);
}
int test_read(mem_arena* arena, str8 path, str8 test_string)
{
log_info("reading\n");
file_handle f = file_open(path, FILE_OPEN_READ);
if(file_last_error(f))
{
log_error("Can't open file %.*s for reading\n", (int)path.len, path.ptr);
return(-1);
}
char buffer[256];
i64 n = file_read(f, 256, buffer);
if(file_last_error(f))
{
log_error("Error while reading %.*s\n", (int)path.len, path.ptr);
return(-1);
}
file_close(f);
if(str8_cmp(test_string, str8_from_buffer(n, buffer)))
{
log_error("Didn't recover test string\n");
return(-1);
}
return(0);
}
int test_stat_size(str8 path, u64 size)
{
log_info("stat size\n");
file_handle f = file_open(path, 0);
file_status status = file_get_status(f);
if(file_last_error(f))
{
log_error("Error while retrieving file status\n");
return(-1);
}
if(status.size != size)
{
log_error("size doesn't match\n");
return(-1);
}
return(0);
}
int test_stat_type(mem_arena* arena, str8 dataDir)
{
str8 regular = path_append(arena, dataDir, STR8("regular.txt"));
str8 dir = path_append(arena, dataDir, STR8("directory"));
str8 link = path_append(arena, dataDir, STR8("symlink"));
log_info("stat type, regular\n");
file_handle f = file_open(regular, 0);
file_status status = file_get_status(f);
if(file_last_error(f))
{
log_error("Error while retrieving file status\n");
return(-1);
}
if(status.type != FILE_REGULAR)
{
log_error("file type doesn't match\n");
return(-1);
}
file_close(f);
log_info("stat type, directory\n");
f = file_open(dir, 0);
status = file_get_status(f);
if(file_last_error(f))
{
log_error("Error while retrieving file status\n");
return(-1);
}
if(status.type != FILE_DIRECTORY)
{
log_error("file type doesn't match\n");
return(-1);
}
file_close(f);
log_info("stat type, symlink\n");
f = file_open(link, FILE_OPEN_SYMLINK);
status = file_get_status(f);
if(file_last_error(f))
{
log_error("Error while retrieving file status\n");
return(-1);
}
if(status.type != FILE_SYMLINK)
{
log_error("file type doesn't match\n");
return(-1);
}
file_close(f);
return(0);
}
int main(int argc, char** argv)
{
mem_arena* arena = mem_scratch();
str8 exePath = path_executable(arena);
str8 dirPath = path_slice_directory(exePath);
dirPath = path_append(arena, dirPath, STR8(".."));
str8 dataDir = path_append(arena, dirPath, STR8("data"));
str8 path = path_append(arena, dirPath, STR8("test.txt"));
str8 test_string = STR8("Hello, world!");
if(test_write(arena, path, test_string)) { return(-1); }
if(test_read(arena, path, test_string)) { return(-1); }
if(test_stat_size(path, test_string.len)) { return(-1); }
if(test_stat_type(arena, dataDir)) { return(-1); }
log_info("OK\n");
return(0);
}