From de9416dcafcf4010ae3a521b752aa474f3b17efa Mon Sep 17 00:00:00 2001 From: Thomas Spader Date: Sun, 10 May 2026 01:05:02 -0400 Subject: [PATCH 01/21] test: add a sketch of a crash handler --- tools/crash.h | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 tools/crash.h diff --git a/tools/crash.h b/tools/crash.h new file mode 100644 index 0000000..19e282c --- /dev/null +++ b/tools/crash.h @@ -0,0 +1,158 @@ + +/////////////////// +// CRASH SANDBOX // +/////////////////// +typedef enum { + UBENCH_CRASH_NONE = 0, + UBENCH_CRASH_SEGV, + UBENCH_CRASH_BUS, + UBENCH_CRASH_FPE, + UBENCH_CRASH_ILL, +} ubench_crash_kind_t; + +typedef void (*ubench_crash_body_t)(void *userdata); + +#if defined(SP_LINUX) || defined(SP_MACOS) +#include +#include + +struct ubench_crash_ctx_s { + sigjmp_buf jmp; + /* Written by the async signal handler; volatile per C99 7.13.2.1. */ + volatile sig_atomic_t kind; + s32 active; + struct ubench_crash_ctx_s *prev; +}; + +static SP_THREAD_LOCAL struct ubench_crash_ctx_s *ubench_crash_current = + SP_NULLPTR; + +static void ubench_crash_handler(s32 sig) { + struct ubench_crash_ctx_s *c = ubench_crash_current; + if (c == SP_NULLPTR || !c->active) { + /* Crash outside a sandbox - fall through to default termination. */ + signal(sig, SIG_DFL); + raise(sig); + return; + } + switch (sig) { + case SIGSEGV: c->kind = UBENCH_CRASH_SEGV; break; + case SIGBUS: c->kind = UBENCH_CRASH_BUS; break; + case SIGFPE: c->kind = UBENCH_CRASH_FPE; break; + case SIGILL: c->kind = UBENCH_CRASH_ILL; break; + default: c->kind = UBENCH_CRASH_SEGV; break; + } + siglongjmp(c->jmp, 1); +} + +static ubench_crash_kind_t ubench_crash_try(ubench_crash_body_t body, + void *userdata) { + struct ubench_crash_ctx_s ctx; + struct sigaction sa = sp_zero; + struct sigaction old_segv = sp_zero, old_bus = sp_zero; + struct sigaction old_fpe = sp_zero, old_ill = sp_zero; + + ctx.kind = UBENCH_CRASH_NONE; + ctx.active = 0; + ctx.prev = ubench_crash_current; + + sa.sa_handler = ubench_crash_handler; + /* SA_NODEFER lets a fault during the handler re-enter rather than deadlock + on a masked signal. */ + sa.sa_flags = SA_NODEFER; + sigemptyset(&sa.sa_mask); + sigaction(SIGSEGV, &sa, &old_segv); + sigaction(SIGBUS, &sa, &old_bus); + sigaction(SIGFPE, &sa, &old_fpe); + sigaction(SIGILL, &sa, &old_ill); + + ubench_crash_current = &ctx; + if (sigsetjmp(ctx.jmp, 1) == 0) { + ctx.active = 1; + body(userdata); + } + ctx.active = 0; + ubench_crash_current = ctx.prev; + + sigaction(SIGSEGV, &old_segv, SP_NULLPTR); + sigaction(SIGBUS, &old_bus, SP_NULLPTR); + sigaction(SIGFPE, &old_fpe, SP_NULLPTR); + sigaction(SIGILL, &old_ill, SP_NULLPTR); + return (ubench_crash_kind_t)ctx.kind; +} + +#elif defined(SP_WIN32) +#include + +struct ubench_crash_ctx_s { + jmp_buf jmp; + volatile LONG kind; + s32 active; + struct ubench_crash_ctx_s *prev; +}; + +static SP_THREAD_LOCAL struct ubench_crash_ctx_s *ubench_crash_current = + SP_NULLPTR; + +static LONG CALLBACK ubench_crash_veh(EXCEPTION_POINTERS *ep) { + struct ubench_crash_ctx_s *c = ubench_crash_current; + ubench_crash_kind_t k; + if (c == SP_NULLPTR || !c->active) return EXCEPTION_CONTINUE_SEARCH; + switch (ep->ExceptionRecord->ExceptionCode) { + case EXCEPTION_ACCESS_VIOLATION: + k = UBENCH_CRASH_SEGV; break; + case EXCEPTION_IN_PAGE_ERROR: + case EXCEPTION_DATATYPE_MISALIGNMENT: + k = UBENCH_CRASH_BUS; break; + case EXCEPTION_INT_DIVIDE_BY_ZERO: + case EXCEPTION_INT_OVERFLOW: + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + case EXCEPTION_FLT_OVERFLOW: + case EXCEPTION_FLT_UNDERFLOW: + case EXCEPTION_FLT_INVALID_OPERATION: + case EXCEPTION_FLT_DENORMAL_OPERAND: + case EXCEPTION_FLT_INEXACT_RESULT: + case EXCEPTION_FLT_STACK_CHECK: + k = UBENCH_CRASH_FPE; break; + case EXCEPTION_ILLEGAL_INSTRUCTION: + case EXCEPTION_PRIV_INSTRUCTION: + k = UBENCH_CRASH_ILL; break; + default: + return EXCEPTION_CONTINUE_SEARCH; + } + c->kind = (LONG)k; + /* longjmp out of the VEH callback; on x64 setjmp/longjmp are unwind-aware + under both MSVC and MinGW-w64, so the OS dispatcher frames unwind. */ + longjmp(c->jmp, 1); +} + +static ubench_crash_kind_t ubench_crash_try(ubench_crash_body_t body, + void *userdata) { + struct ubench_crash_ctx_s ctx; + PVOID h; + + ctx.kind = UBENCH_CRASH_NONE; + ctx.active = 0; + ctx.prev = ubench_crash_current; + + /* First-handler (1) so we get the exception before any debugger or other + handler claims it. */ + h = AddVectoredExceptionHandler(1, ubench_crash_veh); + ubench_crash_current = &ctx; + if (setjmp(ctx.jmp) == 0) { + ctx.active = 1; + body(userdata); + } + ctx.active = 0; + ubench_crash_current = ctx.prev; + RemoveVectoredExceptionHandler(h); + return (ubench_crash_kind_t)ctx.kind; +} + +#else +static SP_INLINE ubench_crash_kind_t +ubench_crash_try(ubench_crash_body_t body, void *userdata) { + body(userdata); + return UBENCH_CRASH_NONE; +} +#endif From 55d115601eaba4e6b1624890f01bcf7499691ae8 Mon Sep 17 00:00:00 2001 From: Thomas Spader Date: Sun, 10 May 2026 01:05:10 -0400 Subject: [PATCH 02/21] io: experiments --- sp.h | 64 ++++++++++++++++++++++++++++ test/cv.c | 1 + test/io.c | 124 ++++++++++++++++++++++++++++++++---------------------- 3 files changed, 139 insertions(+), 50 deletions(-) diff --git a/sp.h b/sp.h index 9c8a58c..3fce949 100644 --- a/sp.h +++ b/sp.h @@ -729,6 +729,7 @@ typedef enum { SP_ERR_IO_READ_ONLY = 1008, SP_ERR_IO_NO_SPACE = 1009, SP_ERR_IO_EOF = 1010, + SP_ERR_IO_INVALID_WRITE = 1011, SP_ERR_FMT_TOO_MANY_RENDERERS = 1100, SP_ERR_FMT_WRONG_PARAM_KIND = 1101, SP_ERR_FMT_UNKNOWN_DIRECTIVE = 1102, @@ -3132,6 +3133,8 @@ struct sp_io_dyn_mem_writer { u64 cursor; }; +SP_API sp_err_t sp_io_copy(sp_io_writer_t* dst, sp_io_reader_t* src, u64* bytes_copied); +SP_API sp_err_t sp_io_copy_b(sp_io_writer_t* dst, sp_io_reader_t* src, u8* buffer, u64 n, u64* bytes_copied); SP_API sp_err_t sp_io_read(sp_io_reader_t* reader, void* ptr, u64 size, u64* bytes_read); SP_API void sp_io_reader_from_mem(sp_io_reader_t* reader, const void* ptr, u64 size); SP_API void sp_io_reader_set_buffer(sp_io_reader_t* reader, u8* buf, u64 capacity); @@ -13194,6 +13197,67 @@ void sp_io_reader_set_buffer(sp_io_reader_t* reader, u8* buf, u64 capacity) { reader->cursor = 0; } +sp_err_t sp_io_copy(sp_io_writer_t* w, sp_io_reader_t* r, u64* bytes_copied) { + u8 buffer[4096]; + return sp_io_copy_b(w, r, buffer, sizeof(buffer), bytes_copied); +} + +sp_err_t sp_io_copy_b(sp_io_writer_t* w, sp_io_reader_t* r, u8* buffer, u64 n, u64* bytes_copied) { + sp_err_t err = SP_OK; + u64 total = 0; + u64 chunk = 0; + + while (true) { + sp_try_goto(sp_io_read(r, buffer, n, &chunk), err, done); + sp_try_goto(sp_io_write(w, buffer, chunk, SP_NULLPTR), err, done); + total += chunk; + } + +done: + if (err == SP_ERR_IO_EOF) err = SP_OK; + if (bytes_copied) *bytes_copied = total; + return err; +} + +sp_err_t sp_io_copy_b2(sp_io_writer_t* w, sp_io_reader_t* r, u8* buffer, u64 n, u64* bytes_copied) { + u64 total = 0; + struct { + u64 r; + u64 w; + } chunk = sp_zero; + struct { + u64 r; + u64 w; + u64 e; + } err = sp_zero; + + while (true) { + err.w = sp_io_read(r, buffer, n, &chunk.r); + if (chunk.r) { + err.r = sp_io_write(w, buffer, chunk.r, &chunk.w); + if (chunk.w != chunk.r) { + err.w = SP_ERR_IO_INVALID_WRITE; + } + total += chunk.w; + if (err.w) { + err.e = err.w; + break; + } + } + + if (err.r) { + if (err.r != SP_ERR_IO_EOF) { + err.e = err.r; + break; + } + } + } + +done: + if (bytes_copied) *bytes_copied = total; + return err.e; +} + sp_err_t sp_io_read(sp_io_reader_t* reader, void* ptr, u64 size, u64* bytes_read) { sp_assert(reader); diff --git a/test/cv.c b/test/cv.c index dd94811..c7dbd11 100644 --- a/test/cv.c +++ b/test/cv.c @@ -391,3 +391,4 @@ UTEST(cv, multithread_producer_consumer) { sp_cv_destroy(&cv); sp_mutex_destroy(&mutex); } + diff --git a/test/io.c b/test/io.c index 4cee6d8..3f4c5b6 100644 --- a/test/io.c +++ b/test/io.c @@ -6,14 +6,14 @@ SP_TEST_MAIN() -struct io_rw { +struct io { sp_str_t file_path; sp_test_file_manager_t file_manager; sp_mem_arena_t* arena; sp_mem_t mem; }; -UTEST_F_SETUP(io_rw) { +UTEST_F_SETUP(io) { SKIP_ON_WASM() ut.arena = sp_mem_arena_new(sp_mem_os_new()); ut.mem = sp_mem_arena_as_allocator(ut.arena); @@ -21,13 +21,13 @@ UTEST_F_SETUP(io_rw) { ut.file_path = sp_test_file_create_empty(&ut.file_manager, sp_str_lit("sp_io_rw.file")); } -UTEST_F_TEARDOWN(io_rw) { +UTEST_F_TEARDOWN(io) { SKIP_ON_WASM() sp_test_file_manager_cleanup(&ut.file_manager); sp_mem_arena_destroy(ut.arena); } -UTEST_F(io_rw, reader_mem_read_full) { +UTEST_F(io, reader_mem_read_full) { SKIP_ON_WASM() u8 source[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; u8 dest[16] = sp_zero; @@ -42,7 +42,7 @@ UTEST_F(io_rw, reader_mem_read_full) { EXPECT_EQ(dest[i], source[i]); } } -UTEST_F(io_rw, reader_mem_read_partial) { +UTEST_F(io, reader_mem_read_partial) { SKIP_ON_WASM() u8 source[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; u8 dest[8] = sp_zero; @@ -57,7 +57,7 @@ UTEST_F(io_rw, reader_mem_read_partial) { EXPECT_EQ(dest[i], source[i]); } } -UTEST_F(io_rw, reader_mem_read_past_end) { +UTEST_F(io, reader_mem_read_past_end) { SKIP_ON_WASM() u8 source[8] = {1,2,3,4,5,6,7,8}; u8 dest[16] = sp_zero; @@ -73,7 +73,7 @@ UTEST_F(io_rw, reader_mem_read_past_end) { EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_ERR_IO_EOF); EXPECT_EQ(bytes, 0); } -UTEST_F(io_rw, reader_mem_read_eof_exact) { +UTEST_F(io, reader_mem_read_eof_exact) { SKIP_ON_WASM() u8 source[8] = {1,2,3,4,5,6,7,8}; u8 dest[8] = sp_zero; @@ -87,7 +87,7 @@ UTEST_F(io_rw, reader_mem_read_eof_exact) { EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_ERR_IO_EOF); EXPECT_EQ(bytes, 0); } -UTEST_F(io_rw, reader_mem_read_eof_empty) { +UTEST_F(io, reader_mem_read_eof_empty) { SKIP_ON_WASM() u8 dest[4] = sp_zero; @@ -97,7 +97,7 @@ UTEST_F(io_rw, reader_mem_read_eof_empty) { EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_ERR_IO_EOF); EXPECT_EQ(bytes, 0); } -UTEST_F(io_rw, reader_mem_seek) { +UTEST_F(io, reader_mem_seek) { SKIP_ON_WASM() u8 buffer[64] = sp_zero; sp_for(i, 64) buffer[i] = (u8)i; @@ -117,7 +117,7 @@ UTEST_F(io_rw, reader_mem_seek) { EXPECT_EQ(sp_io_seeking_reader_seek(&io, 0, SP_IO_SEEK_END, &pos), SP_OK); EXPECT_EQ(pos, 64); } -UTEST_F(io_rw, reader_mem_seek_invalid) { +UTEST_F(io, reader_mem_seek_invalid) { SKIP_ON_WASM() u8 buffer[64] = sp_zero; sp_io_reader_t backing = sp_zero; @@ -131,7 +131,7 @@ UTEST_F(io_rw, reader_mem_seek_invalid) { EXPECT_EQ(sp_io_seeking_reader_seek(&io, -10, SP_IO_SEEK_SET, &pos), SP_ERR_IO_SEEK_INVALID); EXPECT_EQ(pos, -1); } -UTEST_F(io_rw, seeking_reader_file_seek) { +UTEST_F(io, seeking_reader_file_seek) { SKIP_ON_WASM() const char* content = "0123456789ABCDEF"; { @@ -160,7 +160,7 @@ UTEST_F(io_rw, seeking_reader_file_seek) { sp_io_file_reader_close(&fr); } -UTEST_F(io_rw, seeking_reader_file_seek_whence) { +UTEST_F(io, seeking_reader_file_seek_whence) { SKIP_ON_WASM() const char* content = "0123456789ABCDEF"; { @@ -197,7 +197,7 @@ UTEST_F(io_rw, seeking_reader_file_seek_whence) { sp_io_file_reader_close(&fr); } -UTEST_F(io_rw, seeking_reader_file_seek_invalid) { +UTEST_F(io, seeking_reader_file_seek_invalid) { SKIP_ON_WASM() const char* content = "0123456789"; { @@ -218,7 +218,7 @@ UTEST_F(io_rw, seeking_reader_file_seek_invalid) { sp_io_file_reader_close(&fr); } -UTEST_F(io_rw, seeking_reader_file_seek_buffered) { +UTEST_F(io, seeking_reader_file_seek_buffered) { SKIP_ON_WASM() const char* content = "0123456789ABCDEF"; { @@ -249,7 +249,7 @@ UTEST_F(io_rw, seeking_reader_file_seek_buffered) { sp_io_file_reader_close(&fr); } -UTEST_F(io_rw, reader_file_read) { +UTEST_F(io, reader_file_read) { SKIP_ON_WASM() const char* content = "0123456789ABCDEF"; { @@ -271,7 +271,7 @@ UTEST_F(io_rw, reader_file_read) { } sp_io_file_reader_close(&r); } -UTEST_F(io_rw, reader_file_seek) { +UTEST_F(io, reader_file_seek) { SKIP_ON_WASM() const char* content = "0123456789"; { @@ -292,14 +292,14 @@ UTEST_F(io_rw, reader_file_seek) { EXPECT_EQ(buffer[0], '5'); sp_io_file_reader_close(&r); } -UTEST_F(io_rw, reader_file_nonexistent) { +UTEST_F(io, reader_file_nonexistent) { SKIP_ON_WASM() sp_str_t path = sp_test_file_path(&ut.file_manager, sp_str_lit("nonexistent.file")); sp_io_file_reader_t r = sp_zero; EXPECT_EQ(sp_io_file_reader_from_path(&r, path), SP_ERR_IO_OPEN_FAILED); sp_io_file_reader_close(&r); } -UTEST_F(io_rw, reader_file_read_eof_after_drain) { +UTEST_F(io, reader_file_read_eof_after_drain) { SKIP_ON_WASM() const char* content = "0123456789ABCDEF"; { @@ -323,7 +323,7 @@ UTEST_F(io_rw, reader_file_read_eof_after_drain) { sp_io_file_reader_close(&r); } -UTEST_F(io_rw, reader_file_read_eof_short) { +UTEST_F(io, reader_file_read_eof_short) { SKIP_ON_WASM() const char* content = "short"; { @@ -347,7 +347,7 @@ UTEST_F(io_rw, reader_file_read_eof_short) { sp_io_file_reader_close(&r); } -UTEST_F(io_rw, reader_file_read_eof_empty) { +UTEST_F(io, reader_file_read_eof_empty) { SKIP_ON_WASM() sp_io_file_reader_t r = sp_zero; sp_io_file_reader_from_path(&r, ut.file_path); @@ -359,7 +359,7 @@ UTEST_F(io_rw, reader_file_read_eof_empty) { sp_io_file_reader_close(&r); } -UTEST_F(io_rw, writer_mem_write) { +UTEST_F(io, writer_mem_write) { SKIP_ON_WASM() u8 buffer[16] = sp_zero; u8 source[8] = {1,2,3,4,5,6,7,8}; @@ -374,7 +374,7 @@ UTEST_F(io_rw, writer_mem_write) { } /* mem writer has no close */; } -UTEST_F(io_rw, writer_mem_write_overflow) { +UTEST_F(io, writer_mem_write_overflow) { SKIP_ON_WASM() u8 buffer[8] = sp_zero; u8 source[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; @@ -386,7 +386,7 @@ UTEST_F(io_rw, writer_mem_write_overflow) { EXPECT_EQ(bytes, 0); /* mem writer has no close */; } -UTEST_F(io_rw, writer_mem_seek) { +UTEST_F(io, writer_mem_seek) { SKIP_ON_WASM() u8 buffer[64] = sp_zero; sp_io_mem_writer_t w; sp_io_mem_writer_from_buffer(&w,buffer, sizeof(buffer)); @@ -400,7 +400,7 @@ UTEST_F(io_rw, writer_mem_seek) { /* mem writer has no close */; } -UTEST_F(io_rw, writer_mem_size) { +UTEST_F(io, writer_mem_size) { SKIP_ON_WASM() u8 buffer[128]; sp_io_mem_writer_t w; sp_io_mem_writer_from_buffer(&w,buffer, sizeof(buffer)); @@ -409,7 +409,7 @@ UTEST_F(io_rw, writer_mem_size) { EXPECT_EQ(size, 128); /* mem writer has no close */; } -UTEST_F(io_rw, writer_file_write) { +UTEST_F(io, writer_file_write) { SKIP_ON_WASM() const char* content = "test data"; sp_io_file_writer_t w = sp_zero; @@ -424,7 +424,7 @@ UTEST_F(io_rw, writer_file_write) { sp_io_read_file_a(ut.mem, ut.file_path, &loaded); EXPECT_EQ(loaded.len, 9); } -UTEST_F(io_rw, writer_file_overwrite) { +UTEST_F(io, writer_file_overwrite) { SKIP_ON_WASM() const char* first = "XXXXXXXX"; const char* second = "1234"; @@ -449,7 +449,7 @@ UTEST_F(io_rw, writer_file_overwrite) { EXPECT_EQ(loaded.data[0], '1'); EXPECT_EQ(loaded.data[3], '4'); } -UTEST_F(io_rw, writer_file_append) { +UTEST_F(io, writer_file_append) { SKIP_ON_WASM() const char* first = "first"; const char* second = "second"; @@ -473,7 +473,7 @@ UTEST_F(io_rw, writer_file_append) { EXPECT_EQ(loaded.len, 11); EXPECT_TRUE(sp_str_equal(loaded, sp_str_lit("firstsecond"))); } -UTEST_F(io_rw, writer_file_seek) { +UTEST_F(io, writer_file_seek) { SKIP_ON_WASM() const char* content = "0123456789"; { @@ -494,7 +494,7 @@ UTEST_F(io_rw, writer_file_seek) { EXPECT_EQ(buffer[0], '5'); sp_io_file_reader_close(&r); } -UTEST_F(io_rw, writer_file_size) { +UTEST_F(io, writer_file_size) { SKIP_ON_WASM() const char* content = "0123456789ABCDEF"; sp_io_file_writer_t w = sp_zero; @@ -505,7 +505,7 @@ UTEST_F(io_rw, writer_file_size) { EXPECT_EQ(size, 16); sp_io_file_writer_close(&w); } -UTEST_F(io_rw, writer_file_pad) { +UTEST_F(io, writer_file_pad) { SKIP_ON_WASM() sp_io_file_writer_t w = sp_zero; sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); @@ -529,7 +529,7 @@ UTEST_F(io_rw, writer_file_pad) { EXPECT_EQ(result[6], 'B'); sp_io_file_reader_close(&r); } -UTEST_F(io_rw, writer_dyn_write) { +UTEST_F(io, writer_dyn_write) { SKIP_ON_WASM() sp_io_dyn_mem_writer_t w; sp_io_dyn_mem_writer_init_a(ut.mem, &w); u8 data[] = {1, 2, 3, 4}; @@ -542,7 +542,7 @@ UTEST_F(io_rw, writer_dyn_write) { EXPECT_EQ(size, 4); sp_io_dyn_mem_writer_close(&w); } -UTEST_F(io_rw, writer_dyn_grows) { +UTEST_F(io, writer_dyn_grows) { SKIP_ON_WASM() sp_io_dyn_mem_writer_t w; sp_io_dyn_mem_writer_init_a(ut.mem, &w); @@ -558,7 +558,7 @@ UTEST_F(io_rw, writer_dyn_grows) { sp_io_dyn_mem_writer_close(&w); } -UTEST_F(io_rw, writer_dyn_to_str) { +UTEST_F(io, writer_dyn_to_str) { SKIP_ON_WASM() sp_io_dyn_mem_writer_t w; sp_io_dyn_mem_writer_init_a(ut.mem, &w); @@ -571,7 +571,7 @@ UTEST_F(io_rw, writer_dyn_to_str) { sp_io_dyn_mem_writer_close(&w); } -UTEST_F(io_rw, writer_dyn_seek) { +UTEST_F(io, writer_dyn_seek) { SKIP_ON_WASM() sp_io_dyn_mem_writer_t w; sp_io_dyn_mem_writer_init_a(ut.mem, &w); @@ -584,7 +584,7 @@ UTEST_F(io_rw, writer_dyn_seek) { sp_io_dyn_mem_writer_close(&w); } -UTEST_F(io_rw, writer_dyn_multiple_writes) { +UTEST_F(io, writer_dyn_multiple_writes) { SKIP_ON_WASM() sp_io_dyn_mem_writer_t w; sp_io_dyn_mem_writer_init_a(ut.mem, &w); @@ -601,7 +601,7 @@ UTEST_F(io_rw, writer_dyn_multiple_writes) { sp_io_dyn_mem_writer_close(&w); } -UTEST_F(io_rw, writer_buffered_1000_bytes) { +UTEST_F(io, writer_buffered_1000_bytes) { SKIP_ON_WASM() u8 write_buf[64]; sp_io_file_writer_t w = sp_zero; @@ -631,7 +631,7 @@ UTEST_F(io_rw, writer_buffered_1000_bytes) { } sp_io_file_reader_close(&r); } -UTEST_F(io_rw, writer_buffered_larger_than_buffer) { +UTEST_F(io, writer_buffered_larger_than_buffer) { SKIP_ON_WASM() u8 write_buf[32]; u8 data[128]; @@ -657,7 +657,7 @@ UTEST_F(io_rw, writer_buffered_larger_than_buffer) { } sp_io_file_reader_close(&r); } -UTEST_F(io_rw, writer_buffered_implicit_flush) { +UTEST_F(io, writer_buffered_implicit_flush) { SKIP_ON_WASM() u8 write_buf[64]; sp_io_file_writer_t w = sp_zero; @@ -672,7 +672,7 @@ UTEST_F(io_rw, writer_buffered_implicit_flush) { EXPECT_EQ(loaded.len, 5); EXPECT_TRUE(sp_str_equal(loaded, sp_str_lit("hello"))); } -UTEST_F(io_rw, writer_buffered_flush_empty) { +UTEST_F(io, writer_buffered_flush_empty) { SKIP_ON_WASM() u8 write_buf[64]; sp_io_file_writer_t w = sp_zero; @@ -683,7 +683,7 @@ UTEST_F(io_rw, writer_buffered_flush_empty) { EXPECT_EQ(err, SP_OK); sp_io_file_writer_close(&w); } -UTEST_F(io_rw, writer_buffered_set_twice) { +UTEST_F(io, writer_buffered_set_twice) { SKIP_ON_WASM() u8 write_buf1[64]; u8 write_buf2[64]; @@ -701,7 +701,7 @@ UTEST_F(io_rw, writer_buffered_set_twice) { EXPECT_EQ(loaded.len, 11); EXPECT_TRUE(sp_str_equal(loaded, sp_str_lit("firstsecond"))); } -UTEST_F(io_rw, reader_buffered_read) { +UTEST_F(io, reader_buffered_read) { SKIP_ON_WASM() const char* content = "0123456789ABCDEF"; { @@ -725,7 +725,7 @@ UTEST_F(io_rw, reader_buffered_read) { } sp_io_file_reader_close(&r); } -UTEST_F(io_rw, reader_buffered_small_reads) { +UTEST_F(io, reader_buffered_small_reads) { SKIP_ON_WASM() const char* content = "0123456789ABCDEF"; { @@ -749,7 +749,7 @@ UTEST_F(io_rw, reader_buffered_small_reads) { } sp_io_file_reader_close(&r); } -UTEST_F(io_rw, reader_buffered_eof_exact) { +UTEST_F(io, reader_buffered_eof_exact) { SKIP_ON_WASM() const char* content = "0123456789ABCDEF"; { @@ -775,7 +775,7 @@ UTEST_F(io_rw, reader_buffered_eof_exact) { sp_io_file_reader_close(&r); } -UTEST_F(io_rw, reader_buffered_eof_partial) { +UTEST_F(io, reader_buffered_eof_partial) { SKIP_ON_WASM() const char* content = "0123456789ABCDEF"; { @@ -801,7 +801,7 @@ UTEST_F(io_rw, reader_buffered_eof_partial) { sp_io_file_reader_close(&r); } -UTEST_F(io_rw, reader_buffered_fill_preserves_drained) { +UTEST_F(io, reader_buffered_fill_preserves_drained) { SKIP_ON_WASM() const char* content = "0123456789ABCDEF"; { @@ -835,7 +835,7 @@ UTEST_F(io_rw, reader_buffered_fill_preserves_drained) { sp_io_file_reader_close(&r); } -UTEST_F(io_rw, reader_buffered_eof_direct_path) { +UTEST_F(io, reader_buffered_eof_direct_path) { SKIP_ON_WASM() const char* content = "0123456789ABCDEF"; { @@ -865,7 +865,7 @@ UTEST_F(io_rw, reader_buffered_eof_direct_path) { sp_io_file_reader_close(&r); } -UTEST_F(io_rw, reader_buffered_zero_size) { +UTEST_F(io, reader_buffered_zero_size) { SKIP_ON_WASM() u8 source[1] = {0}; u8 read_buf[8]; @@ -877,7 +877,7 @@ UTEST_F(io_rw, reader_buffered_zero_size) { EXPECT_EQ(sp_io_read(&io, source, 0, &bytes), SP_OK); EXPECT_EQ(bytes, 0); } -UTEST_F(io_rw, reader_buffered_eof_byte_by_byte) { +UTEST_F(io, reader_buffered_eof_byte_by_byte) { SKIP_ON_WASM() const char* content = "0123456789ABCDEF"; { @@ -907,7 +907,7 @@ UTEST_F(io_rw, reader_buffered_eof_byte_by_byte) { sp_io_file_reader_close(&r); } -UTEST_F(io_rw, reader_buffered_seek_discards_buffer) { +UTEST_F(io, reader_buffered_seek_discards_buffer) { SKIP_ON_WASM() const char* content = "0123456789ABCDEF"; { @@ -932,7 +932,8 @@ UTEST_F(io_rw, reader_buffered_seek_discards_buffer) { sp_io_file_reader_close(&r); } -UTEST_F(io_rw, seek_beyond_4gb) { + +UTEST_F(io, seek_beyond_4gb) { SKIP_ON_WASM() s64 offset = (s64)5 * 1024 * 1024 * 1024; u8 marker[4] = {0xDE, 0xAD, 0xBE, 0xEF}; @@ -965,3 +966,26 @@ UTEST_F(io_rw, seek_beyond_4gb) { EXPECT_EQ(result[3], 0xEF); sp_io_file_reader_close(&r); } + +UTEST_F(io, copy) { + struct { + u8 a [4096]; + u8 b [4096]; + u8 c [4096]; + } buf = sp_zero; + struct { + sp_io_reader_t r; + sp_io_writer_t w; + } io = sp_zero; + + sp_for(it, 4096) { + buf.a[it] = it; + } + + sp_io_reader_from_mem(&io.r, buf.a, sizeof(buf.a)); + sp_io_writer_set_buffer(&io.w, buf.b, sizeof(buf.b)); + + u64 n = 0; + sp_io_copy_b2(&io.w, &io.r, buf.c, sizeof(buf.c), &n); + EXPECT_EQ(n, 4096); +} From df52f75769475fdcf6ad24d6ec8a7db071d71765 Mon Sep 17 00:00:00 2001 From: Thomas Spader Date: Tue, 12 May 2026 12:20:52 -0400 Subject: [PATCH 03/21] embed example? --- Makefile | 2 +- example/embed.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 example/embed.c diff --git a/Makefile b/Makefile index 68840ea..ff80cf9 100644 --- a/Makefile +++ b/Makefile @@ -46,7 +46,7 @@ CFLAGS = $(CFLAGS_LANG) -g -Werror=return-type -fsanitize=undefined,alignment -f EXE := $(if $(findstring windows,$(TRIPLE)),.exe,) EXE := $(if $(WASM),.wasm,$(EXE)) TESTS = amalg app array asset cv elf env format fmon fs glob ht io linkage math ps rb str thread time mem prompt leak -EXAMPLES = app array elf format hash_table io ls palette prompt prompt_fancy signal wc +EXAMPLES = app array elf embed format hash_table io ls palette prompt prompt_fancy signal wc # TESTS = app amalg str format # EXAMPLES = app format hash_table TRIPLES = \ diff --git a/example/embed.c b/example/embed.c new file mode 100644 index 0000000..3d405ec --- /dev/null +++ b/example/embed.c @@ -0,0 +1,194 @@ +#define SP_IMPLEMENTATION +#include "sp.h" +#include "sp/sp_elf.h" + +typedef struct { + sp_str_t path; + sp_str_t symbol; + u64 size; +} embed_entry_t; + +sp_str_t symbol_from_path(sp_mem_t mem, sp_str_t path) { + sp_str_t symbol = sp_str_copy_a(mem, path); + symbol = sp_str_replace_c8_a(mem, symbol, '/', '_'); + symbol = sp_str_replace_c8_a(mem, symbol, '\\', '_'); + symbol = sp_str_replace_c8_a(mem, symbol, '.', '_'); + symbol = sp_str_replace_c8_a(mem, symbol, '-', '_'); + return symbol; +} + +s32 embed_main(s32 argc, const c8** argv) { + s32 rc = 0; + sp_mem_t mem = sp_mem_os_new(); + + if (argc < 4) { + sp_log_a("usage: embed [extra_files...]"); + rc = 1; + goto cleanup; + } + + sp_str_t src_dir = sp_str_view(argv[1]); + sp_str_t out_obj = sp_str_view(argv[2]); + sp_str_t out_hdr = sp_str_view(argv[3]); + sp_log_a("scanning {}", sp_fmt_str(src_dir)); + + sp_da(sp_fs_entry_t) files = sp_fs_collect_recursive_a(mem, src_dir); + if (sp_da_empty(files)) { + sp_log_a("no files found in {}", sp_fmt_str(src_dir)); + rc = 1; + goto cleanup; + } + + sp_elf_t* elf = sp_elf_new_with_null_section(mem); + sp_elf_symtab_new(elf); + + u32 rodata_idx = sp_elf_add_section(elf, sp_str_lit(".rodata"), SHT_PROGBITS, 8)->index; + sp_elf_find_section_by_index(elf, rodata_idx)->flags = SHF_ALLOC | SHF_WRITE; + + sp_da(embed_entry_t) entries = sp_da_new(mem, embed_entry_t); + + sp_da_for(files, it) { + sp_fs_entry_t ent = files[it]; + if (ent.kind == SP_FS_KIND_DIR) { + continue; + } + + u32 skip = src_dir.len + 1; + sp_str_t rel_path = sp_str_sub(ent.path, skip, ent.path.len - skip); + sp_str_t symbol = symbol_from_path(mem, rel_path); + + sp_str_t content = sp_zero; + sp_io_read_file_a(mem, ent.path, &content); + u64 size = content.len; + + sp_elf_section_t* symtab = sp_elf_find_section_by_name(elf, sp_str_lit(".symtab")); + sp_elf_section_t* section = sp_elf_find_section_by_index(elf, rodata_idx); + + { + u64 offset = section->buffer.size; + u8* ptr = sp_elf_section_reserve_bytes(section, size); + if (size) sp_sys_memcpy(ptr, content.data, size); + + sp_elf_add_symbol( + symtab, elf, + symbol, + offset, size, + STB_GLOBAL, STT_OBJECT, + section->index + ); + } + + { + section = sp_elf_find_section_by_index(elf, rodata_idx); + symtab = sp_elf_find_section_by_name(elf, sp_str_lit(".symtab")); + + u64 offset = section->buffer.size; + u8* ptr = sp_elf_section_reserve_bytes(section, sizeof(u64)); + sp_sys_memcpy(ptr, &size, sizeof(u64)); + + sp_elf_add_symbol( + symtab, elf, + sp_fmt_a(mem, "{}_size", sp_fmt_str(symbol)).value, + offset, sizeof(u64), + STB_GLOBAL, STT_OBJECT, + section->index + ); + } + + sp_da_push(entries, ((embed_entry_t) { + .path = rel_path, + .symbol = symbol, + .size = size, + })); + } + + { + sp_str_t file_path = sp_str_view(argv[4]); + sp_str_t symbol = sp_str_lit("include_spn_h"); + sp_str_t path = sp_str_lit("include/spn.h"); + + sp_str_t content = sp_zero; + sp_io_read_file_a(mem, file_path, &content); + u64 size = content.len; + + sp_elf_section_t* symtab = sp_elf_find_section_by_name(elf, sp_str_lit(".symtab")); + sp_elf_section_t* section = sp_elf_find_section_by_index(elf, rodata_idx); + + { + u64 offset = section->buffer.size; + u8* ptr = sp_elf_section_reserve_bytes(section, size); + if (size) sp_sys_memcpy(ptr, content.data, size); + + sp_elf_add_symbol( + symtab, elf, + symbol, + offset, size, + STB_GLOBAL, STT_OBJECT, + section->index + ); + } + + { + section = sp_elf_find_section_by_index(elf, rodata_idx); + symtab = sp_elf_find_section_by_name(elf, sp_str_lit(".symtab")); + + u64 offset = section->buffer.size; + u8* ptr = sp_elf_section_reserve_bytes(section, sizeof(u64)); + sp_sys_memcpy(ptr, &size, sizeof(u64)); + + sp_elf_add_symbol( + symtab, elf, + sp_fmt_a(mem, "{}_size", sp_fmt_str(symbol)).value, + offset, sizeof(u64), + STB_GLOBAL, STT_OBJECT, + section->index + ); + } + + sp_da_push(entries, ((embed_entry_t) { + .path = path, + .symbol = symbol, + .size = size, + })); + } + + sp_err_t err = sp_elf_write_to_file(elf, out_obj); + if (err != SP_OK) { + sp_log_a("failed to write {}", sp_fmt_str(out_obj)); + rc = 1; + goto cleanup; + } + + sp_io_file_writer_t hdr = sp_zero; + sp_io_file_writer_from_path(&hdr, out_hdr, SP_IO_WRITE_MODE_OVERWRITE); + sp_da_for(entries, it) { + embed_entry_t entry = entries[it]; + sp_fmt_io_a(&hdr.base, mem, + "extern const u8 {} [{}];\n", + sp_fmt_str(entry.symbol), + sp_fmt_uint(entry.size) + ); + sp_fmt_io_a(&hdr.base, mem, + "extern const u64 {}_size;\n\n", + sp_fmt_str(entry.symbol) + ); + } + sp_io_write_str(&hdr.base, sp_str_lit("typedef struct {\n const char* path;\n const void* data;\n unsigned long long size;\n} spn_embed_entry_t;\n"), SP_NULLPTR); + sp_fmt_io_a(&hdr.base, mem, "static const unsigned int spn_embed_count = {};\n", sp_fmt_uint(sp_da_size(entries))); + sp_io_write_str(&hdr.base, sp_str_lit("static const spn_embed_entry_t spn_embed_manifest[] = {\n"), SP_NULLPTR); + sp_da_for(entries, it) { + embed_entry_t entry = entries[it]; + sp_io_write_cstr(&hdr.base, " { ", SP_NULLPTR); + sp_fmt_io_a(&hdr.base, mem, "\"{}\", {}, {}", sp_fmt_str(entry.path), sp_fmt_str(entry.symbol), sp_fmt_uint(entry.size)); + sp_io_write_cstr(&hdr.base, " },\n", SP_NULLPTR); + } + sp_io_write_str(&hdr.base, sp_str_lit("};\n"), SP_NULLPTR); + sp_io_file_writer_close(&hdr); + + sp_log_a("embedded {} files -> {} + {}", sp_fmt_uint(sp_da_size(entries)), sp_fmt_str(out_obj), sp_fmt_str(out_hdr)); + +cleanup: + return rc; +} + +SP_MAIN(embed_main) From 5056b81bdfeacaa864ae1de5e7a7893170c846ad Mon Sep 17 00:00:00 2001 From: Thomas Spader Date: Tue, 12 May 2026 12:34:42 -0400 Subject: [PATCH 04/21] io tests --- test/io.c | 318 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 302 insertions(+), 16 deletions(-) diff --git a/test/io.c b/test/io.c index 3f4c5b6..f49cc89 100644 --- a/test/io.c +++ b/test/io.c @@ -967,25 +967,311 @@ UTEST_F(io, seek_beyond_4gb) { sp_io_file_reader_close(&r); } -UTEST_F(io, copy) { - struct { - u8 a [4096]; - u8 b [4096]; - u8 c [4096]; - } buf = sp_zero; +// UTEST_F(io, copy_playground) { +// struct { +// u8 a [4096]; +// u8 b [4096]; +// u8 c [4096]; +// } buf = sp_zero; +// struct { +// sp_io_reader_t r; +// sp_io_writer_t w; +// } io = sp_zero; +// +// sp_for(it, 4096) { +// buf.a[it] = it; +// } +// +// sp_io_reader_from_mem(&io.r, buf.a, sizeof(buf.a)); +// sp_io_writer_set_buffer(&io.w, buf.b, sizeof(buf.b)); +// +// u64 n = 0; +// sp_io_copy_b2(&io.w, &io.r, buf.c, sizeof(buf.c), &n); +// EXPECT_EQ(n, 4096); +// } + + +#define IO_MAX_STEPS 8 + +typedef enum { + IO_STEP_NONE, + IO_STEP_READ, + IO_STEP_WRITE, + IO_STEP_FLUSH, +} io_step_kind_t; + +typedef struct { + io_step_kind_t kind; + union { + struct { u64 request; sp_err_t err; const c8* content; } read; + struct { const c8* data; sp_err_t err; u64 bytes; } write; + struct { sp_err_t err; } flush; + }; +} io_step_t; + + +////////// +// READ // +////////// +typedef struct { + const c8* source; + io_step_t steps [IO_MAX_STEPS]; +} io_read_test_t; + +void run_io_read_test(int* utest_result, io_read_test_t t) { + sp_io_reader_t r = sp_zero; + sp_io_reader_from_mem(&r, t.source, sp_cstr_len(t.source)); + + sp_carr_for(t.steps, j) { + const io_step_t* step = &t.steps[j]; + if (step->kind == IO_STEP_NONE) break; + + u8 dest [64] = sp_zero; + u64 bytes = 0; + sp_err_t err = sp_io_read(&r, dest, step->read.request, &bytes); + EXPECT_EQ(err, step->read.err); + + u64 expect_bytes = sp_cstr_len(step->read.content); + EXPECT_EQ(bytes, expect_bytes); + sp_for(it, expect_bytes) { + EXPECT_EQ((c8)dest[it], step->read.content[it]); + } + } +} + +UTEST_F(io, read_exact_then_eof) { + run_io_read_test(utest_result, (io_read_test_t){ + .source = "0123", + .steps = { + { .kind = IO_STEP_READ, .read = { 4, SP_OK, "0123" } }, + { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, + }, + }); +} + +UTEST_F(io, read_chunked) { + run_io_read_test(utest_result, (io_read_test_t){ + .source = "01234567", + .steps = { + { .kind = IO_STEP_READ, .read = { 3, SP_OK, "012" } }, + { .kind = IO_STEP_READ, .read = { 3, SP_OK, "345" } }, + { .kind = IO_STEP_READ, .read = { 3, SP_OK, "67" } }, + { .kind = IO_STEP_READ, .read = { 3, SP_ERR_IO_EOF } }, + }, + }); +} + +UTEST_F(io, read_oversized_request) { + run_io_read_test(utest_result, (io_read_test_t){ + .source = "abc", + .steps = { + { .kind = IO_STEP_READ, .read = { 16, SP_OK, "abc" } }, + { .kind = IO_STEP_READ, .read = { 16, SP_ERR_IO_EOF } }, + }, + }); +} + +UTEST_F(io, read_empty_source) { + run_io_read_test(utest_result, (io_read_test_t){ + .source = "", + .steps = { + { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, + }, + }); +} + +UTEST_F(io, read_eof_idempotent) { + run_io_read_test(utest_result, (io_read_test_t){ + .source = "x", + .steps = { + { .kind = IO_STEP_READ, .read = { 4, SP_OK, "x" } }, + { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, + { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, + }, + }); +} + +UTEST_F(io, read_zero_request) { + run_io_read_test(utest_result, (io_read_test_t){ + .source = "abc", + .steps = { + { .kind = IO_STEP_READ, .read = { 0, SP_OK } }, + { .kind = IO_STEP_READ, .read = { 3, SP_OK, "abc" } }, + }, + }); +} + + +/////////// +// WRITE // +/////////// +typedef struct { + u64 capacity; + io_step_t steps [IO_MAX_STEPS]; struct { - sp_io_reader_t r; - sp_io_writer_t w; - } io = sp_zero; + const c8* content; + } expect; +} io_write_test_t; + +void run_io_write_test(int* utest_result, io_write_test_t t) { + u8 backing [64] = sp_zero; + sp_io_mem_writer_t w = sp_zero; + sp_io_mem_writer_from_buffer(&w, backing, t.capacity); + + sp_carr_for(t.steps, j) { + const io_step_t* step = &t.steps[j]; + if (step->kind == IO_STEP_NONE) break; + + switch (step->kind) { + case IO_STEP_WRITE: { + u64 bytes = 0; + sp_err_t err = sp_io_write(&w.base, step->write.data, sp_cstr_len(step->write.data), &bytes); + EXPECT_EQ(err, step->write.err); + EXPECT_EQ(bytes, step->write.bytes); + break; + } + case IO_STEP_FLUSH: { + sp_err_t err = sp_io_flush(&w.base); + EXPECT_EQ(err, step->flush.err); + break; + } + default: { + sp_unreachable_case(); + } + } + } - sp_for(it, 4096) { - buf.a[it] = it; + u64 n = sp_cstr_len(t.expect.content); + sp_for(it, n) { + EXPECT_EQ((c8)backing[it], t.expect.content[it]); } +} - sp_io_reader_from_mem(&io.r, buf.a, sizeof(buf.a)); - sp_io_writer_set_buffer(&io.w, buf.b, sizeof(buf.b)); +UTEST_F(io, write_fits) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 16, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "hello", SP_OK, 5 } }, + }, + .expect.content = "hello", + }); +} - u64 n = 0; - sp_io_copy_b2(&io.w, &io.r, buf.c, sizeof(buf.c), &n); - EXPECT_EQ(n, 4096); +UTEST_F(io, write_exact_fit) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 4, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abcd", SP_OK, 4 } }, + }, + .expect.content = "abcd", + }); } + +UTEST_F(io, write_overflow) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 4, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abcdefgh", SP_ERR_IO_NO_SPACE, 0 } }, + }, + }); +} + +UTEST_F(io, write_appends) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 16, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, + { .kind = IO_STEP_WRITE, .write = { "def", SP_OK, 3 } }, + { .kind = IO_STEP_WRITE, .write = { "ghi", SP_OK, 3 } }, + }, + .expect.content = "abcdefghi", + }); +} + +UTEST_F(io, write_zero) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 8, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "", SP_OK, 0 } }, + }, + }); +} + +UTEST_F(io, write_flush_empty) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 8, + .steps = { + { .kind = IO_STEP_FLUSH, .flush = { SP_OK } }, + }, + }); +} + + +////////// +// COPY // +////////// +typedef struct { + const c8* source; + u64 capacity; + u64 buffer; + struct { + sp_err_t err; + const c8* final; + } expect; +} io_copy_test_t; + +void run_io_copy_test(int* utest_result, io_copy_test_t t) { + sp_io_reader_t r = sp_zero; + sp_io_reader_from_mem(&r, t.source, sp_cstr_len(t.source)); + + u8 backing [64] = sp_zero; + sp_io_mem_writer_t w = sp_zero; + sp_io_mem_writer_from_buffer(&w, backing, t.capacity); + + u8 copy_buf [64] = sp_zero; + u64 copied = 0; + sp_err_t err = sp_io_copy_b(&w.base, &r, copy_buf, t.buffer, &copied); + + EXPECT_EQ(err, t.expect.err); + u64 n = sp_cstr_len(t.expect.final); + EXPECT_EQ(copied, n); + sp_for(it, n) { + EXPECT_EQ((c8)backing[it], t.expect.final[it]); + } +} + +UTEST_F(io, copy_full) { + run_io_copy_test(utest_result, (io_copy_test_t){ + .source = "0123456789", + .capacity = 32, .buffer = 8, + .expect = { .err = SP_OK, .final = "0123456789" }, + }); +} + +UTEST_F(io, copy_loops) { + run_io_copy_test(utest_result, (io_copy_test_t){ + .source = "ABCDEFGHIJKLMNOP", + .capacity = 32, .buffer = 4, + .expect = { .err = SP_OK, .final = "ABCDEFGHIJKLMNOP" }, + }); +} + +UTEST_F(io, copy_empty) { + run_io_copy_test(utest_result, (io_copy_test_t){ + .source = "", + .capacity = 8, .buffer = 8, + .expect = { .err = SP_OK }, + }); +} + +// TODO: this case exposes the sp_io_copy_b bug: today it returns bytes_copied=0 +// because the write fails before `total += chunk` runs, and the mem writer's +// all-or-nothing semantics drop the partial. Uncomment after fixing. +// +// UTEST_F(io, copy_writer_no_space) { +// run_io_copy_test(utest_result, (io_copy_test_t){ +// .source = "0123456789", +// .capacity = 4, .buffer = 8, +// .expect = { .err = SP_ERR_IO_NO_SPACE, .final = "0123" }, +// }); +// } From fee2c922a072d0ac75ea873333ae9ca1cff2488a Mon Sep 17 00:00:00 2001 From: Thomas Spader Date: Tue, 12 May 2026 12:50:31 -0400 Subject: [PATCH 05/21] cleanem --- Makefile | 2 +- test/fs.c | 10 +- test/fs/canonicalize_path.c | 2 - test/fs/collect.c | 2 - test/fs/copy.c | 2 +- test/fs/copy_glob.c | 2 +- test/fs/create_dir.c | 2 +- test/fs/create_file.c | 2 +- test/fs/get_cwd.c | 2 +- test/fs/get_exe_path.c | 2 +- test/fs/get_ext.c | 2 +- test/fs/get_name.c | 2 +- test/fs/get_stem.c | 2 +- test/fs/is_glob.c | 2 +- test/fs/is_root.c | 2 +- test/fs/join_path.c | 2 +- test/fs/links.c | 2 +- test/fs/mod_time.c | 2 +- test/fs/normalize_path.c | 2 - test/fs/parent_path.c | 2 +- test/fs/predicates.c | 2 +- test/fs/remove.c | 2 +- test/fs/replace_ext.c | 2 +- test/fs/system_paths.c | 2 +- test/fs/trim_path.c | 2 +- test/fs/windows/nt_path.c | 2 +- test/fs/windows/wtf8.c | 2 +- test/io.c | 1280 +---------------------------------- test/io/copy.c | 67 ++ test/io/io.h | 55 ++ test/io/loose.c | 964 ++++++++++++++++++++++++++ test/io/read.c | 89 +++ test/io/write.c | 102 +++ 33 files changed, 1307 insertions(+), 1312 deletions(-) create mode 100644 test/io/copy.c create mode 100644 test/io/io.h create mode 100644 test/io/loose.c create mode 100644 test/io/read.c create mode 100644 test/io/write.c diff --git a/Makefile b/Makefile index ff80cf9..66f162e 100644 --- a/Makefile +++ b/Makefile @@ -89,7 +89,7 @@ $(EXAMPLE_DIR)/%$(EXE): example/%.c sp.h | $(EXAMPLE_DIR) ######### # TESTS # ######### -CFLAGS_TEST = -DSP_IMPLEMENTATION -DSP_TEST_IMPLEMENTATION -I. -Itools -Itest/tools -Itest/tools/process -Itest/fs -Itest/mem +CFLAGS_TEST = -DSP_IMPLEMENTATION -DSP_TEST_IMPLEMENTATION -I. -Itools -Itest/tools -Itest/tools/process -Itest/fs -Itest/mem -Itest/io TEST_SOURCES = $(wildcard test/*/*.c) $(wildcard test/*/*.h) $(wildcard test/*/*/*.c) $(wildcard test/*/*/*.h) diff --git a/test/fs.c b/test/fs.c index 7eee7fe..4d2cbe3 100644 --- a/test/fs.c +++ b/test/fs.c @@ -1,7 +1,5 @@ -#ifndef SP_TEST_AMALGAMATION - #define SP_TEST_AMALGAMATION - #define FS_TEST_OWNS_MAIN -#endif +#include "fs/fs.h" +SP_TEST_MAIN() #include "fs/normalize_path.c" #include "fs/trim_path.c" @@ -28,7 +26,3 @@ #include "fs/system_paths.c" #include "fs/windows/wtf8.c" #include "fs/windows/nt_path.c" - -#ifdef FS_TEST_OWNS_MAIN -UTEST_MAIN() -#endif diff --git a/test/fs/canonicalize_path.c b/test/fs/canonicalize_path.c index c1bd02a..bfdec66 100644 --- a/test/fs/canonicalize_path.c +++ b/test/fs/canonicalize_path.c @@ -313,5 +313,3 @@ UTEST_F(fs, canon_cwd_matches_dot) { SP_EXPECT_STR_EQ(cwd, canonical_dot); ASSERT_EQ(sp_sys_chdir_s(old_cwd), 0); } - -SP_TEST_MAIN() diff --git a/test/fs/collect.c b/test/fs/collect.c index 93112e3..7fd76ed 100644 --- a/test/fs/collect.c +++ b/test/fs/collect.c @@ -327,5 +327,3 @@ UTEST_F(fs_collect, unicode_entries) { } }); } - -SP_TEST_MAIN() diff --git a/test/fs/copy.c b/test/fs/copy.c index 710663e..6e019ec 100644 --- a/test/fs/copy.c +++ b/test/fs/copy.c @@ -197,4 +197,4 @@ UTEST_F(fs, copy_preserves_file_attributes) { } #endif -SP_TEST_MAIN() + diff --git a/test/fs/copy_glob.c b/test/fs/copy_glob.c index a803881..8402150 100644 --- a/test/fs/copy_glob.c +++ b/test/fs/copy_glob.c @@ -75,4 +75,4 @@ UTEST_F(fs, copy_glob_empty_src) { }); } -SP_TEST_MAIN() + diff --git a/test/fs/create_dir.c b/test/fs/create_dir.c index b95269a..7374967 100644 --- a/test/fs/create_dir.c +++ b/test/fs/create_dir.c @@ -227,4 +227,4 @@ UTEST_F(fs_create_dir, destination_is_symlink_to_file) { } #endif -SP_TEST_MAIN() + diff --git a/test/fs/create_file.c b/test/fs/create_file.c index 07d1a30..49c9479 100644 --- a/test/fs/create_file.c +++ b/test/fs/create_file.c @@ -89,4 +89,4 @@ UTEST_F(fs, create_file_with_content) { } -SP_TEST_MAIN() + diff --git a/test/fs/get_cwd.c b/test/fs/get_cwd.c index cc09e90..d2c6271 100644 --- a/test/fs/get_cwd.c +++ b/test/fs/get_cwd.c @@ -27,4 +27,4 @@ UTEST(fs_get_cwd, is_absolute) { ASSERT_TRUE(is_absolute); } -SP_TEST_MAIN() + diff --git a/test/fs/get_exe_path.c b/test/fs/get_exe_path.c index f8f658a..79be645 100644 --- a/test/fs/get_exe_path.c +++ b/test/fs/get_exe_path.c @@ -51,4 +51,4 @@ UTEST(fs_get_exe_path, no_dotdot) { ASSERT_FALSE(sp_str_contains(exe, sp_str_lit(".."))); } -SP_TEST_MAIN() + diff --git a/test/fs/get_ext.c b/test/fs/get_ext.c index a2d2727..631b215 100644 --- a/test/fs/get_ext.c +++ b/test/fs/get_ext.c @@ -28,4 +28,4 @@ UTEST(fs_get_ext, cases) { } } -SP_TEST_MAIN() + diff --git a/test/fs/get_name.c b/test/fs/get_name.c index 3a53052..fddbc38 100644 --- a/test/fs/get_name.c +++ b/test/fs/get_name.c @@ -25,4 +25,4 @@ UTEST(fs_get_name, cases) { } } -SP_TEST_MAIN() + diff --git a/test/fs/get_stem.c b/test/fs/get_stem.c index 2935073..90a1a81 100644 --- a/test/fs/get_stem.c +++ b/test/fs/get_stem.c @@ -32,4 +32,4 @@ UTEST(fs_get_stem, cases) { } } -SP_TEST_MAIN() + diff --git a/test/fs/is_glob.c b/test/fs/is_glob.c index 2c30c8e..f9d56ef 100644 --- a/test/fs/is_glob.c +++ b/test/fs/is_glob.c @@ -24,4 +24,4 @@ UTEST(fs_is_glob, cases) { } } -SP_TEST_MAIN() + diff --git a/test/fs/is_root.c b/test/fs/is_root.c index 2e53975..e430325 100644 --- a/test/fs/is_root.c +++ b/test/fs/is_root.c @@ -26,4 +26,4 @@ UTEST(fs_is_root, cases) { } } -SP_TEST_MAIN() + diff --git a/test/fs/join_path.c b/test/fs/join_path.c index 1ec546d..f25d8c6 100644 --- a/test/fs/join_path.c +++ b/test/fs/join_path.c @@ -26,4 +26,4 @@ UTEST(fs_join_path, cases) { } } -SP_TEST_MAIN() + diff --git a/test/fs/links.c b/test/fs/links.c index 601037a..3068ba2 100644 --- a/test/fs/links.c +++ b/test/fs/links.c @@ -175,4 +175,4 @@ UTEST_F(fs, canonicalize_through_symlink) { SP_EXPECT_STR_EQ(canon_link, canon_real); } -SP_TEST_MAIN() + diff --git a/test/fs/mod_time.c b/test/fs/mod_time.c index 923c66a..30c9083 100644 --- a/test/fs/mod_time.c +++ b/test/fs/mod_time.c @@ -31,4 +31,4 @@ UTEST_F(fs, mod_time_updates_after_write) { ASSERT_TRUE(after.s > before.s || (after.s == before.s && after.ns > before.ns)); } -SP_TEST_MAIN() + diff --git a/test/fs/normalize_path.c b/test/fs/normalize_path.c index 677cfce..3c511aa 100644 --- a/test/fs/normalize_path.c +++ b/test/fs/normalize_path.c @@ -57,5 +57,3 @@ UTEST(fs_normalize_path, nonexistent_path) { "C:/no/such/path/file.txt" ); } - -SP_TEST_MAIN() diff --git a/test/fs/parent_path.c b/test/fs/parent_path.c index 44b2cff..ac15b4b 100644 --- a/test/fs/parent_path.c +++ b/test/fs/parent_path.c @@ -30,4 +30,4 @@ UTEST(fs_parent_path, cases) { } } -SP_TEST_MAIN() + diff --git a/test/fs/predicates.c b/test/fs/predicates.c index 60424e9..7f9bd6d 100644 --- a/test/fs/predicates.c +++ b/test/fs/predicates.c @@ -122,4 +122,4 @@ UTEST_F(fs, unicode_predicate_matrix) { }); } -SP_TEST_MAIN() + diff --git a/test/fs/remove.c b/test/fs/remove.c index d2392b4..0e201aa 100644 --- a/test/fs/remove.c +++ b/test/fs/remove.c @@ -111,4 +111,4 @@ UTEST_F(fs, unicode_remove_dir) { }); } -SP_TEST_MAIN() + diff --git a/test/fs/replace_ext.c b/test/fs/replace_ext.c index d9461b7..c147f76 100644 --- a/test/fs/replace_ext.c +++ b/test/fs/replace_ext.c @@ -24,4 +24,4 @@ UTEST(fs_replace_ext, cases) { } } -SP_TEST_MAIN() + diff --git a/test/fs/system_paths.c b/test/fs/system_paths.c index 5fa87b5..0cfa9f7 100644 --- a/test/fs/system_paths.c +++ b/test/fs/system_paths.c @@ -49,4 +49,4 @@ UTEST(fs_system_paths, config_path_exists) { ASSERT_TRUE(sp_fs_is_dir_a(path)); } -SP_TEST_MAIN() + diff --git a/test/fs/trim_path.c b/test/fs/trim_path.c index 6d88142..72aaf4e 100644 --- a/test/fs/trim_path.c +++ b/test/fs/trim_path.c @@ -29,4 +29,4 @@ UTEST(fs_trim_path, cases) { } } -SP_TEST_MAIN() + diff --git a/test/fs/windows/nt_path.c b/test/fs/windows/nt_path.c index 4b1d2d0..d23b367 100644 --- a/test/fs/windows/nt_path.c +++ b/test/fs/windows/nt_path.c @@ -212,4 +212,4 @@ UTEST(nt_path, skipped_non_windows) { #endif -SP_TEST_MAIN() + diff --git a/test/fs/windows/wtf8.c b/test/fs/windows/wtf8.c index 94a3bd9..6cacbc7 100644 --- a/test/fs/windows/wtf8.c +++ b/test/fs/windows/wtf8.c @@ -132,4 +132,4 @@ UTEST(wtf8, validate) { EXPECT_FALSE(sp_wtf8_validate(sp_str_lit("\xC3\x28"))); } -SP_TEST_MAIN() + diff --git a/test/io.c b/test/io.c index f49cc89..5bd958a 100644 --- a/test/io.c +++ b/test/io.c @@ -1,1277 +1,7 @@ -#include "sp.h" -#include "test.h" - -#include "utest.h" - +#include "io/io.h" SP_TEST_MAIN() - -struct io { - sp_str_t file_path; - sp_test_file_manager_t file_manager; - sp_mem_arena_t* arena; - sp_mem_t mem; -}; - -UTEST_F_SETUP(io) { - SKIP_ON_WASM() - ut.arena = sp_mem_arena_new(sp_mem_os_new()); - ut.mem = sp_mem_arena_as_allocator(ut.arena); - sp_test_file_manager_init(&ut.file_manager); - ut.file_path = sp_test_file_create_empty(&ut.file_manager, sp_str_lit("sp_io_rw.file")); -} - -UTEST_F_TEARDOWN(io) { - SKIP_ON_WASM() - sp_test_file_manager_cleanup(&ut.file_manager); - sp_mem_arena_destroy(ut.arena); -} - -UTEST_F(io, reader_mem_read_full) { - SKIP_ON_WASM() - u8 source[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; - u8 dest[16] = sp_zero; - - sp_io_reader_t io = sp_zero; - sp_io_reader_from_mem(&io, source, sizeof(source)); - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_OK); - - EXPECT_EQ(bytes, 16); - sp_for(i, 16) { - EXPECT_EQ(dest[i], source[i]); - } -} -UTEST_F(io, reader_mem_read_partial) { - SKIP_ON_WASM() - u8 source[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; - u8 dest[8] = sp_zero; - - sp_io_reader_t io = sp_zero; - sp_io_reader_from_mem(&io, source, sizeof(source)); - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_OK); - - EXPECT_EQ(bytes, 8); - sp_for(i, 8) { - EXPECT_EQ(dest[i], source[i]); - } -} -UTEST_F(io, reader_mem_read_past_end) { - SKIP_ON_WASM() - u8 source[8] = {1,2,3,4,5,6,7,8}; - u8 dest[16] = sp_zero; - - sp_io_reader_t io = sp_zero; - sp_io_reader_from_mem(&io, source, sizeof(source)); - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_OK); - - EXPECT_EQ(bytes, 8); - - bytes = 0; - EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_ERR_IO_EOF); - EXPECT_EQ(bytes, 0); -} -UTEST_F(io, reader_mem_read_eof_exact) { - SKIP_ON_WASM() - u8 source[8] = {1,2,3,4,5,6,7,8}; - u8 dest[8] = sp_zero; - - sp_io_reader_t io = sp_zero; - sp_io_reader_from_mem(&io, source, sizeof(source)); - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_OK); - EXPECT_EQ(bytes, 8); - - EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_ERR_IO_EOF); - EXPECT_EQ(bytes, 0); -} -UTEST_F(io, reader_mem_read_eof_empty) { - SKIP_ON_WASM() - u8 dest[4] = sp_zero; - - sp_io_reader_t io = sp_zero; - sp_io_reader_from_mem(&io, SP_NULLPTR, 0); - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_ERR_IO_EOF); - EXPECT_EQ(bytes, 0); -} -UTEST_F(io, reader_mem_seek) { - SKIP_ON_WASM() - u8 buffer[64] = sp_zero; - sp_for(i, 64) buffer[i] = (u8)i; - - sp_io_reader_t backing = sp_zero; - sp_io_seeking_reader_t io = sp_zero; - sp_io_seeking_reader_from_mem(&io, &backing, buffer, sizeof(buffer)); - - s64 pos = 0; - EXPECT_EQ(sp_io_seeking_reader_seek(&io, 32, SP_IO_SEEK_SET, &pos), SP_OK); - EXPECT_EQ(pos, 32); - - u8 val; - sp_io_read(io.reader, &val, 1, SP_NULLPTR); - EXPECT_EQ(val, 32); - - EXPECT_EQ(sp_io_seeking_reader_seek(&io, 0, SP_IO_SEEK_END, &pos), SP_OK); - EXPECT_EQ(pos, 64); -} -UTEST_F(io, reader_mem_seek_invalid) { - SKIP_ON_WASM() - u8 buffer[64] = sp_zero; - sp_io_reader_t backing = sp_zero; - sp_io_seeking_reader_t io = sp_zero; - sp_io_seeking_reader_from_mem(&io, &backing, buffer, sizeof(buffer)); - - s64 pos = 0; - EXPECT_EQ(sp_io_seeking_reader_seek(&io, 100, SP_IO_SEEK_SET, &pos), SP_ERR_IO_SEEK_INVALID); - EXPECT_EQ(pos, -1); - - EXPECT_EQ(sp_io_seeking_reader_seek(&io, -10, SP_IO_SEEK_SET, &pos), SP_ERR_IO_SEEK_INVALID); - EXPECT_EQ(pos, -1); -} -UTEST_F(io, seeking_reader_file_seek) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - sp_io_file_reader_t fr = sp_zero; - sp_io_file_reader_from_path(&fr, ut.file_path); - - sp_io_seeking_reader_t sr = sp_zero; - sp_io_seeking_reader_from_file_reader(&sr, &fr); - - s64 pos = 0; - EXPECT_EQ(sp_io_seeking_reader_seek(&sr, 5, SP_IO_SEEK_SET, &pos), SP_OK); - EXPECT_EQ(pos, 5); - - char buffer[5] = {0}; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(sr.reader, buffer, 5, &bytes), SP_OK); - EXPECT_EQ(bytes, 5); - EXPECT_EQ(buffer[0], '5'); - EXPECT_EQ(buffer[4], '9'); - - sp_io_file_reader_close(&fr); -} -UTEST_F(io, seeking_reader_file_seek_whence) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - sp_io_file_reader_t fr = sp_zero; - sp_io_file_reader_from_path(&fr, ut.file_path); - - sp_io_seeking_reader_t sr = sp_zero; - sp_io_seeking_reader_from_file_reader(&sr, &fr); - - s64 pos = 0; - EXPECT_EQ(sp_io_seeking_reader_seek(&sr, 4, SP_IO_SEEK_SET, &pos), SP_OK); - EXPECT_EQ(pos, 4); - - EXPECT_EQ(sp_io_seeking_reader_seek(&sr, 3, SP_IO_SEEK_CUR, &pos), SP_OK); - EXPECT_EQ(pos, 7); - - char c = 0; - sp_io_read(sr.reader, &c, 1, SP_NULLPTR); - EXPECT_EQ(c, '7'); - - EXPECT_EQ(sp_io_seeking_reader_seek(&sr, 0, SP_IO_SEEK_END, &pos), SP_OK); - EXPECT_EQ(pos, 16); - - EXPECT_EQ(sp_io_seeking_reader_seek(&sr, -2, SP_IO_SEEK_END, &pos), SP_OK); - EXPECT_EQ(pos, 14); - sp_io_read(sr.reader, &c, 1, SP_NULLPTR); - EXPECT_EQ(c, 'E'); - - sp_io_file_reader_close(&fr); -} -UTEST_F(io, seeking_reader_file_seek_invalid) { - SKIP_ON_WASM() - const char* content = "0123456789"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 10, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - sp_io_file_reader_t fr = sp_zero; - sp_io_file_reader_from_path(&fr, ut.file_path); - - sp_io_seeking_reader_t sr = sp_zero; - sp_io_seeking_reader_from_file_reader(&sr, &fr); - - s64 pos = 0; - EXPECT_EQ(sp_io_seeking_reader_seek(&sr, -10, SP_IO_SEEK_SET, &pos), SP_ERR_IO_SEEK_FAILED); - - sp_io_file_reader_close(&fr); -} -UTEST_F(io, seeking_reader_file_seek_buffered) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - u8 read_buf[8]; - sp_io_file_reader_t fr = sp_zero; - sp_io_file_reader_from_path(&fr, ut.file_path); - sp_io_reader_set_buffer(&fr.base, read_buf, sizeof(read_buf)); - - sp_io_seeking_reader_t sr = sp_zero; - sp_io_seeking_reader_from_file_reader(&sr, &fr); - - char c = 0; - sp_io_read(sr.reader, &c, 1, SP_NULLPTR); - EXPECT_EQ(c, '0'); - - s64 pos = 0; - EXPECT_EQ(sp_io_seeking_reader_seek(&sr, 10, SP_IO_SEEK_SET, &pos), SP_OK); - EXPECT_EQ(pos, 10); - - sp_io_read(sr.reader, &c, 1, SP_NULLPTR); - EXPECT_EQ(c, 'A'); - - sp_io_file_reader_close(&fr); -} -UTEST_F(io, reader_file_read) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - sp_io_file_reader_t r = sp_zero; - EXPECT_EQ(sp_io_file_reader_from_path(&r, ut.file_path), SP_OK); - - char buffer[16] = {0}; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, buffer, 16, &bytes), SP_OK); - EXPECT_EQ(bytes, 16); - sp_for(i, 16) { - EXPECT_EQ(buffer[i], content[i]); - } - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_file_seek) { - SKIP_ON_WASM() - const char* content = "0123456789"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 10, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - s64 pos = 0; - EXPECT_EQ(sp_io_file_reader_seek(&r, 5, SP_IO_SEEK_SET, &pos), SP_OK); - EXPECT_EQ(pos, 5); - - char buffer[5] = {0}; - sp_io_read(&r.base, buffer, 5, SP_NULLPTR); - EXPECT_EQ(buffer[0], '5'); - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_file_nonexistent) { - SKIP_ON_WASM() - sp_str_t path = sp_test_file_path(&ut.file_manager, sp_str_lit("nonexistent.file")); - sp_io_file_reader_t r = sp_zero; - EXPECT_EQ(sp_io_file_reader_from_path(&r, path), SP_ERR_IO_OPEN_FAILED); - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_file_read_eof_after_drain) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - - char buffer[16] = {0}; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, buffer, 16, &bytes), SP_OK); - EXPECT_EQ(bytes, 16); - - bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, buffer, 16, &bytes), SP_ERR_IO_EOF); - EXPECT_EQ(bytes, 0); - - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_file_read_eof_short) { - SKIP_ON_WASM() - const char* content = "short"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 5, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - - char buffer[32] = {0}; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, buffer, sizeof(buffer), &bytes), SP_OK); - EXPECT_EQ(bytes, 5); - - bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, buffer, sizeof(buffer), &bytes), SP_ERR_IO_EOF); - EXPECT_EQ(bytes, 0); - - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_file_read_eof_empty) { - SKIP_ON_WASM() - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - - char buffer[16] = {0}; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, buffer, sizeof(buffer), &bytes), SP_ERR_IO_EOF); - EXPECT_EQ(bytes, 0); - - sp_io_file_reader_close(&r); -} -UTEST_F(io, writer_mem_write) { - SKIP_ON_WASM() - u8 buffer[16] = sp_zero; - u8 source[8] = {1,2,3,4,5,6,7,8}; - - sp_io_mem_writer_t w; sp_io_mem_writer_from_buffer(&w,buffer, sizeof(buffer)); - u64 bytes = 0; - EXPECT_EQ(sp_io_write(&w.base, source, sizeof(source), &bytes), SP_OK); - - EXPECT_EQ(bytes, 8); - sp_for(i, 8) { - EXPECT_EQ(buffer[i], source[i]); - } - /* mem writer has no close */; -} -UTEST_F(io, writer_mem_write_overflow) { - SKIP_ON_WASM() - u8 buffer[8] = sp_zero; - u8 source[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; - - sp_io_mem_writer_t w; sp_io_mem_writer_from_buffer(&w,buffer, sizeof(buffer)); - u64 bytes = 0; - EXPECT_EQ(sp_io_write(&w.base, source, sizeof(source), &bytes), SP_ERR_IO_NO_SPACE); - - EXPECT_EQ(bytes, 0); - /* mem writer has no close */; -} -UTEST_F(io, writer_mem_seek) { - SKIP_ON_WASM() - u8 buffer[64] = sp_zero; - sp_io_mem_writer_t w; sp_io_mem_writer_from_buffer(&w,buffer, sizeof(buffer)); - - s64 pos = 0; - EXPECT_EQ(sp_io_mem_writer_seek(&w, 32, SP_IO_SEEK_SET, &pos), SP_OK); - EXPECT_EQ(pos, 32); - - EXPECT_EQ(sp_io_mem_writer_seek(&w, 0, SP_IO_SEEK_END, &pos), SP_OK); - EXPECT_EQ(pos, 64); - - /* mem writer has no close */; -} -UTEST_F(io, writer_mem_size) { - SKIP_ON_WASM() - u8 buffer[128]; - sp_io_mem_writer_t w; sp_io_mem_writer_from_buffer(&w,buffer, sizeof(buffer)); - u64 size = 0; - EXPECT_EQ(sp_io_mem_writer_size(&w, &size), SP_OK); - EXPECT_EQ(size, 128); - /* mem writer has no close */; -} -UTEST_F(io, writer_file_write) { - SKIP_ON_WASM() - const char* content = "test data"; - sp_io_file_writer_t w = sp_zero; - EXPECT_EQ(sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE), SP_OK); - - u64 bytes = 0; - EXPECT_EQ(sp_io_write(&w.base, content, 9, &bytes), SP_OK); - EXPECT_EQ(bytes, 9); - sp_io_file_writer_close(&w); - - sp_str_t loaded = sp_zero; - sp_io_read_file_a(ut.mem, ut.file_path, &loaded); - EXPECT_EQ(loaded.len, 9); -} -UTEST_F(io, writer_file_overwrite) { - SKIP_ON_WASM() - const char* first = "XXXXXXXX"; - const char* second = "1234"; - - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, first, 8, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, second, 4, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - sp_str_t loaded = sp_zero; - sp_io_read_file_a(ut.mem, ut.file_path, &loaded); - EXPECT_EQ(loaded.len, 4); - EXPECT_EQ(loaded.data[0], '1'); - EXPECT_EQ(loaded.data[3], '4'); -} -UTEST_F(io, writer_file_append) { - SKIP_ON_WASM() - const char* first = "first"; - const char* second = "second"; - - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, first, 5, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_APPEND); - sp_io_write(&w.base, second, 6, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - sp_str_t loaded = sp_zero; - sp_io_read_file_a(ut.mem, ut.file_path, &loaded); - EXPECT_EQ(loaded.len, 11); - EXPECT_TRUE(sp_str_equal(loaded, sp_str_lit("firstsecond"))); -} -UTEST_F(io, writer_file_seek) { - SKIP_ON_WASM() - const char* content = "0123456789"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 10, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - s64 pos = 0; - EXPECT_EQ(sp_io_file_reader_seek(&r, 5, SP_IO_SEEK_SET, &pos), SP_OK); - EXPECT_EQ(pos, 5); - - char buffer[5] = {0}; - sp_io_read(&r.base, buffer, 5, SP_NULLPTR); - EXPECT_EQ(buffer[0], '5'); - sp_io_file_reader_close(&r); -} -UTEST_F(io, writer_file_size) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - u64 size = 0; - EXPECT_EQ(sp_io_file_writer_size(&w, &size), SP_OK); - EXPECT_EQ(size, 16); - sp_io_file_writer_close(&w); -} -UTEST_F(io, writer_file_pad) { - SKIP_ON_WASM() - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, "AA", 2, SP_NULLPTR); - sp_io_pad(&w.base, 3, SP_NULLPTR); - sp_io_write(&w.base, "BB", 2, SP_NULLPTR); - sp_io_file_writer_close(&w); - - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - u8 result[7] = sp_zero; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, result, 7, &bytes), SP_OK); - EXPECT_EQ(bytes, 7); - EXPECT_EQ(result[0], 'A'); - EXPECT_EQ(result[1], 'A'); - EXPECT_EQ(result[2], 0); - EXPECT_EQ(result[3], 0); - EXPECT_EQ(result[4], 0); - EXPECT_EQ(result[5], 'B'); - EXPECT_EQ(result[6], 'B'); - sp_io_file_reader_close(&r); -} -UTEST_F(io, writer_dyn_write) { - SKIP_ON_WASM() - sp_io_dyn_mem_writer_t w; sp_io_dyn_mem_writer_init_a(ut.mem, &w); - u8 data[] = {1, 2, 3, 4}; - u64 written = 0; - EXPECT_EQ(sp_io_write(&w.base, data, 4, &written), SP_OK); - - EXPECT_EQ(written, 4); - u64 size = 0; - sp_io_dyn_mem_writer_size(&w, &size); - EXPECT_EQ(size, 4); - sp_io_dyn_mem_writer_close(&w); -} -UTEST_F(io, writer_dyn_grows) { - SKIP_ON_WASM() - sp_io_dyn_mem_writer_t w; sp_io_dyn_mem_writer_init_a(ut.mem, &w); - - u8 data[256]; - sp_for(i, 256) data[i] = (u8)i; - - u64 written = 0; - EXPECT_EQ(sp_io_write(&w.base, data, 256, &written), SP_OK); - EXPECT_EQ(written, 256); - u64 size = 0; - sp_io_dyn_mem_writer_size(&w, &size); - EXPECT_EQ(size, 256); - - sp_io_dyn_mem_writer_close(&w); -} -UTEST_F(io, writer_dyn_to_str) { - SKIP_ON_WASM() - sp_io_dyn_mem_writer_t w; sp_io_dyn_mem_writer_init_a(ut.mem, &w); - - const char* text = "hello world"; - sp_io_write(&w.base, text, 11, SP_NULLPTR); - - sp_str_t str = sp_mem_buffer_as_str(&w.storage); - EXPECT_EQ(str.len, 11); - EXPECT_TRUE(sp_str_equal(str, sp_str_lit("hello world"))); - - sp_io_dyn_mem_writer_close(&w); -} -UTEST_F(io, writer_dyn_seek) { - SKIP_ON_WASM() - sp_io_dyn_mem_writer_t w; sp_io_dyn_mem_writer_init_a(ut.mem, &w); - - u8 data[] = {1, 2, 3, 4, 5, 6, 7, 8}; - sp_io_write(&w.base, data, 8, SP_NULLPTR); - - s64 pos = 0; - EXPECT_EQ(sp_io_dyn_mem_writer_seek(&w, 4, SP_IO_SEEK_SET, &pos), SP_OK); - EXPECT_EQ(pos, 4); - - sp_io_dyn_mem_writer_close(&w); -} -UTEST_F(io, writer_dyn_multiple_writes) { - SKIP_ON_WASM() - sp_io_dyn_mem_writer_t w; sp_io_dyn_mem_writer_init_a(ut.mem, &w); - - sp_io_write(&w.base, "abc", 3, SP_NULLPTR); - sp_io_write(&w.base, "def", 3, SP_NULLPTR); - sp_io_write(&w.base, "ghi", 3, SP_NULLPTR); - - u64 size = 0; - sp_io_dyn_mem_writer_size(&w, &size); - EXPECT_EQ(size, 9); - - sp_str_t str = sp_mem_buffer_as_str(&w.storage); - EXPECT_TRUE(sp_str_equal(str, sp_str_lit("abcdefghi"))); - - sp_io_dyn_mem_writer_close(&w); -} -UTEST_F(io, writer_buffered_1000_bytes) { - SKIP_ON_WASM() - u8 write_buf[64]; - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_writer_set_buffer(&w.base, write_buf, sizeof(write_buf)); - - sp_for(i, 1000) { - u8 byte = (u8)(i & 0xFF); - u64 bytes = 0; - EXPECT_EQ(sp_io_write(&w.base, &byte, 1, &bytes), SP_OK); - EXPECT_EQ(bytes, 1); - } - sp_io_file_writer_close(&w); - - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - s64 end = sp_sys_lseek(r.file, 0, SP_IO_SEEK_END); - sp_sys_lseek(r.file, 0, SP_IO_SEEK_SET); - EXPECT_EQ((u64)end, 1000); - - sp_for(i, 1000) { - u8 byte; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, &byte, 1, &bytes), SP_OK); - EXPECT_EQ(bytes, 1); - EXPECT_EQ(byte, (u8)(i & 0xFF)); - } - sp_io_file_reader_close(&r); -} -UTEST_F(io, writer_buffered_larger_than_buffer) { - SKIP_ON_WASM() - u8 write_buf[32]; - u8 data[128]; - sp_for(i, 128) data[i] = (u8)i; - - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_writer_set_buffer(&w.base, write_buf, sizeof(write_buf)); - - u64 bytes = 0; - EXPECT_EQ(sp_io_write(&w.base, data, 128, &bytes), SP_OK); - EXPECT_EQ(bytes, 128); - sp_io_file_writer_close(&w); - - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - u8 result[128] = sp_zero; - u64 read_bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, result, 128, &read_bytes), SP_OK); - EXPECT_EQ(read_bytes, 128); - sp_for(i, 128) { - EXPECT_EQ(result[i], data[i]); - } - sp_io_file_reader_close(&r); -} -UTEST_F(io, writer_buffered_implicit_flush) { - SKIP_ON_WASM() - u8 write_buf[64]; - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_writer_set_buffer(&w.base, write_buf, sizeof(write_buf)); - - sp_io_write(&w.base, "hello", 5, SP_NULLPTR); - sp_io_file_writer_close(&w); - - sp_str_t loaded = sp_zero; - sp_io_read_file_a(ut.mem, ut.file_path, &loaded); - EXPECT_EQ(loaded.len, 5); - EXPECT_TRUE(sp_str_equal(loaded, sp_str_lit("hello"))); -} -UTEST_F(io, writer_buffered_flush_empty) { - SKIP_ON_WASM() - u8 write_buf[64]; - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_writer_set_buffer(&w.base, write_buf, sizeof(write_buf)); - - sp_err_t err = sp_io_flush(&w.base); - EXPECT_EQ(err, SP_OK); - sp_io_file_writer_close(&w); -} -UTEST_F(io, writer_buffered_set_twice) { - SKIP_ON_WASM() - u8 write_buf1[64]; - u8 write_buf2[64]; - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_writer_set_buffer(&w.base, write_buf1, sizeof(write_buf1)); - - sp_io_write(&w.base, "first", 5, SP_NULLPTR); - sp_io_writer_set_buffer(&w.base, write_buf2, sizeof(write_buf2)); - sp_io_write(&w.base, "second", 6, SP_NULLPTR); - sp_io_file_writer_close(&w); - - sp_str_t loaded = sp_zero; - sp_io_read_file_a(ut.mem, ut.file_path, &loaded); - EXPECT_EQ(loaded.len, 11); - EXPECT_TRUE(sp_str_equal(loaded, sp_str_lit("firstsecond"))); -} -UTEST_F(io, reader_buffered_read) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - u8 read_buf[8]; - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); - - char result[16] = {0}; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, result, 16, &bytes), SP_OK); - EXPECT_EQ(bytes, 16); - sp_for(i, 16) { - EXPECT_EQ(result[i], content[i]); - } - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_buffered_small_reads) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - u8 read_buf[8]; - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); - - sp_for(i, 16) { - char c; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, &c, 1, &bytes), SP_OK); - EXPECT_EQ(bytes, 1); - EXPECT_EQ(c, content[i]); - } - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_buffered_eof_exact) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - u8 read_buf[8]; - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); - - char result[16] = {0}; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, result, 16, &bytes), SP_OK); - EXPECT_EQ(bytes, 16); - - bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, result, 1, &bytes), SP_ERR_IO_EOF); - EXPECT_EQ(bytes, 0); - - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_buffered_eof_partial) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - u8 read_buf[8]; - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); - - char result[32] = {0}; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, result, sizeof(result), &bytes), SP_OK); - EXPECT_EQ(bytes, 16); - - bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, result, sizeof(result), &bytes), SP_ERR_IO_EOF); - EXPECT_EQ(bytes, 0); - - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_buffered_fill_preserves_drained) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - u8 read_buf[8]; - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); - - char first[4] = {0}; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, first, 4, &bytes), SP_OK); - EXPECT_EQ(bytes, 4); - - char second[7] = {0}; - bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, second, 7, &bytes), SP_OK); - EXPECT_EQ(bytes, 7); - EXPECT_EQ(second[0], '4'); - EXPECT_EQ(second[1], '5'); - EXPECT_EQ(second[2], '6'); - EXPECT_EQ(second[3], '7'); - EXPECT_EQ(second[4], '8'); - EXPECT_EQ(second[5], '9'); - EXPECT_EQ(second[6], 'A'); - - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_buffered_eof_direct_path) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - u8 read_buf[4]; - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); - - char prime; - EXPECT_EQ(sp_io_read(&r.base, &prime, 1, SP_NULLPTR), SP_OK); - EXPECT_EQ(prime, '0'); - - char result[32] = {0}; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, result, sizeof(result), &bytes), SP_OK); - EXPECT_EQ(bytes, 15); - - bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, result, sizeof(result), &bytes), SP_ERR_IO_EOF); - EXPECT_EQ(bytes, 0); - - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_buffered_zero_size) { - SKIP_ON_WASM() - u8 source[1] = {0}; - u8 read_buf[8]; - sp_io_reader_t io = sp_zero; - sp_io_reader_from_mem(&io, source, sizeof(source)); - sp_io_reader_set_buffer(&io, read_buf, sizeof(read_buf)); - - u64 bytes = 1; - EXPECT_EQ(sp_io_read(&io, source, 0, &bytes), SP_OK); - EXPECT_EQ(bytes, 0); -} -UTEST_F(io, reader_buffered_eof_byte_by_byte) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - u8 read_buf[8]; - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); - - sp_for(i, 16) { - char c; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, &c, 1, &bytes), SP_OK); - EXPECT_EQ(bytes, 1); - EXPECT_EQ(c, content[i]); - } - - char c; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, &c, 1, &bytes), SP_ERR_IO_EOF); - EXPECT_EQ(bytes, 0); - - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_buffered_seek_discards_buffer) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - u8 read_buf[8]; - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); - - char c; - sp_io_read(&r.base, &c, 1, SP_NULLPTR); - EXPECT_EQ(c, '0'); - - sp_io_file_reader_seek(&r, 10, SP_IO_SEEK_SET, SP_NULLPTR); - sp_io_read(&r.base, &c, 1, SP_NULLPTR); - EXPECT_EQ(c, 'A'); - - sp_io_file_reader_close(&r); -} - -UTEST_F(io, seek_beyond_4gb) { - SKIP_ON_WASM() - s64 offset = (s64)5 * 1024 * 1024 * 1024; - u8 marker[4] = {0xDE, 0xAD, 0xBE, 0xEF}; - - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - s64 seek_pos = 0; - EXPECT_EQ(sp_io_file_writer_seek(&w, offset, SP_IO_SEEK_SET, &seek_pos), SP_OK); - EXPECT_EQ(seek_pos, offset); - sp_io_write(&w.base, marker, 4, SP_NULLPTR); - sp_io_file_writer_close(&w); - - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - s64 end = sp_sys_lseek(r.file, 0, SP_IO_SEEK_END); - sp_sys_lseek(r.file, 0, SP_IO_SEEK_SET); - EXPECT_EQ((u64)end, (u64)offset + 4); - - s64 read_pos = 0; - EXPECT_EQ(sp_io_file_reader_seek(&r, offset, SP_IO_SEEK_SET, &read_pos), SP_OK); - EXPECT_EQ(read_pos, offset); - - u8 result[4] = sp_zero; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, result, 4, &bytes), SP_OK); - EXPECT_EQ(bytes, 4); - EXPECT_EQ(result[0], 0xDE); - EXPECT_EQ(result[1], 0xAD); - EXPECT_EQ(result[2], 0xBE); - EXPECT_EQ(result[3], 0xEF); - sp_io_file_reader_close(&r); -} - -// UTEST_F(io, copy_playground) { -// struct { -// u8 a [4096]; -// u8 b [4096]; -// u8 c [4096]; -// } buf = sp_zero; -// struct { -// sp_io_reader_t r; -// sp_io_writer_t w; -// } io = sp_zero; -// -// sp_for(it, 4096) { -// buf.a[it] = it; -// } -// -// sp_io_reader_from_mem(&io.r, buf.a, sizeof(buf.a)); -// sp_io_writer_set_buffer(&io.w, buf.b, sizeof(buf.b)); -// -// u64 n = 0; -// sp_io_copy_b2(&io.w, &io.r, buf.c, sizeof(buf.c), &n); -// EXPECT_EQ(n, 4096); -// } - - -#define IO_MAX_STEPS 8 - -typedef enum { - IO_STEP_NONE, - IO_STEP_READ, - IO_STEP_WRITE, - IO_STEP_FLUSH, -} io_step_kind_t; - -typedef struct { - io_step_kind_t kind; - union { - struct { u64 request; sp_err_t err; const c8* content; } read; - struct { const c8* data; sp_err_t err; u64 bytes; } write; - struct { sp_err_t err; } flush; - }; -} io_step_t; - - -////////// -// READ // -////////// -typedef struct { - const c8* source; - io_step_t steps [IO_MAX_STEPS]; -} io_read_test_t; - -void run_io_read_test(int* utest_result, io_read_test_t t) { - sp_io_reader_t r = sp_zero; - sp_io_reader_from_mem(&r, t.source, sp_cstr_len(t.source)); - - sp_carr_for(t.steps, j) { - const io_step_t* step = &t.steps[j]; - if (step->kind == IO_STEP_NONE) break; - - u8 dest [64] = sp_zero; - u64 bytes = 0; - sp_err_t err = sp_io_read(&r, dest, step->read.request, &bytes); - EXPECT_EQ(err, step->read.err); - - u64 expect_bytes = sp_cstr_len(step->read.content); - EXPECT_EQ(bytes, expect_bytes); - sp_for(it, expect_bytes) { - EXPECT_EQ((c8)dest[it], step->read.content[it]); - } - } -} - -UTEST_F(io, read_exact_then_eof) { - run_io_read_test(utest_result, (io_read_test_t){ - .source = "0123", - .steps = { - { .kind = IO_STEP_READ, .read = { 4, SP_OK, "0123" } }, - { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, - }, - }); -} - -UTEST_F(io, read_chunked) { - run_io_read_test(utest_result, (io_read_test_t){ - .source = "01234567", - .steps = { - { .kind = IO_STEP_READ, .read = { 3, SP_OK, "012" } }, - { .kind = IO_STEP_READ, .read = { 3, SP_OK, "345" } }, - { .kind = IO_STEP_READ, .read = { 3, SP_OK, "67" } }, - { .kind = IO_STEP_READ, .read = { 3, SP_ERR_IO_EOF } }, - }, - }); -} - -UTEST_F(io, read_oversized_request) { - run_io_read_test(utest_result, (io_read_test_t){ - .source = "abc", - .steps = { - { .kind = IO_STEP_READ, .read = { 16, SP_OK, "abc" } }, - { .kind = IO_STEP_READ, .read = { 16, SP_ERR_IO_EOF } }, - }, - }); -} - -UTEST_F(io, read_empty_source) { - run_io_read_test(utest_result, (io_read_test_t){ - .source = "", - .steps = { - { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, - }, - }); -} - -UTEST_F(io, read_eof_idempotent) { - run_io_read_test(utest_result, (io_read_test_t){ - .source = "x", - .steps = { - { .kind = IO_STEP_READ, .read = { 4, SP_OK, "x" } }, - { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, - { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, - }, - }); -} - -UTEST_F(io, read_zero_request) { - run_io_read_test(utest_result, (io_read_test_t){ - .source = "abc", - .steps = { - { .kind = IO_STEP_READ, .read = { 0, SP_OK } }, - { .kind = IO_STEP_READ, .read = { 3, SP_OK, "abc" } }, - }, - }); -} - - -/////////// -// WRITE // -/////////// -typedef struct { - u64 capacity; - io_step_t steps [IO_MAX_STEPS]; - struct { - const c8* content; - } expect; -} io_write_test_t; - -void run_io_write_test(int* utest_result, io_write_test_t t) { - u8 backing [64] = sp_zero; - sp_io_mem_writer_t w = sp_zero; - sp_io_mem_writer_from_buffer(&w, backing, t.capacity); - - sp_carr_for(t.steps, j) { - const io_step_t* step = &t.steps[j]; - if (step->kind == IO_STEP_NONE) break; - - switch (step->kind) { - case IO_STEP_WRITE: { - u64 bytes = 0; - sp_err_t err = sp_io_write(&w.base, step->write.data, sp_cstr_len(step->write.data), &bytes); - EXPECT_EQ(err, step->write.err); - EXPECT_EQ(bytes, step->write.bytes); - break; - } - case IO_STEP_FLUSH: { - sp_err_t err = sp_io_flush(&w.base); - EXPECT_EQ(err, step->flush.err); - break; - } - default: { - sp_unreachable_case(); - } - } - } - - u64 n = sp_cstr_len(t.expect.content); - sp_for(it, n) { - EXPECT_EQ((c8)backing[it], t.expect.content[it]); - } -} - -UTEST_F(io, write_fits) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 16, - .steps = { - { .kind = IO_STEP_WRITE, .write = { "hello", SP_OK, 5 } }, - }, - .expect.content = "hello", - }); -} - -UTEST_F(io, write_exact_fit) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 4, - .steps = { - { .kind = IO_STEP_WRITE, .write = { "abcd", SP_OK, 4 } }, - }, - .expect.content = "abcd", - }); -} - -UTEST_F(io, write_overflow) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 4, - .steps = { - { .kind = IO_STEP_WRITE, .write = { "abcdefgh", SP_ERR_IO_NO_SPACE, 0 } }, - }, - }); -} - -UTEST_F(io, write_appends) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 16, - .steps = { - { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, - { .kind = IO_STEP_WRITE, .write = { "def", SP_OK, 3 } }, - { .kind = IO_STEP_WRITE, .write = { "ghi", SP_OK, 3 } }, - }, - .expect.content = "abcdefghi", - }); -} - -UTEST_F(io, write_zero) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 8, - .steps = { - { .kind = IO_STEP_WRITE, .write = { "", SP_OK, 0 } }, - }, - }); -} - -UTEST_F(io, write_flush_empty) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 8, - .steps = { - { .kind = IO_STEP_FLUSH, .flush = { SP_OK } }, - }, - }); -} - - -////////// -// COPY // -////////// -typedef struct { - const c8* source; - u64 capacity; - u64 buffer; - struct { - sp_err_t err; - const c8* final; - } expect; -} io_copy_test_t; - -void run_io_copy_test(int* utest_result, io_copy_test_t t) { - sp_io_reader_t r = sp_zero; - sp_io_reader_from_mem(&r, t.source, sp_cstr_len(t.source)); - - u8 backing [64] = sp_zero; - sp_io_mem_writer_t w = sp_zero; - sp_io_mem_writer_from_buffer(&w, backing, t.capacity); - - u8 copy_buf [64] = sp_zero; - u64 copied = 0; - sp_err_t err = sp_io_copy_b(&w.base, &r, copy_buf, t.buffer, &copied); - - EXPECT_EQ(err, t.expect.err); - u64 n = sp_cstr_len(t.expect.final); - EXPECT_EQ(copied, n); - sp_for(it, n) { - EXPECT_EQ((c8)backing[it], t.expect.final[it]); - } -} - -UTEST_F(io, copy_full) { - run_io_copy_test(utest_result, (io_copy_test_t){ - .source = "0123456789", - .capacity = 32, .buffer = 8, - .expect = { .err = SP_OK, .final = "0123456789" }, - }); -} - -UTEST_F(io, copy_loops) { - run_io_copy_test(utest_result, (io_copy_test_t){ - .source = "ABCDEFGHIJKLMNOP", - .capacity = 32, .buffer = 4, - .expect = { .err = SP_OK, .final = "ABCDEFGHIJKLMNOP" }, - }); -} - -UTEST_F(io, copy_empty) { - run_io_copy_test(utest_result, (io_copy_test_t){ - .source = "", - .capacity = 8, .buffer = 8, - .expect = { .err = SP_OK }, - }); -} - -// TODO: this case exposes the sp_io_copy_b bug: today it returns bytes_copied=0 -// because the write fails before `total += chunk` runs, and the mem writer's -// all-or-nothing semantics drop the partial. Uncomment after fixing. -// -// UTEST_F(io, copy_writer_no_space) { -// run_io_copy_test(utest_result, (io_copy_test_t){ -// .source = "0123456789", -// .capacity = 4, .buffer = 8, -// .expect = { .err = SP_ERR_IO_NO_SPACE, .final = "0123" }, -// }); -// } +#include "io/read.c" +#include "io/write.c" +#include "io/copy.c" +#include "io/loose.c" diff --git a/test/io/copy.c b/test/io/copy.c new file mode 100644 index 0000000..8bcddf8 --- /dev/null +++ b/test/io/copy.c @@ -0,0 +1,67 @@ +#include "io.h" + +typedef struct { + const c8* source; + u64 capacity; + u64 buffer; + struct { + sp_err_t err; + const c8* final; + } expect; +} io_copy_test_t; + +void run_io_copy_test(int* utest_result, io_copy_test_t t) { + sp_io_reader_t r = sp_zero; + sp_io_reader_from_mem(&r, t.source, sp_cstr_len(t.source)); + + u8 backing [64] = sp_zero; + sp_io_mem_writer_t w = sp_zero; + sp_io_mem_writer_from_buffer(&w, backing, t.capacity); + + u8 copy_buf [64] = sp_zero; + u64 copied = 0; + sp_err_t err = sp_io_copy_b(&w.base, &r, copy_buf, t.buffer, &copied); + + EXPECT_EQ(err, t.expect.err); + u64 n = sp_cstr_len(t.expect.final); + EXPECT_EQ(copied, n); + sp_for(it, n) { + EXPECT_EQ((c8)backing[it], t.expect.final[it]); + } +} + +UTEST_F(io, copy_full) { + run_io_copy_test(utest_result, (io_copy_test_t){ + .source = "0123456789", + .capacity = 32, .buffer = 8, + .expect = { .err = SP_OK, .final = "0123456789" }, + }); +} + +UTEST_F(io, copy_loops) { + run_io_copy_test(utest_result, (io_copy_test_t){ + .source = "ABCDEFGHIJKLMNOP", + .capacity = 32, .buffer = 4, + .expect = { .err = SP_OK, .final = "ABCDEFGHIJKLMNOP" }, + }); +} + +UTEST_F(io, copy_empty) { + run_io_copy_test(utest_result, (io_copy_test_t){ + .source = "", + .capacity = 8, .buffer = 8, + .expect = { .err = SP_OK }, + }); +} + +// TODO: this case exposes the sp_io_copy_b bug: today it returns bytes_copied=0 +// because the write fails before `total += chunk` runs, and the mem writer's +// all-or-nothing semantics drop the partial. Uncomment after fixing. +// +// UTEST_F(io, copy_writer_no_space) { +// run_io_copy_test(utest_result, (io_copy_test_t){ +// .source = "0123456789", +// .capacity = 4, .buffer = 8, +// .expect = { .err = SP_ERR_IO_NO_SPACE, .final = "0123" }, +// }); +// } diff --git a/test/io/io.h b/test/io/io.h new file mode 100644 index 0000000..52a79d2 --- /dev/null +++ b/test/io/io.h @@ -0,0 +1,55 @@ +#ifndef IO_TEST_H +#define IO_TEST_H + +#include "sp.h" +#include "test.h" +#include "utest.h" + + +////////////////// +// io FIXTURE // +////////////////// +struct io { + sp_str_t file_path; + sp_test_file_manager_t file_manager; + sp_mem_arena_t* arena; + sp_mem_t mem; +}; + +UTEST_F_SETUP(io) { + SKIP_ON_WASM() + ut.arena = sp_mem_arena_new(sp_mem_os_new()); + ut.mem = sp_mem_arena_as_allocator(ut.arena); + sp_test_file_manager_init(&ut.file_manager); + ut.file_path = sp_test_file_create_empty(&ut.file_manager, sp_str_lit("sp_io_rw.file")); +} + +UTEST_F_TEARDOWN(io) { + SKIP_ON_WASM() + sp_test_file_manager_cleanup(&ut.file_manager); + sp_mem_arena_destroy(ut.arena); +} + + +////////////////////// +// SHARED MATRIX // +////////////////////// +#define IO_MAX_STEPS 8 + +typedef enum { + IO_STEP_NONE, + IO_STEP_READ, + IO_STEP_WRITE, + IO_STEP_FLUSH, +} io_step_kind_t; + +typedef struct { + io_step_kind_t kind; + union { + struct { u64 request; sp_err_t err; const c8* content; } read; + struct { const c8* data; sp_err_t err; u64 bytes; } write; + struct { sp_err_t err; } flush; + }; +} io_step_t; + +#endif // IO_TEST_H diff --git a/test/io/loose.c b/test/io/loose.c new file mode 100644 index 0000000..fe0f0a4 --- /dev/null +++ b/test/io/loose.c @@ -0,0 +1,964 @@ +#include "io.h" + +UTEST_F(io, reader_mem_read_full) { + SKIP_ON_WASM() + u8 source[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; + u8 dest[16] = sp_zero; + + sp_io_reader_t io = sp_zero; + sp_io_reader_from_mem(&io, source, sizeof(source)); + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_OK); + + EXPECT_EQ(bytes, 16); + sp_for(i, 16) { + EXPECT_EQ(dest[i], source[i]); + } +} +UTEST_F(io, reader_mem_read_partial) { + SKIP_ON_WASM() + u8 source[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; + u8 dest[8] = sp_zero; + + sp_io_reader_t io = sp_zero; + sp_io_reader_from_mem(&io, source, sizeof(source)); + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_OK); + + EXPECT_EQ(bytes, 8); + sp_for(i, 8) { + EXPECT_EQ(dest[i], source[i]); + } +} +UTEST_F(io, reader_mem_read_past_end) { + SKIP_ON_WASM() + u8 source[8] = {1,2,3,4,5,6,7,8}; + u8 dest[16] = sp_zero; + + sp_io_reader_t io = sp_zero; + sp_io_reader_from_mem(&io, source, sizeof(source)); + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_OK); + + EXPECT_EQ(bytes, 8); + + bytes = 0; + EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_ERR_IO_EOF); + EXPECT_EQ(bytes, 0); +} +UTEST_F(io, reader_mem_read_eof_exact) { + SKIP_ON_WASM() + u8 source[8] = {1,2,3,4,5,6,7,8}; + u8 dest[8] = sp_zero; + + sp_io_reader_t io = sp_zero; + sp_io_reader_from_mem(&io, source, sizeof(source)); + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_OK); + EXPECT_EQ(bytes, 8); + + EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_ERR_IO_EOF); + EXPECT_EQ(bytes, 0); +} +UTEST_F(io, reader_mem_read_eof_empty) { + SKIP_ON_WASM() + u8 dest[4] = sp_zero; + + sp_io_reader_t io = sp_zero; + sp_io_reader_from_mem(&io, SP_NULLPTR, 0); + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_ERR_IO_EOF); + EXPECT_EQ(bytes, 0); +} +UTEST_F(io, reader_mem_seek) { + SKIP_ON_WASM() + u8 buffer[64] = sp_zero; + sp_for(i, 64) buffer[i] = (u8)i; + + sp_io_reader_t backing = sp_zero; + sp_io_seeking_reader_t io = sp_zero; + sp_io_seeking_reader_from_mem(&io, &backing, buffer, sizeof(buffer)); + + s64 pos = 0; + EXPECT_EQ(sp_io_seeking_reader_seek(&io, 32, SP_IO_SEEK_SET, &pos), SP_OK); + EXPECT_EQ(pos, 32); + + u8 val; + sp_io_read(io.reader, &val, 1, SP_NULLPTR); + EXPECT_EQ(val, 32); + + EXPECT_EQ(sp_io_seeking_reader_seek(&io, 0, SP_IO_SEEK_END, &pos), SP_OK); + EXPECT_EQ(pos, 64); +} +UTEST_F(io, reader_mem_seek_invalid) { + SKIP_ON_WASM() + u8 buffer[64] = sp_zero; + sp_io_reader_t backing = sp_zero; + sp_io_seeking_reader_t io = sp_zero; + sp_io_seeking_reader_from_mem(&io, &backing, buffer, sizeof(buffer)); + + s64 pos = 0; + EXPECT_EQ(sp_io_seeking_reader_seek(&io, 100, SP_IO_SEEK_SET, &pos), SP_ERR_IO_SEEK_INVALID); + EXPECT_EQ(pos, -1); + + EXPECT_EQ(sp_io_seeking_reader_seek(&io, -10, SP_IO_SEEK_SET, &pos), SP_ERR_IO_SEEK_INVALID); + EXPECT_EQ(pos, -1); +} +UTEST_F(io, seeking_reader_file_seek) { + SKIP_ON_WASM() + const char* content = "0123456789ABCDEF"; + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, content, 16, SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + sp_io_file_reader_t fr = sp_zero; + sp_io_file_reader_from_path(&fr, ut.file_path); + + sp_io_seeking_reader_t sr = sp_zero; + sp_io_seeking_reader_from_file_reader(&sr, &fr); + + s64 pos = 0; + EXPECT_EQ(sp_io_seeking_reader_seek(&sr, 5, SP_IO_SEEK_SET, &pos), SP_OK); + EXPECT_EQ(pos, 5); + + char buffer[5] = {0}; + u64 bytes = 0; + EXPECT_EQ(sp_io_read(sr.reader, buffer, 5, &bytes), SP_OK); + EXPECT_EQ(bytes, 5); + EXPECT_EQ(buffer[0], '5'); + EXPECT_EQ(buffer[4], '9'); + + sp_io_file_reader_close(&fr); +} +UTEST_F(io, seeking_reader_file_seek_whence) { + SKIP_ON_WASM() + const char* content = "0123456789ABCDEF"; + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, content, 16, SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + sp_io_file_reader_t fr = sp_zero; + sp_io_file_reader_from_path(&fr, ut.file_path); + + sp_io_seeking_reader_t sr = sp_zero; + sp_io_seeking_reader_from_file_reader(&sr, &fr); + + s64 pos = 0; + EXPECT_EQ(sp_io_seeking_reader_seek(&sr, 4, SP_IO_SEEK_SET, &pos), SP_OK); + EXPECT_EQ(pos, 4); + + EXPECT_EQ(sp_io_seeking_reader_seek(&sr, 3, SP_IO_SEEK_CUR, &pos), SP_OK); + EXPECT_EQ(pos, 7); + + char c = 0; + sp_io_read(sr.reader, &c, 1, SP_NULLPTR); + EXPECT_EQ(c, '7'); + + EXPECT_EQ(sp_io_seeking_reader_seek(&sr, 0, SP_IO_SEEK_END, &pos), SP_OK); + EXPECT_EQ(pos, 16); + + EXPECT_EQ(sp_io_seeking_reader_seek(&sr, -2, SP_IO_SEEK_END, &pos), SP_OK); + EXPECT_EQ(pos, 14); + sp_io_read(sr.reader, &c, 1, SP_NULLPTR); + EXPECT_EQ(c, 'E'); + + sp_io_file_reader_close(&fr); +} +UTEST_F(io, seeking_reader_file_seek_invalid) { + SKIP_ON_WASM() + const char* content = "0123456789"; + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, content, 10, SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + sp_io_file_reader_t fr = sp_zero; + sp_io_file_reader_from_path(&fr, ut.file_path); + + sp_io_seeking_reader_t sr = sp_zero; + sp_io_seeking_reader_from_file_reader(&sr, &fr); + + s64 pos = 0; + EXPECT_EQ(sp_io_seeking_reader_seek(&sr, -10, SP_IO_SEEK_SET, &pos), SP_ERR_IO_SEEK_FAILED); + + sp_io_file_reader_close(&fr); +} +UTEST_F(io, seeking_reader_file_seek_buffered) { + SKIP_ON_WASM() + const char* content = "0123456789ABCDEF"; + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, content, 16, SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + u8 read_buf[8]; + sp_io_file_reader_t fr = sp_zero; + sp_io_file_reader_from_path(&fr, ut.file_path); + sp_io_reader_set_buffer(&fr.base, read_buf, sizeof(read_buf)); + + sp_io_seeking_reader_t sr = sp_zero; + sp_io_seeking_reader_from_file_reader(&sr, &fr); + + char c = 0; + sp_io_read(sr.reader, &c, 1, SP_NULLPTR); + EXPECT_EQ(c, '0'); + + s64 pos = 0; + EXPECT_EQ(sp_io_seeking_reader_seek(&sr, 10, SP_IO_SEEK_SET, &pos), SP_OK); + EXPECT_EQ(pos, 10); + + sp_io_read(sr.reader, &c, 1, SP_NULLPTR); + EXPECT_EQ(c, 'A'); + + sp_io_file_reader_close(&fr); +} +UTEST_F(io, reader_file_read) { + SKIP_ON_WASM() + const char* content = "0123456789ABCDEF"; + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, content, 16, SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + sp_io_file_reader_t r = sp_zero; + EXPECT_EQ(sp_io_file_reader_from_path(&r, ut.file_path), SP_OK); + + char buffer[16] = {0}; + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, buffer, 16, &bytes), SP_OK); + EXPECT_EQ(bytes, 16); + sp_for(i, 16) { + EXPECT_EQ(buffer[i], content[i]); + } + sp_io_file_reader_close(&r); +} +UTEST_F(io, reader_file_seek) { + SKIP_ON_WASM() + const char* content = "0123456789"; + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, content, 10, SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, ut.file_path); + s64 pos = 0; + EXPECT_EQ(sp_io_file_reader_seek(&r, 5, SP_IO_SEEK_SET, &pos), SP_OK); + EXPECT_EQ(pos, 5); + + char buffer[5] = {0}; + sp_io_read(&r.base, buffer, 5, SP_NULLPTR); + EXPECT_EQ(buffer[0], '5'); + sp_io_file_reader_close(&r); +} +UTEST_F(io, reader_file_nonexistent) { + SKIP_ON_WASM() + sp_str_t path = sp_test_file_path(&ut.file_manager, sp_str_lit("nonexistent.file")); + sp_io_file_reader_t r = sp_zero; + EXPECT_EQ(sp_io_file_reader_from_path(&r, path), SP_ERR_IO_OPEN_FAILED); + sp_io_file_reader_close(&r); +} +UTEST_F(io, reader_file_read_eof_after_drain) { + SKIP_ON_WASM() + const char* content = "0123456789ABCDEF"; + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, content, 16, SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, ut.file_path); + + char buffer[16] = {0}; + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, buffer, 16, &bytes), SP_OK); + EXPECT_EQ(bytes, 16); + + bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, buffer, 16, &bytes), SP_ERR_IO_EOF); + EXPECT_EQ(bytes, 0); + + sp_io_file_reader_close(&r); +} +UTEST_F(io, reader_file_read_eof_short) { + SKIP_ON_WASM() + const char* content = "short"; + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, content, 5, SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, ut.file_path); + + char buffer[32] = {0}; + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, buffer, sizeof(buffer), &bytes), SP_OK); + EXPECT_EQ(bytes, 5); + + bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, buffer, sizeof(buffer), &bytes), SP_ERR_IO_EOF); + EXPECT_EQ(bytes, 0); + + sp_io_file_reader_close(&r); +} +UTEST_F(io, reader_file_read_eof_empty) { + SKIP_ON_WASM() + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, ut.file_path); + + char buffer[16] = {0}; + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, buffer, sizeof(buffer), &bytes), SP_ERR_IO_EOF); + EXPECT_EQ(bytes, 0); + + sp_io_file_reader_close(&r); +} +UTEST_F(io, writer_mem_write) { + SKIP_ON_WASM() + u8 buffer[16] = sp_zero; + u8 source[8] = {1,2,3,4,5,6,7,8}; + + sp_io_mem_writer_t w; sp_io_mem_writer_from_buffer(&w,buffer, sizeof(buffer)); + u64 bytes = 0; + EXPECT_EQ(sp_io_write(&w.base, source, sizeof(source), &bytes), SP_OK); + + EXPECT_EQ(bytes, 8); + sp_for(i, 8) { + EXPECT_EQ(buffer[i], source[i]); + } + /* mem writer has no close */; +} +UTEST_F(io, writer_mem_write_overflow) { + SKIP_ON_WASM() + u8 buffer[8] = sp_zero; + u8 source[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; + + sp_io_mem_writer_t w; sp_io_mem_writer_from_buffer(&w,buffer, sizeof(buffer)); + u64 bytes = 0; + EXPECT_EQ(sp_io_write(&w.base, source, sizeof(source), &bytes), SP_ERR_IO_NO_SPACE); + + EXPECT_EQ(bytes, 0); + /* mem writer has no close */; +} +UTEST_F(io, writer_mem_seek) { + SKIP_ON_WASM() + u8 buffer[64] = sp_zero; + sp_io_mem_writer_t w; sp_io_mem_writer_from_buffer(&w,buffer, sizeof(buffer)); + + s64 pos = 0; + EXPECT_EQ(sp_io_mem_writer_seek(&w, 32, SP_IO_SEEK_SET, &pos), SP_OK); + EXPECT_EQ(pos, 32); + + EXPECT_EQ(sp_io_mem_writer_seek(&w, 0, SP_IO_SEEK_END, &pos), SP_OK); + EXPECT_EQ(pos, 64); + + /* mem writer has no close */; +} +UTEST_F(io, writer_mem_size) { + SKIP_ON_WASM() + u8 buffer[128]; + sp_io_mem_writer_t w; sp_io_mem_writer_from_buffer(&w,buffer, sizeof(buffer)); + u64 size = 0; + EXPECT_EQ(sp_io_mem_writer_size(&w, &size), SP_OK); + EXPECT_EQ(size, 128); + /* mem writer has no close */; +} +UTEST_F(io, writer_file_write) { + SKIP_ON_WASM() + const char* content = "test data"; + sp_io_file_writer_t w = sp_zero; + EXPECT_EQ(sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE), SP_OK); + + u64 bytes = 0; + EXPECT_EQ(sp_io_write(&w.base, content, 9, &bytes), SP_OK); + EXPECT_EQ(bytes, 9); + sp_io_file_writer_close(&w); + + sp_str_t loaded = sp_zero; + sp_io_read_file_a(ut.mem, ut.file_path, &loaded); + EXPECT_EQ(loaded.len, 9); +} +UTEST_F(io, writer_file_overwrite) { + SKIP_ON_WASM() + const char* first = "XXXXXXXX"; + const char* second = "1234"; + + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, first, 8, SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, second, 4, SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + sp_str_t loaded = sp_zero; + sp_io_read_file_a(ut.mem, ut.file_path, &loaded); + EXPECT_EQ(loaded.len, 4); + EXPECT_EQ(loaded.data[0], '1'); + EXPECT_EQ(loaded.data[3], '4'); +} +UTEST_F(io, writer_file_append) { + SKIP_ON_WASM() + const char* first = "first"; + const char* second = "second"; + + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, first, 5, SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_APPEND); + sp_io_write(&w.base, second, 6, SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + sp_str_t loaded = sp_zero; + sp_io_read_file_a(ut.mem, ut.file_path, &loaded); + EXPECT_EQ(loaded.len, 11); + EXPECT_TRUE(sp_str_equal(loaded, sp_str_lit("firstsecond"))); +} +UTEST_F(io, writer_file_seek) { + SKIP_ON_WASM() + const char* content = "0123456789"; + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, content, 10, SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, ut.file_path); + s64 pos = 0; + EXPECT_EQ(sp_io_file_reader_seek(&r, 5, SP_IO_SEEK_SET, &pos), SP_OK); + EXPECT_EQ(pos, 5); + + char buffer[5] = {0}; + sp_io_read(&r.base, buffer, 5, SP_NULLPTR); + EXPECT_EQ(buffer[0], '5'); + sp_io_file_reader_close(&r); +} +UTEST_F(io, writer_file_size) { + SKIP_ON_WASM() + const char* content = "0123456789ABCDEF"; + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, content, 16, SP_NULLPTR); + u64 size = 0; + EXPECT_EQ(sp_io_file_writer_size(&w, &size), SP_OK); + EXPECT_EQ(size, 16); + sp_io_file_writer_close(&w); +} +UTEST_F(io, writer_file_pad) { + SKIP_ON_WASM() + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, "AA", 2, SP_NULLPTR); + sp_io_pad(&w.base, 3, SP_NULLPTR); + sp_io_write(&w.base, "BB", 2, SP_NULLPTR); + sp_io_file_writer_close(&w); + + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, ut.file_path); + u8 result[7] = sp_zero; + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, result, 7, &bytes), SP_OK); + EXPECT_EQ(bytes, 7); + EXPECT_EQ(result[0], 'A'); + EXPECT_EQ(result[1], 'A'); + EXPECT_EQ(result[2], 0); + EXPECT_EQ(result[3], 0); + EXPECT_EQ(result[4], 0); + EXPECT_EQ(result[5], 'B'); + EXPECT_EQ(result[6], 'B'); + sp_io_file_reader_close(&r); +} +UTEST_F(io, writer_dyn_write) { + SKIP_ON_WASM() + sp_io_dyn_mem_writer_t w; sp_io_dyn_mem_writer_init_a(ut.mem, &w); + u8 data[] = {1, 2, 3, 4}; + u64 written = 0; + EXPECT_EQ(sp_io_write(&w.base, data, 4, &written), SP_OK); + + EXPECT_EQ(written, 4); + u64 size = 0; + sp_io_dyn_mem_writer_size(&w, &size); + EXPECT_EQ(size, 4); + sp_io_dyn_mem_writer_close(&w); +} +UTEST_F(io, writer_dyn_grows) { + SKIP_ON_WASM() + sp_io_dyn_mem_writer_t w; sp_io_dyn_mem_writer_init_a(ut.mem, &w); + + u8 data[256]; + sp_for(i, 256) data[i] = (u8)i; + + u64 written = 0; + EXPECT_EQ(sp_io_write(&w.base, data, 256, &written), SP_OK); + EXPECT_EQ(written, 256); + u64 size = 0; + sp_io_dyn_mem_writer_size(&w, &size); + EXPECT_EQ(size, 256); + + sp_io_dyn_mem_writer_close(&w); +} +UTEST_F(io, writer_dyn_to_str) { + SKIP_ON_WASM() + sp_io_dyn_mem_writer_t w; sp_io_dyn_mem_writer_init_a(ut.mem, &w); + + const char* text = "hello world"; + sp_io_write(&w.base, text, 11, SP_NULLPTR); + + sp_str_t str = sp_mem_buffer_as_str(&w.storage); + EXPECT_EQ(str.len, 11); + EXPECT_TRUE(sp_str_equal(str, sp_str_lit("hello world"))); + + sp_io_dyn_mem_writer_close(&w); +} +UTEST_F(io, writer_dyn_seek) { + SKIP_ON_WASM() + sp_io_dyn_mem_writer_t w; sp_io_dyn_mem_writer_init_a(ut.mem, &w); + + u8 data[] = {1, 2, 3, 4, 5, 6, 7, 8}; + sp_io_write(&w.base, data, 8, SP_NULLPTR); + + s64 pos = 0; + EXPECT_EQ(sp_io_dyn_mem_writer_seek(&w, 4, SP_IO_SEEK_SET, &pos), SP_OK); + EXPECT_EQ(pos, 4); + + sp_io_dyn_mem_writer_close(&w); +} +UTEST_F(io, writer_dyn_multiple_writes) { + SKIP_ON_WASM() + sp_io_dyn_mem_writer_t w; sp_io_dyn_mem_writer_init_a(ut.mem, &w); + + sp_io_write(&w.base, "abc", 3, SP_NULLPTR); + sp_io_write(&w.base, "def", 3, SP_NULLPTR); + sp_io_write(&w.base, "ghi", 3, SP_NULLPTR); + + u64 size = 0; + sp_io_dyn_mem_writer_size(&w, &size); + EXPECT_EQ(size, 9); + + sp_str_t str = sp_mem_buffer_as_str(&w.storage); + EXPECT_TRUE(sp_str_equal(str, sp_str_lit("abcdefghi"))); + + sp_io_dyn_mem_writer_close(&w); +} +UTEST_F(io, writer_buffered_1000_bytes) { + SKIP_ON_WASM() + u8 write_buf[64]; + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_writer_set_buffer(&w.base, write_buf, sizeof(write_buf)); + + sp_for(i, 1000) { + u8 byte = (u8)(i & 0xFF); + u64 bytes = 0; + EXPECT_EQ(sp_io_write(&w.base, &byte, 1, &bytes), SP_OK); + EXPECT_EQ(bytes, 1); + } + sp_io_file_writer_close(&w); + + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, ut.file_path); + s64 end = sp_sys_lseek(r.file, 0, SP_IO_SEEK_END); + sp_sys_lseek(r.file, 0, SP_IO_SEEK_SET); + EXPECT_EQ((u64)end, 1000); + + sp_for(i, 1000) { + u8 byte; + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, &byte, 1, &bytes), SP_OK); + EXPECT_EQ(bytes, 1); + EXPECT_EQ(byte, (u8)(i & 0xFF)); + } + sp_io_file_reader_close(&r); +} +UTEST_F(io, writer_buffered_larger_than_buffer) { + SKIP_ON_WASM() + u8 write_buf[32]; + u8 data[128]; + sp_for(i, 128) data[i] = (u8)i; + + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_writer_set_buffer(&w.base, write_buf, sizeof(write_buf)); + + u64 bytes = 0; + EXPECT_EQ(sp_io_write(&w.base, data, 128, &bytes), SP_OK); + EXPECT_EQ(bytes, 128); + sp_io_file_writer_close(&w); + + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, ut.file_path); + u8 result[128] = sp_zero; + u64 read_bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, result, 128, &read_bytes), SP_OK); + EXPECT_EQ(read_bytes, 128); + sp_for(i, 128) { + EXPECT_EQ(result[i], data[i]); + } + sp_io_file_reader_close(&r); +} +UTEST_F(io, writer_buffered_implicit_flush) { + SKIP_ON_WASM() + u8 write_buf[64]; + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_writer_set_buffer(&w.base, write_buf, sizeof(write_buf)); + + sp_io_write(&w.base, "hello", 5, SP_NULLPTR); + sp_io_file_writer_close(&w); + + sp_str_t loaded = sp_zero; + sp_io_read_file_a(ut.mem, ut.file_path, &loaded); + EXPECT_EQ(loaded.len, 5); + EXPECT_TRUE(sp_str_equal(loaded, sp_str_lit("hello"))); +} +UTEST_F(io, writer_buffered_flush_empty) { + SKIP_ON_WASM() + u8 write_buf[64]; + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_writer_set_buffer(&w.base, write_buf, sizeof(write_buf)); + + sp_err_t err = sp_io_flush(&w.base); + EXPECT_EQ(err, SP_OK); + sp_io_file_writer_close(&w); +} +UTEST_F(io, writer_buffered_set_twice) { + SKIP_ON_WASM() + u8 write_buf1[64]; + u8 write_buf2[64]; + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_writer_set_buffer(&w.base, write_buf1, sizeof(write_buf1)); + + sp_io_write(&w.base, "first", 5, SP_NULLPTR); + sp_io_writer_set_buffer(&w.base, write_buf2, sizeof(write_buf2)); + sp_io_write(&w.base, "second", 6, SP_NULLPTR); + sp_io_file_writer_close(&w); + + sp_str_t loaded = sp_zero; + sp_io_read_file_a(ut.mem, ut.file_path, &loaded); + EXPECT_EQ(loaded.len, 11); + EXPECT_TRUE(sp_str_equal(loaded, sp_str_lit("firstsecond"))); +} +UTEST_F(io, reader_buffered_read) { + SKIP_ON_WASM() + const char* content = "0123456789ABCDEF"; + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, content, 16, SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + u8 read_buf[8]; + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, ut.file_path); + sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); + + char result[16] = {0}; + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, result, 16, &bytes), SP_OK); + EXPECT_EQ(bytes, 16); + sp_for(i, 16) { + EXPECT_EQ(result[i], content[i]); + } + sp_io_file_reader_close(&r); +} +UTEST_F(io, reader_buffered_small_reads) { + SKIP_ON_WASM() + const char* content = "0123456789ABCDEF"; + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, content, 16, SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + u8 read_buf[8]; + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, ut.file_path); + sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); + + sp_for(i, 16) { + char c; + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, &c, 1, &bytes), SP_OK); + EXPECT_EQ(bytes, 1); + EXPECT_EQ(c, content[i]); + } + sp_io_file_reader_close(&r); +} +UTEST_F(io, reader_buffered_eof_exact) { + SKIP_ON_WASM() + const char* content = "0123456789ABCDEF"; + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, content, 16, SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + u8 read_buf[8]; + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, ut.file_path); + sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); + + char result[16] = {0}; + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, result, 16, &bytes), SP_OK); + EXPECT_EQ(bytes, 16); + + bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, result, 1, &bytes), SP_ERR_IO_EOF); + EXPECT_EQ(bytes, 0); + + sp_io_file_reader_close(&r); +} +UTEST_F(io, reader_buffered_eof_partial) { + SKIP_ON_WASM() + const char* content = "0123456789ABCDEF"; + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, content, 16, SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + u8 read_buf[8]; + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, ut.file_path); + sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); + + char result[32] = {0}; + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, result, sizeof(result), &bytes), SP_OK); + EXPECT_EQ(bytes, 16); + + bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, result, sizeof(result), &bytes), SP_ERR_IO_EOF); + EXPECT_EQ(bytes, 0); + + sp_io_file_reader_close(&r); +} +UTEST_F(io, reader_buffered_fill_preserves_drained) { + SKIP_ON_WASM() + const char* content = "0123456789ABCDEF"; + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, content, 16, SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + u8 read_buf[8]; + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, ut.file_path); + sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); + + char first[4] = {0}; + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, first, 4, &bytes), SP_OK); + EXPECT_EQ(bytes, 4); + + char second[7] = {0}; + bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, second, 7, &bytes), SP_OK); + EXPECT_EQ(bytes, 7); + EXPECT_EQ(second[0], '4'); + EXPECT_EQ(second[1], '5'); + EXPECT_EQ(second[2], '6'); + EXPECT_EQ(second[3], '7'); + EXPECT_EQ(second[4], '8'); + EXPECT_EQ(second[5], '9'); + EXPECT_EQ(second[6], 'A'); + + sp_io_file_reader_close(&r); +} +UTEST_F(io, reader_buffered_eof_direct_path) { + SKIP_ON_WASM() + const char* content = "0123456789ABCDEF"; + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, content, 16, SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + u8 read_buf[4]; + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, ut.file_path); + sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); + + char prime; + EXPECT_EQ(sp_io_read(&r.base, &prime, 1, SP_NULLPTR), SP_OK); + EXPECT_EQ(prime, '0'); + + char result[32] = {0}; + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, result, sizeof(result), &bytes), SP_OK); + EXPECT_EQ(bytes, 15); + + bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, result, sizeof(result), &bytes), SP_ERR_IO_EOF); + EXPECT_EQ(bytes, 0); + + sp_io_file_reader_close(&r); +} +UTEST_F(io, reader_buffered_zero_size) { + SKIP_ON_WASM() + u8 source[1] = {0}; + u8 read_buf[8]; + sp_io_reader_t io = sp_zero; + sp_io_reader_from_mem(&io, source, sizeof(source)); + sp_io_reader_set_buffer(&io, read_buf, sizeof(read_buf)); + + u64 bytes = 1; + EXPECT_EQ(sp_io_read(&io, source, 0, &bytes), SP_OK); + EXPECT_EQ(bytes, 0); +} +UTEST_F(io, reader_buffered_eof_byte_by_byte) { + SKIP_ON_WASM() + const char* content = "0123456789ABCDEF"; + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, content, 16, SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + u8 read_buf[8]; + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, ut.file_path); + sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); + + sp_for(i, 16) { + char c; + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, &c, 1, &bytes), SP_OK); + EXPECT_EQ(bytes, 1); + EXPECT_EQ(c, content[i]); + } + + char c; + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, &c, 1, &bytes), SP_ERR_IO_EOF); + EXPECT_EQ(bytes, 0); + + sp_io_file_reader_close(&r); +} +UTEST_F(io, reader_buffered_seek_discards_buffer) { + SKIP_ON_WASM() + const char* content = "0123456789ABCDEF"; + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, content, 16, SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + u8 read_buf[8]; + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, ut.file_path); + sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); + + char c; + sp_io_read(&r.base, &c, 1, SP_NULLPTR); + EXPECT_EQ(c, '0'); + + sp_io_file_reader_seek(&r, 10, SP_IO_SEEK_SET, SP_NULLPTR); + sp_io_read(&r.base, &c, 1, SP_NULLPTR); + EXPECT_EQ(c, 'A'); + + sp_io_file_reader_close(&r); +} + +UTEST_F(io, seek_beyond_4gb) { + SKIP_ON_WASM() + s64 offset = (s64)5 * 1024 * 1024 * 1024; + u8 marker[4] = {0xDE, 0xAD, 0xBE, 0xEF}; + + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + s64 seek_pos = 0; + EXPECT_EQ(sp_io_file_writer_seek(&w, offset, SP_IO_SEEK_SET, &seek_pos), SP_OK); + EXPECT_EQ(seek_pos, offset); + sp_io_write(&w.base, marker, 4, SP_NULLPTR); + sp_io_file_writer_close(&w); + + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, ut.file_path); + s64 end = sp_sys_lseek(r.file, 0, SP_IO_SEEK_END); + sp_sys_lseek(r.file, 0, SP_IO_SEEK_SET); + EXPECT_EQ((u64)end, (u64)offset + 4); + + s64 read_pos = 0; + EXPECT_EQ(sp_io_file_reader_seek(&r, offset, SP_IO_SEEK_SET, &read_pos), SP_OK); + EXPECT_EQ(read_pos, offset); + + u8 result[4] = sp_zero; + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, result, 4, &bytes), SP_OK); + EXPECT_EQ(bytes, 4); + EXPECT_EQ(result[0], 0xDE); + EXPECT_EQ(result[1], 0xAD); + EXPECT_EQ(result[2], 0xBE); + EXPECT_EQ(result[3], 0xEF); + sp_io_file_reader_close(&r); +} + +// UTEST_F(io, copy_playground) { +// struct { +// u8 a [4096]; +// u8 b [4096]; +// u8 c [4096]; +// } buf = sp_zero; +// struct { +// sp_io_reader_t r; +// sp_io_writer_t w; +// } io = sp_zero; +// +// sp_for(it, 4096) { +// buf.a[it] = it; +// } +// +// sp_io_reader_from_mem(&io.r, buf.a, sizeof(buf.a)); +// sp_io_writer_set_buffer(&io.w, buf.b, sizeof(buf.b)); +// +// u64 n = 0; +// sp_io_copy_b2(&io.w, &io.r, buf.c, sizeof(buf.c), &n); +// EXPECT_EQ(n, 4096); +// } diff --git a/test/io/read.c b/test/io/read.c new file mode 100644 index 0000000..7af84f6 --- /dev/null +++ b/test/io/read.c @@ -0,0 +1,89 @@ +#include "io.h" + +typedef struct { + const c8* source; + io_step_t steps [IO_MAX_STEPS]; +} io_read_test_t; + +void run_io_read_test(int* utest_result, io_read_test_t t) { + sp_io_reader_t r = sp_zero; + sp_io_reader_from_mem(&r, t.source, sp_cstr_len(t.source)); + + sp_carr_for(t.steps, j) { + const io_step_t* step = &t.steps[j]; + if (step->kind == IO_STEP_NONE) break; + + u8 dest [64] = sp_zero; + u64 bytes = 0; + sp_err_t err = sp_io_read(&r, dest, step->read.request, &bytes); + EXPECT_EQ(err, step->read.err); + + u64 expect_bytes = sp_cstr_len(step->read.content); + EXPECT_EQ(bytes, expect_bytes); + sp_for(it, expect_bytes) { + EXPECT_EQ((c8)dest[it], step->read.content[it]); + } + } +} + +UTEST_F(io, read_exact_then_eof) { + run_io_read_test(utest_result, (io_read_test_t){ + .source = "0123", + .steps = { + { .kind = IO_STEP_READ, .read = { 4, SP_OK, "0123" } }, + { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, + }, + }); +} + +UTEST_F(io, read_chunked) { + run_io_read_test(utest_result, (io_read_test_t){ + .source = "01234567", + .steps = { + { .kind = IO_STEP_READ, .read = { 3, SP_OK, "012" } }, + { .kind = IO_STEP_READ, .read = { 3, SP_OK, "345" } }, + { .kind = IO_STEP_READ, .read = { 3, SP_OK, "67" } }, + { .kind = IO_STEP_READ, .read = { 3, SP_ERR_IO_EOF } }, + }, + }); +} + +UTEST_F(io, read_oversized_request) { + run_io_read_test(utest_result, (io_read_test_t){ + .source = "abc", + .steps = { + { .kind = IO_STEP_READ, .read = { 16, SP_OK, "abc" } }, + { .kind = IO_STEP_READ, .read = { 16, SP_ERR_IO_EOF } }, + }, + }); +} + +UTEST_F(io, read_empty_source) { + run_io_read_test(utest_result, (io_read_test_t){ + .source = "", + .steps = { + { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, + }, + }); +} + +UTEST_F(io, read_eof_idempotent) { + run_io_read_test(utest_result, (io_read_test_t){ + .source = "x", + .steps = { + { .kind = IO_STEP_READ, .read = { 4, SP_OK, "x" } }, + { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, + { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, + }, + }); +} + +UTEST_F(io, read_zero_request) { + run_io_read_test(utest_result, (io_read_test_t){ + .source = "abc", + .steps = { + { .kind = IO_STEP_READ, .read = { 0, SP_OK } }, + { .kind = IO_STEP_READ, .read = { 3, SP_OK, "abc" } }, + }, + }); +} diff --git a/test/io/write.c b/test/io/write.c new file mode 100644 index 0000000..50ee441 --- /dev/null +++ b/test/io/write.c @@ -0,0 +1,102 @@ +#include "io.h" + +typedef struct { + u64 capacity; + io_step_t steps [IO_MAX_STEPS]; + struct { + const c8* content; + } expect; +} io_write_test_t; + +void run_io_write_test(int* utest_result, io_write_test_t t) { + u8 backing [64] = sp_zero; + sp_io_mem_writer_t w = sp_zero; + sp_io_mem_writer_from_buffer(&w, backing, t.capacity); + + sp_carr_for(t.steps, j) { + const io_step_t* step = &t.steps[j]; + if (step->kind == IO_STEP_NONE) break; + + switch (step->kind) { + case IO_STEP_WRITE: { + u64 bytes = 0; + sp_err_t err = sp_io_write(&w.base, step->write.data, sp_cstr_len(step->write.data), &bytes); + EXPECT_EQ(err, step->write.err); + EXPECT_EQ(bytes, step->write.bytes); + break; + } + case IO_STEP_FLUSH: { + sp_err_t err = sp_io_flush(&w.base); + EXPECT_EQ(err, step->flush.err); + break; + } + default: { + sp_unreachable_case(); + } + } + } + + u64 n = sp_cstr_len(t.expect.content); + sp_for(it, n) { + EXPECT_EQ((c8)backing[it], t.expect.content[it]); + } +} + +UTEST_F(io, write_fits) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 16, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "hello", SP_OK, 5 } }, + }, + .expect.content = "hello", + }); +} + +UTEST_F(io, write_exact_fit) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 4, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abcd", SP_OK, 4 } }, + }, + .expect.content = "abcd", + }); +} + +UTEST_F(io, write_overflow) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 4, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abcdefgh", SP_ERR_IO_NO_SPACE, 0 } }, + }, + }); +} + +UTEST_F(io, write_appends) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 16, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, + { .kind = IO_STEP_WRITE, .write = { "def", SP_OK, 3 } }, + { .kind = IO_STEP_WRITE, .write = { "ghi", SP_OK, 3 } }, + }, + .expect.content = "abcdefghi", + }); +} + +UTEST_F(io, write_zero) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 8, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "", SP_OK, 0 } }, + }, + }); +} + +UTEST_F(io, write_flush_empty) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 8, + .steps = { + { .kind = IO_STEP_FLUSH, .flush = { SP_OK } }, + }, + }); +} From 0a125bc7a648f21fc3a5ad1ae520e63fa31bad53 Mon Sep 17 00:00:00 2001 From: Thomas Spader Date: Tue, 12 May 2026 14:58:40 -0400 Subject: [PATCH 06/21] tweaks --- test/io/copy.c | 18 +++++++----------- test/io/write.c | 2 +- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/test/io/copy.c b/test/io/copy.c index 8bcddf8..f19986b 100644 --- a/test/io/copy.c +++ b/test/io/copy.c @@ -54,14 +54,10 @@ UTEST_F(io, copy_empty) { }); } -// TODO: this case exposes the sp_io_copy_b bug: today it returns bytes_copied=0 -// because the write fails before `total += chunk` runs, and the mem writer's -// all-or-nothing semantics drop the partial. Uncomment after fixing. -// -// UTEST_F(io, copy_writer_no_space) { -// run_io_copy_test(utest_result, (io_copy_test_t){ -// .source = "0123456789", -// .capacity = 4, .buffer = 8, -// .expect = { .err = SP_ERR_IO_NO_SPACE, .final = "0123" }, -// }); -// } +UTEST_F(io, copy_writer_no_space) { + run_io_copy_test(utest_result, (io_copy_test_t){ + .source = "0123456789", + .capacity = 4, .buffer = 8, + .expect = { .err = SP_ERR_IO_NO_SPACE, .final = "0123" }, + }); +} diff --git a/test/io/write.c b/test/io/write.c index 50ee441..72ea8d7 100644 --- a/test/io/write.c +++ b/test/io/write.c @@ -66,7 +66,7 @@ UTEST_F(io, write_overflow) { run_io_write_test(utest_result, (io_write_test_t){ .capacity = 4, .steps = { - { .kind = IO_STEP_WRITE, .write = { "abcdefgh", SP_ERR_IO_NO_SPACE, 0 } }, + { .kind = IO_STEP_WRITE, .write = { "abcdefgh", SP_ERR_IO_NO_SPACE, 4 } }, }, }); } From e784543e3d4d90685b4feb1dd1a700d42aaa04d0 Mon Sep 17 00:00:00 2001 From: Thomas Spader Date: Tue, 12 May 2026 15:24:43 -0400 Subject: [PATCH 07/21] mock 1 --- test/io.c | 1 + test/io/mock.c | 486 ++++++++++++++++++++++++++++++++++++++++++++++++ test/io/write.c | 22 +++ 3 files changed, 509 insertions(+) create mode 100644 test/io/mock.c diff --git a/test/io.c b/test/io.c index 5bd958a..6dda240 100644 --- a/test/io.c +++ b/test/io.c @@ -4,4 +4,5 @@ SP_TEST_MAIN() #include "io/read.c" #include "io/write.c" #include "io/copy.c" +#include "io/mock.c" #include "io/loose.c" diff --git a/test/io/mock.c b/test/io/mock.c new file mode 100644 index 0000000..454d491 --- /dev/null +++ b/test/io/mock.c @@ -0,0 +1,486 @@ +#include "io.h" + +#define IO_MAX_RESPONSES 8 +#define IO_MOCK_BUFFER_MAX 256 + +typedef struct { + u64 bytes; + sp_err_t err; + const c8* data; +} io_mock_response_t; + +typedef struct { + sp_io_reader_t base; + io_mock_response_t responses[IO_MAX_RESPONSES]; + u64 num_responses; + u64 cursor; +} io_mock_reader_t; + +typedef struct { + sp_io_writer_t base; + io_mock_response_t responses[IO_MAX_RESPONSES]; + u64 num_responses; + u64 cursor; + u8 received[IO_MOCK_BUFFER_MAX]; + u64 received_len; +} io_mock_writer_t; + +static sp_err_t io_mock_reader_read(sp_io_reader_t* r, void* ptr, u64 size, u64* bytes_read) { + io_mock_reader_t* m = (io_mock_reader_t*)r; + sp_assert(m->cursor < m->num_responses); + io_mock_response_t* resp = &m->responses[m->cursor++]; + u64 n = sp_min(size, resp->bytes); + if (n && resp->data) { + sp_mem_copy(resp->data, ptr, n); + } + if (bytes_read) *bytes_read = n; + return resp->err; +} + +static sp_err_t io_mock_writer_write(sp_io_writer_t* w, const void* ptr, u64 size, u64* bytes_written) { + io_mock_writer_t* m = (io_mock_writer_t*)w; + sp_assert(m->cursor < m->num_responses); + io_mock_response_t* resp = &m->responses[m->cursor++]; + u64 n = sp_min(size, resp->bytes); + if (n) { + sp_assert(m->received_len + n <= IO_MOCK_BUFFER_MAX); + sp_mem_copy(ptr, m->received + m->received_len, n); + m->received_len += n; + } + if (bytes_written) *bytes_written = n; + return resp->err; +} + +static u64 io_mock_response_count(const io_mock_response_t* responses, u64 max) { + u64 n = 0; + sp_for(it, max) { + if (!responses[it].bytes && !responses[it].err && !responses[it].data) break; + n = it + 1; + } + return n; +} + +static void io_mock_reader_init(io_mock_reader_t* m, const io_mock_response_t* responses, u64 n) { + *m = sp_zero_s(io_mock_reader_t); + m->base.read = io_mock_reader_read; + m->num_responses = n; + sp_for(it, n) m->responses[it] = responses[it]; +} + +static void io_mock_writer_init(io_mock_writer_t* m, const io_mock_response_t* responses, u64 n) { + *m = sp_zero_s(io_mock_writer_t); + m->base.write = io_mock_writer_write; + m->num_responses = n; + sp_for(it, n) m->responses[it] = responses[it]; +} + + +////////////////////////// +// READ MOCK TEST RUNNER +////////////////////////// +typedef struct { + io_mock_response_t responses[IO_MAX_RESPONSES]; + io_step_t steps[IO_MAX_STEPS]; +} io_mock_read_test_t; + +void run_io_mock_read_test(int* utest_result, io_mock_read_test_t t) { + u64 num_responses = io_mock_response_count(t.responses, IO_MAX_RESPONSES); + io_mock_reader_t r = sp_zero; + io_mock_reader_init(&r, t.responses, num_responses); + + sp_carr_for(t.steps, j) { + const io_step_t* step = &t.steps[j]; + if (step->kind == IO_STEP_NONE) break; + + u8 dest[64] = sp_zero; + u64 bytes = 0; + sp_err_t err = sp_io_read(&r.base, dest, step->read.request, &bytes); + EXPECT_EQ(err, step->read.err); + + u64 expect_bytes = sp_cstr_len(step->read.content); + EXPECT_EQ(bytes, expect_bytes); + sp_for(it, expect_bytes) { + EXPECT_EQ((c8)dest[it], step->read.content[it]); + } + } + + EXPECT_EQ(r.cursor, r.num_responses); +} + + +////////////////////////// +// WRITE MOCK TEST RUNNER +////////////////////////// +typedef struct { + io_mock_response_t responses[IO_MAX_RESPONSES]; + io_step_t steps[IO_MAX_STEPS]; + struct { + const c8* received; + } expect; +} io_mock_write_test_t; + +void run_io_mock_write_test(int* utest_result, io_mock_write_test_t t) { + u64 num_responses = io_mock_response_count(t.responses, IO_MAX_RESPONSES); + io_mock_writer_t w = sp_zero; + io_mock_writer_init(&w, t.responses, num_responses); + + sp_carr_for(t.steps, j) { + const io_step_t* step = &t.steps[j]; + if (step->kind == IO_STEP_NONE) break; + + switch (step->kind) { + case IO_STEP_WRITE: { + u64 bytes = 0; + sp_err_t err = sp_io_write(&w.base, step->write.data, sp_cstr_len(step->write.data), &bytes); + EXPECT_EQ(err, step->write.err); + EXPECT_EQ(bytes, step->write.bytes); + break; + } + case IO_STEP_FLUSH: { + sp_err_t err = sp_io_flush(&w.base); + EXPECT_EQ(err, step->flush.err); + break; + } + case IO_STEP_NONE: + case IO_STEP_READ: { + sp_unreachable_case(); + } + } + } + + EXPECT_EQ(w.cursor, w.num_responses); + + u64 n = sp_cstr_len(t.expect.received); + EXPECT_EQ(w.received_len, n); + sp_for(it, n) { + EXPECT_EQ((c8)w.received[it], t.expect.received[it]); + } +} + + +////////////////////////// +// COPY MOCK TEST RUNNERS +////////////////////////// +typedef struct { + io_mock_response_t responses[IO_MAX_RESPONSES]; + u64 writer_capacity; + u64 buffer; + struct { + sp_err_t err; + u64 copied; + const c8* final; + } expect; +} io_mock_copy_reader_test_t; + +void run_io_mock_copy_reader_test(int* utest_result, io_mock_copy_reader_test_t t) { + u64 num_responses = io_mock_response_count(t.responses, IO_MAX_RESPONSES); + io_mock_reader_t r = sp_zero; + io_mock_reader_init(&r, t.responses, num_responses); + + u8 backing[64] = sp_zero; + sp_io_mem_writer_t w = sp_zero; + sp_io_mem_writer_from_buffer(&w, backing, t.writer_capacity); + + u8 copy_buf[64] = sp_zero; + u64 copied = 0; + sp_err_t err = sp_io_copy_b(&w.base, &r.base, copy_buf, t.buffer, &copied); + + EXPECT_EQ(err, t.expect.err); + EXPECT_EQ(copied, t.expect.copied); + u64 n = sp_cstr_len(t.expect.final); + sp_for(it, n) { + EXPECT_EQ((c8)backing[it], t.expect.final[it]); + } +} + +typedef struct { + const c8* source; + io_mock_response_t responses[IO_MAX_RESPONSES]; + u64 buffer; + struct { + sp_err_t err; + u64 copied; + const c8* received; + } expect; +} io_mock_copy_writer_test_t; + +void run_io_mock_copy_writer_test(int* utest_result, io_mock_copy_writer_test_t t) { + sp_io_reader_t r = sp_zero; + sp_io_reader_from_mem(&r, t.source, sp_cstr_len(t.source)); + + u64 num_responses = io_mock_response_count(t.responses, IO_MAX_RESPONSES); + io_mock_writer_t w = sp_zero; + io_mock_writer_init(&w, t.responses, num_responses); + + u8 copy_buf[64] = sp_zero; + u64 copied = 0; + sp_err_t err = sp_io_copy_b(&w.base, &r, copy_buf, t.buffer, &copied); + + EXPECT_EQ(err, t.expect.err); + EXPECT_EQ(copied, t.expect.copied); + u64 n = sp_cstr_len(t.expect.received); + EXPECT_EQ(w.received_len, n); + sp_for(it, n) { + EXPECT_EQ((c8)w.received[it], t.expect.received[it]); + } +} + + +////////////////////////// +// SMOKE TESTS +////////////////////////// +UTEST_F(io, mock_read_smoke) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .responses = { + { .bytes = 3, .data = "abc", .err = SP_OK }, + }, + .steps = { + { .kind = IO_STEP_READ, .read = { 8, SP_OK, "abc" } }, + }, + }); +} + +UTEST_F(io, mock_write_smoke) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .responses = { + { .bytes = 3, .err = SP_OK }, + }, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, + }, + .expect.received = "abc", + }); +} + + +////////////////////////// +// READ FAILURE MODES +////////////////////////// + +// Short read with OK, then EOF. Pins that short reads do NOT signal EOF. +UTEST_F(io, mock_read_short_then_eof) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .responses = { + { .bytes = 3, .data = "abc", .err = SP_OK }, + { .bytes = 0, .err = SP_ERR_IO_EOF }, + }, + .steps = { + { .kind = IO_STEP_READ, .read = { 8, SP_OK, "abc" } }, + { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_EOF } }, + }, + }); +} + +// EOF returned at offset 0 (empty stream). +UTEST_F(io, mock_read_eof_empty) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .responses = { + { .bytes = 0, .err = SP_ERR_IO_EOF }, + }, + .steps = { + { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_EOF } }, + }, + }); +} + +// EOF is idempotent: each repeated read returns the same terminal state. +UTEST_F(io, mock_read_eof_idempotent) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .responses = { + { .bytes = 0, .err = SP_ERR_IO_EOF }, + { .bytes = 0, .err = SP_ERR_IO_EOF }, + { .bytes = 0, .err = SP_ERR_IO_EOF }, + }, + .steps = { + { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, + { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, + { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, + }, + }); +} + +// Hard error with zero bytes: not-done -> broken. +UTEST_F(io, mock_read_error_zero_bytes) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .responses = { + { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, + }, + .steps = { + { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_READ_FAILED } }, + }, + }); +} + +// Successful read, then a hard error on the next call. +UTEST_F(io, mock_read_error_after_success) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .responses = { + { .bytes = 4, .data = "abcd", .err = SP_OK }, + { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, + }, + .steps = { + { .kind = IO_STEP_READ, .read = { 8, SP_OK, "abcd" } }, + { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_READ_FAILED } }, + }, + }); +} + +// Orthogonal contract: backend returns bytes AND error in the same call. +// Caller must see the bytes AND the error. +UTEST_F(io, mock_read_bytes_and_error) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .responses = { + { .bytes = 3, .data = "abc", .err = SP_ERR_IO_READ_FAILED }, + }, + .steps = { + { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_READ_FAILED, "abc" } }, + }, + }); +} + +// After a hard error, subsequent reads continue to report the error if the +// backend continues to surface it. Pins that the wrapper does NOT latch +// errors itself: it is the backend's job to decide whether to retry. +UTEST_F(io, mock_read_error_then_error) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .responses = { + { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, + { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, + }, + .steps = { + { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_READ_FAILED } }, + { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_READ_FAILED } }, + }, + }); +} + + +////////////////////////// +// WRITE FAILURE MODES +////////////////////////// + +// Partial write paired with NO_SPACE. Caller sees both bytes_written>0 and +// the error. +UTEST_F(io, mock_write_partial_no_space) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .responses = { + { .bytes = 4, .err = SP_ERR_IO_NO_SPACE }, + }, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abcdefgh", SP_ERR_IO_NO_SPACE, 4 } }, + }, + .expect.received = "abcd", + }); +} + +// Hard error with zero bytes accepted. +UTEST_F(io, mock_write_error_zero_bytes) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .responses = { + { .bytes = 0, .err = SP_ERR_IO_WRITE_FAILED }, + }, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abc", SP_ERR_IO_WRITE_FAILED, 0 } }, + }, + }); +} + +// Successful write, then a hard error on the next call. +UTEST_F(io, mock_write_error_after_success) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .responses = { + { .bytes = 3, .err = SP_OK }, + { .bytes = 0, .err = SP_ERR_IO_WRITE_FAILED }, + }, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, + { .kind = IO_STEP_WRITE, .write = { "xyz", SP_ERR_IO_WRITE_FAILED, 0 } }, + }, + .expect.received = "abc", + }); +} + +// Orthogonal contract for writes: partial bytes AND error in one call. +UTEST_F(io, mock_write_bytes_and_error) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .responses = { + { .bytes = 2, .err = SP_ERR_IO_WRITE_FAILED }, + }, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abc", SP_ERR_IO_WRITE_FAILED, 2 } }, + }, + .expect.received = "ab", + }); +} + +// After a NO_SPACE, a smaller write that fits should still succeed: the +// wrapper does not latch the error. +UTEST_F(io, mock_write_smaller_after_overflow) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .responses = { + { .bytes = 0, .err = SP_ERR_IO_NO_SPACE }, + { .bytes = 2, .err = SP_OK }, + }, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abcd", SP_ERR_IO_NO_SPACE, 0 } }, + { .kind = IO_STEP_WRITE, .write = { "xy", SP_OK, 2 } }, + }, + .expect.received = "xy", + }); +} + + +////////////////////////// +// COPY FAILURE MODES +////////////////////////// + +// Source fails AFTER returning some bytes in a prior call. Copy should +// preserve the bytes that did transfer. +UTEST_F(io, mock_copy_reader_fails_after_success) { + run_io_mock_copy_reader_test(utest_result, (io_mock_copy_reader_test_t){ + .responses = { + { .bytes = 3, .data = "abc", .err = SP_OK }, + { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, + }, + .writer_capacity = 32, .buffer = 8, + .expect = { .err = SP_ERR_IO_READ_FAILED, .copied = 3, .final = "abc" }, + }); +} + +// Source returns bytes AND error in the same call. Per the orthogonal +// contract, copy should still commit those bytes to the destination. +UTEST_F(io, mock_copy_reader_bytes_and_error) { + run_io_mock_copy_reader_test(utest_result, (io_mock_copy_reader_test_t){ + .responses = { + { .bytes = 3, .data = "abc", .err = SP_ERR_IO_READ_FAILED }, + }, + .writer_capacity = 32, .buffer = 8, + .expect = { .err = SP_ERR_IO_READ_FAILED, .copied = 3, .final = "abc" }, + }); +} + +// Writer accepts a partial prefix then signals NO_SPACE on a single call. +// Copy should report the prefix as copied and surface the error. +UTEST_F(io, mock_copy_writer_partial_in_call) { + run_io_mock_copy_writer_test(utest_result, (io_mock_copy_writer_test_t){ + .source = "0123456789", + .responses = { + { .bytes = 4, .err = SP_ERR_IO_NO_SPACE }, + }, + .buffer = 8, + .expect = { .err = SP_ERR_IO_NO_SPACE, .copied = 4, .received = "0123" }, + }); +} + +// Writer rejects everything on the first call with zero bytes accepted. +// Copy should report copied=0 and the destination should be untouched. +UTEST_F(io, mock_copy_writer_fails_immediately) { + run_io_mock_copy_writer_test(utest_result, (io_mock_copy_writer_test_t){ + .source = "0123456789", + .responses = { + { .bytes = 0, .err = SP_ERR_IO_WRITE_FAILED }, + }, + .buffer = 8, + .expect = { .err = SP_ERR_IO_WRITE_FAILED, .copied = 0, .received = "" }, + }); +} diff --git a/test/io/write.c b/test/io/write.c index 72ea8d7..2c10771 100644 --- a/test/io/write.c +++ b/test/io/write.c @@ -68,6 +68,28 @@ UTEST_F(io, write_overflow) { .steps = { { .kind = IO_STEP_WRITE, .write = { "abcdefgh", SP_ERR_IO_NO_SPACE, 4 } }, }, + .expect.content = "abcd", + }); +} + +UTEST_F(io, write_barely_overflows) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 4, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abcde", SP_ERR_IO_NO_SPACE, 4 } }, + }, + .expect.content = "abcd", + }); +} + +UTEST_F(io, write_smaller_after_overflow) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 4, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abcd", SP_OK, 4 } }, + { .kind = IO_STEP_WRITE, .write = { "x", SP_ERR_IO_NO_SPACE, 0 } }, + }, + .expect.content = "abcd", }); } From 03dabaf96ab59a12a282e69b86ccff6f89db19d3 Mon Sep 17 00:00:00 2001 From: Thomas Spader Date: Tue, 12 May 2026 16:00:59 -0400 Subject: [PATCH 08/21] refactor? --- test/io.c | 52 +++++- test/io/copy.c | 136 +++++++++++++- test/io/io.h | 40 +++- test/io/mock.c | 486 ------------------------------------------------ test/io/read.c | 157 +++++++++++++++- test/io/write.c | 156 +++++++++++++++- tools/utest.h | 5 + 7 files changed, 524 insertions(+), 508 deletions(-) delete mode 100644 test/io/mock.c diff --git a/test/io.c b/test/io.c index 6dda240..2e559fd 100644 --- a/test/io.c +++ b/test/io.c @@ -4,5 +4,55 @@ SP_TEST_MAIN() #include "io/read.c" #include "io/write.c" #include "io/copy.c" -#include "io/mock.c" #include "io/loose.c" + +u64 io_mock_response_count(const io_mock_response_t* responses, u64 max) { + u64 n = 0; + sp_for(it, max) { + if (!responses[it].bytes && !responses[it].err && !responses[it].data) break; + n = it + 1; + } + return n; +} + +sp_err_t io_mock_reader_read(sp_io_reader_t* r, void* ptr, u64 size, u64* bytes_read) { + io_mock_reader_t* m = (io_mock_reader_t*)r; + sp_assert(m->cursor < m->num_responses); + io_mock_response_t* resp = &m->responses[m->cursor++]; + u64 n = sp_min(size, resp->bytes); + if (n && resp->data) { + sp_mem_copy(resp->data, ptr, n); + } + if (bytes_read) *bytes_read = n; + return resp->err; +} + +sp_err_t io_mock_writer_write(sp_io_writer_t* w, const void* ptr, u64 size, u64* bytes_written) { + io_mock_writer_t* m = (io_mock_writer_t*)w; + sp_assert(m->cursor < m->num_responses); + io_mock_response_t* resp = &m->responses[m->cursor++]; + u64 n = sp_min(size, resp->bytes); + if (n) { + sp_assert(m->received_len + n <= sizeof(m->received)); + sp_mem_copy(ptr, m->received + m->received_len, n); + m->received_len += n; + } + if (bytes_written) *bytes_written = n; + return resp->err; +} + + +void io_mock_reader_init(io_mock_reader_t* m, const io_mock_response_t* responses, u64 n) { + *m = sp_zero_s(io_mock_reader_t); + m->base.read = io_mock_reader_read; + m->num_responses = n; + sp_for(it, n) m->responses[it] = responses[it]; +} + +void io_mock_writer_init(io_mock_writer_t* m, const io_mock_response_t* responses, u64 n) { + *m = sp_zero_s(io_mock_writer_t); + m->base.write = io_mock_writer_write; + m->num_responses = n; + sp_for(it, n) m->responses[it] = responses[it]; +} + diff --git a/test/io/copy.c b/test/io/copy.c index f19986b..7a20b24 100644 --- a/test/io/copy.c +++ b/test/io/copy.c @@ -1,5 +1,132 @@ #include "io.h" +UTEST_EMPTY_FIXTURE(io_copy) + +typedef struct { + io_mock_response_t responses[IO_MAX_RESPONSES]; + u64 writer_capacity; + u64 buffer; + struct { + sp_err_t err; + u64 copied; + const c8* final; + } expect; +} io_mock_copy_reader_test_t; + +void run_io_mock_copy_reader_test(int* utest_result, io_mock_copy_reader_test_t t) { + u64 num_responses = io_mock_response_count(t.responses, IO_MAX_RESPONSES); + io_mock_reader_t r = sp_zero; + io_mock_reader_init(&r, t.responses, num_responses); + + u8 backing[64] = sp_zero; + sp_io_mem_writer_t w = sp_zero; + sp_io_mem_writer_from_buffer(&w, backing, t.writer_capacity); + + u8 copy_buf[64] = sp_zero; + u64 copied = 0; + sp_err_t err = sp_io_copy_b(&w.base, &r.base, copy_buf, t.buffer, &copied); + + EXPECT_EQ(err, t.expect.err); + EXPECT_EQ(copied, t.expect.copied); + u64 n = sp_cstr_len(t.expect.final); + sp_for(it, n) { + EXPECT_EQ((c8)backing[it], t.expect.final[it]); + } +} + +typedef struct { + const c8* source; + io_mock_response_t responses[IO_MAX_RESPONSES]; + u64 buffer; + struct { + sp_err_t err; + u64 copied; + const c8* received; + } expect; +} io_mock_copy_writer_test_t; + +void run_io_mock_copy_writer_test(int* utest_result, io_mock_copy_writer_test_t t) { + sp_io_reader_t r = sp_zero; + sp_io_reader_from_mem(&r, t.source, sp_cstr_len(t.source)); + + u64 num_responses = io_mock_response_count(t.responses, IO_MAX_RESPONSES); + io_mock_writer_t w = sp_zero; + io_mock_writer_init(&w, t.responses, num_responses); + + u8 copy_buf[64] = sp_zero; + u64 copied = 0; + sp_err_t err = sp_io_copy_b(&w.base, &r, copy_buf, t.buffer, &copied); + + EXPECT_EQ(err, t.expect.err); + EXPECT_EQ(copied, t.expect.copied); + u64 n = sp_cstr_len(t.expect.received); + EXPECT_EQ(w.received_len, n); + sp_for(it, n) { + EXPECT_EQ((c8)w.received[it], t.expect.received[it]); + } +} + + +////////////////////////// +// SMOKE TESTS +////////////////////////// + + +// Source fails AFTER returning some bytes in a prior call. Copy should +// preserve the bytes that did transfer. +UTEST_F(io_copy, reader_fails_after_success) { + run_io_mock_copy_reader_test(utest_result, (io_mock_copy_reader_test_t){ + .responses = { + { .bytes = 3, .data = "abc", .err = SP_OK }, + { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, + }, + .writer_capacity = 32, .buffer = 8, + .expect = { .err = SP_ERR_IO_READ_FAILED, .copied = 3, .final = "abc" }, + }); +} + +// Source returns bytes AND error in the same call. Per the orthogonal +// contract, copy should still commit those bytes to the destination. +UTEST_F(io_copy, reader_bytes_and_error) { + run_io_mock_copy_reader_test(utest_result, (io_mock_copy_reader_test_t){ + .responses = { + { .bytes = 3, .data = "abc", .err = SP_ERR_IO_READ_FAILED }, + }, + .writer_capacity = 32, .buffer = 8, + .expect = { .err = SP_ERR_IO_READ_FAILED, .copied = 3, .final = "abc" }, + }); +} + +// Writer accepts a partial prefix then signals NO_SPACE on a single call. +// Copy should report the prefix as copied and surface the error. +UTEST_F(io_copy, writer_partial_in_call) { + run_io_mock_copy_writer_test(utest_result, (io_mock_copy_writer_test_t){ + .source = "0123456789", + .responses = { + { .bytes = 4, .err = SP_ERR_IO_NO_SPACE }, + }, + .buffer = 8, + .expect = { .err = SP_ERR_IO_NO_SPACE, .copied = 4, .received = "0123" }, + }); +} + +// Writer rejects everything on the first call with zero bytes accepted. +// Copy should report copied=0 and the destination should be untouched. +UTEST_F(io_copy, writer_fails_immediately) { + run_io_mock_copy_writer_test(utest_result, (io_mock_copy_writer_test_t){ + .source = "0123456789", + .responses = { + { .bytes = 0, .err = SP_ERR_IO_WRITE_FAILED }, + }, + .buffer = 8, + .expect = { .err = SP_ERR_IO_WRITE_FAILED, .copied = 0, .received = "" }, + }); +} + + +//////////// +// ERRORS // +//////////// typedef struct { const c8* source; u64 capacity; @@ -30,7 +157,7 @@ void run_io_copy_test(int* utest_result, io_copy_test_t t) { } } -UTEST_F(io, copy_full) { +UTEST_F(io_copy, copy_full) { run_io_copy_test(utest_result, (io_copy_test_t){ .source = "0123456789", .capacity = 32, .buffer = 8, @@ -38,7 +165,7 @@ UTEST_F(io, copy_full) { }); } -UTEST_F(io, copy_loops) { +UTEST_F(io_copy, copy_loops) { run_io_copy_test(utest_result, (io_copy_test_t){ .source = "ABCDEFGHIJKLMNOP", .capacity = 32, .buffer = 4, @@ -46,7 +173,7 @@ UTEST_F(io, copy_loops) { }); } -UTEST_F(io, copy_empty) { +UTEST_F(io_copy, copy_empty) { run_io_copy_test(utest_result, (io_copy_test_t){ .source = "", .capacity = 8, .buffer = 8, @@ -54,10 +181,11 @@ UTEST_F(io, copy_empty) { }); } -UTEST_F(io, copy_writer_no_space) { +UTEST_F(io_copy, copy_writer_no_space) { run_io_copy_test(utest_result, (io_copy_test_t){ .source = "0123456789", .capacity = 4, .buffer = 8, .expect = { .err = SP_ERR_IO_NO_SPACE, .final = "0123" }, }); } + diff --git a/test/io/io.h b/test/io/io.h index 52a79d2..731b279 100644 --- a/test/io/io.h +++ b/test/io/io.h @@ -6,9 +6,6 @@ #include "utest.h" -////////////////// -// io FIXTURE // -////////////////// struct io { sp_str_t file_path; sp_test_file_manager_t file_manager; @@ -35,6 +32,7 @@ UTEST_F_TEARDOWN(io) { // SHARED MATRIX // ////////////////////// #define IO_MAX_STEPS 8 +#define IO_MAX_RESPONSES 8 typedef enum { IO_STEP_NONE, @@ -52,4 +50,40 @@ typedef struct { }; } io_step_t; +typedef struct { + u64 bytes; + sp_err_t err; + const c8* data; +} io_mock_response_t; + +typedef struct { + sp_io_reader_t base; + io_mock_response_t responses[IO_MAX_RESPONSES]; + u64 num_responses; + u64 cursor; +} io_mock_reader_t; + +typedef struct { + sp_io_writer_t base; + io_mock_response_t responses[IO_MAX_RESPONSES]; + u64 num_responses; + u64 cursor; + u8 received [256]; + u64 received_len; +} io_mock_writer_t; + +typedef struct { + sp_io_file_reader_t file; + sp_io_pipe_reader_t pipe; + sp_io_reader_t mem; + io_mock_reader_t mock; +} io_readers_t; + +u64 io_mock_response_count(const io_mock_response_t* responses, u64 max); +sp_err_t io_mock_reader_read(sp_io_reader_t* r, void* ptr, u64 size, u64* bytes_read); +sp_err_t io_mock_writer_write(sp_io_writer_t* w, const void* ptr, u64 size, u64* bytes_written); +void io_mock_reader_init(io_mock_reader_t* m, const io_mock_response_t* responses, u64 n); +void io_mock_writer_init(io_mock_writer_t* m, const io_mock_response_t* responses, u64 n); + + #endif // IO_TEST_H diff --git a/test/io/mock.c b/test/io/mock.c deleted file mode 100644 index 454d491..0000000 --- a/test/io/mock.c +++ /dev/null @@ -1,486 +0,0 @@ -#include "io.h" - -#define IO_MAX_RESPONSES 8 -#define IO_MOCK_BUFFER_MAX 256 - -typedef struct { - u64 bytes; - sp_err_t err; - const c8* data; -} io_mock_response_t; - -typedef struct { - sp_io_reader_t base; - io_mock_response_t responses[IO_MAX_RESPONSES]; - u64 num_responses; - u64 cursor; -} io_mock_reader_t; - -typedef struct { - sp_io_writer_t base; - io_mock_response_t responses[IO_MAX_RESPONSES]; - u64 num_responses; - u64 cursor; - u8 received[IO_MOCK_BUFFER_MAX]; - u64 received_len; -} io_mock_writer_t; - -static sp_err_t io_mock_reader_read(sp_io_reader_t* r, void* ptr, u64 size, u64* bytes_read) { - io_mock_reader_t* m = (io_mock_reader_t*)r; - sp_assert(m->cursor < m->num_responses); - io_mock_response_t* resp = &m->responses[m->cursor++]; - u64 n = sp_min(size, resp->bytes); - if (n && resp->data) { - sp_mem_copy(resp->data, ptr, n); - } - if (bytes_read) *bytes_read = n; - return resp->err; -} - -static sp_err_t io_mock_writer_write(sp_io_writer_t* w, const void* ptr, u64 size, u64* bytes_written) { - io_mock_writer_t* m = (io_mock_writer_t*)w; - sp_assert(m->cursor < m->num_responses); - io_mock_response_t* resp = &m->responses[m->cursor++]; - u64 n = sp_min(size, resp->bytes); - if (n) { - sp_assert(m->received_len + n <= IO_MOCK_BUFFER_MAX); - sp_mem_copy(ptr, m->received + m->received_len, n); - m->received_len += n; - } - if (bytes_written) *bytes_written = n; - return resp->err; -} - -static u64 io_mock_response_count(const io_mock_response_t* responses, u64 max) { - u64 n = 0; - sp_for(it, max) { - if (!responses[it].bytes && !responses[it].err && !responses[it].data) break; - n = it + 1; - } - return n; -} - -static void io_mock_reader_init(io_mock_reader_t* m, const io_mock_response_t* responses, u64 n) { - *m = sp_zero_s(io_mock_reader_t); - m->base.read = io_mock_reader_read; - m->num_responses = n; - sp_for(it, n) m->responses[it] = responses[it]; -} - -static void io_mock_writer_init(io_mock_writer_t* m, const io_mock_response_t* responses, u64 n) { - *m = sp_zero_s(io_mock_writer_t); - m->base.write = io_mock_writer_write; - m->num_responses = n; - sp_for(it, n) m->responses[it] = responses[it]; -} - - -////////////////////////// -// READ MOCK TEST RUNNER -////////////////////////// -typedef struct { - io_mock_response_t responses[IO_MAX_RESPONSES]; - io_step_t steps[IO_MAX_STEPS]; -} io_mock_read_test_t; - -void run_io_mock_read_test(int* utest_result, io_mock_read_test_t t) { - u64 num_responses = io_mock_response_count(t.responses, IO_MAX_RESPONSES); - io_mock_reader_t r = sp_zero; - io_mock_reader_init(&r, t.responses, num_responses); - - sp_carr_for(t.steps, j) { - const io_step_t* step = &t.steps[j]; - if (step->kind == IO_STEP_NONE) break; - - u8 dest[64] = sp_zero; - u64 bytes = 0; - sp_err_t err = sp_io_read(&r.base, dest, step->read.request, &bytes); - EXPECT_EQ(err, step->read.err); - - u64 expect_bytes = sp_cstr_len(step->read.content); - EXPECT_EQ(bytes, expect_bytes); - sp_for(it, expect_bytes) { - EXPECT_EQ((c8)dest[it], step->read.content[it]); - } - } - - EXPECT_EQ(r.cursor, r.num_responses); -} - - -////////////////////////// -// WRITE MOCK TEST RUNNER -////////////////////////// -typedef struct { - io_mock_response_t responses[IO_MAX_RESPONSES]; - io_step_t steps[IO_MAX_STEPS]; - struct { - const c8* received; - } expect; -} io_mock_write_test_t; - -void run_io_mock_write_test(int* utest_result, io_mock_write_test_t t) { - u64 num_responses = io_mock_response_count(t.responses, IO_MAX_RESPONSES); - io_mock_writer_t w = sp_zero; - io_mock_writer_init(&w, t.responses, num_responses); - - sp_carr_for(t.steps, j) { - const io_step_t* step = &t.steps[j]; - if (step->kind == IO_STEP_NONE) break; - - switch (step->kind) { - case IO_STEP_WRITE: { - u64 bytes = 0; - sp_err_t err = sp_io_write(&w.base, step->write.data, sp_cstr_len(step->write.data), &bytes); - EXPECT_EQ(err, step->write.err); - EXPECT_EQ(bytes, step->write.bytes); - break; - } - case IO_STEP_FLUSH: { - sp_err_t err = sp_io_flush(&w.base); - EXPECT_EQ(err, step->flush.err); - break; - } - case IO_STEP_NONE: - case IO_STEP_READ: { - sp_unreachable_case(); - } - } - } - - EXPECT_EQ(w.cursor, w.num_responses); - - u64 n = sp_cstr_len(t.expect.received); - EXPECT_EQ(w.received_len, n); - sp_for(it, n) { - EXPECT_EQ((c8)w.received[it], t.expect.received[it]); - } -} - - -////////////////////////// -// COPY MOCK TEST RUNNERS -////////////////////////// -typedef struct { - io_mock_response_t responses[IO_MAX_RESPONSES]; - u64 writer_capacity; - u64 buffer; - struct { - sp_err_t err; - u64 copied; - const c8* final; - } expect; -} io_mock_copy_reader_test_t; - -void run_io_mock_copy_reader_test(int* utest_result, io_mock_copy_reader_test_t t) { - u64 num_responses = io_mock_response_count(t.responses, IO_MAX_RESPONSES); - io_mock_reader_t r = sp_zero; - io_mock_reader_init(&r, t.responses, num_responses); - - u8 backing[64] = sp_zero; - sp_io_mem_writer_t w = sp_zero; - sp_io_mem_writer_from_buffer(&w, backing, t.writer_capacity); - - u8 copy_buf[64] = sp_zero; - u64 copied = 0; - sp_err_t err = sp_io_copy_b(&w.base, &r.base, copy_buf, t.buffer, &copied); - - EXPECT_EQ(err, t.expect.err); - EXPECT_EQ(copied, t.expect.copied); - u64 n = sp_cstr_len(t.expect.final); - sp_for(it, n) { - EXPECT_EQ((c8)backing[it], t.expect.final[it]); - } -} - -typedef struct { - const c8* source; - io_mock_response_t responses[IO_MAX_RESPONSES]; - u64 buffer; - struct { - sp_err_t err; - u64 copied; - const c8* received; - } expect; -} io_mock_copy_writer_test_t; - -void run_io_mock_copy_writer_test(int* utest_result, io_mock_copy_writer_test_t t) { - sp_io_reader_t r = sp_zero; - sp_io_reader_from_mem(&r, t.source, sp_cstr_len(t.source)); - - u64 num_responses = io_mock_response_count(t.responses, IO_MAX_RESPONSES); - io_mock_writer_t w = sp_zero; - io_mock_writer_init(&w, t.responses, num_responses); - - u8 copy_buf[64] = sp_zero; - u64 copied = 0; - sp_err_t err = sp_io_copy_b(&w.base, &r, copy_buf, t.buffer, &copied); - - EXPECT_EQ(err, t.expect.err); - EXPECT_EQ(copied, t.expect.copied); - u64 n = sp_cstr_len(t.expect.received); - EXPECT_EQ(w.received_len, n); - sp_for(it, n) { - EXPECT_EQ((c8)w.received[it], t.expect.received[it]); - } -} - - -////////////////////////// -// SMOKE TESTS -////////////////////////// -UTEST_F(io, mock_read_smoke) { - run_io_mock_read_test(utest_result, (io_mock_read_test_t){ - .responses = { - { .bytes = 3, .data = "abc", .err = SP_OK }, - }, - .steps = { - { .kind = IO_STEP_READ, .read = { 8, SP_OK, "abc" } }, - }, - }); -} - -UTEST_F(io, mock_write_smoke) { - run_io_mock_write_test(utest_result, (io_mock_write_test_t){ - .responses = { - { .bytes = 3, .err = SP_OK }, - }, - .steps = { - { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, - }, - .expect.received = "abc", - }); -} - - -////////////////////////// -// READ FAILURE MODES -////////////////////////// - -// Short read with OK, then EOF. Pins that short reads do NOT signal EOF. -UTEST_F(io, mock_read_short_then_eof) { - run_io_mock_read_test(utest_result, (io_mock_read_test_t){ - .responses = { - { .bytes = 3, .data = "abc", .err = SP_OK }, - { .bytes = 0, .err = SP_ERR_IO_EOF }, - }, - .steps = { - { .kind = IO_STEP_READ, .read = { 8, SP_OK, "abc" } }, - { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_EOF } }, - }, - }); -} - -// EOF returned at offset 0 (empty stream). -UTEST_F(io, mock_read_eof_empty) { - run_io_mock_read_test(utest_result, (io_mock_read_test_t){ - .responses = { - { .bytes = 0, .err = SP_ERR_IO_EOF }, - }, - .steps = { - { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_EOF } }, - }, - }); -} - -// EOF is idempotent: each repeated read returns the same terminal state. -UTEST_F(io, mock_read_eof_idempotent) { - run_io_mock_read_test(utest_result, (io_mock_read_test_t){ - .responses = { - { .bytes = 0, .err = SP_ERR_IO_EOF }, - { .bytes = 0, .err = SP_ERR_IO_EOF }, - { .bytes = 0, .err = SP_ERR_IO_EOF }, - }, - .steps = { - { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, - { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, - { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, - }, - }); -} - -// Hard error with zero bytes: not-done -> broken. -UTEST_F(io, mock_read_error_zero_bytes) { - run_io_mock_read_test(utest_result, (io_mock_read_test_t){ - .responses = { - { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, - }, - .steps = { - { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_READ_FAILED } }, - }, - }); -} - -// Successful read, then a hard error on the next call. -UTEST_F(io, mock_read_error_after_success) { - run_io_mock_read_test(utest_result, (io_mock_read_test_t){ - .responses = { - { .bytes = 4, .data = "abcd", .err = SP_OK }, - { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, - }, - .steps = { - { .kind = IO_STEP_READ, .read = { 8, SP_OK, "abcd" } }, - { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_READ_FAILED } }, - }, - }); -} - -// Orthogonal contract: backend returns bytes AND error in the same call. -// Caller must see the bytes AND the error. -UTEST_F(io, mock_read_bytes_and_error) { - run_io_mock_read_test(utest_result, (io_mock_read_test_t){ - .responses = { - { .bytes = 3, .data = "abc", .err = SP_ERR_IO_READ_FAILED }, - }, - .steps = { - { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_READ_FAILED, "abc" } }, - }, - }); -} - -// After a hard error, subsequent reads continue to report the error if the -// backend continues to surface it. Pins that the wrapper does NOT latch -// errors itself: it is the backend's job to decide whether to retry. -UTEST_F(io, mock_read_error_then_error) { - run_io_mock_read_test(utest_result, (io_mock_read_test_t){ - .responses = { - { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, - { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, - }, - .steps = { - { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_READ_FAILED } }, - { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_READ_FAILED } }, - }, - }); -} - - -////////////////////////// -// WRITE FAILURE MODES -////////////////////////// - -// Partial write paired with NO_SPACE. Caller sees both bytes_written>0 and -// the error. -UTEST_F(io, mock_write_partial_no_space) { - run_io_mock_write_test(utest_result, (io_mock_write_test_t){ - .responses = { - { .bytes = 4, .err = SP_ERR_IO_NO_SPACE }, - }, - .steps = { - { .kind = IO_STEP_WRITE, .write = { "abcdefgh", SP_ERR_IO_NO_SPACE, 4 } }, - }, - .expect.received = "abcd", - }); -} - -// Hard error with zero bytes accepted. -UTEST_F(io, mock_write_error_zero_bytes) { - run_io_mock_write_test(utest_result, (io_mock_write_test_t){ - .responses = { - { .bytes = 0, .err = SP_ERR_IO_WRITE_FAILED }, - }, - .steps = { - { .kind = IO_STEP_WRITE, .write = { "abc", SP_ERR_IO_WRITE_FAILED, 0 } }, - }, - }); -} - -// Successful write, then a hard error on the next call. -UTEST_F(io, mock_write_error_after_success) { - run_io_mock_write_test(utest_result, (io_mock_write_test_t){ - .responses = { - { .bytes = 3, .err = SP_OK }, - { .bytes = 0, .err = SP_ERR_IO_WRITE_FAILED }, - }, - .steps = { - { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, - { .kind = IO_STEP_WRITE, .write = { "xyz", SP_ERR_IO_WRITE_FAILED, 0 } }, - }, - .expect.received = "abc", - }); -} - -// Orthogonal contract for writes: partial bytes AND error in one call. -UTEST_F(io, mock_write_bytes_and_error) { - run_io_mock_write_test(utest_result, (io_mock_write_test_t){ - .responses = { - { .bytes = 2, .err = SP_ERR_IO_WRITE_FAILED }, - }, - .steps = { - { .kind = IO_STEP_WRITE, .write = { "abc", SP_ERR_IO_WRITE_FAILED, 2 } }, - }, - .expect.received = "ab", - }); -} - -// After a NO_SPACE, a smaller write that fits should still succeed: the -// wrapper does not latch the error. -UTEST_F(io, mock_write_smaller_after_overflow) { - run_io_mock_write_test(utest_result, (io_mock_write_test_t){ - .responses = { - { .bytes = 0, .err = SP_ERR_IO_NO_SPACE }, - { .bytes = 2, .err = SP_OK }, - }, - .steps = { - { .kind = IO_STEP_WRITE, .write = { "abcd", SP_ERR_IO_NO_SPACE, 0 } }, - { .kind = IO_STEP_WRITE, .write = { "xy", SP_OK, 2 } }, - }, - .expect.received = "xy", - }); -} - - -////////////////////////// -// COPY FAILURE MODES -////////////////////////// - -// Source fails AFTER returning some bytes in a prior call. Copy should -// preserve the bytes that did transfer. -UTEST_F(io, mock_copy_reader_fails_after_success) { - run_io_mock_copy_reader_test(utest_result, (io_mock_copy_reader_test_t){ - .responses = { - { .bytes = 3, .data = "abc", .err = SP_OK }, - { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, - }, - .writer_capacity = 32, .buffer = 8, - .expect = { .err = SP_ERR_IO_READ_FAILED, .copied = 3, .final = "abc" }, - }); -} - -// Source returns bytes AND error in the same call. Per the orthogonal -// contract, copy should still commit those bytes to the destination. -UTEST_F(io, mock_copy_reader_bytes_and_error) { - run_io_mock_copy_reader_test(utest_result, (io_mock_copy_reader_test_t){ - .responses = { - { .bytes = 3, .data = "abc", .err = SP_ERR_IO_READ_FAILED }, - }, - .writer_capacity = 32, .buffer = 8, - .expect = { .err = SP_ERR_IO_READ_FAILED, .copied = 3, .final = "abc" }, - }); -} - -// Writer accepts a partial prefix then signals NO_SPACE on a single call. -// Copy should report the prefix as copied and surface the error. -UTEST_F(io, mock_copy_writer_partial_in_call) { - run_io_mock_copy_writer_test(utest_result, (io_mock_copy_writer_test_t){ - .source = "0123456789", - .responses = { - { .bytes = 4, .err = SP_ERR_IO_NO_SPACE }, - }, - .buffer = 8, - .expect = { .err = SP_ERR_IO_NO_SPACE, .copied = 4, .received = "0123" }, - }); -} - -// Writer rejects everything on the first call with zero bytes accepted. -// Copy should report copied=0 and the destination should be untouched. -UTEST_F(io, mock_copy_writer_fails_immediately) { - run_io_mock_copy_writer_test(utest_result, (io_mock_copy_writer_test_t){ - .source = "0123456789", - .responses = { - { .bytes = 0, .err = SP_ERR_IO_WRITE_FAILED }, - }, - .buffer = 8, - .expect = { .err = SP_ERR_IO_WRITE_FAILED, .copied = 0, .received = "" }, - }); -} diff --git a/test/io/read.c b/test/io/read.c index 7af84f6..c5e7c08 100644 --- a/test/io/read.c +++ b/test/io/read.c @@ -1,5 +1,7 @@ #include "io.h" +UTEST_EMPTY_FIXTURE(io_read) + typedef struct { const c8* source; io_step_t steps [IO_MAX_STEPS]; @@ -26,7 +28,7 @@ void run_io_read_test(int* utest_result, io_read_test_t t) { } } -UTEST_F(io, read_exact_then_eof) { +UTEST_F(io_read, read_exact_then_eof) { run_io_read_test(utest_result, (io_read_test_t){ .source = "0123", .steps = { @@ -36,7 +38,7 @@ UTEST_F(io, read_exact_then_eof) { }); } -UTEST_F(io, read_chunked) { +UTEST_F(io_read, read_chunked) { run_io_read_test(utest_result, (io_read_test_t){ .source = "01234567", .steps = { @@ -48,7 +50,7 @@ UTEST_F(io, read_chunked) { }); } -UTEST_F(io, read_oversized_request) { +UTEST_F(io_read, read_oversized_request) { run_io_read_test(utest_result, (io_read_test_t){ .source = "abc", .steps = { @@ -58,7 +60,7 @@ UTEST_F(io, read_oversized_request) { }); } -UTEST_F(io, read_empty_source) { +UTEST_F(io_read, read_empty_source) { run_io_read_test(utest_result, (io_read_test_t){ .source = "", .steps = { @@ -67,7 +69,7 @@ UTEST_F(io, read_empty_source) { }); } -UTEST_F(io, read_eof_idempotent) { +UTEST_F(io_read, read_eof_idempotent) { run_io_read_test(utest_result, (io_read_test_t){ .source = "x", .steps = { @@ -78,7 +80,7 @@ UTEST_F(io, read_eof_idempotent) { }); } -UTEST_F(io, read_zero_request) { +UTEST_F(io_read, read_zero_request) { run_io_read_test(utest_result, (io_read_test_t){ .source = "abc", .steps = { @@ -87,3 +89,146 @@ UTEST_F(io, read_zero_request) { }, }); } + + +//////////// +// ERRORS // +//////////// +typedef struct { + io_mock_response_t responses[IO_MAX_RESPONSES]; + io_step_t steps[IO_MAX_STEPS]; +} io_mock_read_test_t; + +void run_io_mock_read_test(int* utest_result, io_mock_read_test_t t) { + u64 num_responses = io_mock_response_count(t.responses, IO_MAX_RESPONSES); + io_mock_reader_t r = sp_zero; + io_mock_reader_init(&r, t.responses, num_responses); + + sp_carr_for(t.steps, j) { + const io_step_t* step = &t.steps[j]; + if (step->kind == IO_STEP_NONE) break; + + u8 dest[64] = sp_zero; + u64 bytes = 0; + sp_err_t err = sp_io_read(&r.base, dest, step->read.request, &bytes); + EXPECT_EQ(err, step->read.err); + + u64 expect_bytes = sp_cstr_len(step->read.content); + EXPECT_EQ(bytes, expect_bytes); + sp_for(it, expect_bytes) { + EXPECT_EQ((c8)dest[it], step->read.content[it]); + } + } + + EXPECT_EQ(r.cursor, r.num_responses); +} + + +UTEST_F(io_read, smoke) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .responses = { + { .bytes = 3, .data = "abc", .err = SP_OK }, + }, + .steps = { + { .kind = IO_STEP_READ, .read = { 8, SP_OK, "abc" } }, + }, + }); +} + +// Short read with OK, then EOF. Pins that short reads do NOT signal EOF. +UTEST_F(io_read, short_then_eof) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .responses = { + { .bytes = 3, .data = "abc", .err = SP_OK }, + { .bytes = 0, .err = SP_ERR_IO_EOF }, + }, + .steps = { + { .kind = IO_STEP_READ, .read = { 8, SP_OK, "abc" } }, + { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_EOF } }, + }, + }); +} + +// EOF returned at offset 0 (empty stream). +UTEST_F(io_read, eof_empty) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .responses = { + { .bytes = 0, .err = SP_ERR_IO_EOF }, + }, + .steps = { + { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_EOF } }, + }, + }); +} + +// EOF is idempotent: each repeated read returns the same terminal state. +UTEST_F(io_read, eof_idempotent) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .responses = { + { .bytes = 0, .err = SP_ERR_IO_EOF }, + { .bytes = 0, .err = SP_ERR_IO_EOF }, + { .bytes = 0, .err = SP_ERR_IO_EOF }, + }, + .steps = { + { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, + { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, + { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, + }, + }); +} + +// Hard error with zero bytes: not-done -> broken. +UTEST_F(io_read, error_zero_bytes) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .responses = { + { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, + }, + .steps = { + { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_READ_FAILED } }, + }, + }); +} + +// Successful read, then a hard error on the next call. +UTEST_F(io_read, error_after_success) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .responses = { + { .bytes = 4, .data = "abcd", .err = SP_OK }, + { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, + }, + .steps = { + { .kind = IO_STEP_READ, .read = { 8, SP_OK, "abcd" } }, + { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_READ_FAILED } }, + }, + }); +} + +// Orthogonal contract: backend returns bytes AND error in the same call. +// Caller must see the bytes AND the error. +UTEST_F(io_read, bytes_and_error) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .responses = { + { .bytes = 3, .data = "abc", .err = SP_ERR_IO_READ_FAILED }, + }, + .steps = { + { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_READ_FAILED, "abc" } }, + }, + }); +} + +// After a hard error, subsequent reads continue to report the error if the +// backend continues to surface it. Pins that the wrapper does NOT latch +// errors itself: it is the backend's job to decide whether to retry. +UTEST_F(io_read, error_then_error) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .responses = { + { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, + { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, + }, + .steps = { + { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_READ_FAILED } }, + { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_READ_FAILED } }, + }, + }); +} + diff --git a/test/io/write.c b/test/io/write.c index 2c10771..1017997 100644 --- a/test/io/write.c +++ b/test/io/write.c @@ -1,5 +1,144 @@ #include "io.h" +UTEST_EMPTY_FIXTURE(io_write) + +typedef struct { + io_mock_response_t responses[IO_MAX_RESPONSES]; + io_step_t steps[IO_MAX_STEPS]; + struct { + const c8* received; + } expect; +} io_mock_write_test_t; + +//////////// +// RUNNER // +//////////// +void run_io_mock_write_test(int* utest_result, io_mock_write_test_t t) { + u64 num_responses = io_mock_response_count(t.responses, IO_MAX_RESPONSES); + io_mock_writer_t w = sp_zero; + io_mock_writer_init(&w, t.responses, num_responses); + + sp_carr_for(t.steps, j) { + const io_step_t* step = &t.steps[j]; + if (step->kind == IO_STEP_NONE) break; + + switch (step->kind) { + case IO_STEP_WRITE: { + u64 bytes = 0; + sp_err_t err = sp_io_write(&w.base, step->write.data, sp_cstr_len(step->write.data), &bytes); + EXPECT_EQ(err, step->write.err); + EXPECT_EQ(bytes, step->write.bytes); + break; + } + case IO_STEP_FLUSH: { + sp_err_t err = sp_io_flush(&w.base); + EXPECT_EQ(err, step->flush.err); + break; + } + case IO_STEP_NONE: + case IO_STEP_READ: { + sp_unreachable_case(); + } + } + } + + EXPECT_EQ(w.cursor, w.num_responses); + + u64 n = sp_cstr_len(t.expect.received); + EXPECT_EQ(w.received_len, n); + sp_for(it, n) { + EXPECT_EQ((c8)w.received[it], t.expect.received[it]); + } +} + + + +UTEST_F(io_write, smoke) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .responses = { + { .bytes = 3, .err = SP_OK }, + }, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, + }, + .expect.received = "abc", + }); +} + +// Partial write paired with NO_SPACE. Caller sees both bytes_written>0 and +// the error. +UTEST_F(io_write, partial_no_space) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .responses = { + { .bytes = 4, .err = SP_ERR_IO_NO_SPACE }, + }, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abcdefgh", SP_ERR_IO_NO_SPACE, 4 } }, + }, + .expect.received = "abcd", + }); +} + +// Hard error with zero bytes accepted. +UTEST_F(io_write, error_zero_bytes) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .responses = { + { .bytes = 0, .err = SP_ERR_IO_WRITE_FAILED }, + }, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abc", SP_ERR_IO_WRITE_FAILED, 0 } }, + }, + }); +} + +// Successful write, then a hard error on the next call. +UTEST_F(io_write, error_after_success) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .responses = { + { .bytes = 3, .err = SP_OK }, + { .bytes = 0, .err = SP_ERR_IO_WRITE_FAILED }, + }, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, + { .kind = IO_STEP_WRITE, .write = { "xyz", SP_ERR_IO_WRITE_FAILED, 0 } }, + }, + .expect.received = "abc", + }); +} + +// Orthogonal contract for writes: partial bytes AND error in one call. +UTEST_F(io_write, bytes_and_error) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .responses = { + { .bytes = 2, .err = SP_ERR_IO_WRITE_FAILED }, + }, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abc", SP_ERR_IO_WRITE_FAILED, 2 } }, + }, + .expect.received = "ab", + }); +} + +// After a NO_SPACE, a smaller write that fits should still succeed: the +// wrapper does not latch the error. +UTEST_F(io_write, smaller_after_overflow) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .responses = { + { .bytes = 0, .err = SP_ERR_IO_NO_SPACE }, + { .bytes = 2, .err = SP_OK }, + }, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abcd", SP_ERR_IO_NO_SPACE, 0 } }, + { .kind = IO_STEP_WRITE, .write = { "xy", SP_OK, 2 } }, + }, + .expect.received = "xy", + }); +} + + +//////////// +// ERRORS // +//////////// typedef struct { u64 capacity; io_step_t steps [IO_MAX_STEPS]; @@ -42,7 +181,7 @@ void run_io_write_test(int* utest_result, io_write_test_t t) { } } -UTEST_F(io, write_fits) { +UTEST_F(io_write, write_fits) { run_io_write_test(utest_result, (io_write_test_t){ .capacity = 16, .steps = { @@ -52,7 +191,7 @@ UTEST_F(io, write_fits) { }); } -UTEST_F(io, write_exact_fit) { +UTEST_F(io_write, write_exact_fit) { run_io_write_test(utest_result, (io_write_test_t){ .capacity = 4, .steps = { @@ -62,7 +201,7 @@ UTEST_F(io, write_exact_fit) { }); } -UTEST_F(io, write_overflow) { +UTEST_F(io_write, write_overflow) { run_io_write_test(utest_result, (io_write_test_t){ .capacity = 4, .steps = { @@ -72,7 +211,7 @@ UTEST_F(io, write_overflow) { }); } -UTEST_F(io, write_barely_overflows) { +UTEST_F(io_write, write_barely_overflows) { run_io_write_test(utest_result, (io_write_test_t){ .capacity = 4, .steps = { @@ -82,7 +221,7 @@ UTEST_F(io, write_barely_overflows) { }); } -UTEST_F(io, write_smaller_after_overflow) { +UTEST_F(io_write, write_smaller_after_overflow) { run_io_write_test(utest_result, (io_write_test_t){ .capacity = 4, .steps = { @@ -93,7 +232,7 @@ UTEST_F(io, write_smaller_after_overflow) { }); } -UTEST_F(io, write_appends) { +UTEST_F(io_write, write_appends) { run_io_write_test(utest_result, (io_write_test_t){ .capacity = 16, .steps = { @@ -105,7 +244,7 @@ UTEST_F(io, write_appends) { }); } -UTEST_F(io, write_zero) { +UTEST_F(io_write, write_zero) { run_io_write_test(utest_result, (io_write_test_t){ .capacity = 8, .steps = { @@ -114,7 +253,7 @@ UTEST_F(io, write_zero) { }); } -UTEST_F(io, write_flush_empty) { +UTEST_F(io_write, write_flush_empty) { run_io_write_test(utest_result, (io_write_test_t){ .capacity = 8, .steps = { @@ -122,3 +261,4 @@ UTEST_F(io, write_flush_empty) { }, }); } + diff --git a/tools/utest.h b/tools/utest.h index 98f2040..eb526e1 100644 --- a/tools/utest.h +++ b/tools/utest.h @@ -623,6 +623,11 @@ static UTEST_INLINE int utest_strncmp(const c8 *a, const c8 *b, u32 n) { static void utest_f_teardown_##FIXTURE(int *utest_result, \ struct FIXTURE *utest_fixture) +#define UTEST_EMPTY_FIXTURE(FIXTURE) \ + struct FIXTURE { u32 placeholder; }; \ + UTEST_F_SETUP(FIXTURE) {} \ + UTEST_F_TEARDOWN(FIXTURE) {} + #define UTEST_F(FIXTURE, NAME) \ UTEST_SURPRESS_WARNINGS_BEGIN \ UTEST_EXTERN struct utest_state_s utest_state; \ From e3b2439677a9ea7c7c45e35d4afab77f71a747fe Mon Sep 17 00:00:00 2001 From: Thomas Spader Date: Tue, 12 May 2026 16:18:33 -0400 Subject: [PATCH 09/21] mem reorder --- Makefile | 2 +- sp.h | 78 +++++++++++++++++++++++----------------------- sp/sp_elf.h | 12 +++---- sp/sp_macho.h | 6 ++-- sp/sp_prompt.h | 4 +-- test/elf.c | 6 ++-- test/io.c | 10 +++--- test/mem/builtin.c | 4 +-- test/tools/test.h | 2 +- tools/utest.h | 14 ++++----- 10 files changed, 69 insertions(+), 69 deletions(-) diff --git a/Makefile b/Makefile index 66f162e..d0c8bed 100644 --- a/Makefile +++ b/Makefile @@ -89,7 +89,7 @@ $(EXAMPLE_DIR)/%$(EXE): example/%.c sp.h | $(EXAMPLE_DIR) ######### # TESTS # ######### -CFLAGS_TEST = -DSP_IMPLEMENTATION -DSP_TEST_IMPLEMENTATION -I. -Itools -Itest/tools -Itest/tools/process -Itest/fs -Itest/mem -Itest/io +CFLAGS_TEST = -DSP_IMPLEMENTATION -DSP_TEST_IMPLEMENTATION -I. -Itools -Itest/tools -Itest/tools/process -Itest TEST_SOURCES = $(wildcard test/*/*.c) $(wildcard test/*/*.h) $(wildcard test/*/*/*.c) $(wildcard test/*/*/*.h) diff --git a/sp.h b/sp.h index 3fce949..e3c3f20 100644 --- a/sp.h +++ b/sp.h @@ -1459,8 +1459,8 @@ typedef struct { u8 alignment; } sp_mem_fixed_t; -SP_API void sp_mem_copy(const void* source, void* dest, u64 num_bytes); -SP_API void sp_mem_move(const void* source, void* dest, u64 num_bytes); +SP_API void sp_mem_copy(void* dest, const void* source, u64 num_bytes); +SP_API void sp_mem_move(void* dest, const void* source, u64 num_bytes); SP_API bool sp_mem_is_equal(const void* a, const void* b, u64 len); SP_API void sp_mem_fill(void* buffer, u64 bsize, void* fill, u64 fsize); SP_API void sp_mem_fill_u8(void* buffer, u64 buffer_size, u8 fill); @@ -4475,7 +4475,7 @@ s32 sp_sys_rename(const c8* from, u32 from_len, const c8* to, u32 to_len) { *info = sp_zero_s(sp_nt_file_rename_information_t); info->ReplaceIfExists = 1; info->FileNameLength = name_bytes; - sp_mem_copy(nt.name.Buffer, info->FileName, name_bytes); + sp_mem_copy(info->FileName, nt.name.Buffer, name_bytes); sp_nt_io_status_block_t iosb = sp_zero; sp_nt_status_t status = SP_NT(NtSetInformationFile)(handle, &iosb, info, info_bytes, SP_NT_FILE_RENAME_INFORMATION); @@ -5933,7 +5933,7 @@ s64 sp_sys_getcwd(char* buf, u64 size) { if (wlen && cwd->Buffer[wlen - 1] == '\\') wlen--; sp_str_t utf8 = sp_wtf16_to_wtf8_a(sp_mem_get_scratch(), (sp_wide_str_t) { .data = cwd->Buffer, .len = wlen }); if (utf8.len >= size) return -1; - sp_mem_copy(utf8.data, buf, utf8.len); + sp_mem_copy(buf, utf8.data, utf8.len); buf[utf8.len] = 0; return (s64)utf8.len; } @@ -5984,7 +5984,7 @@ s32 sp_sys_link(const c8* existing, u32 existing_len, const c8* alias, u32 alias *info = sp_zero_s(sp_nt_file_link_information_t); info->ReplaceIfExists = 0; info->FileNameLength = name_bytes; - sp_mem_copy(nt.name.Buffer, info->FileName, name_bytes); + sp_mem_copy(info->FileName, nt.name.Buffer, name_bytes); sp_nt_io_status_block_t iosb = sp_zero; sp_nt_status_t status = SP_NT(NtSetInformationFile)(handle, &iosb, info, info_bytes, SP_NT_FILE_LINK_INFORMATION); @@ -6168,7 +6168,7 @@ s64 sp_sys_canonicalize_path(const c8* path, u32 len, c8* buf, u64 size) { if (wlen >= 4 && wbuf[0] == '\\' && wbuf[1] == '\\' && wbuf[2] == '?' && wbuf[3] == '\\') skip = 4; sp_str_t utf8 = sp_wtf16_to_wtf8_a(sp_mem_get_scratch(), (sp_wide_str_t) { .data = wbuf + skip, .len = wlen - skip }); if (utf8.len >= size) return -1; - sp_mem_copy(utf8.data, buf, utf8.len); + sp_mem_copy(buf, utf8.data, utf8.len); buf[utf8.len] = 0; return (s64)utf8.len; } @@ -6227,7 +6227,7 @@ s64 sp_sys_get_exe_path(c8* buf, u64 size) { u32 wlen = image->Length / (u32)sizeof(u16); sp_str_t utf8 = sp_wtf16_to_wtf8_a(sp_mem_get_scratch(), (sp_wide_str_t) { .data = image->Buffer, .len = wlen }); if (utf8.len >= size) return -1; - sp_mem_copy(utf8.data, buf, utf8.len); + sp_mem_copy(buf, utf8.data, utf8.len); buf[utf8.len] = 0; return (s64)utf8.len; } @@ -6477,7 +6477,7 @@ void sp_ht_resize_impl(void** data, u64 old_cap, u64 new_cap, sp_ht_info_t info) new_idx = (new_idx + 1) % new_cap; } - sp_mem_copy((c8*)old_data + offset, (c8*)new_data + new_idx * info.stride.entry, info.stride.kv); + sp_mem_copy((c8*)new_data + new_idx * info.stride.entry, (c8*)old_data + offset, info.stride.kv); *(sp_ht_entry_state*)((c8*)new_data + new_idx * info.stride.entry + info.stride.kv) = SP_HT_ENTRY_ACTIVE; } @@ -6519,15 +6519,15 @@ void sp_ht_insert_impl(void* ht, void* key, void* val, sp_ht_info_t info) { void* k = (u8*)(*data) + offset; if (info.fn.compare(k, key, info.size.key)) { u8* entry = (u8*)(*data) + offset; - sp_mem_copy(val, entry + info.stride.value, info.size.value); + sp_mem_copy(entry + info.stride.value, val, info.size.value); return; } } u64 idx = first_free != SP_HT_INVALID_INDEX ? first_free : hash_idx; u8* entry = (u8*)(*data) + idx * info.stride.entry; - sp_mem_copy(key, entry, info.size.key); - sp_mem_copy(val, entry + info.stride.value, info.size.value); + sp_mem_copy(entry, key, info.size.key); + sp_mem_copy(entry + info.stride.value, val, info.size.value); *(sp_ht_entry_state*)(entry + info.stride.kv) = SP_HT_ENTRY_ACTIVE; (*size)++; } @@ -6594,7 +6594,7 @@ void* sp_da_grow_ex(void* arr, u32 stride, u64 n) { void sp_da_push_ex(void** arr, void* val, u32 stride) { *arr = sp_da_grow_ex(*arr, stride, 1); if (*arr) { - sp_mem_copy(val, ((u8*)(*arr)) + sp_da_size(*arr) * stride, stride); + sp_mem_copy(((u8*)(*arr)) + sp_da_size(*arr) * stride, val, stride); sp_da_head(*arr)->size++; } } @@ -6654,11 +6654,11 @@ void* sp_rb_grow_ex(void* arr, u32 stride, u32 capacity) { u32 first_chunk = old_cap - old_head; if (first_chunk > old_size) first_chunk = old_size; - sp_mem_copy(old_arr + old_head * stride, new_arr, first_chunk * stride); + sp_mem_copy(new_arr, old_arr + old_head * stride, first_chunk * stride); u32 second_chunk = old_size - first_chunk; if (second_chunk > 0) { - sp_mem_copy(old_arr, new_arr + first_chunk * stride, second_chunk * stride); + sp_mem_copy(new_arr + first_chunk * stride, old_arr, second_chunk * stride); } sp_mem_allocator_free(mem, old); @@ -7932,7 +7932,7 @@ void* sp_mem_arena_on_alloc(void* user_data, sp_mem_alloc_mode_t mode, u64 size, void* ptr = sp_mem_arena_alloc_with_header(arena, size); if (old_memory) { sp_mem_arena_header_t* header = sp_mem_arena_get_header(old_memory); - sp_mem_move(old_memory, ptr, sp_min(header->size, size)); + sp_mem_move(ptr, old_memory, sp_min(header->size, size)); } return ptr; } @@ -8050,14 +8050,14 @@ bool sp_mem_is_equal(const void* a, const void* b, u64 len) { return !sp_sys_memcmp(a, b, len); } -void sp_mem_copy(const void* source, void* dest, u64 num_bytes) { +void sp_mem_copy(void* dest, const void* source, u64 num_bytes) { if (!source) return; if (!dest) return; if (!num_bytes) return; sp_sys_memcpy(dest, source, num_bytes); } -void sp_mem_move(const void* source, void* dest, u64 num_bytes) { +void sp_mem_move(void* dest, const void* source, u64 num_bytes) { if (!source) return; if (!dest) return; sp_sys_memmove(dest, source, num_bytes); @@ -8069,7 +8069,7 @@ void sp_mem_fill(void* buffer, u64 buffer_size, void* fill, u64 fill_size) { u64 i = 0; while (true) { if (i + fill_size > buffer_size) return; - sp_mem_copy((u8*)fill, current_byte + i, fill_size); + sp_mem_copy(current_byte + i, (u8*)fill, fill_size); i += fill_size; } } @@ -8113,7 +8113,7 @@ void* sp_mem_os_realloc(void* ptr, u64 size) { void* mem = sp_mem_os_alloc(size); if (!mem) return SP_NULLPTR; - sp_mem_copy(ptr, mem, header->size); + sp_mem_copy(mem, ptr, header->size); sp_mem_os_free(ptr); return mem; } @@ -8522,7 +8522,7 @@ sp_str_t sp_str(const c8* str, u32 len) { sp_str_t sp_str_copy_a(sp_mem_t mem, sp_str_t str) { if (!str.data || !str.len) return sp_zero_s(sp_str_t); c8* buffer = sp_alloc_n_a(mem, c8, str.len); - sp_mem_copy(str.data, buffer, str.len); + sp_mem_copy(buffer, str.data, str.len); return sp_str(buffer, str.len); } @@ -8674,7 +8674,7 @@ sp_str_t sp_str_sub_reverse(sp_str_t str, s32 index, s32 len) { c8* sp_cstr_from_str_a(sp_mem_t mem, sp_str_t str) { c8* buffer = (c8*)sp_mem_allocator_alloc(mem, str.len + 1); - if (str.len) sp_mem_copy(str.data, buffer, str.len); + if (str.len) sp_mem_copy(buffer, str.data, str.len); buffer[str.len] = '\0'; return buffer; } @@ -8684,7 +8684,7 @@ sp_str_t sp_str_from_cstr_n_a(sp_mem_t mem, const c8* str, u32 length) { u32 len = sp_min(sp_cstr_len(str), length); if (!len) return sp_zero_s(sp_str_t); c8* buffer = (c8*)sp_mem_allocator_alloc(mem, length); - sp_mem_copy(str, buffer, len); + sp_mem_copy(buffer, str, len); return sp_str(buffer, len); } @@ -8694,14 +8694,14 @@ sp_str_t sp_str_from_cstr_a(sp_mem_t mem, const c8* str) { u32 len = sp_cstr_len(str); if (!len) return sp_zero_s(sp_str_t); c8* buffer = sp_alloc_n_a(mem, c8, len + 1); - sp_mem_copy(str, buffer, len); + sp_mem_copy(buffer, str, len); return sp_str(buffer, len); } void sp_str_copy_to(sp_str_t str, c8* buffer, u32 capacity) { if (!str.data) return; - sp_mem_copy(str.data, buffer, sp_min(str.len, capacity)); + sp_mem_copy(buffer, str.data, sp_min(str.len, capacity)); } sp_str_it_t sp_str_it(sp_str_t str) { @@ -8725,8 +8725,8 @@ sp_str_t sp_str_concat_a(sp_mem_t mem, sp_str_t a, sp_str_t b) { u32 len = a.len + b.len; if (!len) return sp_zero_s(sp_str_t); c8* buffer = (c8*)sp_mem_allocator_alloc(mem, len); - if (a.len) sp_mem_copy(a.data, buffer, a.len); - if (b.len) sp_mem_copy(b.data, buffer + a.len, b.len); + if (a.len) sp_mem_copy(buffer, a.data, a.len); + if (b.len) sp_mem_copy(buffer + a.len, b.data, b.len); return sp_str(buffer, len); } @@ -8735,9 +8735,9 @@ sp_str_t sp_str_join_a(sp_mem_t mem, sp_str_t a, sp_str_t b, sp_str_t join) { if (!len) return sp_zero_s(sp_str_t); c8* buffer = (c8*)sp_mem_allocator_alloc(mem, len); c8* p = buffer; - if (a.len) { sp_mem_copy(a.data, p, a.len); p += a.len; } - if (join.len) { sp_mem_copy(join.data, p, join.len); p += join.len; } - if (b.len) { sp_mem_copy(b.data, p, b.len); } + if (a.len) { sp_mem_copy(p, a.data, a.len); p += a.len; } + if (join.len) { sp_mem_copy(p, join.data, join.len); p += join.len; } + if (b.len) { sp_mem_copy(p, b.data, b.len); } return sp_str(buffer, len); } @@ -8981,13 +8981,13 @@ sp_str_t sp_str_map_kernel_append(sp_str_map_context_t* context) { sp_str_t sp_str_map_kernel_prefix(sp_str_map_context_t* context) { u32 len; - sp_mem_copy(context->user_data, &len, sizeof(len)); + sp_mem_copy(&len, context->user_data, sizeof(len)); return sp_str_sub(context->str, 0, len); } sp_str_t sp_str_map_kernel_pad(sp_str_map_context_t* context) { u32 len; - sp_mem_copy(context->user_data, &len, sizeof(len)); + sp_mem_copy(&len, context->user_data, sizeof(len)); return sp_str_pad_a(context->mem, context->str, len); } @@ -9077,7 +9077,7 @@ s32 sp_sys_fs_it_open(sp_sys_fs_it_t* it, const c8* path, u32 path_len, void* bu u32 prefixed_len = 4 + (nt.name.Length / sizeof(u16)) - w_off; u16 wpat [SP_PATH_MAX + 8]; wpat[0] = '\\'; wpat[1] = '\\'; wpat[2] = '?'; wpat[3] = '\\'; - sp_mem_copy(nt.name.Buffer + w_off, wpat + 4, nt.name.Length - w_off * sizeof(u16)); + sp_mem_copy(wpat + 4, nt.name.Buffer + w_off, nt.name.Length - w_off * sizeof(u16)); wpat[prefixed_len] = 0; sp_sys_nt_path_free(&nt); @@ -11082,9 +11082,9 @@ c8** sp_env_to_posix_envp_a(sp_mem_t mem, sp_env_t* env) { u32 size = key.len + 1 + val.len + 1; c8* entry = sp_alloc_n_a(mem, c8, size); - sp_mem_copy(key.data, entry, key.len); + sp_mem_copy(entry, key.data, key.len); entry[key.len] = '='; - sp_mem_copy(val.data, entry + key.len + 1, val.len); + sp_mem_copy(entry + key.len + 1, val.data, val.len); entry[size - 1] = 0; sp_da_push(envp, entry); } @@ -13272,7 +13272,7 @@ sp_err_t sp_io_read(sp_io_reader_t* reader, void* ptr, u64 size, u64* bytes_read // Drain what we can from what's already buffered num_drained = sp_min(size, reader->buffer.len - reader->cursor); - sp_mem_copy(reader->buffer.data + reader->cursor, buffer, num_drained); + sp_mem_copy(buffer, reader->buffer.data + reader->cursor, num_drained); reader->cursor += num_drained; // Issue a call to the backend for the rest @@ -13289,7 +13289,7 @@ sp_err_t sp_io_read(sp_io_reader_t* reader, void* ptr, u64 size, u64* bytes_read num_read = sp_min(remaining, reader->buffer.len); reader->cursor = num_read; - sp_mem_copy(reader->buffer.data, buffer + num_drained, num_read); + sp_mem_copy(buffer + num_drained, reader->buffer.data, num_read); } u64 num_total = num_drained + num_read; @@ -13511,7 +13511,7 @@ sp_err_t sp_io_write(sp_io_writer_t* writer, const void* data, u64 size, u64* by if (writer->buffer.capacity - writer->buffer.len < size) { sp_try_goto(sp_io_flush(writer), err, done); } - sp_mem_copy(ptr, writer->buffer.data + writer->buffer.len, size); + sp_mem_copy(writer->buffer.data + writer->buffer.len, ptr, size); writer->buffer.len += size; total = size; } @@ -13737,7 +13737,7 @@ sp_err_t sp_io_mem_writer_write(sp_io_writer_t* writer, const void* ptr, u64 siz goto done; } - sp_mem_copy(ptr, w->ptr + w->pos, size); + sp_mem_copy(w->ptr + w->pos, ptr, size); w->pos += size; written = size; @@ -13807,7 +13807,7 @@ sp_err_t sp_io_dyn_mem_writer_write(sp_io_writer_t* writer, const void* ptr, u64 w->storage.capacity = new_capacity; } - sp_mem_copy(ptr, w->storage.data + w->cursor, size); + sp_mem_copy(w->storage.data + w->cursor, ptr, size); w->cursor += size; if (w->cursor > w->storage.len) { w->storage.len = w->cursor; diff --git a/sp/sp_elf.h b/sp/sp_elf.h index dc985d5..aad2335 100644 --- a/sp/sp_elf.h +++ b/sp/sp_elf.h @@ -316,7 +316,7 @@ u32 sp_elf_add_string(sp_elf_section_t* strtab, sp_str_t str) { u32 index = (u32)strtab->buffer.size; u8* p = sp_elf_section_reserve_bytes(strtab, str.len + 1); - sp_mem_copy(str.data, p, str.len); + sp_mem_copy(p, str.data, str.len); p[str.len] = 0; return index; @@ -449,7 +449,7 @@ void sp_elf_symtab_sort(sp_elf_section_t* symtab, sp_elf_t* elf) { } } - sp_mem_copy(new_syms, syms, count * sizeof(Elf64_Sym)); + sp_mem_copy(syms, new_syms, count * sizeof(Elf64_Sym)); symtab->info = first_nonlocal; if (elf) { @@ -608,7 +608,7 @@ sp_elf_t* sp_elf_read(sp_mem_t mem, const u8* data, u64 size) { sp_require_as_null(size >= sizeof(Elf64_Ehdr)); Elf64_Ehdr ehdr; - sp_mem_copy(data, &ehdr, sizeof(Elf64_Ehdr)); + sp_mem_copy(&ehdr, data, sizeof(Elf64_Ehdr)); u16 num_sections = ehdr.e_shnum; sp_require_as_null(ehdr.e_shstrndx < num_sections); @@ -639,7 +639,7 @@ sp_elf_t* sp_elf_read(sp_mem_t mem, const u8* data, u64 size) { sp_elf_t* elf = sp_elf_new(mem); Elf64_Shdr* section_headers = sp_alloc_n_a(elf->mem, Elf64_Shdr, num_sections); - sp_mem_copy(data + ehdr.e_shoff, section_headers, sh_table_bytes); + sp_mem_copy(section_headers, data + ehdr.e_shoff, sh_table_bytes); Elf64_Shdr* string_header = §ion_headers[ehdr.e_shstrndx]; @@ -650,7 +650,7 @@ sp_elf_t* sp_elf_read(sp_mem_t mem, const u8* data, u64 size) { return SP_NULLPTR; } string_table = sp_alloc_n_a(elf->mem, c8, string_header->sh_size); - sp_mem_copy(data + string_header->sh_offset, string_table, string_header->sh_size); + sp_mem_copy(string_table, data + string_header->sh_offset, string_header->sh_size); } sp_assert(section_headers[0].sh_type == SHT_NULL); @@ -691,7 +691,7 @@ sp_elf_t* sp_elf_read(sp_mem_t mem, const u8* data, u64 size) { return SP_NULLPTR; } u8* ptr = sp_elf_section_reserve_bytes(section, header->sh_size); - sp_mem_copy(data + header->sh_offset, ptr, header->sh_size); + sp_mem_copy(ptr, data + header->sh_offset, header->sh_size); break; } } diff --git a/sp/sp_macho.h b/sp/sp_macho.h index f81b7b0..1a4b0ba 100644 --- a/sp/sp_macho.h +++ b/sp/sp_macho.h @@ -102,7 +102,7 @@ u8* sp_macho_section_push(sp_macho_t* m, u32 sect, const void* data, u64 size) { sp_da_head(s->data)->size = (u32)new_size; u8* dest = s->data + old_size; if (data) { - sp_mem_copy(data, dest, (u32)size); + sp_mem_copy(dest, data, (u32)size); } return dest; @@ -233,8 +233,8 @@ sp_err_t sp_macho_write(sp_macho_t* m, sp_io_writer_t* out) { sp_str_t sectname = sec->name; sp_str_t segname = sp_str_lit("__DATA"); if (sectname.len > 16) sectname.len = 16; - sp_mem_copy(sectname.data, sect.sectname, (u32)sectname.len); - sp_mem_copy(segname.data, sect.segname, (u32)segname.len); + sp_mem_copy(sect.sectname, sectname.data, (u32)sectname.len); + sp_mem_copy(sect.segname, segname.data, (u32)segname.len); sp_try(sp_io_write(out, §, sizeof(sect), SP_NULLPTR)); vmaddr += section_sizes[i]; diff --git a/sp/sp_prompt.h b/sp/sp_prompt.h index 710af3f..2e54d5b 100644 --- a/sp/sp_prompt.h +++ b/sp/sp_prompt.h @@ -1441,7 +1441,7 @@ void sp_prompt_app_on_deinit(sp_app_t* app) { .cells = sp_mem_arena_alloc_n(ctx->arena, sp_prompt_cell_t, cell_count), }; - sp_mem_copy(ctx->framebuffer, frame.cells, sizeof(sp_prompt_cell_t) * cell_count); + sp_mem_copy(frame.cells, ctx->framebuffer, sizeof(sp_prompt_cell_t) * cell_count); sp_da_push(ctx->frames, frame); } } @@ -2894,7 +2894,7 @@ static void sp_prompt_spinner_render(sp_prompt_ctx_t* ctx) { sp_prompt_widget_t sp_prompt_spinner_widget(sp_prompt_ctx_t* ctx, sp_prompt_spinner_t config) { if (!config.frames[0]) { u32 frames [] = SP_PROMPT_SPINNER_FALLING_SAND; - sp_mem_copy(frames, config.frames, sizeof(frames)); + sp_mem_copy(config.frames, frames, sizeof(frames)); } sp_prompt_spinner_widget_t* user_data = sp_mem_arena_alloc_type(ctx->arena, sp_prompt_spinner_widget_t); user_data->config = config; diff --git a/test/elf.c b/test/elf.c index 444e852..21e0141 100644 --- a/test/elf.c +++ b/test/elf.c @@ -509,7 +509,7 @@ UTEST_F(elf, roundtrip_populated) { text->flags = SHF_ALLOC | SHF_EXECINSTR; u8 code [] = {0xb8, 0x3c, 0x00, 0x00, 0x00, 0x31, 0xff, 0x0f, 0x05}; u8* p = sp_elf_section_reserve_bytes(text, sizeof(code)); - sp_mem_copy(code, p, sizeof(code)); + sp_mem_copy(p, code, sizeof(code)); sp_elf_section_t* data = sp_elf_add_section(elf, sp_str_lit(".data"), SHT_PROGBITS, 8); data->flags = SHF_ALLOC | SHF_WRITE; @@ -621,7 +621,7 @@ UTEST_F(elf, oracle_readelf_populated) { text->flags = SHF_ALLOC | SHF_EXECINSTR; u8 code[] = {0xb8, 0x3c, 0x00, 0x00, 0x00, 0x31, 0xff, 0x0f, 0x05}; u8* p = sp_elf_section_reserve_bytes(text, sizeof(code)); - sp_mem_copy(code, p, sizeof(code)); + sp_mem_copy(p, code, sizeof(code)); sp_elf_section_t* data = sp_elf_add_section(elf, sp_str_lit(".data"), SHT_PROGBITS, 8); data->flags = SHF_ALLOC | SHF_WRITE; @@ -700,7 +700,7 @@ UTEST_F(elf, oracle_cc_link) { data->flags = SHF_ALLOC | SHF_WRITE; u8 test_bytes[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE}; u8* p = sp_elf_section_reserve_bytes(data, sizeof(test_bytes)); - sp_mem_copy(test_bytes, p, sizeof(test_bytes)); + sp_mem_copy(p, test_bytes, sizeof(test_bytes)); sp_elf_section_t* symtab = sp_elf_symtab_new(elf); data = sp_elf_find_section_by_name(elf, sp_str_lit(".data")); diff --git a/test/io.c b/test/io.c index 2e559fd..665f861 100644 --- a/test/io.c +++ b/test/io.c @@ -1,9 +1,9 @@ #include "io/io.h" SP_TEST_MAIN() -#include "io/read.c" -#include "io/write.c" -#include "io/copy.c" +// #include "io/read.c" +// #include "io/write.c" +// #include "io/copy.c" #include "io/loose.c" u64 io_mock_response_count(const io_mock_response_t* responses, u64 max) { @@ -21,7 +21,7 @@ sp_err_t io_mock_reader_read(sp_io_reader_t* r, void* ptr, u64 size, u64* bytes_ io_mock_response_t* resp = &m->responses[m->cursor++]; u64 n = sp_min(size, resp->bytes); if (n && resp->data) { - sp_mem_copy(resp->data, ptr, n); + sp_mem_copy(ptr, resp->data, n); } if (bytes_read) *bytes_read = n; return resp->err; @@ -34,7 +34,7 @@ sp_err_t io_mock_writer_write(sp_io_writer_t* w, const void* ptr, u64 size, u64* u64 n = sp_min(size, resp->bytes); if (n) { sp_assert(m->received_len + n <= sizeof(m->received)); - sp_mem_copy(ptr, m->received + m->received_len, n); + sp_mem_copy(m->received + m->received_len, ptr, n); m->received_len += n; } if (bytes_written) *bytes_written = n; diff --git a/test/mem/builtin.c b/test/mem/builtin.c index 8c4e088..71e8a5f 100644 --- a/test/mem/builtin.c +++ b/test/mem/builtin.c @@ -12,7 +12,7 @@ static void run_mem_copy_test(int* utest_result, mem_copy_test_t t) { for (u32 i = 0; i < sizeof(dest); i++) dest[i] = sentinel; - sp_mem_copy(t.src, dest, t.copy_len); + sp_mem_copy(dest, t.src, t.copy_len); for (u32 i = 0; i < t.copy_len; i++) { EXPECT_EQ(dest[i], t.src[i]); @@ -48,7 +48,7 @@ static void run_mem_move_test(int* utest_result, mem_move_test_t t) { expected[t.dst_offset + i] = tmp[i]; } - sp_mem_move(buf + t.src_offset, buf + t.dst_offset, t.move_len); + sp_mem_move(buf + t.dst_offset, buf + t.src_offset, t.move_len); for (u32 i = 0; i < t.buf_size; i++) { EXPECT_EQ(buf[i], expected[i]); diff --git a/test/tools/test.h b/test/tools/test.h index 4deef39..eb28113 100644 --- a/test/tools/test.h +++ b/test/tools/test.h @@ -371,7 +371,7 @@ static void* sp_mem_tracking_do_realloc(sp_mem_tracking_t* t, void* old, u64 siz void* fresh = sp_mem_tracking_do_alloc(t, size); if (!fresh) return SP_NULLPTR; - sp_mem_copy(old, fresh, node->size); + sp_mem_copy(fresh, old, node->size); sp_mem_tracking_do_free(t, old); return fresh; } diff --git a/tools/utest.h b/tools/utest.h index eb526e1..ff66722 100644 --- a/tools/utest.h +++ b/tools/utest.h @@ -597,9 +597,9 @@ static UTEST_INLINE int utest_strncmp(const c8 *a, const c8 *b, u32 n) { utest_state.tests[index].set = set; \ utest_state.tests[index].test = test; \ utest_state.tests[index].index = 0; \ - sp_mem_copy(name_part, name, name_size); \ - sp_mem_copy(set_part, set, set_size); \ - sp_mem_copy(test_part, test, test_size); \ + sp_mem_copy(name, name_part, name_size); \ + sp_mem_copy(set, set_part, set_size); \ + sp_mem_copy(test, test_part, test_size); \ } else { \ if (utest_state.tests) { \ sp_mem_os_free(utest_state.tests); \ @@ -669,9 +669,9 @@ static UTEST_INLINE int utest_strncmp(const c8 *a, const c8 *b, u32 n) { utest_state.tests[index].set = set; \ utest_state.tests[index].test = test; \ utest_state.tests[index].index = 0; \ - sp_mem_copy(name_part, name, name_size); \ - sp_mem_copy(set_part, set, set_size); \ - sp_mem_copy(test_part, test, test_size); \ + sp_mem_copy(name, name_part, name_size); \ + sp_mem_copy(set, set_part, set_size); \ + sp_mem_copy(test, test_part, test_size); \ } else { \ if (utest_state.tests) { \ sp_mem_os_free(utest_state.tests); \ @@ -729,7 +729,7 @@ static UTEST_INLINE int utest_strncmp(const c8 *a, const c8 *b, u32 n) { utest_state.tests[index].func = &utest_i_##FIXTURE##_##NAME##_##INDEX; \ utest_state.tests[index].index = i; \ utest_state.tests[index].name = name; \ - sp_mem_copy(fmtd.data, name, fmtd.len); \ + sp_mem_copy(name, fmtd.data, fmtd.len); \ name[fmtd.len] = '\0'; \ } else { \ if (utest_state.tests) { \ From d8fe3b5c79e509f74362d67a4c8518493075f0cb Mon Sep 17 00:00:00 2001 From: Thomas Spader Date: Tue, 12 May 2026 16:24:15 -0400 Subject: [PATCH 10/21] snip snap --- test/io.c | 7 +- test/io/copy.c | 75 +------------ test/io/io.h | 7 -- test/io/mem.c | 289 ++++++++++++++++++++++++++++++++++++++++++++++++ test/io/read.c | 95 +--------------- test/io/write.c | 128 --------------------- 6 files changed, 299 insertions(+), 302 deletions(-) create mode 100644 test/io/mem.c diff --git a/test/io.c b/test/io.c index 665f861..4c97115 100644 --- a/test/io.c +++ b/test/io.c @@ -1,9 +1,10 @@ #include "io/io.h" SP_TEST_MAIN() -// #include "io/read.c" -// #include "io/write.c" -// #include "io/copy.c" +#include "io/read.c" +#include "io/write.c" +#include "io/copy.c" +#include "io/mem.c" #include "io/loose.c" u64 io_mock_response_count(const io_mock_response_t* responses, u64 max) { diff --git a/test/io/copy.c b/test/io/copy.c index 7a20b24..3347d46 100644 --- a/test/io/copy.c +++ b/test/io/copy.c @@ -13,6 +13,9 @@ typedef struct { } expect; } io_mock_copy_reader_test_t; +//////////// +// RUNNER // +//////////// void run_io_mock_copy_reader_test(int* utest_result, io_mock_copy_reader_test_t t) { u64 num_responses = io_mock_response_count(t.responses, IO_MAX_RESPONSES); io_mock_reader_t r = sp_zero; @@ -67,11 +70,6 @@ void run_io_mock_copy_writer_test(int* utest_result, io_mock_copy_writer_test_t } -////////////////////////// -// SMOKE TESTS -////////////////////////// - - // Source fails AFTER returning some bytes in a prior call. Copy should // preserve the bytes that did transfer. UTEST_F(io_copy, reader_fails_after_success) { @@ -122,70 +120,3 @@ UTEST_F(io_copy, writer_fails_immediately) { .expect = { .err = SP_ERR_IO_WRITE_FAILED, .copied = 0, .received = "" }, }); } - - -//////////// -// ERRORS // -//////////// -typedef struct { - const c8* source; - u64 capacity; - u64 buffer; - struct { - sp_err_t err; - const c8* final; - } expect; -} io_copy_test_t; - -void run_io_copy_test(int* utest_result, io_copy_test_t t) { - sp_io_reader_t r = sp_zero; - sp_io_reader_from_mem(&r, t.source, sp_cstr_len(t.source)); - - u8 backing [64] = sp_zero; - sp_io_mem_writer_t w = sp_zero; - sp_io_mem_writer_from_buffer(&w, backing, t.capacity); - - u8 copy_buf [64] = sp_zero; - u64 copied = 0; - sp_err_t err = sp_io_copy_b(&w.base, &r, copy_buf, t.buffer, &copied); - - EXPECT_EQ(err, t.expect.err); - u64 n = sp_cstr_len(t.expect.final); - EXPECT_EQ(copied, n); - sp_for(it, n) { - EXPECT_EQ((c8)backing[it], t.expect.final[it]); - } -} - -UTEST_F(io_copy, copy_full) { - run_io_copy_test(utest_result, (io_copy_test_t){ - .source = "0123456789", - .capacity = 32, .buffer = 8, - .expect = { .err = SP_OK, .final = "0123456789" }, - }); -} - -UTEST_F(io_copy, copy_loops) { - run_io_copy_test(utest_result, (io_copy_test_t){ - .source = "ABCDEFGHIJKLMNOP", - .capacity = 32, .buffer = 4, - .expect = { .err = SP_OK, .final = "ABCDEFGHIJKLMNOP" }, - }); -} - -UTEST_F(io_copy, copy_empty) { - run_io_copy_test(utest_result, (io_copy_test_t){ - .source = "", - .capacity = 8, .buffer = 8, - .expect = { .err = SP_OK }, - }); -} - -UTEST_F(io_copy, copy_writer_no_space) { - run_io_copy_test(utest_result, (io_copy_test_t){ - .source = "0123456789", - .capacity = 4, .buffer = 8, - .expect = { .err = SP_ERR_IO_NO_SPACE, .final = "0123" }, - }); -} - diff --git a/test/io/io.h b/test/io/io.h index 731b279..4f5eeab 100644 --- a/test/io/io.h +++ b/test/io/io.h @@ -72,13 +72,6 @@ typedef struct { u64 received_len; } io_mock_writer_t; -typedef struct { - sp_io_file_reader_t file; - sp_io_pipe_reader_t pipe; - sp_io_reader_t mem; - io_mock_reader_t mock; -} io_readers_t; - u64 io_mock_response_count(const io_mock_response_t* responses, u64 max); sp_err_t io_mock_reader_read(sp_io_reader_t* r, void* ptr, u64 size, u64* bytes_read); sp_err_t io_mock_writer_write(sp_io_writer_t* w, const void* ptr, u64 size, u64* bytes_written); diff --git a/test/io/mem.c b/test/io/mem.c new file mode 100644 index 0000000..5ab9606 --- /dev/null +++ b/test/io/mem.c @@ -0,0 +1,289 @@ +#include "io.h" + +UTEST_EMPTY_FIXTURE(io_mem) + +typedef struct { + const c8* source; + io_step_t steps [IO_MAX_STEPS]; +} io_read_test_t; + +void run_io_read_test(int* utest_result, io_read_test_t t) { + sp_io_reader_t r = sp_zero; + sp_io_reader_from_mem(&r, t.source, sp_cstr_len(t.source)); + + sp_carr_for(t.steps, j) { + const io_step_t* step = &t.steps[j]; + if (step->kind == IO_STEP_NONE) break; + + u8 dest [64] = sp_zero; + u64 bytes = 0; + sp_err_t err = sp_io_read(&r, dest, step->read.request, &bytes); + EXPECT_EQ(err, step->read.err); + + u64 expect_bytes = sp_cstr_len(step->read.content); + EXPECT_EQ(bytes, expect_bytes); + sp_for(it, expect_bytes) { + EXPECT_EQ((c8)dest[it], step->read.content[it]); + } + } +} + +UTEST_F(io_mem, read_exact_then_eof) { + run_io_read_test(utest_result, (io_read_test_t){ + .source = "0123", + .steps = { + { .kind = IO_STEP_READ, .read = { 4, SP_OK, "0123" } }, + { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, + }, + }); +} + +UTEST_F(io_mem, read_chunked) { + run_io_read_test(utest_result, (io_read_test_t){ + .source = "01234567", + .steps = { + { .kind = IO_STEP_READ, .read = { 3, SP_OK, "012" } }, + { .kind = IO_STEP_READ, .read = { 3, SP_OK, "345" } }, + { .kind = IO_STEP_READ, .read = { 3, SP_OK, "67" } }, + { .kind = IO_STEP_READ, .read = { 3, SP_ERR_IO_EOF } }, + }, + }); +} + +UTEST_F(io_mem, read_oversized_request) { + run_io_read_test(utest_result, (io_read_test_t){ + .source = "abc", + .steps = { + { .kind = IO_STEP_READ, .read = { 16, SP_OK, "abc" } }, + { .kind = IO_STEP_READ, .read = { 16, SP_ERR_IO_EOF } }, + }, + }); +} + +UTEST_F(io_mem, read_empty_source) { + run_io_read_test(utest_result, (io_read_test_t){ + .source = "", + .steps = { + { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, + }, + }); +} + +UTEST_F(io_mem, read_eof_idempotent) { + run_io_read_test(utest_result, (io_read_test_t){ + .source = "x", + .steps = { + { .kind = IO_STEP_READ, .read = { 4, SP_OK, "x" } }, + { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, + { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, + }, + }); +} + +UTEST_F(io_mem, read_zero_request) { + run_io_read_test(utest_result, (io_read_test_t){ + .source = "abc", + .steps = { + { .kind = IO_STEP_READ, .read = { 0, SP_OK } }, + { .kind = IO_STEP_READ, .read = { 3, SP_OK, "abc" } }, + }, + }); +} + + + + + +/////////// +// WRITE // +/////////// +typedef struct { + u64 capacity; + io_step_t steps [IO_MAX_STEPS]; + struct { + const c8* content; + } expect; +} io_write_test_t; + +void run_io_write_test(int* utest_result, io_write_test_t t) { + u8 backing [64] = sp_zero; + sp_io_mem_writer_t w = sp_zero; + sp_io_mem_writer_from_buffer(&w, backing, t.capacity); + + sp_carr_for(t.steps, j) { + const io_step_t* step = &t.steps[j]; + if (step->kind == IO_STEP_NONE) break; + + switch (step->kind) { + case IO_STEP_WRITE: { + u64 bytes = 0; + sp_err_t err = sp_io_write(&w.base, step->write.data, sp_cstr_len(step->write.data), &bytes); + EXPECT_EQ(err, step->write.err); + EXPECT_EQ(bytes, step->write.bytes); + break; + } + case IO_STEP_FLUSH: { + sp_err_t err = sp_io_flush(&w.base); + EXPECT_EQ(err, step->flush.err); + break; + } + default: { + sp_unreachable_case(); + } + } + } + + u64 n = sp_cstr_len(t.expect.content); + sp_for(it, n) { + EXPECT_EQ((c8)backing[it], t.expect.content[it]); + } +} + +UTEST_F(io_mem, write_fits) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 16, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "hello", SP_OK, 5 } }, + }, + .expect.content = "hello", + }); +} + +UTEST_F(io_mem, write_exact_fit) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 4, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abcd", SP_OK, 4 } }, + }, + .expect.content = "abcd", + }); +} + +UTEST_F(io_mem, write_overflow) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 4, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abcdefgh", SP_ERR_IO_NO_SPACE, 4 } }, + }, + .expect.content = "abcd", + }); +} + +UTEST_F(io_mem, write_barely_overflows) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 4, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abcde", SP_ERR_IO_NO_SPACE, 4 } }, + }, + .expect.content = "abcd", + }); +} + +UTEST_F(io_mem, write_smaller_after_overflow) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 4, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abcd", SP_OK, 4 } }, + { .kind = IO_STEP_WRITE, .write = { "x", SP_ERR_IO_NO_SPACE, 0 } }, + }, + .expect.content = "abcd", + }); +} + +UTEST_F(io_mem, write_appends) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 16, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, + { .kind = IO_STEP_WRITE, .write = { "def", SP_OK, 3 } }, + { .kind = IO_STEP_WRITE, .write = { "ghi", SP_OK, 3 } }, + }, + .expect.content = "abcdefghi", + }); +} + +UTEST_F(io_mem, write_zero) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 8, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "", SP_OK, 0 } }, + }, + }); +} + +UTEST_F(io_mem, write_flush_empty) { + run_io_write_test(utest_result, (io_write_test_t){ + .capacity = 8, + .steps = { + { .kind = IO_STEP_FLUSH, .flush = { SP_OK } }, + }, + }); +} + + +////////// +// COPY // +////////// +typedef struct { + const c8* source; + u64 capacity; + u64 buffer; + struct { + sp_err_t err; + const c8* final; + } expect; +} io_copy_test_t; + +void run_io_copy_test(int* utest_result, io_copy_test_t t) { + sp_io_reader_t r = sp_zero; + sp_io_reader_from_mem(&r, t.source, sp_cstr_len(t.source)); + + u8 backing [64] = sp_zero; + sp_io_mem_writer_t w = sp_zero; + sp_io_mem_writer_from_buffer(&w, backing, t.capacity); + + u8 copy_buf [64] = sp_zero; + u64 copied = 0; + sp_err_t err = sp_io_copy_b(&w.base, &r, copy_buf, t.buffer, &copied); + + EXPECT_EQ(err, t.expect.err); + u64 n = sp_cstr_len(t.expect.final); + EXPECT_EQ(copied, n); + sp_for(it, n) { + EXPECT_EQ((c8)backing[it], t.expect.final[it]); + } +} + +UTEST_F(io_mem, copy_full) { + run_io_copy_test(utest_result, (io_copy_test_t){ + .source = "0123456789", + .capacity = 32, .buffer = 8, + .expect = { .err = SP_OK, .final = "0123456789" }, + }); +} + +UTEST_F(io_mem, copy_loops) { + run_io_copy_test(utest_result, (io_copy_test_t){ + .source = "ABCDEFGHIJKLMNOP", + .capacity = 32, .buffer = 4, + .expect = { .err = SP_OK, .final = "ABCDEFGHIJKLMNOP" }, + }); +} + +UTEST_F(io_mem, copy_empty) { + run_io_copy_test(utest_result, (io_copy_test_t){ + .source = "", + .capacity = 8, .buffer = 8, + .expect = { .err = SP_OK }, + }); +} + +UTEST_F(io_mem, copy_writer_no_space) { + run_io_copy_test(utest_result, (io_copy_test_t){ + .source = "0123456789", + .capacity = 4, .buffer = 8, + .expect = { .err = SP_ERR_IO_NO_SPACE, .final = "0123" }, + }); +} + + diff --git a/test/io/read.c b/test/io/read.c index c5e7c08..3bb244b 100644 --- a/test/io/read.c +++ b/test/io/read.c @@ -2,103 +2,14 @@ UTEST_EMPTY_FIXTURE(io_read) -typedef struct { - const c8* source; - io_step_t steps [IO_MAX_STEPS]; -} io_read_test_t; - -void run_io_read_test(int* utest_result, io_read_test_t t) { - sp_io_reader_t r = sp_zero; - sp_io_reader_from_mem(&r, t.source, sp_cstr_len(t.source)); - - sp_carr_for(t.steps, j) { - const io_step_t* step = &t.steps[j]; - if (step->kind == IO_STEP_NONE) break; - - u8 dest [64] = sp_zero; - u64 bytes = 0; - sp_err_t err = sp_io_read(&r, dest, step->read.request, &bytes); - EXPECT_EQ(err, step->read.err); - - u64 expect_bytes = sp_cstr_len(step->read.content); - EXPECT_EQ(bytes, expect_bytes); - sp_for(it, expect_bytes) { - EXPECT_EQ((c8)dest[it], step->read.content[it]); - } - } -} - -UTEST_F(io_read, read_exact_then_eof) { - run_io_read_test(utest_result, (io_read_test_t){ - .source = "0123", - .steps = { - { .kind = IO_STEP_READ, .read = { 4, SP_OK, "0123" } }, - { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, - }, - }); -} - -UTEST_F(io_read, read_chunked) { - run_io_read_test(utest_result, (io_read_test_t){ - .source = "01234567", - .steps = { - { .kind = IO_STEP_READ, .read = { 3, SP_OK, "012" } }, - { .kind = IO_STEP_READ, .read = { 3, SP_OK, "345" } }, - { .kind = IO_STEP_READ, .read = { 3, SP_OK, "67" } }, - { .kind = IO_STEP_READ, .read = { 3, SP_ERR_IO_EOF } }, - }, - }); -} - -UTEST_F(io_read, read_oversized_request) { - run_io_read_test(utest_result, (io_read_test_t){ - .source = "abc", - .steps = { - { .kind = IO_STEP_READ, .read = { 16, SP_OK, "abc" } }, - { .kind = IO_STEP_READ, .read = { 16, SP_ERR_IO_EOF } }, - }, - }); -} - -UTEST_F(io_read, read_empty_source) { - run_io_read_test(utest_result, (io_read_test_t){ - .source = "", - .steps = { - { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, - }, - }); -} - -UTEST_F(io_read, read_eof_idempotent) { - run_io_read_test(utest_result, (io_read_test_t){ - .source = "x", - .steps = { - { .kind = IO_STEP_READ, .read = { 4, SP_OK, "x" } }, - { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, - { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, - }, - }); -} - -UTEST_F(io_read, read_zero_request) { - run_io_read_test(utest_result, (io_read_test_t){ - .source = "abc", - .steps = { - { .kind = IO_STEP_READ, .read = { 0, SP_OK } }, - { .kind = IO_STEP_READ, .read = { 3, SP_OK, "abc" } }, - }, - }); -} - - -//////////// -// ERRORS // -//////////// typedef struct { io_mock_response_t responses[IO_MAX_RESPONSES]; io_step_t steps[IO_MAX_STEPS]; } io_mock_read_test_t; +//////////// +// RUNNER // +//////////// void run_io_mock_read_test(int* utest_result, io_mock_read_test_t t) { u64 num_responses = io_mock_response_count(t.responses, IO_MAX_RESPONSES); io_mock_reader_t r = sp_zero; diff --git a/test/io/write.c b/test/io/write.c index 1017997..dda37e0 100644 --- a/test/io/write.c +++ b/test/io/write.c @@ -134,131 +134,3 @@ UTEST_F(io_write, smaller_after_overflow) { .expect.received = "xy", }); } - - -//////////// -// ERRORS // -//////////// -typedef struct { - u64 capacity; - io_step_t steps [IO_MAX_STEPS]; - struct { - const c8* content; - } expect; -} io_write_test_t; - -void run_io_write_test(int* utest_result, io_write_test_t t) { - u8 backing [64] = sp_zero; - sp_io_mem_writer_t w = sp_zero; - sp_io_mem_writer_from_buffer(&w, backing, t.capacity); - - sp_carr_for(t.steps, j) { - const io_step_t* step = &t.steps[j]; - if (step->kind == IO_STEP_NONE) break; - - switch (step->kind) { - case IO_STEP_WRITE: { - u64 bytes = 0; - sp_err_t err = sp_io_write(&w.base, step->write.data, sp_cstr_len(step->write.data), &bytes); - EXPECT_EQ(err, step->write.err); - EXPECT_EQ(bytes, step->write.bytes); - break; - } - case IO_STEP_FLUSH: { - sp_err_t err = sp_io_flush(&w.base); - EXPECT_EQ(err, step->flush.err); - break; - } - default: { - sp_unreachable_case(); - } - } - } - - u64 n = sp_cstr_len(t.expect.content); - sp_for(it, n) { - EXPECT_EQ((c8)backing[it], t.expect.content[it]); - } -} - -UTEST_F(io_write, write_fits) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 16, - .steps = { - { .kind = IO_STEP_WRITE, .write = { "hello", SP_OK, 5 } }, - }, - .expect.content = "hello", - }); -} - -UTEST_F(io_write, write_exact_fit) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 4, - .steps = { - { .kind = IO_STEP_WRITE, .write = { "abcd", SP_OK, 4 } }, - }, - .expect.content = "abcd", - }); -} - -UTEST_F(io_write, write_overflow) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 4, - .steps = { - { .kind = IO_STEP_WRITE, .write = { "abcdefgh", SP_ERR_IO_NO_SPACE, 4 } }, - }, - .expect.content = "abcd", - }); -} - -UTEST_F(io_write, write_barely_overflows) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 4, - .steps = { - { .kind = IO_STEP_WRITE, .write = { "abcde", SP_ERR_IO_NO_SPACE, 4 } }, - }, - .expect.content = "abcd", - }); -} - -UTEST_F(io_write, write_smaller_after_overflow) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 4, - .steps = { - { .kind = IO_STEP_WRITE, .write = { "abcd", SP_OK, 4 } }, - { .kind = IO_STEP_WRITE, .write = { "x", SP_ERR_IO_NO_SPACE, 0 } }, - }, - .expect.content = "abcd", - }); -} - -UTEST_F(io_write, write_appends) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 16, - .steps = { - { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, - { .kind = IO_STEP_WRITE, .write = { "def", SP_OK, 3 } }, - { .kind = IO_STEP_WRITE, .write = { "ghi", SP_OK, 3 } }, - }, - .expect.content = "abcdefghi", - }); -} - -UTEST_F(io_write, write_zero) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 8, - .steps = { - { .kind = IO_STEP_WRITE, .write = { "", SP_OK, 0 } }, - }, - }); -} - -UTEST_F(io_write, write_flush_empty) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 8, - .steps = { - { .kind = IO_STEP_FLUSH, .flush = { SP_OK } }, - }, - }); -} - From 9aa37a5f00d97983c1235d9748fbcf1543a0011f Mon Sep 17 00:00:00 2001 From: Thomas Spader Date: Tue, 12 May 2026 16:43:13 -0400 Subject: [PATCH 11/21] rw --- test/io.c | 23 ++-- test/io/copy.c | 129 ++++++++++++++++--- test/io/io.h | 22 ++-- test/io/loose.c | 166 ------------------------ test/io/mem.c | 265 +++++++++++++++++++++------------------ test/io/read.c | 179 ++++++++++++++++++++++++-- test/io/seeking_reader.c | 73 +++++++++++ test/io/write.c | 188 +++++++++++++++++++++++++-- 8 files changed, 699 insertions(+), 346 deletions(-) create mode 100644 test/io/seeking_reader.c diff --git a/test/io.c b/test/io.c index 4c97115..b96e2e6 100644 --- a/test/io.c +++ b/test/io.c @@ -5,9 +5,10 @@ SP_TEST_MAIN() #include "io/write.c" #include "io/copy.c" #include "io/mem.c" +#include "io/seeking_reader.c" #include "io/loose.c" -u64 io_mock_response_count(const io_mock_response_t* responses, u64 max) { +u64 io_get_num_results(const io_result_t* responses, u64 max) { u64 n = 0; sp_for(it, max) { if (!responses[it].bytes && !responses[it].err && !responses[it].data) break; @@ -18,8 +19,8 @@ u64 io_mock_response_count(const io_mock_response_t* responses, u64 max) { sp_err_t io_mock_reader_read(sp_io_reader_t* r, void* ptr, u64 size, u64* bytes_read) { io_mock_reader_t* m = (io_mock_reader_t*)r; - sp_assert(m->cursor < m->num_responses); - io_mock_response_t* resp = &m->responses[m->cursor++]; + sp_assert(m->cursor < m->num_results); + io_result_t* resp = &m->results[m->cursor++]; u64 n = sp_min(size, resp->bytes); if (n && resp->data) { sp_mem_copy(ptr, resp->data, n); @@ -30,8 +31,8 @@ sp_err_t io_mock_reader_read(sp_io_reader_t* r, void* ptr, u64 size, u64* bytes_ sp_err_t io_mock_writer_write(sp_io_writer_t* w, const void* ptr, u64 size, u64* bytes_written) { io_mock_writer_t* m = (io_mock_writer_t*)w; - sp_assert(m->cursor < m->num_responses); - io_mock_response_t* resp = &m->responses[m->cursor++]; + sp_assert(m->cursor < m->num_results); + io_result_t* resp = &m->results[m->cursor++]; u64 n = sp_min(size, resp->bytes); if (n) { sp_assert(m->received_len + n <= sizeof(m->received)); @@ -43,17 +44,17 @@ sp_err_t io_mock_writer_write(sp_io_writer_t* w, const void* ptr, u64 size, u64* } -void io_mock_reader_init(io_mock_reader_t* m, const io_mock_response_t* responses, u64 n) { +void io_mock_reader_init(io_mock_reader_t* m, const io_result_t* responses, u64 n) { *m = sp_zero_s(io_mock_reader_t); m->base.read = io_mock_reader_read; - m->num_responses = n; - sp_for(it, n) m->responses[it] = responses[it]; + m->num_results = n; + sp_for(it, n) m->results[it] = responses[it]; } -void io_mock_writer_init(io_mock_writer_t* m, const io_mock_response_t* responses, u64 n) { +void io_mock_writer_init(io_mock_writer_t* m, const io_result_t* responses, u64 n) { *m = sp_zero_s(io_mock_writer_t); m->base.write = io_mock_writer_write; - m->num_responses = n; - sp_for(it, n) m->responses[it] = responses[it]; + m->num_results = n; + sp_for(it, n) m->results[it] = responses[it]; } diff --git a/test/io/copy.c b/test/io/copy.c index 3347d46..420d4e4 100644 --- a/test/io/copy.c +++ b/test/io/copy.c @@ -3,9 +3,14 @@ UTEST_EMPTY_FIXTURE(io_copy) typedef struct { - io_mock_response_t responses[IO_MAX_RESPONSES]; - u64 writer_capacity; - u64 buffer; + io_result_t results [IO_MAX_RESPONSES]; + struct { + u64 copy; + u64 r; + } buffer; + struct { + u64 writer; + } capacity; struct { sp_err_t err; u64 copied; @@ -17,17 +22,22 @@ typedef struct { // RUNNER // //////////// void run_io_mock_copy_reader_test(int* utest_result, io_mock_copy_reader_test_t t) { - u64 num_responses = io_mock_response_count(t.responses, IO_MAX_RESPONSES); + u64 num_responses = io_get_num_results(t.results, IO_MAX_RESPONSES); io_mock_reader_t r = sp_zero; - io_mock_reader_init(&r, t.responses, num_responses); + io_mock_reader_init(&r, t.results, num_responses); + + u8 reader_buf[64] = sp_zero; + if (t.buffer.r) { + sp_io_reader_set_buffer(&r.base, reader_buf, t.buffer.r); + } u8 backing[64] = sp_zero; sp_io_mem_writer_t w = sp_zero; - sp_io_mem_writer_from_buffer(&w, backing, t.writer_capacity); + sp_io_mem_writer_from_buffer(&w, backing, t.capacity.writer); u8 copy_buf[64] = sp_zero; u64 copied = 0; - sp_err_t err = sp_io_copy_b(&w.base, &r.base, copy_buf, t.buffer, &copied); + sp_err_t err = sp_io_copy_b(&w.base, &r.base, copy_buf, t.buffer.copy, &copied); EXPECT_EQ(err, t.expect.err); EXPECT_EQ(copied, t.expect.copied); @@ -39,8 +49,12 @@ void run_io_mock_copy_reader_test(int* utest_result, io_mock_copy_reader_test_t typedef struct { const c8* source; - io_mock_response_t responses[IO_MAX_RESPONSES]; - u64 buffer; + io_result_t responses[IO_MAX_RESPONSES]; + struct { + u64 copy; + u64 write; + } buffer; + struct { sp_err_t err; u64 copied; @@ -52,13 +66,18 @@ void run_io_mock_copy_writer_test(int* utest_result, io_mock_copy_writer_test_t sp_io_reader_t r = sp_zero; sp_io_reader_from_mem(&r, t.source, sp_cstr_len(t.source)); - u64 num_responses = io_mock_response_count(t.responses, IO_MAX_RESPONSES); + u64 num_responses = io_get_num_results(t.responses, IO_MAX_RESPONSES); io_mock_writer_t w = sp_zero; io_mock_writer_init(&w, t.responses, num_responses); + u8 writer_buf[64] = sp_zero; + if (t.buffer.write) { + sp_io_writer_set_buffer(&w.base, writer_buf, t.buffer.write); + } + u8 copy_buf[64] = sp_zero; u64 copied = 0; - sp_err_t err = sp_io_copy_b(&w.base, &r, copy_buf, t.buffer, &copied); + sp_err_t err = sp_io_copy_b(&w.base, &r, copy_buf, t.buffer.copy, &copied); EXPECT_EQ(err, t.expect.err); EXPECT_EQ(copied, t.expect.copied); @@ -74,11 +93,11 @@ void run_io_mock_copy_writer_test(int* utest_result, io_mock_copy_writer_test_t // preserve the bytes that did transfer. UTEST_F(io_copy, reader_fails_after_success) { run_io_mock_copy_reader_test(utest_result, (io_mock_copy_reader_test_t){ - .responses = { + .results = { { .bytes = 3, .data = "abc", .err = SP_OK }, { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, }, - .writer_capacity = 32, .buffer = 8, + .capacity.writer = 32, .buffer.copy = 8, .expect = { .err = SP_ERR_IO_READ_FAILED, .copied = 3, .final = "abc" }, }); } @@ -87,10 +106,10 @@ UTEST_F(io_copy, reader_fails_after_success) { // contract, copy should still commit those bytes to the destination. UTEST_F(io_copy, reader_bytes_and_error) { run_io_mock_copy_reader_test(utest_result, (io_mock_copy_reader_test_t){ - .responses = { + .results = { { .bytes = 3, .data = "abc", .err = SP_ERR_IO_READ_FAILED }, }, - .writer_capacity = 32, .buffer = 8, + .capacity.writer = 32, .buffer.copy = 8, .expect = { .err = SP_ERR_IO_READ_FAILED, .copied = 3, .final = "abc" }, }); } @@ -103,7 +122,7 @@ UTEST_F(io_copy, writer_partial_in_call) { .responses = { { .bytes = 4, .err = SP_ERR_IO_NO_SPACE }, }, - .buffer = 8, + .buffer.copy = 8, .expect = { .err = SP_ERR_IO_NO_SPACE, .copied = 4, .received = "0123" }, }); } @@ -116,7 +135,83 @@ UTEST_F(io_copy, writer_fails_immediately) { .responses = { { .bytes = 0, .err = SP_ERR_IO_WRITE_FAILED }, }, - .buffer = 8, + .buffer.copy = 8, .expect = { .err = SP_ERR_IO_WRITE_FAILED, .copied = 0, .received = "" }, }); } + + +////////////// +// BUFFERED // +////////////// + +// Reader has a wrapper buffer smaller than the copy buffer. Each backend +// read fills the wrapper buffer; user-facing reads come from there until +// the wrapper buffer drains and the backend is called again. All source +// bytes still reach the destination. +UTEST_F(io_copy, buffered_reader_drains_through_buffer) { + run_io_mock_copy_reader_test(utest_result, (io_mock_copy_reader_test_t){ + .results = { + { .bytes = 4, .data = "0123", .err = SP_OK }, + { .bytes = 4, .data = "4567", .err = SP_OK }, + { .bytes = 0, .err = SP_ERR_IO_EOF }, + }, + .buffer = { .r = 2, .copy = 2 }, + .capacity.writer = 32, + .expect = { .err = SP_OK, .copied = 8, .final = "01234567" }, + }); +} + +// Reader wrapper buffer is larger than the copy buffer; one backend read +// fills it and the copy drains it across multiple small reads. +UTEST_F(io_copy, buffered_reader_one_fill_many_drains) { + run_io_mock_copy_reader_test(utest_result, (io_mock_copy_reader_test_t){ + .results = { + { .bytes = 8, .data = "ABCDEFGH", .err = SP_OK }, + { .bytes = 0, .err = SP_ERR_IO_EOF }, + }, + .buffer = { .r = 8, .copy = 2 }, + .capacity.writer = 32, + .expect = { .err = SP_OK, .copied = 8, .final = "ABCDEFGH" }, + }); +} + +// Writer wrapper buffer is large enough to hold the entire copy. The +// backend must NOT be called: bytes stay buffered. Copy reports the full +// count, but received_len at the backend is 0. +UTEST_F(io_copy, buffered_writer_absorbs_entire_copy) { + run_io_mock_copy_writer_test(utest_result, (io_mock_copy_writer_test_t){ + .source = "01234567", + .responses = {0}, + .buffer = { .write = 16, .copy = 4 }, + .expect = { .err = SP_OK, .copied = 8, .received = "" }, + }); +} + +// Writer wrapper buffer smaller than copy size. As the buffer fills, the +// wrapper flushes to the backend. The tail of the data is left in the +// buffer when copy returns because sp_io_copy_b does not flush at end. +// Pins this behavior explicitly. +UTEST_F(io_copy, buffered_writer_overflow_flushes_partial) { + run_io_mock_copy_writer_test(utest_result, (io_mock_copy_writer_test_t){ + .source = "01234567", + .responses = { + { .bytes = 4, .err = SP_OK }, + }, + .buffer = { .write = 4, .copy = 2 }, + .expect = { .err = SP_OK, .copied = 8, .received = "0123" }, + }); +} + +// Writer buffer attached; backend errors on the flush that the wrapper +// triggers when the buffer overflows. Copy surfaces the error. +UTEST_F(io_copy, buffered_writer_flush_error) { + run_io_mock_copy_writer_test(utest_result, (io_mock_copy_writer_test_t){ + .source = "01234567", + .responses = { + { .bytes = 0, .err = SP_ERR_IO_WRITE_FAILED }, + }, + .buffer = { .write = 4, .copy = 2 }, + .expect = { .err = SP_ERR_IO_WRITE_FAILED, .copied = 4, .received = "" }, + }); +} diff --git a/test/io/io.h b/test/io/io.h index 4f5eeab..742f36c 100644 --- a/test/io/io.h +++ b/test/io/io.h @@ -39,6 +39,9 @@ typedef enum { IO_STEP_READ, IO_STEP_WRITE, IO_STEP_FLUSH, + IO_STEP_SEEK, + IO_STEP_SIZE, + IO_STEP_COPY, } io_step_kind_t; typedef struct { @@ -47,6 +50,9 @@ typedef struct { struct { u64 request; sp_err_t err; const c8* content; } read; struct { const c8* data; sp_err_t err; u64 bytes; } write; struct { sp_err_t err; } flush; + struct { s64 offset; sp_io_whence_t whence; sp_err_t err; s64 pos; } seek; + struct { sp_err_t err; u64 size; } size; + struct { u64 buffer; sp_err_t err; u64 copied; } copy; }; } io_step_t; @@ -54,29 +60,29 @@ typedef struct { u64 bytes; sp_err_t err; const c8* data; -} io_mock_response_t; +} io_result_t; typedef struct { sp_io_reader_t base; - io_mock_response_t responses[IO_MAX_RESPONSES]; - u64 num_responses; + io_result_t results [IO_MAX_RESPONSES]; + u64 num_results; u64 cursor; } io_mock_reader_t; typedef struct { sp_io_writer_t base; - io_mock_response_t responses[IO_MAX_RESPONSES]; - u64 num_responses; + io_result_t results [IO_MAX_RESPONSES]; + u64 num_results; u64 cursor; u8 received [256]; u64 received_len; } io_mock_writer_t; -u64 io_mock_response_count(const io_mock_response_t* responses, u64 max); +u64 io_get_num_results(const io_result_t* responses, u64 max); sp_err_t io_mock_reader_read(sp_io_reader_t* r, void* ptr, u64 size, u64* bytes_read); sp_err_t io_mock_writer_write(sp_io_writer_t* w, const void* ptr, u64 size, u64* bytes_written); -void io_mock_reader_init(io_mock_reader_t* m, const io_mock_response_t* responses, u64 n); -void io_mock_writer_init(io_mock_writer_t* m, const io_mock_response_t* responses, u64 n); +void io_mock_reader_init(io_mock_reader_t* m, const io_result_t* responses, u64 n); +void io_mock_writer_init(io_mock_writer_t* m, const io_result_t* responses, u64 n); #endif // IO_TEST_H diff --git a/test/io/loose.c b/test/io/loose.c index fe0f0a4..f4c4444 100644 --- a/test/io/loose.c +++ b/test/io/loose.c @@ -1,109 +1,5 @@ #include "io.h" -UTEST_F(io, reader_mem_read_full) { - SKIP_ON_WASM() - u8 source[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; - u8 dest[16] = sp_zero; - - sp_io_reader_t io = sp_zero; - sp_io_reader_from_mem(&io, source, sizeof(source)); - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_OK); - - EXPECT_EQ(bytes, 16); - sp_for(i, 16) { - EXPECT_EQ(dest[i], source[i]); - } -} -UTEST_F(io, reader_mem_read_partial) { - SKIP_ON_WASM() - u8 source[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; - u8 dest[8] = sp_zero; - - sp_io_reader_t io = sp_zero; - sp_io_reader_from_mem(&io, source, sizeof(source)); - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_OK); - - EXPECT_EQ(bytes, 8); - sp_for(i, 8) { - EXPECT_EQ(dest[i], source[i]); - } -} -UTEST_F(io, reader_mem_read_past_end) { - SKIP_ON_WASM() - u8 source[8] = {1,2,3,4,5,6,7,8}; - u8 dest[16] = sp_zero; - - sp_io_reader_t io = sp_zero; - sp_io_reader_from_mem(&io, source, sizeof(source)); - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_OK); - - EXPECT_EQ(bytes, 8); - - bytes = 0; - EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_ERR_IO_EOF); - EXPECT_EQ(bytes, 0); -} -UTEST_F(io, reader_mem_read_eof_exact) { - SKIP_ON_WASM() - u8 source[8] = {1,2,3,4,5,6,7,8}; - u8 dest[8] = sp_zero; - - sp_io_reader_t io = sp_zero; - sp_io_reader_from_mem(&io, source, sizeof(source)); - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_OK); - EXPECT_EQ(bytes, 8); - - EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_ERR_IO_EOF); - EXPECT_EQ(bytes, 0); -} -UTEST_F(io, reader_mem_read_eof_empty) { - SKIP_ON_WASM() - u8 dest[4] = sp_zero; - - sp_io_reader_t io = sp_zero; - sp_io_reader_from_mem(&io, SP_NULLPTR, 0); - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&io, dest, sizeof(dest), &bytes), SP_ERR_IO_EOF); - EXPECT_EQ(bytes, 0); -} -UTEST_F(io, reader_mem_seek) { - SKIP_ON_WASM() - u8 buffer[64] = sp_zero; - sp_for(i, 64) buffer[i] = (u8)i; - - sp_io_reader_t backing = sp_zero; - sp_io_seeking_reader_t io = sp_zero; - sp_io_seeking_reader_from_mem(&io, &backing, buffer, sizeof(buffer)); - - s64 pos = 0; - EXPECT_EQ(sp_io_seeking_reader_seek(&io, 32, SP_IO_SEEK_SET, &pos), SP_OK); - EXPECT_EQ(pos, 32); - - u8 val; - sp_io_read(io.reader, &val, 1, SP_NULLPTR); - EXPECT_EQ(val, 32); - - EXPECT_EQ(sp_io_seeking_reader_seek(&io, 0, SP_IO_SEEK_END, &pos), SP_OK); - EXPECT_EQ(pos, 64); -} -UTEST_F(io, reader_mem_seek_invalid) { - SKIP_ON_WASM() - u8 buffer[64] = sp_zero; - sp_io_reader_t backing = sp_zero; - sp_io_seeking_reader_t io = sp_zero; - sp_io_seeking_reader_from_mem(&io, &backing, buffer, sizeof(buffer)); - - s64 pos = 0; - EXPECT_EQ(sp_io_seeking_reader_seek(&io, 100, SP_IO_SEEK_SET, &pos), SP_ERR_IO_SEEK_INVALID); - EXPECT_EQ(pos, -1); - - EXPECT_EQ(sp_io_seeking_reader_seek(&io, -10, SP_IO_SEEK_SET, &pos), SP_ERR_IO_SEEK_INVALID); - EXPECT_EQ(pos, -1); -} UTEST_F(io, seeking_reader_file_seek) { SKIP_ON_WASM() const char* content = "0123456789ABCDEF"; @@ -332,56 +228,6 @@ UTEST_F(io, reader_file_read_eof_empty) { sp_io_file_reader_close(&r); } -UTEST_F(io, writer_mem_write) { - SKIP_ON_WASM() - u8 buffer[16] = sp_zero; - u8 source[8] = {1,2,3,4,5,6,7,8}; - - sp_io_mem_writer_t w; sp_io_mem_writer_from_buffer(&w,buffer, sizeof(buffer)); - u64 bytes = 0; - EXPECT_EQ(sp_io_write(&w.base, source, sizeof(source), &bytes), SP_OK); - - EXPECT_EQ(bytes, 8); - sp_for(i, 8) { - EXPECT_EQ(buffer[i], source[i]); - } - /* mem writer has no close */; -} -UTEST_F(io, writer_mem_write_overflow) { - SKIP_ON_WASM() - u8 buffer[8] = sp_zero; - u8 source[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; - - sp_io_mem_writer_t w; sp_io_mem_writer_from_buffer(&w,buffer, sizeof(buffer)); - u64 bytes = 0; - EXPECT_EQ(sp_io_write(&w.base, source, sizeof(source), &bytes), SP_ERR_IO_NO_SPACE); - - EXPECT_EQ(bytes, 0); - /* mem writer has no close */; -} -UTEST_F(io, writer_mem_seek) { - SKIP_ON_WASM() - u8 buffer[64] = sp_zero; - sp_io_mem_writer_t w; sp_io_mem_writer_from_buffer(&w,buffer, sizeof(buffer)); - - s64 pos = 0; - EXPECT_EQ(sp_io_mem_writer_seek(&w, 32, SP_IO_SEEK_SET, &pos), SP_OK); - EXPECT_EQ(pos, 32); - - EXPECT_EQ(sp_io_mem_writer_seek(&w, 0, SP_IO_SEEK_END, &pos), SP_OK); - EXPECT_EQ(pos, 64); - - /* mem writer has no close */; -} -UTEST_F(io, writer_mem_size) { - SKIP_ON_WASM() - u8 buffer[128]; - sp_io_mem_writer_t w; sp_io_mem_writer_from_buffer(&w,buffer, sizeof(buffer)); - u64 size = 0; - EXPECT_EQ(sp_io_mem_writer_size(&w, &size), SP_OK); - EXPECT_EQ(size, 128); - /* mem writer has no close */; -} UTEST_F(io, writer_file_write) { SKIP_ON_WASM() const char* content = "test data"; @@ -838,18 +684,6 @@ UTEST_F(io, reader_buffered_eof_direct_path) { sp_io_file_reader_close(&r); } -UTEST_F(io, reader_buffered_zero_size) { - SKIP_ON_WASM() - u8 source[1] = {0}; - u8 read_buf[8]; - sp_io_reader_t io = sp_zero; - sp_io_reader_from_mem(&io, source, sizeof(source)); - sp_io_reader_set_buffer(&io, read_buf, sizeof(read_buf)); - - u64 bytes = 1; - EXPECT_EQ(sp_io_read(&io, source, 0, &bytes), SP_OK); - EXPECT_EQ(bytes, 0); -} UTEST_F(io, reader_buffered_eof_byte_by_byte) { SKIP_ON_WASM() const char* content = "0123456789ABCDEF"; diff --git a/test/io/mem.c b/test/io/mem.c index 5ab9606..d2a52f5 100644 --- a/test/io/mem.c +++ b/test/io/mem.c @@ -4,32 +4,98 @@ UTEST_EMPTY_FIXTURE(io_mem) typedef struct { const c8* source; + u64 writer_capacity; io_step_t steps [IO_MAX_STEPS]; -} io_read_test_t; + struct { + const c8* content; + } expect; +} io_mem_test_t; -void run_io_read_test(int* utest_result, io_read_test_t t) { +//////////// +// RUNNER // +//////////// +void run_io_mem_test(int* utest_result, io_mem_test_t t) { sp_io_reader_t r = sp_zero; - sp_io_reader_from_mem(&r, t.source, sp_cstr_len(t.source)); + if (t.source) { + sp_io_reader_from_mem(&r, t.source, sp_cstr_len(t.source)); + } + + u8 backing [64] = sp_zero; + sp_io_mem_writer_t w = sp_zero; + if (t.writer_capacity) { + sp_io_mem_writer_from_buffer(&w, backing, t.writer_capacity); + } + + u8 copy_buf [64] = sp_zero; sp_carr_for(t.steps, j) { const io_step_t* step = &t.steps[j]; if (step->kind == IO_STEP_NONE) break; - u8 dest [64] = sp_zero; - u64 bytes = 0; - sp_err_t err = sp_io_read(&r, dest, step->read.request, &bytes); - EXPECT_EQ(err, step->read.err); - - u64 expect_bytes = sp_cstr_len(step->read.content); - EXPECT_EQ(bytes, expect_bytes); - sp_for(it, expect_bytes) { - EXPECT_EQ((c8)dest[it], step->read.content[it]); + switch (step->kind) { + case IO_STEP_READ: { + u8 dest [64] = sp_zero; + u64 bytes = 0; + sp_err_t err = sp_io_read(&r, dest, step->read.request, &bytes); + EXPECT_EQ(err, step->read.err); + u64 expect_bytes = sp_cstr_len(step->read.content); + EXPECT_EQ(bytes, expect_bytes); + sp_for(it, expect_bytes) { + EXPECT_EQ((c8)dest[it], step->read.content[it]); + } + break; + } + case IO_STEP_WRITE: { + u64 bytes = 0; + sp_err_t err = sp_io_write(&w.base, step->write.data, sp_cstr_len(step->write.data), &bytes); + EXPECT_EQ(err, step->write.err); + EXPECT_EQ(bytes, step->write.bytes); + break; + } + case IO_STEP_FLUSH: { + sp_err_t err = sp_io_flush(&w.base); + EXPECT_EQ(err, step->flush.err); + break; + } + case IO_STEP_SEEK: { + s64 pos = 0; + sp_err_t err = sp_io_mem_writer_seek(&w, step->seek.offset, step->seek.whence, &pos); + EXPECT_EQ(err, step->seek.err); + EXPECT_EQ(pos, step->seek.pos); + break; + } + case IO_STEP_SIZE: { + u64 size = 0; + sp_err_t err = sp_io_mem_writer_size(&w, &size); + EXPECT_EQ(err, step->size.err); + EXPECT_EQ(size, step->size.size); + break; + } + case IO_STEP_COPY: { + u64 copied = 0; + sp_err_t err = sp_io_copy_b(&w.base, &r, copy_buf, step->copy.buffer, &copied); + EXPECT_EQ(err, step->copy.err); + EXPECT_EQ(copied, step->copy.copied); + break; + } + case IO_STEP_NONE: { + sp_unreachable_case(); + } } } + + u64 n = sp_cstr_len(t.expect.content); + sp_for(it, n) { + EXPECT_EQ((c8)backing[it], t.expect.content[it]); + } } + +////////// +// READ // +////////// UTEST_F(io_mem, read_exact_then_eof) { - run_io_read_test(utest_result, (io_read_test_t){ + run_io_mem_test(utest_result, (io_mem_test_t){ .source = "0123", .steps = { { .kind = IO_STEP_READ, .read = { 4, SP_OK, "0123" } }, @@ -39,7 +105,7 @@ UTEST_F(io_mem, read_exact_then_eof) { } UTEST_F(io_mem, read_chunked) { - run_io_read_test(utest_result, (io_read_test_t){ + run_io_mem_test(utest_result, (io_mem_test_t){ .source = "01234567", .steps = { { .kind = IO_STEP_READ, .read = { 3, SP_OK, "012" } }, @@ -51,7 +117,7 @@ UTEST_F(io_mem, read_chunked) { } UTEST_F(io_mem, read_oversized_request) { - run_io_read_test(utest_result, (io_read_test_t){ + run_io_mem_test(utest_result, (io_mem_test_t){ .source = "abc", .steps = { { .kind = IO_STEP_READ, .read = { 16, SP_OK, "abc" } }, @@ -61,7 +127,7 @@ UTEST_F(io_mem, read_oversized_request) { } UTEST_F(io_mem, read_empty_source) { - run_io_read_test(utest_result, (io_read_test_t){ + run_io_mem_test(utest_result, (io_mem_test_t){ .source = "", .steps = { { .kind = IO_STEP_READ, .read = { 4, SP_ERR_IO_EOF } }, @@ -70,7 +136,7 @@ UTEST_F(io_mem, read_empty_source) { } UTEST_F(io_mem, read_eof_idempotent) { - run_io_read_test(utest_result, (io_read_test_t){ + run_io_mem_test(utest_result, (io_mem_test_t){ .source = "x", .steps = { { .kind = IO_STEP_READ, .read = { 4, SP_OK, "x" } }, @@ -81,7 +147,7 @@ UTEST_F(io_mem, read_eof_idempotent) { } UTEST_F(io_mem, read_zero_request) { - run_io_read_test(utest_result, (io_read_test_t){ + run_io_mem_test(utest_result, (io_mem_test_t){ .source = "abc", .steps = { { .kind = IO_STEP_READ, .read = { 0, SP_OK } }, @@ -91,57 +157,12 @@ UTEST_F(io_mem, read_zero_request) { } - - - /////////// // WRITE // /////////// -typedef struct { - u64 capacity; - io_step_t steps [IO_MAX_STEPS]; - struct { - const c8* content; - } expect; -} io_write_test_t; - -void run_io_write_test(int* utest_result, io_write_test_t t) { - u8 backing [64] = sp_zero; - sp_io_mem_writer_t w = sp_zero; - sp_io_mem_writer_from_buffer(&w, backing, t.capacity); - - sp_carr_for(t.steps, j) { - const io_step_t* step = &t.steps[j]; - if (step->kind == IO_STEP_NONE) break; - - switch (step->kind) { - case IO_STEP_WRITE: { - u64 bytes = 0; - sp_err_t err = sp_io_write(&w.base, step->write.data, sp_cstr_len(step->write.data), &bytes); - EXPECT_EQ(err, step->write.err); - EXPECT_EQ(bytes, step->write.bytes); - break; - } - case IO_STEP_FLUSH: { - sp_err_t err = sp_io_flush(&w.base); - EXPECT_EQ(err, step->flush.err); - break; - } - default: { - sp_unreachable_case(); - } - } - } - - u64 n = sp_cstr_len(t.expect.content); - sp_for(it, n) { - EXPECT_EQ((c8)backing[it], t.expect.content[it]); - } -} - UTEST_F(io_mem, write_fits) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 16, + run_io_mem_test(utest_result, (io_mem_test_t){ + .writer_capacity = 16, .steps = { { .kind = IO_STEP_WRITE, .write = { "hello", SP_OK, 5 } }, }, @@ -150,8 +171,8 @@ UTEST_F(io_mem, write_fits) { } UTEST_F(io_mem, write_exact_fit) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 4, + run_io_mem_test(utest_result, (io_mem_test_t){ + .writer_capacity = 4, .steps = { { .kind = IO_STEP_WRITE, .write = { "abcd", SP_OK, 4 } }, }, @@ -160,8 +181,8 @@ UTEST_F(io_mem, write_exact_fit) { } UTEST_F(io_mem, write_overflow) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 4, + run_io_mem_test(utest_result, (io_mem_test_t){ + .writer_capacity = 4, .steps = { { .kind = IO_STEP_WRITE, .write = { "abcdefgh", SP_ERR_IO_NO_SPACE, 4 } }, }, @@ -170,8 +191,8 @@ UTEST_F(io_mem, write_overflow) { } UTEST_F(io_mem, write_barely_overflows) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 4, + run_io_mem_test(utest_result, (io_mem_test_t){ + .writer_capacity = 4, .steps = { { .kind = IO_STEP_WRITE, .write = { "abcde", SP_ERR_IO_NO_SPACE, 4 } }, }, @@ -180,8 +201,8 @@ UTEST_F(io_mem, write_barely_overflows) { } UTEST_F(io_mem, write_smaller_after_overflow) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 4, + run_io_mem_test(utest_result, (io_mem_test_t){ + .writer_capacity = 4, .steps = { { .kind = IO_STEP_WRITE, .write = { "abcd", SP_OK, 4 } }, { .kind = IO_STEP_WRITE, .write = { "x", SP_ERR_IO_NO_SPACE, 0 } }, @@ -191,8 +212,8 @@ UTEST_F(io_mem, write_smaller_after_overflow) { } UTEST_F(io_mem, write_appends) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 16, + run_io_mem_test(utest_result, (io_mem_test_t){ + .writer_capacity = 16, .steps = { { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, { .kind = IO_STEP_WRITE, .write = { "def", SP_OK, 3 } }, @@ -203,8 +224,8 @@ UTEST_F(io_mem, write_appends) { } UTEST_F(io_mem, write_zero) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 8, + run_io_mem_test(utest_result, (io_mem_test_t){ + .writer_capacity = 8, .steps = { { .kind = IO_STEP_WRITE, .write = { "", SP_OK, 0 } }, }, @@ -212,8 +233,8 @@ UTEST_F(io_mem, write_zero) { } UTEST_F(io_mem, write_flush_empty) { - run_io_write_test(utest_result, (io_write_test_t){ - .capacity = 8, + run_io_mem_test(utest_result, (io_mem_test_t){ + .writer_capacity = 8, .steps = { { .kind = IO_STEP_FLUSH, .flush = { SP_OK } }, }, @@ -224,66 +245,68 @@ UTEST_F(io_mem, write_flush_empty) { ////////// // COPY // ////////// -typedef struct { - const c8* source; - u64 capacity; - u64 buffer; - struct { - sp_err_t err; - const c8* final; - } expect; -} io_copy_test_t; - -void run_io_copy_test(int* utest_result, io_copy_test_t t) { - sp_io_reader_t r = sp_zero; - sp_io_reader_from_mem(&r, t.source, sp_cstr_len(t.source)); - - u8 backing [64] = sp_zero; - sp_io_mem_writer_t w = sp_zero; - sp_io_mem_writer_from_buffer(&w, backing, t.capacity); - - u8 copy_buf [64] = sp_zero; - u64 copied = 0; - sp_err_t err = sp_io_copy_b(&w.base, &r, copy_buf, t.buffer, &copied); - - EXPECT_EQ(err, t.expect.err); - u64 n = sp_cstr_len(t.expect.final); - EXPECT_EQ(copied, n); - sp_for(it, n) { - EXPECT_EQ((c8)backing[it], t.expect.final[it]); - } -} - UTEST_F(io_mem, copy_full) { - run_io_copy_test(utest_result, (io_copy_test_t){ + run_io_mem_test(utest_result, (io_mem_test_t){ .source = "0123456789", - .capacity = 32, .buffer = 8, - .expect = { .err = SP_OK, .final = "0123456789" }, + .writer_capacity = 32, + .steps = { + { .kind = IO_STEP_COPY, .copy = { 8, SP_OK, 10 } }, + }, + .expect.content = "0123456789", }); } UTEST_F(io_mem, copy_loops) { - run_io_copy_test(utest_result, (io_copy_test_t){ + run_io_mem_test(utest_result, (io_mem_test_t){ .source = "ABCDEFGHIJKLMNOP", - .capacity = 32, .buffer = 4, - .expect = { .err = SP_OK, .final = "ABCDEFGHIJKLMNOP" }, + .writer_capacity = 32, + .steps = { + { .kind = IO_STEP_COPY, .copy = { 4, SP_OK, 16 } }, + }, + .expect.content = "ABCDEFGHIJKLMNOP", }); } UTEST_F(io_mem, copy_empty) { - run_io_copy_test(utest_result, (io_copy_test_t){ + run_io_mem_test(utest_result, (io_mem_test_t){ .source = "", - .capacity = 8, .buffer = 8, - .expect = { .err = SP_OK }, + .writer_capacity = 8, + .steps = { + { .kind = IO_STEP_COPY, .copy = { 8, SP_OK, 0 } }, + }, }); } UTEST_F(io_mem, copy_writer_no_space) { - run_io_copy_test(utest_result, (io_copy_test_t){ + run_io_mem_test(utest_result, (io_mem_test_t){ .source = "0123456789", - .capacity = 4, .buffer = 8, - .expect = { .err = SP_ERR_IO_NO_SPACE, .final = "0123" }, + .writer_capacity = 4, + .steps = { + { .kind = IO_STEP_COPY, .copy = { 8, SP_ERR_IO_NO_SPACE, 4 } }, + }, + .expect.content = "0123", }); } +/////////////// +// SEEK/SIZE // +/////////////// +UTEST_F(io_mem, writer_seek) { + run_io_mem_test(utest_result, (io_mem_test_t){ + .writer_capacity = 64, + .steps = { + { .kind = IO_STEP_SEEK, .seek = { 32, SP_IO_SEEK_SET, SP_OK, 32 } }, + { .kind = IO_STEP_SEEK, .seek = { 0, SP_IO_SEEK_END, SP_OK, 64 } }, + }, + }); +} + +UTEST_F(io_mem, writer_size) { + run_io_mem_test(utest_result, (io_mem_test_t){ + .writer_capacity = 128, + .steps = { + { .kind = IO_STEP_SIZE, .size = { SP_OK, 128 } }, + }, + }); +} diff --git a/test/io/read.c b/test/io/read.c index 3bb244b..083bfe4 100644 --- a/test/io/read.c +++ b/test/io/read.c @@ -3,7 +3,8 @@ UTEST_EMPTY_FIXTURE(io_read) typedef struct { - io_mock_response_t responses[IO_MAX_RESPONSES]; + io_result_t results [IO_MAX_RESPONSES]; + u64 buffer; io_step_t steps[IO_MAX_STEPS]; } io_mock_read_test_t; @@ -11,9 +12,14 @@ typedef struct { // RUNNER // //////////// void run_io_mock_read_test(int* utest_result, io_mock_read_test_t t) { - u64 num_responses = io_mock_response_count(t.responses, IO_MAX_RESPONSES); + u64 num_responses = io_get_num_results(t.results, IO_MAX_RESPONSES); io_mock_reader_t r = sp_zero; - io_mock_reader_init(&r, t.responses, num_responses); + io_mock_reader_init(&r, t.results, num_responses); + + u8 wrapper_buf[64] = sp_zero; + if (t.buffer) { + sp_io_reader_set_buffer(&r.base, wrapper_buf, t.buffer); + } sp_carr_for(t.steps, j) { const io_step_t* step = &t.steps[j]; @@ -31,13 +37,13 @@ void run_io_mock_read_test(int* utest_result, io_mock_read_test_t t) { } } - EXPECT_EQ(r.cursor, r.num_responses); + EXPECT_EQ(r.cursor, r.num_results); } UTEST_F(io_read, smoke) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ - .responses = { + .results = { { .bytes = 3, .data = "abc", .err = SP_OK }, }, .steps = { @@ -49,7 +55,7 @@ UTEST_F(io_read, smoke) { // Short read with OK, then EOF. Pins that short reads do NOT signal EOF. UTEST_F(io_read, short_then_eof) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ - .responses = { + .results = { { .bytes = 3, .data = "abc", .err = SP_OK }, { .bytes = 0, .err = SP_ERR_IO_EOF }, }, @@ -63,7 +69,7 @@ UTEST_F(io_read, short_then_eof) { // EOF returned at offset 0 (empty stream). UTEST_F(io_read, eof_empty) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ - .responses = { + .results = { { .bytes = 0, .err = SP_ERR_IO_EOF }, }, .steps = { @@ -75,7 +81,7 @@ UTEST_F(io_read, eof_empty) { // EOF is idempotent: each repeated read returns the same terminal state. UTEST_F(io_read, eof_idempotent) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ - .responses = { + .results = { { .bytes = 0, .err = SP_ERR_IO_EOF }, { .bytes = 0, .err = SP_ERR_IO_EOF }, { .bytes = 0, .err = SP_ERR_IO_EOF }, @@ -91,7 +97,7 @@ UTEST_F(io_read, eof_idempotent) { // Hard error with zero bytes: not-done -> broken. UTEST_F(io_read, error_zero_bytes) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ - .responses = { + .results = { { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, }, .steps = { @@ -103,7 +109,7 @@ UTEST_F(io_read, error_zero_bytes) { // Successful read, then a hard error on the next call. UTEST_F(io_read, error_after_success) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ - .responses = { + .results = { { .bytes = 4, .data = "abcd", .err = SP_OK }, { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, }, @@ -118,7 +124,7 @@ UTEST_F(io_read, error_after_success) { // Caller must see the bytes AND the error. UTEST_F(io_read, bytes_and_error) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ - .responses = { + .results = { { .bytes = 3, .data = "abc", .err = SP_ERR_IO_READ_FAILED }, }, .steps = { @@ -132,7 +138,7 @@ UTEST_F(io_read, bytes_and_error) { // errors itself: it is the backend's job to decide whether to retry. UTEST_F(io_read, error_then_error) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ - .responses = { + .results = { { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, }, @@ -143,3 +149,152 @@ UTEST_F(io_read, error_then_error) { }); } + +////////////// +// BUFFERED // +////////////// + +// First read with empty wrapper buffer: backend is called once to fill it, +// user gets the bytes they asked for, the rest remain in the buffer. +UTEST_F(io_read, buffered_first_read_fills) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .results = { + { .bytes = 8, .data = "01234567", .err = SP_OK }, + }, + .buffer = 8, + .steps = { + { .kind = IO_STEP_READ, .read = { 3, SP_OK, "012" } }, + }, + }); +} + +// Two reads of a buffer-sized prefill: the second read must NOT call the +// backend (script has only one entry; assertion would fail if it did). +UTEST_F(io_read, buffered_drains_without_backend_call) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .results = { + { .bytes = 8, .data = "01234567", .err = SP_OK }, + }, + .buffer = 8, + .steps = { + { .kind = IO_STEP_READ, .read = { 3, SP_OK, "012" } }, + { .kind = IO_STEP_READ, .read = { 3, SP_OK, "345" } }, + }, + }); +} + +// Drain the buffer completely, then read again. The second physical read +// must trigger a fresh backend call to refill. +UTEST_F(io_read, buffered_refill_after_exhaustion) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .results = { + { .bytes = 4, .data = "abcd", .err = SP_OK }, + { .bytes = 4, .data = "efgh", .err = SP_OK }, + }, + .buffer = 4, + .steps = { + { .kind = IO_STEP_READ, .read = { 4, SP_OK, "abcd" } }, + { .kind = IO_STEP_READ, .read = { 4, SP_OK, "efgh" } }, + }, + }); +} + +// Request exceeds wrapper buffer capacity: wrapper bypasses its buffer and +// passes the request directly to the backend. +UTEST_F(io_read, buffered_large_request_bypasses) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .results = { + { .bytes = 16, .data = "0123456789ABCDEF", .err = SP_OK }, + }, + .buffer = 4, + .steps = { + { .kind = IO_STEP_READ, .read = { 16, SP_OK, "0123456789ABCDEF" } }, + }, + }); +} + +// Buffer has some bytes, request is larger than capacity: drain what's +// buffered, then bypass for the remainder. +UTEST_F(io_read, buffered_drain_then_bypass) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .results = { + { .bytes = 4, .data = "abcd", .err = SP_OK }, + { .bytes = 12, .data = "EFGHIJKLMNOP", .err = SP_OK }, + }, + .buffer = 4, + .steps = { + { .kind = IO_STEP_READ, .read = { 2, SP_OK, "ab" } }, + { .kind = IO_STEP_READ, .read = { 14, SP_OK, "cdEFGHIJKLMNOP" } }, + }, + }); +} + +// Empty buffer, request 8: backend returns EOF with no bytes. +UTEST_F(io_read, buffered_eof_immediate) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .results = { + { .bytes = 0, .err = SP_ERR_IO_EOF }, + }, + .buffer = 8, + .steps = { + { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_EOF } }, + }, + }); +} + +// Buffer has some bytes. Request exceeds them. Backend EOFs on the refill. +// Wrapper normalizes "EOF + bytes" to OK; the next call sees the EOF. +UTEST_F(io_read, buffered_eof_after_partial_drain) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .results = { + { .bytes = 3, .data = "abc", .err = SP_OK }, + { .bytes = 0, .err = SP_ERR_IO_EOF }, + { .bytes = 0, .err = SP_ERR_IO_EOF }, + }, + .buffer = 4, + .steps = { + { .kind = IO_STEP_READ, .read = { 8, SP_OK, "abc" } }, + { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_EOF } }, + }, + }); +} + +// Empty buffer, backend errors immediately. Error propagates. +UTEST_F(io_read, buffered_error_immediate) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .results = { + { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, + }, + .buffer = 8, + .steps = { + { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_READ_FAILED } }, + }, + }); +} + +// Backend short-fills the wrapper buffer. User asked for 3 but backend +// only delivered 2. Wrapper does a single backend call per sp_io_read so +// the user sees a short read. +UTEST_F(io_read, buffered_short_fill) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .results = { + { .bytes = 2, .data = "ab", .err = SP_OK }, + }, + .buffer = 8, + .steps = { + { .kind = IO_STEP_READ, .read = { 3, SP_OK, "ab" } }, + }, + }); +} + +// Zero-byte request must not call the backend. +UTEST_F(io_read, buffered_zero_request) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .results = {0}, + .buffer = 8, + .steps = { + { .kind = IO_STEP_READ, .read = { 0, SP_OK } }, + }, + }); +} + diff --git a/test/io/seeking_reader.c b/test/io/seeking_reader.c new file mode 100644 index 0000000..7d7dc9c --- /dev/null +++ b/test/io/seeking_reader.c @@ -0,0 +1,73 @@ +#include "io.h" + +UTEST_EMPTY_FIXTURE(io_seeking_reader) + +typedef struct { + const c8* source; + io_step_t steps [IO_MAX_STEPS]; +} io_seeking_reader_test_t; + +//////////// +// RUNNER // +//////////// +void run_io_seeking_reader_test(int* utest_result, io_seeking_reader_test_t t) { + sp_io_reader_t backing = sp_zero; + sp_io_seeking_reader_t r = sp_zero; + sp_io_seeking_reader_from_mem(&r, &backing, t.source, sp_cstr_len(t.source)); + + sp_carr_for(t.steps, j) { + const io_step_t* step = &t.steps[j]; + if (step->kind == IO_STEP_NONE) break; + + switch (step->kind) { + case IO_STEP_READ: { + u8 dest [64] = sp_zero; + u64 bytes = 0; + sp_err_t err = sp_io_read(r.reader, dest, step->read.request, &bytes); + EXPECT_EQ(err, step->read.err); + u64 expect_bytes = sp_cstr_len(step->read.content); + EXPECT_EQ(bytes, expect_bytes); + sp_for(it, expect_bytes) { + EXPECT_EQ((c8)dest[it], step->read.content[it]); + } + break; + } + case IO_STEP_SEEK: { + s64 pos = 0; + sp_err_t err = sp_io_seeking_reader_seek(&r, step->seek.offset, step->seek.whence, &pos); + EXPECT_EQ(err, step->seek.err); + EXPECT_EQ(pos, step->seek.pos); + break; + } + case IO_STEP_NONE: + case IO_STEP_WRITE: + case IO_STEP_FLUSH: + case IO_STEP_SIZE: + case IO_STEP_COPY: { + sp_unreachable_case(); + } + } + } +} + + +UTEST_F(io_seeking_reader, seek) { + run_io_seeking_reader_test(utest_result, (io_seeking_reader_test_t){ + .source = "0123456789ABCDEF", + .steps = { + { .kind = IO_STEP_SEEK, .seek = { 8, SP_IO_SEEK_SET, SP_OK, 8 } }, + { .kind = IO_STEP_READ, .read = { 1, SP_OK, "8" } }, + { .kind = IO_STEP_SEEK, .seek = { 0, SP_IO_SEEK_END, SP_OK, 16 } }, + }, + }); +} + +UTEST_F(io_seeking_reader, seek_invalid) { + run_io_seeking_reader_test(utest_result, (io_seeking_reader_test_t){ + .source = "0123456789", + .steps = { + { .kind = IO_STEP_SEEK, .seek = { 100, SP_IO_SEEK_SET, SP_ERR_IO_SEEK_INVALID, -1 } }, + { .kind = IO_STEP_SEEK, .seek = { -10, SP_IO_SEEK_SET, SP_ERR_IO_SEEK_INVALID, -1 } }, + }, + }); +} diff --git a/test/io/write.c b/test/io/write.c index dda37e0..f516423 100644 --- a/test/io/write.c +++ b/test/io/write.c @@ -3,7 +3,8 @@ UTEST_EMPTY_FIXTURE(io_write) typedef struct { - io_mock_response_t responses[IO_MAX_RESPONSES]; + io_result_t results [IO_MAX_RESPONSES]; + u64 buffer; io_step_t steps[IO_MAX_STEPS]; struct { const c8* received; @@ -14,9 +15,14 @@ typedef struct { // RUNNER // //////////// void run_io_mock_write_test(int* utest_result, io_mock_write_test_t t) { - u64 num_responses = io_mock_response_count(t.responses, IO_MAX_RESPONSES); + u64 num_responses = io_get_num_results(t.results, IO_MAX_RESPONSES); io_mock_writer_t w = sp_zero; - io_mock_writer_init(&w, t.responses, num_responses); + io_mock_writer_init(&w, t.results, num_responses); + + u8 wrapper_buf[64] = sp_zero; + if (t.buffer) { + sp_io_writer_set_buffer(&w.base, wrapper_buf, t.buffer); + } sp_carr_for(t.steps, j) { const io_step_t* step = &t.steps[j]; @@ -36,13 +42,16 @@ void run_io_mock_write_test(int* utest_result, io_mock_write_test_t t) { break; } case IO_STEP_NONE: - case IO_STEP_READ: { + case IO_STEP_READ: + case IO_STEP_SEEK: + case IO_STEP_SIZE: + case IO_STEP_COPY: { sp_unreachable_case(); } } } - EXPECT_EQ(w.cursor, w.num_responses); + EXPECT_EQ(w.cursor, w.num_results); u64 n = sp_cstr_len(t.expect.received); EXPECT_EQ(w.received_len, n); @@ -55,7 +64,7 @@ void run_io_mock_write_test(int* utest_result, io_mock_write_test_t t) { UTEST_F(io_write, smoke) { run_io_mock_write_test(utest_result, (io_mock_write_test_t){ - .responses = { + .results = { { .bytes = 3, .err = SP_OK }, }, .steps = { @@ -69,7 +78,7 @@ UTEST_F(io_write, smoke) { // the error. UTEST_F(io_write, partial_no_space) { run_io_mock_write_test(utest_result, (io_mock_write_test_t){ - .responses = { + .results = { { .bytes = 4, .err = SP_ERR_IO_NO_SPACE }, }, .steps = { @@ -82,7 +91,7 @@ UTEST_F(io_write, partial_no_space) { // Hard error with zero bytes accepted. UTEST_F(io_write, error_zero_bytes) { run_io_mock_write_test(utest_result, (io_mock_write_test_t){ - .responses = { + .results = { { .bytes = 0, .err = SP_ERR_IO_WRITE_FAILED }, }, .steps = { @@ -94,7 +103,7 @@ UTEST_F(io_write, error_zero_bytes) { // Successful write, then a hard error on the next call. UTEST_F(io_write, error_after_success) { run_io_mock_write_test(utest_result, (io_mock_write_test_t){ - .responses = { + .results = { { .bytes = 3, .err = SP_OK }, { .bytes = 0, .err = SP_ERR_IO_WRITE_FAILED }, }, @@ -109,7 +118,7 @@ UTEST_F(io_write, error_after_success) { // Orthogonal contract for writes: partial bytes AND error in one call. UTEST_F(io_write, bytes_and_error) { run_io_mock_write_test(utest_result, (io_mock_write_test_t){ - .responses = { + .results = { { .bytes = 2, .err = SP_ERR_IO_WRITE_FAILED }, }, .steps = { @@ -123,7 +132,7 @@ UTEST_F(io_write, bytes_and_error) { // wrapper does not latch the error. UTEST_F(io_write, smaller_after_overflow) { run_io_mock_write_test(utest_result, (io_mock_write_test_t){ - .responses = { + .results = { { .bytes = 0, .err = SP_ERR_IO_NO_SPACE }, { .bytes = 2, .err = SP_OK }, }, @@ -134,3 +143,160 @@ UTEST_F(io_write, smaller_after_overflow) { .expect.received = "xy", }); } + + +////////////// +// BUFFERED // +////////////// + +// Small write fits in wrapper buffer: backend must NOT be called. +UTEST_F(io_write, buffered_small_no_backend_call) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .results = {0}, + .buffer = 8, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, + }, + }); +} + +// Multiple small writes accumulate in the buffer; backend stays untouched. +UTEST_F(io_write, buffered_writes_accumulate) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .results = {0}, + .buffer = 8, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, + { .kind = IO_STEP_WRITE, .write = { "de", SP_OK, 2 } }, + }, + }); +} + +// Write that exactly fills the buffer: still no backend call (no overflow). +UTEST_F(io_write, buffered_exact_fit) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .results = {0}, + .buffer = 4, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abcd", SP_OK, 4 } }, + }, + }); +} + +// Explicit flush of a buffer with data calls the backend exactly once. +UTEST_F(io_write, buffered_flush_drains_buffer) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .results = { + { .bytes = 3, .err = SP_OK }, + }, + .buffer = 8, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, + { .kind = IO_STEP_FLUSH, .flush = { SP_OK } }, + }, + .expect.received = "abc", + }); +} + +// Multiple writes then a single flush: one backend call with the +// concatenation. +UTEST_F(io_write, buffered_multiple_writes_one_flush) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .results = { + { .bytes = 6, .err = SP_OK }, + }, + .buffer = 8, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, + { .kind = IO_STEP_WRITE, .write = { "def", SP_OK, 3 } }, + { .kind = IO_STEP_FLUSH, .flush = { SP_OK } }, + }, + .expect.received = "abcdef", + }); +} + +// Flushing an empty buffer must NOT call the backend. +UTEST_F(io_write, buffered_flush_empty) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .results = {0}, + .buffer = 8, + .steps = { + { .kind = IO_STEP_FLUSH, .flush = { SP_OK } }, + }, + }); +} + +// Buffer has data; next write does not fit in remaining space. Wrapper +// flushes first, then buffers the new write. Backend called once for the +// flush. +UTEST_F(io_write, buffered_overflow_flushes_then_buffers) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .results = { + { .bytes = 5, .err = SP_OK }, + }, + .buffer = 8, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abcde", SP_OK, 5 } }, + { .kind = IO_STEP_WRITE, .write = { "fghij", SP_OK, 5 } }, + }, + .expect.received = "abcde", + }); +} + +// Write that is larger than the wrapper buffer bypasses the buffer +// entirely. Backend gets the bytes directly. +UTEST_F(io_write, buffered_large_write_bypasses) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .results = { + { .bytes = 16, .err = SP_OK }, + }, + .buffer = 4, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "0123456789ABCDEF", SP_OK, 16 } }, + }, + .expect.received = "0123456789ABCDEF", + }); +} + +// Buffer has some bytes, then a large write arrives. Wrapper flushes the +// buffered prefix and then writes the large payload directly. Two backend +// calls. +UTEST_F(io_write, buffered_drain_then_bypass) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .results = { + { .bytes = 3, .err = SP_OK }, + { .bytes = 16, .err = SP_OK }, + }, + .buffer = 4, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, + { .kind = IO_STEP_WRITE, .write = { "0123456789ABCDEF", SP_OK, 16 } }, + }, + .expect.received = "abc0123456789ABCDEF", + }); +} + +// Backend fails when flush tries to drain. Flush surfaces the error. +UTEST_F(io_write, buffered_flush_backend_error) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .results = { + { .bytes = 0, .err = SP_ERR_IO_WRITE_FAILED }, + }, + .buffer = 8, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, + { .kind = IO_STEP_FLUSH, .flush = { SP_ERR_IO_WRITE_FAILED } }, + }, + }); +} + +// Zero-length write is a no-op: no buffer change, no backend call. +UTEST_F(io_write, buffered_zero_write) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .results = {0}, + .buffer = 8, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "", SP_OK, 0 } }, + }, + }); +} From 66f94813b605b2531b54b7f148cf1eab0a7a3767 Mon Sep 17 00:00:00 2001 From: Thomas Spader Date: Tue, 12 May 2026 17:35:50 -0400 Subject: [PATCH 12/21] categorize all --- test/io.c | 3 +- test/io/copy.c | 2 +- test/io/dyn.c | 114 ++++++ test/io/file.c | 355 +++++++++++++++++ test/io/io.h | 2 + test/io/loose.c | 798 --------------------------------------- test/io/mem.c | 3 +- test/io/read.c | 5 +- test/io/seeking_reader.c | 113 +++++- test/io/write.c | 3 +- 10 files changed, 593 insertions(+), 805 deletions(-) create mode 100644 test/io/dyn.c create mode 100644 test/io/file.c delete mode 100644 test/io/loose.c diff --git a/test/io.c b/test/io.c index b96e2e6..00533cb 100644 --- a/test/io.c +++ b/test/io.c @@ -6,7 +6,8 @@ SP_TEST_MAIN() #include "io/copy.c" #include "io/mem.c" #include "io/seeking_reader.c" -#include "io/loose.c" +#include "io/file.c" +#include "io/dyn.c" u64 io_get_num_results(const io_result_t* responses, u64 max) { u64 n = 0; diff --git a/test/io/copy.c b/test/io/copy.c index 420d4e4..7e9fed3 100644 --- a/test/io/copy.c +++ b/test/io/copy.c @@ -156,7 +156,7 @@ UTEST_F(io_copy, buffered_reader_drains_through_buffer) { { .bytes = 4, .data = "4567", .err = SP_OK }, { .bytes = 0, .err = SP_ERR_IO_EOF }, }, - .buffer = { .r = 2, .copy = 2 }, + .buffer = { .r = 4, .copy = 2 }, .capacity.writer = 32, .expect = { .err = SP_OK, .copied = 8, .final = "01234567" }, }); diff --git a/test/io/dyn.c b/test/io/dyn.c new file mode 100644 index 0000000..a00391c --- /dev/null +++ b/test/io/dyn.c @@ -0,0 +1,114 @@ +#include "io.h" + +typedef struct { + io_step_t steps [IO_MAX_STEPS]; + struct { + const c8* content; + } expect; +} io_dyn_test_t; + +//////////// +// RUNNER // +//////////// +void run_io_dyn_test(int* utest_result, sp_mem_t mem, io_dyn_test_t t) { + sp_io_dyn_mem_writer_t w; + sp_io_dyn_mem_writer_init_a(mem, &w); + + sp_carr_for(t.steps, j) { + const io_step_t* step = &t.steps[j]; + if (step->kind == IO_STEP_NONE) break; + + switch (step->kind) { + case IO_STEP_WRITE: { + u64 bytes = 0; + sp_err_t err = sp_io_write(&w.base, step->write.data, sp_cstr_len(step->write.data), &bytes); + EXPECT_EQ(err, step->write.err); + EXPECT_EQ(bytes, step->write.bytes); + break; + } + case IO_STEP_SEEK: { + s64 pos = 0; + sp_err_t err = sp_io_dyn_mem_writer_seek(&w, step->seek.offset, step->seek.whence, &pos); + EXPECT_EQ(err, step->seek.err); + EXPECT_EQ(pos, step->seek.pos); + break; + } + case IO_STEP_SIZE: { + u64 size = 0; + sp_err_t err = sp_io_dyn_mem_writer_size(&w, &size); + EXPECT_EQ(err, step->size.err); + EXPECT_EQ(size, step->size.size); + break; + } + case IO_STEP_NONE: + case IO_STEP_READ: + case IO_STEP_FLUSH: + case IO_STEP_COPY: + case IO_STEP_PAD: { + sp_unreachable_case(); + } + } + } + + if (t.expect.content) { + sp_str_t str = sp_mem_buffer_as_str(&w.storage); + u64 n = sp_cstr_len(t.expect.content); + EXPECT_EQ(str.len, n); + sp_for(it, n) { + EXPECT_EQ((c8)str.data[it], t.expect.content[it]); + } + } + + sp_io_dyn_mem_writer_close(&w); +} + +UTEST_F(io, dyn_write) { + run_io_dyn_test(utest_result, ut.mem, (io_dyn_test_t){ + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abcd", SP_OK, 4 } }, + { .kind = IO_STEP_SIZE, .size = { SP_OK, 4 } }, + }, + .expect.content = "abcd", + }); +} + +UTEST_F(io, dyn_multiple_writes) { + run_io_dyn_test(utest_result, ut.mem, (io_dyn_test_t){ + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, + { .kind = IO_STEP_WRITE, .write = { "def", SP_OK, 3 } }, + { .kind = IO_STEP_WRITE, .write = { "ghi", SP_OK, 3 } }, + { .kind = IO_STEP_SIZE, .size = { SP_OK, 9 } }, + }, + .expect.content = "abcdefghi", + }); +} + +UTEST_F(io, dyn_seek) { + run_io_dyn_test(utest_result, ut.mem, (io_dyn_test_t){ + .steps = { + { .kind = IO_STEP_WRITE, .write = { "12345678", SP_OK, 8 } }, + { .kind = IO_STEP_SEEK, .seek = { 4, SP_IO_SEEK_SET, SP_OK, 4 } }, + }, + .expect.content = "12345678", + }); +} + +// Growth past any reasonable initial capacity. +UTEST_F(io, dyn_grows) { + SKIP_ON_WASM() + sp_io_dyn_mem_writer_t w; + sp_io_dyn_mem_writer_init_a(ut.mem, &w); + + u8 data [256]; + sp_for(i, 256) data[i] = (u8)i; + + u64 written = 0; + EXPECT_EQ(sp_io_write(&w.base, data, 256, &written), SP_OK); + EXPECT_EQ(written, 256); + u64 size = 0; + sp_io_dyn_mem_writer_size(&w, &size); + EXPECT_EQ(size, 256); + + sp_io_dyn_mem_writer_close(&w); +} diff --git a/test/io/file.c b/test/io/file.c new file mode 100644 index 0000000..2428e93 --- /dev/null +++ b/test/io/file.c @@ -0,0 +1,355 @@ +#include "io.h" + +//////////// +// READER // +//////////// +typedef struct { + const c8* content; + u64 buffer; + io_step_t steps [IO_MAX_STEPS]; +} io_file_reader_test_t; + +void run_io_file_reader_test(int* utest_result, sp_str_t path, io_file_reader_test_t t) { + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, path, SP_IO_WRITE_MODE_OVERWRITE); + if (t.content) sp_io_write(&w.base, t.content, sp_cstr_len(t.content), SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, path); + + u8 wrapper_buf [64] = sp_zero; + if (t.buffer) sp_io_reader_set_buffer(&r.base, wrapper_buf, t.buffer); + + sp_carr_for(t.steps, j) { + const io_step_t* step = &t.steps[j]; + if (step->kind == IO_STEP_NONE) break; + + switch (step->kind) { + case IO_STEP_READ: { + u8 dest [64] = sp_zero; + u64 bytes = 0; + sp_err_t err = sp_io_read(&r.base, dest, step->read.request, &bytes); + EXPECT_EQ(err, step->read.err); + u64 expect_bytes = sp_cstr_len(step->read.content); + EXPECT_EQ(bytes, expect_bytes); + sp_for(it, expect_bytes) { + EXPECT_EQ((c8)dest[it], step->read.content[it]); + } + break; + } + case IO_STEP_SEEK: { + s64 pos = 0; + sp_err_t err = sp_io_file_reader_seek(&r, step->seek.offset, step->seek.whence, &pos); + EXPECT_EQ(err, step->seek.err); + EXPECT_EQ(pos, step->seek.pos); + break; + } + case IO_STEP_NONE: + case IO_STEP_WRITE: + case IO_STEP_FLUSH: + case IO_STEP_SIZE: + case IO_STEP_COPY: + case IO_STEP_PAD: { + sp_unreachable_case(); + } + } + } + + sp_io_file_reader_close(&r); +} + +UTEST_F(io, file_reader_read_full) { + run_io_file_reader_test(utest_result, ut.file_path, (io_file_reader_test_t){ + .content = "0123456789ABCDEF", + .steps = { + { .kind = IO_STEP_READ, .read = { 16, SP_OK, "0123456789ABCDEF" } }, + }, + }); +} + +UTEST_F(io, file_reader_eof_after_drain) { + run_io_file_reader_test(utest_result, ut.file_path, (io_file_reader_test_t){ + .content = "0123456789ABCDEF", + .steps = { + { .kind = IO_STEP_READ, .read = { 16, SP_OK, "0123456789ABCDEF" } }, + { .kind = IO_STEP_READ, .read = { 16, SP_ERR_IO_EOF } }, + }, + }); +} + +UTEST_F(io, file_reader_eof_short) { + run_io_file_reader_test(utest_result, ut.file_path, (io_file_reader_test_t){ + .content = "short", + .steps = { + { .kind = IO_STEP_READ, .read = { 32, SP_OK, "short" } }, + { .kind = IO_STEP_READ, .read = { 32, SP_ERR_IO_EOF } }, + }, + }); +} + +UTEST_F(io, file_reader_eof_empty) { + run_io_file_reader_test(utest_result, ut.file_path, (io_file_reader_test_t){ + .content = "", + .steps = { + { .kind = IO_STEP_READ, .read = { 16, SP_ERR_IO_EOF } }, + }, + }); +} + +UTEST_F(io, file_reader_seek) { + run_io_file_reader_test(utest_result, ut.file_path, (io_file_reader_test_t){ + .content = "0123456789", + .steps = { + { .kind = IO_STEP_SEEK, .seek = { 5, SP_IO_SEEK_SET, SP_OK, 5 } }, + { .kind = IO_STEP_READ, .read = { 5, SP_OK, "56789" } }, + }, + }); +} + +UTEST_F(io, file_reader_buffered_read) { + run_io_file_reader_test(utest_result, ut.file_path, (io_file_reader_test_t){ + .content = "0123456789ABCDEF", + .buffer = 8, + .steps = { + { .kind = IO_STEP_READ, .read = { 16, SP_OK, "0123456789ABCDEF" } }, + }, + }); +} + +UTEST_F(io, file_reader_buffered_seek_discards_buffer) { + run_io_file_reader_test(utest_result, ut.file_path, (io_file_reader_test_t){ + .content = "0123456789ABCDEF", + .buffer = 8, + .steps = { + { .kind = IO_STEP_READ, .read = { 1, SP_OK, "0" } }, + { .kind = IO_STEP_SEEK, .seek = { 10, SP_IO_SEEK_SET, SP_OK, 10 } }, + { .kind = IO_STEP_READ, .read = { 1, SP_OK, "A" } }, + }, + }); +} + +UTEST_F(io, file_reader_nonexistent) { + SKIP_ON_WASM() + sp_str_t path = sp_test_file_path(&ut.file_manager, sp_str_lit("nonexistent.file")); + sp_io_file_reader_t r = sp_zero; + EXPECT_EQ(sp_io_file_reader_from_path(&r, path), SP_ERR_IO_OPEN_FAILED); + sp_io_file_reader_close(&r); +} + + +//////////// +// WRITER // +//////////// +typedef struct { + const c8* pre_content; + sp_io_write_mode_t mode; + u64 buffer; + io_step_t steps [IO_MAX_STEPS]; + struct { + const c8* content; + } expect; +} io_file_writer_test_t; + +void run_io_file_writer_test(int* utest_result, sp_mem_t mem, sp_str_t path, io_file_writer_test_t t) { + if (t.pre_content) { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, t.pre_content, sp_cstr_len(t.pre_content), SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, path, t.mode); + + u8 wrapper_buf [64] = sp_zero; + if (t.buffer) sp_io_writer_set_buffer(&w.base, wrapper_buf, t.buffer); + + sp_carr_for(t.steps, j) { + const io_step_t* step = &t.steps[j]; + if (step->kind == IO_STEP_NONE) break; + + switch (step->kind) { + case IO_STEP_WRITE: { + u64 bytes = 0; + sp_err_t err = sp_io_write(&w.base, step->write.data, sp_cstr_len(step->write.data), &bytes); + EXPECT_EQ(err, step->write.err); + EXPECT_EQ(bytes, step->write.bytes); + break; + } + case IO_STEP_FLUSH: { + sp_err_t err = sp_io_flush(&w.base); + EXPECT_EQ(err, step->flush.err); + break; + } + case IO_STEP_SEEK: { + s64 pos = 0; + sp_err_t err = sp_io_file_writer_seek(&w, step->seek.offset, step->seek.whence, &pos); + EXPECT_EQ(err, step->seek.err); + EXPECT_EQ(pos, step->seek.pos); + break; + } + case IO_STEP_SIZE: { + u64 size = 0; + sp_err_t err = sp_io_file_writer_size(&w, &size); + EXPECT_EQ(err, step->size.err); + EXPECT_EQ(size, step->size.size); + break; + } + case IO_STEP_NONE: + case IO_STEP_READ: + case IO_STEP_COPY: + case IO_STEP_PAD: { + sp_unreachable_case(); + } + } + } + + sp_io_file_writer_close(&w); + + if (t.expect.content) { + sp_str_t loaded = sp_zero; + sp_io_read_file_a(mem, path, &loaded); + u64 n = sp_cstr_len(t.expect.content); + EXPECT_EQ(loaded.len, n); + sp_for(it, n) { + EXPECT_EQ((c8)loaded.data[it], t.expect.content[it]); + } + } +} + +UTEST_F(io, file_writer_write) { + run_io_file_writer_test(utest_result, ut.mem, ut.file_path, (io_file_writer_test_t){ + .mode = SP_IO_WRITE_MODE_OVERWRITE, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "test data", SP_OK, 9 } }, + }, + .expect.content = "test data", + }); +} + +UTEST_F(io, file_writer_overwrite) { + run_io_file_writer_test(utest_result, ut.mem, ut.file_path, (io_file_writer_test_t){ + .pre_content = "XXXXXXXX", + .mode = SP_IO_WRITE_MODE_OVERWRITE, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "1234", SP_OK, 4 } }, + }, + .expect.content = "1234", + }); +} + +UTEST_F(io, file_writer_append) { + run_io_file_writer_test(utest_result, ut.mem, ut.file_path, (io_file_writer_test_t){ + .pre_content = "first", + .mode = SP_IO_WRITE_MODE_APPEND, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "second", SP_OK, 6 } }, + }, + .expect.content = "firstsecond", + }); +} + +UTEST_F(io, file_writer_size) { + run_io_file_writer_test(utest_result, ut.mem, ut.file_path, (io_file_writer_test_t){ + .mode = SP_IO_WRITE_MODE_OVERWRITE, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "0123456789ABCDEF", SP_OK, 16 } }, + { .kind = IO_STEP_SIZE, .size = { SP_OK, 16 } }, + }, + .expect.content = "0123456789ABCDEF", + }); +} + +UTEST_F(io, file_writer_buffered_implicit_flush) { + run_io_file_writer_test(utest_result, ut.mem, ut.file_path, (io_file_writer_test_t){ + .mode = SP_IO_WRITE_MODE_OVERWRITE, + .buffer = 64, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "hello", SP_OK, 5 } }, + }, + .expect.content = "hello", + }); +} + +UTEST_F(io, file_writer_buffered_larger_than_buffer) { + run_io_file_writer_test(utest_result, ut.mem, ut.file_path, (io_file_writer_test_t){ + .mode = SP_IO_WRITE_MODE_OVERWRITE, + .buffer = 4, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "0123456789ABCDEF", SP_OK, 16 } }, + }, + .expect.content = "0123456789ABCDEF", + }); +} + + +////////////// +// SPECIALS // +////////////// + +// sp_io_pad writes a sequence of zero bytes; the declarative runner can't +// express the resulting content (c-string verification stops at the first +// nul). Kept imperative. +UTEST_F(io, file_writer_pad) { + SKIP_ON_WASM() + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&w.base, "AA", 2, SP_NULLPTR); + sp_io_pad(&w.base, 3, SP_NULLPTR); + sp_io_write(&w.base, "BB", 2, SP_NULLPTR); + sp_io_file_writer_close(&w); + + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, ut.file_path); + u8 result [7] = sp_zero; + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, result, 7, &bytes), SP_OK); + EXPECT_EQ(bytes, 7); + EXPECT_EQ(result[0], 'A'); + EXPECT_EQ(result[1], 'A'); + EXPECT_EQ(result[2], 0); + EXPECT_EQ(result[3], 0); + EXPECT_EQ(result[4], 0); + EXPECT_EQ(result[5], 'B'); + EXPECT_EQ(result[6], 'B'); + sp_io_file_reader_close(&r); +} + +// Two file handles (writer then reader) operating on the same large offset; +// doesn't fit the single-subject runner pattern. +UTEST_F(io, file_seek_beyond_4gb) { + SKIP_ON_WASM() + s64 offset = (s64)5 * 1024 * 1024 * 1024; + u8 marker [4] = {0xDE, 0xAD, 0xBE, 0xEF}; + + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + s64 seek_pos = 0; + EXPECT_EQ(sp_io_file_writer_seek(&w, offset, SP_IO_SEEK_SET, &seek_pos), SP_OK); + EXPECT_EQ(seek_pos, offset); + sp_io_write(&w.base, marker, 4, SP_NULLPTR); + sp_io_file_writer_close(&w); + + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, ut.file_path); + s64 end = sp_sys_lseek(r.file, 0, SP_IO_SEEK_END); + sp_sys_lseek(r.file, 0, SP_IO_SEEK_SET); + EXPECT_EQ((u64)end, (u64)offset + 4); + + s64 read_pos = 0; + EXPECT_EQ(sp_io_file_reader_seek(&r, offset, SP_IO_SEEK_SET, &read_pos), SP_OK); + EXPECT_EQ(read_pos, offset); + + u8 result [4] = sp_zero; + u64 bytes = 0; + EXPECT_EQ(sp_io_read(&r.base, result, 4, &bytes), SP_OK); + EXPECT_EQ(bytes, 4); + EXPECT_EQ(result[0], 0xDE); + EXPECT_EQ(result[1], 0xAD); + EXPECT_EQ(result[2], 0xBE); + EXPECT_EQ(result[3], 0xEF); + sp_io_file_reader_close(&r); +} diff --git a/test/io/io.h b/test/io/io.h index 742f36c..1673d36 100644 --- a/test/io/io.h +++ b/test/io/io.h @@ -42,6 +42,7 @@ typedef enum { IO_STEP_SEEK, IO_STEP_SIZE, IO_STEP_COPY, + IO_STEP_PAD, } io_step_kind_t; typedef struct { @@ -53,6 +54,7 @@ typedef struct { struct { s64 offset; sp_io_whence_t whence; sp_err_t err; s64 pos; } seek; struct { sp_err_t err; u64 size; } size; struct { u64 buffer; sp_err_t err; u64 copied; } copy; + struct { u64 size; sp_err_t err; u64 bytes; } pad; }; } io_step_t; diff --git a/test/io/loose.c b/test/io/loose.c deleted file mode 100644 index f4c4444..0000000 --- a/test/io/loose.c +++ /dev/null @@ -1,798 +0,0 @@ -#include "io.h" - -UTEST_F(io, seeking_reader_file_seek) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - sp_io_file_reader_t fr = sp_zero; - sp_io_file_reader_from_path(&fr, ut.file_path); - - sp_io_seeking_reader_t sr = sp_zero; - sp_io_seeking_reader_from_file_reader(&sr, &fr); - - s64 pos = 0; - EXPECT_EQ(sp_io_seeking_reader_seek(&sr, 5, SP_IO_SEEK_SET, &pos), SP_OK); - EXPECT_EQ(pos, 5); - - char buffer[5] = {0}; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(sr.reader, buffer, 5, &bytes), SP_OK); - EXPECT_EQ(bytes, 5); - EXPECT_EQ(buffer[0], '5'); - EXPECT_EQ(buffer[4], '9'); - - sp_io_file_reader_close(&fr); -} -UTEST_F(io, seeking_reader_file_seek_whence) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - sp_io_file_reader_t fr = sp_zero; - sp_io_file_reader_from_path(&fr, ut.file_path); - - sp_io_seeking_reader_t sr = sp_zero; - sp_io_seeking_reader_from_file_reader(&sr, &fr); - - s64 pos = 0; - EXPECT_EQ(sp_io_seeking_reader_seek(&sr, 4, SP_IO_SEEK_SET, &pos), SP_OK); - EXPECT_EQ(pos, 4); - - EXPECT_EQ(sp_io_seeking_reader_seek(&sr, 3, SP_IO_SEEK_CUR, &pos), SP_OK); - EXPECT_EQ(pos, 7); - - char c = 0; - sp_io_read(sr.reader, &c, 1, SP_NULLPTR); - EXPECT_EQ(c, '7'); - - EXPECT_EQ(sp_io_seeking_reader_seek(&sr, 0, SP_IO_SEEK_END, &pos), SP_OK); - EXPECT_EQ(pos, 16); - - EXPECT_EQ(sp_io_seeking_reader_seek(&sr, -2, SP_IO_SEEK_END, &pos), SP_OK); - EXPECT_EQ(pos, 14); - sp_io_read(sr.reader, &c, 1, SP_NULLPTR); - EXPECT_EQ(c, 'E'); - - sp_io_file_reader_close(&fr); -} -UTEST_F(io, seeking_reader_file_seek_invalid) { - SKIP_ON_WASM() - const char* content = "0123456789"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 10, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - sp_io_file_reader_t fr = sp_zero; - sp_io_file_reader_from_path(&fr, ut.file_path); - - sp_io_seeking_reader_t sr = sp_zero; - sp_io_seeking_reader_from_file_reader(&sr, &fr); - - s64 pos = 0; - EXPECT_EQ(sp_io_seeking_reader_seek(&sr, -10, SP_IO_SEEK_SET, &pos), SP_ERR_IO_SEEK_FAILED); - - sp_io_file_reader_close(&fr); -} -UTEST_F(io, seeking_reader_file_seek_buffered) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - u8 read_buf[8]; - sp_io_file_reader_t fr = sp_zero; - sp_io_file_reader_from_path(&fr, ut.file_path); - sp_io_reader_set_buffer(&fr.base, read_buf, sizeof(read_buf)); - - sp_io_seeking_reader_t sr = sp_zero; - sp_io_seeking_reader_from_file_reader(&sr, &fr); - - char c = 0; - sp_io_read(sr.reader, &c, 1, SP_NULLPTR); - EXPECT_EQ(c, '0'); - - s64 pos = 0; - EXPECT_EQ(sp_io_seeking_reader_seek(&sr, 10, SP_IO_SEEK_SET, &pos), SP_OK); - EXPECT_EQ(pos, 10); - - sp_io_read(sr.reader, &c, 1, SP_NULLPTR); - EXPECT_EQ(c, 'A'); - - sp_io_file_reader_close(&fr); -} -UTEST_F(io, reader_file_read) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - sp_io_file_reader_t r = sp_zero; - EXPECT_EQ(sp_io_file_reader_from_path(&r, ut.file_path), SP_OK); - - char buffer[16] = {0}; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, buffer, 16, &bytes), SP_OK); - EXPECT_EQ(bytes, 16); - sp_for(i, 16) { - EXPECT_EQ(buffer[i], content[i]); - } - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_file_seek) { - SKIP_ON_WASM() - const char* content = "0123456789"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 10, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - s64 pos = 0; - EXPECT_EQ(sp_io_file_reader_seek(&r, 5, SP_IO_SEEK_SET, &pos), SP_OK); - EXPECT_EQ(pos, 5); - - char buffer[5] = {0}; - sp_io_read(&r.base, buffer, 5, SP_NULLPTR); - EXPECT_EQ(buffer[0], '5'); - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_file_nonexistent) { - SKIP_ON_WASM() - sp_str_t path = sp_test_file_path(&ut.file_manager, sp_str_lit("nonexistent.file")); - sp_io_file_reader_t r = sp_zero; - EXPECT_EQ(sp_io_file_reader_from_path(&r, path), SP_ERR_IO_OPEN_FAILED); - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_file_read_eof_after_drain) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - - char buffer[16] = {0}; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, buffer, 16, &bytes), SP_OK); - EXPECT_EQ(bytes, 16); - - bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, buffer, 16, &bytes), SP_ERR_IO_EOF); - EXPECT_EQ(bytes, 0); - - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_file_read_eof_short) { - SKIP_ON_WASM() - const char* content = "short"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 5, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - - char buffer[32] = {0}; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, buffer, sizeof(buffer), &bytes), SP_OK); - EXPECT_EQ(bytes, 5); - - bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, buffer, sizeof(buffer), &bytes), SP_ERR_IO_EOF); - EXPECT_EQ(bytes, 0); - - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_file_read_eof_empty) { - SKIP_ON_WASM() - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - - char buffer[16] = {0}; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, buffer, sizeof(buffer), &bytes), SP_ERR_IO_EOF); - EXPECT_EQ(bytes, 0); - - sp_io_file_reader_close(&r); -} -UTEST_F(io, writer_file_write) { - SKIP_ON_WASM() - const char* content = "test data"; - sp_io_file_writer_t w = sp_zero; - EXPECT_EQ(sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE), SP_OK); - - u64 bytes = 0; - EXPECT_EQ(sp_io_write(&w.base, content, 9, &bytes), SP_OK); - EXPECT_EQ(bytes, 9); - sp_io_file_writer_close(&w); - - sp_str_t loaded = sp_zero; - sp_io_read_file_a(ut.mem, ut.file_path, &loaded); - EXPECT_EQ(loaded.len, 9); -} -UTEST_F(io, writer_file_overwrite) { - SKIP_ON_WASM() - const char* first = "XXXXXXXX"; - const char* second = "1234"; - - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, first, 8, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, second, 4, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - sp_str_t loaded = sp_zero; - sp_io_read_file_a(ut.mem, ut.file_path, &loaded); - EXPECT_EQ(loaded.len, 4); - EXPECT_EQ(loaded.data[0], '1'); - EXPECT_EQ(loaded.data[3], '4'); -} -UTEST_F(io, writer_file_append) { - SKIP_ON_WASM() - const char* first = "first"; - const char* second = "second"; - - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, first, 5, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_APPEND); - sp_io_write(&w.base, second, 6, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - sp_str_t loaded = sp_zero; - sp_io_read_file_a(ut.mem, ut.file_path, &loaded); - EXPECT_EQ(loaded.len, 11); - EXPECT_TRUE(sp_str_equal(loaded, sp_str_lit("firstsecond"))); -} -UTEST_F(io, writer_file_seek) { - SKIP_ON_WASM() - const char* content = "0123456789"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 10, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - s64 pos = 0; - EXPECT_EQ(sp_io_file_reader_seek(&r, 5, SP_IO_SEEK_SET, &pos), SP_OK); - EXPECT_EQ(pos, 5); - - char buffer[5] = {0}; - sp_io_read(&r.base, buffer, 5, SP_NULLPTR); - EXPECT_EQ(buffer[0], '5'); - sp_io_file_reader_close(&r); -} -UTEST_F(io, writer_file_size) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - u64 size = 0; - EXPECT_EQ(sp_io_file_writer_size(&w, &size), SP_OK); - EXPECT_EQ(size, 16); - sp_io_file_writer_close(&w); -} -UTEST_F(io, writer_file_pad) { - SKIP_ON_WASM() - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, "AA", 2, SP_NULLPTR); - sp_io_pad(&w.base, 3, SP_NULLPTR); - sp_io_write(&w.base, "BB", 2, SP_NULLPTR); - sp_io_file_writer_close(&w); - - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - u8 result[7] = sp_zero; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, result, 7, &bytes), SP_OK); - EXPECT_EQ(bytes, 7); - EXPECT_EQ(result[0], 'A'); - EXPECT_EQ(result[1], 'A'); - EXPECT_EQ(result[2], 0); - EXPECT_EQ(result[3], 0); - EXPECT_EQ(result[4], 0); - EXPECT_EQ(result[5], 'B'); - EXPECT_EQ(result[6], 'B'); - sp_io_file_reader_close(&r); -} -UTEST_F(io, writer_dyn_write) { - SKIP_ON_WASM() - sp_io_dyn_mem_writer_t w; sp_io_dyn_mem_writer_init_a(ut.mem, &w); - u8 data[] = {1, 2, 3, 4}; - u64 written = 0; - EXPECT_EQ(sp_io_write(&w.base, data, 4, &written), SP_OK); - - EXPECT_EQ(written, 4); - u64 size = 0; - sp_io_dyn_mem_writer_size(&w, &size); - EXPECT_EQ(size, 4); - sp_io_dyn_mem_writer_close(&w); -} -UTEST_F(io, writer_dyn_grows) { - SKIP_ON_WASM() - sp_io_dyn_mem_writer_t w; sp_io_dyn_mem_writer_init_a(ut.mem, &w); - - u8 data[256]; - sp_for(i, 256) data[i] = (u8)i; - - u64 written = 0; - EXPECT_EQ(sp_io_write(&w.base, data, 256, &written), SP_OK); - EXPECT_EQ(written, 256); - u64 size = 0; - sp_io_dyn_mem_writer_size(&w, &size); - EXPECT_EQ(size, 256); - - sp_io_dyn_mem_writer_close(&w); -} -UTEST_F(io, writer_dyn_to_str) { - SKIP_ON_WASM() - sp_io_dyn_mem_writer_t w; sp_io_dyn_mem_writer_init_a(ut.mem, &w); - - const char* text = "hello world"; - sp_io_write(&w.base, text, 11, SP_NULLPTR); - - sp_str_t str = sp_mem_buffer_as_str(&w.storage); - EXPECT_EQ(str.len, 11); - EXPECT_TRUE(sp_str_equal(str, sp_str_lit("hello world"))); - - sp_io_dyn_mem_writer_close(&w); -} -UTEST_F(io, writer_dyn_seek) { - SKIP_ON_WASM() - sp_io_dyn_mem_writer_t w; sp_io_dyn_mem_writer_init_a(ut.mem, &w); - - u8 data[] = {1, 2, 3, 4, 5, 6, 7, 8}; - sp_io_write(&w.base, data, 8, SP_NULLPTR); - - s64 pos = 0; - EXPECT_EQ(sp_io_dyn_mem_writer_seek(&w, 4, SP_IO_SEEK_SET, &pos), SP_OK); - EXPECT_EQ(pos, 4); - - sp_io_dyn_mem_writer_close(&w); -} -UTEST_F(io, writer_dyn_multiple_writes) { - SKIP_ON_WASM() - sp_io_dyn_mem_writer_t w; sp_io_dyn_mem_writer_init_a(ut.mem, &w); - - sp_io_write(&w.base, "abc", 3, SP_NULLPTR); - sp_io_write(&w.base, "def", 3, SP_NULLPTR); - sp_io_write(&w.base, "ghi", 3, SP_NULLPTR); - - u64 size = 0; - sp_io_dyn_mem_writer_size(&w, &size); - EXPECT_EQ(size, 9); - - sp_str_t str = sp_mem_buffer_as_str(&w.storage); - EXPECT_TRUE(sp_str_equal(str, sp_str_lit("abcdefghi"))); - - sp_io_dyn_mem_writer_close(&w); -} -UTEST_F(io, writer_buffered_1000_bytes) { - SKIP_ON_WASM() - u8 write_buf[64]; - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_writer_set_buffer(&w.base, write_buf, sizeof(write_buf)); - - sp_for(i, 1000) { - u8 byte = (u8)(i & 0xFF); - u64 bytes = 0; - EXPECT_EQ(sp_io_write(&w.base, &byte, 1, &bytes), SP_OK); - EXPECT_EQ(bytes, 1); - } - sp_io_file_writer_close(&w); - - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - s64 end = sp_sys_lseek(r.file, 0, SP_IO_SEEK_END); - sp_sys_lseek(r.file, 0, SP_IO_SEEK_SET); - EXPECT_EQ((u64)end, 1000); - - sp_for(i, 1000) { - u8 byte; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, &byte, 1, &bytes), SP_OK); - EXPECT_EQ(bytes, 1); - EXPECT_EQ(byte, (u8)(i & 0xFF)); - } - sp_io_file_reader_close(&r); -} -UTEST_F(io, writer_buffered_larger_than_buffer) { - SKIP_ON_WASM() - u8 write_buf[32]; - u8 data[128]; - sp_for(i, 128) data[i] = (u8)i; - - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_writer_set_buffer(&w.base, write_buf, sizeof(write_buf)); - - u64 bytes = 0; - EXPECT_EQ(sp_io_write(&w.base, data, 128, &bytes), SP_OK); - EXPECT_EQ(bytes, 128); - sp_io_file_writer_close(&w); - - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - u8 result[128] = sp_zero; - u64 read_bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, result, 128, &read_bytes), SP_OK); - EXPECT_EQ(read_bytes, 128); - sp_for(i, 128) { - EXPECT_EQ(result[i], data[i]); - } - sp_io_file_reader_close(&r); -} -UTEST_F(io, writer_buffered_implicit_flush) { - SKIP_ON_WASM() - u8 write_buf[64]; - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_writer_set_buffer(&w.base, write_buf, sizeof(write_buf)); - - sp_io_write(&w.base, "hello", 5, SP_NULLPTR); - sp_io_file_writer_close(&w); - - sp_str_t loaded = sp_zero; - sp_io_read_file_a(ut.mem, ut.file_path, &loaded); - EXPECT_EQ(loaded.len, 5); - EXPECT_TRUE(sp_str_equal(loaded, sp_str_lit("hello"))); -} -UTEST_F(io, writer_buffered_flush_empty) { - SKIP_ON_WASM() - u8 write_buf[64]; - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_writer_set_buffer(&w.base, write_buf, sizeof(write_buf)); - - sp_err_t err = sp_io_flush(&w.base); - EXPECT_EQ(err, SP_OK); - sp_io_file_writer_close(&w); -} -UTEST_F(io, writer_buffered_set_twice) { - SKIP_ON_WASM() - u8 write_buf1[64]; - u8 write_buf2[64]; - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_writer_set_buffer(&w.base, write_buf1, sizeof(write_buf1)); - - sp_io_write(&w.base, "first", 5, SP_NULLPTR); - sp_io_writer_set_buffer(&w.base, write_buf2, sizeof(write_buf2)); - sp_io_write(&w.base, "second", 6, SP_NULLPTR); - sp_io_file_writer_close(&w); - - sp_str_t loaded = sp_zero; - sp_io_read_file_a(ut.mem, ut.file_path, &loaded); - EXPECT_EQ(loaded.len, 11); - EXPECT_TRUE(sp_str_equal(loaded, sp_str_lit("firstsecond"))); -} -UTEST_F(io, reader_buffered_read) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - u8 read_buf[8]; - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); - - char result[16] = {0}; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, result, 16, &bytes), SP_OK); - EXPECT_EQ(bytes, 16); - sp_for(i, 16) { - EXPECT_EQ(result[i], content[i]); - } - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_buffered_small_reads) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - u8 read_buf[8]; - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); - - sp_for(i, 16) { - char c; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, &c, 1, &bytes), SP_OK); - EXPECT_EQ(bytes, 1); - EXPECT_EQ(c, content[i]); - } - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_buffered_eof_exact) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - u8 read_buf[8]; - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); - - char result[16] = {0}; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, result, 16, &bytes), SP_OK); - EXPECT_EQ(bytes, 16); - - bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, result, 1, &bytes), SP_ERR_IO_EOF); - EXPECT_EQ(bytes, 0); - - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_buffered_eof_partial) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - u8 read_buf[8]; - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); - - char result[32] = {0}; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, result, sizeof(result), &bytes), SP_OK); - EXPECT_EQ(bytes, 16); - - bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, result, sizeof(result), &bytes), SP_ERR_IO_EOF); - EXPECT_EQ(bytes, 0); - - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_buffered_fill_preserves_drained) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - u8 read_buf[8]; - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); - - char first[4] = {0}; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, first, 4, &bytes), SP_OK); - EXPECT_EQ(bytes, 4); - - char second[7] = {0}; - bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, second, 7, &bytes), SP_OK); - EXPECT_EQ(bytes, 7); - EXPECT_EQ(second[0], '4'); - EXPECT_EQ(second[1], '5'); - EXPECT_EQ(second[2], '6'); - EXPECT_EQ(second[3], '7'); - EXPECT_EQ(second[4], '8'); - EXPECT_EQ(second[5], '9'); - EXPECT_EQ(second[6], 'A'); - - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_buffered_eof_direct_path) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - u8 read_buf[4]; - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); - - char prime; - EXPECT_EQ(sp_io_read(&r.base, &prime, 1, SP_NULLPTR), SP_OK); - EXPECT_EQ(prime, '0'); - - char result[32] = {0}; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, result, sizeof(result), &bytes), SP_OK); - EXPECT_EQ(bytes, 15); - - bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, result, sizeof(result), &bytes), SP_ERR_IO_EOF); - EXPECT_EQ(bytes, 0); - - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_buffered_eof_byte_by_byte) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - u8 read_buf[8]; - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); - - sp_for(i, 16) { - char c; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, &c, 1, &bytes), SP_OK); - EXPECT_EQ(bytes, 1); - EXPECT_EQ(c, content[i]); - } - - char c; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, &c, 1, &bytes), SP_ERR_IO_EOF); - EXPECT_EQ(bytes, 0); - - sp_io_file_reader_close(&r); -} -UTEST_F(io, reader_buffered_seek_discards_buffer) { - SKIP_ON_WASM() - const char* content = "0123456789ABCDEF"; - { - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - sp_io_write(&w.base, content, 16, SP_NULLPTR); - sp_io_file_writer_close(&w); - } - - u8 read_buf[8]; - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - sp_io_reader_set_buffer(&r.base, read_buf, sizeof(read_buf)); - - char c; - sp_io_read(&r.base, &c, 1, SP_NULLPTR); - EXPECT_EQ(c, '0'); - - sp_io_file_reader_seek(&r, 10, SP_IO_SEEK_SET, SP_NULLPTR); - sp_io_read(&r.base, &c, 1, SP_NULLPTR); - EXPECT_EQ(c, 'A'); - - sp_io_file_reader_close(&r); -} - -UTEST_F(io, seek_beyond_4gb) { - SKIP_ON_WASM() - s64 offset = (s64)5 * 1024 * 1024 * 1024; - u8 marker[4] = {0xDE, 0xAD, 0xBE, 0xEF}; - - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - s64 seek_pos = 0; - EXPECT_EQ(sp_io_file_writer_seek(&w, offset, SP_IO_SEEK_SET, &seek_pos), SP_OK); - EXPECT_EQ(seek_pos, offset); - sp_io_write(&w.base, marker, 4, SP_NULLPTR); - sp_io_file_writer_close(&w); - - sp_io_file_reader_t r = sp_zero; - sp_io_file_reader_from_path(&r, ut.file_path); - s64 end = sp_sys_lseek(r.file, 0, SP_IO_SEEK_END); - sp_sys_lseek(r.file, 0, SP_IO_SEEK_SET); - EXPECT_EQ((u64)end, (u64)offset + 4); - - s64 read_pos = 0; - EXPECT_EQ(sp_io_file_reader_seek(&r, offset, SP_IO_SEEK_SET, &read_pos), SP_OK); - EXPECT_EQ(read_pos, offset); - - u8 result[4] = sp_zero; - u64 bytes = 0; - EXPECT_EQ(sp_io_read(&r.base, result, 4, &bytes), SP_OK); - EXPECT_EQ(bytes, 4); - EXPECT_EQ(result[0], 0xDE); - EXPECT_EQ(result[1], 0xAD); - EXPECT_EQ(result[2], 0xBE); - EXPECT_EQ(result[3], 0xEF); - sp_io_file_reader_close(&r); -} - -// UTEST_F(io, copy_playground) { -// struct { -// u8 a [4096]; -// u8 b [4096]; -// u8 c [4096]; -// } buf = sp_zero; -// struct { -// sp_io_reader_t r; -// sp_io_writer_t w; -// } io = sp_zero; -// -// sp_for(it, 4096) { -// buf.a[it] = it; -// } -// -// sp_io_reader_from_mem(&io.r, buf.a, sizeof(buf.a)); -// sp_io_writer_set_buffer(&io.w, buf.b, sizeof(buf.b)); -// -// u64 n = 0; -// sp_io_copy_b2(&io.w, &io.r, buf.c, sizeof(buf.c), &n); -// EXPECT_EQ(n, 4096); -// } diff --git a/test/io/mem.c b/test/io/mem.c index d2a52f5..5217ba3 100644 --- a/test/io/mem.c +++ b/test/io/mem.c @@ -78,7 +78,8 @@ void run_io_mem_test(int* utest_result, io_mem_test_t t) { EXPECT_EQ(copied, step->copy.copied); break; } - case IO_STEP_NONE: { + case IO_STEP_NONE: + case IO_STEP_PAD: { sp_unreachable_case(); } } diff --git a/test/io/read.c b/test/io/read.c index 083bfe4..66327cc 100644 --- a/test/io/read.c +++ b/test/io/read.c @@ -247,13 +247,14 @@ UTEST_F(io_read, buffered_eof_immediate) { UTEST_F(io_read, buffered_eof_after_partial_drain) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ .results = { - { .bytes = 3, .data = "abc", .err = SP_OK }, + { .bytes = 4, .data = "abcd", .err = SP_OK }, { .bytes = 0, .err = SP_ERR_IO_EOF }, { .bytes = 0, .err = SP_ERR_IO_EOF }, }, .buffer = 4, .steps = { - { .kind = IO_STEP_READ, .read = { 8, SP_OK, "abc" } }, + { .kind = IO_STEP_READ, .read = { 2, SP_OK, "ab" } }, + { .kind = IO_STEP_READ, .read = { 8, SP_OK, "cd" } }, { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_EOF } }, }, }); diff --git a/test/io/seeking_reader.c b/test/io/seeking_reader.c index 7d7dc9c..c21516f 100644 --- a/test/io/seeking_reader.c +++ b/test/io/seeking_reader.c @@ -43,7 +43,8 @@ void run_io_seeking_reader_test(int* utest_result, io_seeking_reader_test_t t) { case IO_STEP_WRITE: case IO_STEP_FLUSH: case IO_STEP_SIZE: - case IO_STEP_COPY: { + case IO_STEP_COPY: + case IO_STEP_PAD: { sp_unreachable_case(); } } @@ -71,3 +72,113 @@ UTEST_F(io_seeking_reader, seek_invalid) { }, }); } + + +////////////////// +// FILE BACKING // +////////////////// +typedef struct { + const c8* content; + u64 buffer; + io_step_t steps [IO_MAX_STEPS]; +} io_seeking_reader_file_test_t; + +void run_io_seeking_reader_file_test(int* utest_result, sp_str_t path, io_seeking_reader_file_test_t t) { + { + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, path, SP_IO_WRITE_MODE_OVERWRITE); + if (t.content) sp_io_write(&w.base, t.content, sp_cstr_len(t.content), SP_NULLPTR); + sp_io_file_writer_close(&w); + } + + sp_io_file_reader_t fr = sp_zero; + sp_io_file_reader_from_path(&fr, path); + + u8 wrapper_buf [64] = sp_zero; + if (t.buffer) sp_io_reader_set_buffer(&fr.base, wrapper_buf, t.buffer); + + sp_io_seeking_reader_t r = sp_zero; + sp_io_seeking_reader_from_file_reader(&r, &fr); + + sp_carr_for(t.steps, j) { + const io_step_t* step = &t.steps[j]; + if (step->kind == IO_STEP_NONE) break; + + switch (step->kind) { + case IO_STEP_READ: { + u8 dest [64] = sp_zero; + u64 bytes = 0; + sp_err_t err = sp_io_read(r.reader, dest, step->read.request, &bytes); + EXPECT_EQ(err, step->read.err); + u64 expect_bytes = sp_cstr_len(step->read.content); + EXPECT_EQ(bytes, expect_bytes); + sp_for(it, expect_bytes) { + EXPECT_EQ((c8)dest[it], step->read.content[it]); + } + break; + } + case IO_STEP_SEEK: { + s64 pos = 0; + sp_err_t err = sp_io_seeking_reader_seek(&r, step->seek.offset, step->seek.whence, &pos); + EXPECT_EQ(err, step->seek.err); + EXPECT_EQ(pos, step->seek.pos); + break; + } + case IO_STEP_NONE: + case IO_STEP_WRITE: + case IO_STEP_FLUSH: + case IO_STEP_SIZE: + case IO_STEP_COPY: + case IO_STEP_PAD: { + sp_unreachable_case(); + } + } + } + + sp_io_file_reader_close(&fr); +} + +UTEST_F(io, seeking_reader_file_seek) { + run_io_seeking_reader_file_test(utest_result, ut.file_path, (io_seeking_reader_file_test_t){ + .content = "0123456789ABCDEF", + .steps = { + { .kind = IO_STEP_SEEK, .seek = { 5, SP_IO_SEEK_SET, SP_OK, 5 } }, + { .kind = IO_STEP_READ, .read = { 5, SP_OK, "56789" } }, + }, + }); +} + +UTEST_F(io, seeking_reader_file_seek_whence) { + run_io_seeking_reader_file_test(utest_result, ut.file_path, (io_seeking_reader_file_test_t){ + .content = "0123456789ABCDEF", + .steps = { + { .kind = IO_STEP_SEEK, .seek = { 4, SP_IO_SEEK_SET, SP_OK, 4 } }, + { .kind = IO_STEP_SEEK, .seek = { 3, SP_IO_SEEK_CUR, SP_OK, 7 } }, + { .kind = IO_STEP_READ, .read = { 1, SP_OK, "7" } }, + { .kind = IO_STEP_SEEK, .seek = { 0, SP_IO_SEEK_END, SP_OK, 16 } }, + { .kind = IO_STEP_SEEK, .seek = { -2, SP_IO_SEEK_END, SP_OK, 14 } }, + { .kind = IO_STEP_READ, .read = { 1, SP_OK, "E" } }, + }, + }); +} + +UTEST_F(io, seeking_reader_file_seek_invalid) { + run_io_seeking_reader_file_test(utest_result, ut.file_path, (io_seeking_reader_file_test_t){ + .content = "0123456789", + .steps = { + { .kind = IO_STEP_SEEK, .seek = { -10, SP_IO_SEEK_SET, SP_ERR_IO_SEEK_FAILED, -1 } }, + }, + }); +} + +UTEST_F(io, seeking_reader_file_seek_buffered) { + run_io_seeking_reader_file_test(utest_result, ut.file_path, (io_seeking_reader_file_test_t){ + .content = "0123456789ABCDEF", + .buffer = 8, + .steps = { + { .kind = IO_STEP_READ, .read = { 1, SP_OK, "0" } }, + { .kind = IO_STEP_SEEK, .seek = { 10, SP_IO_SEEK_SET, SP_OK, 10 } }, + { .kind = IO_STEP_READ, .read = { 1, SP_OK, "A" } }, + }, + }); +} diff --git a/test/io/write.c b/test/io/write.c index f516423..9607fc5 100644 --- a/test/io/write.c +++ b/test/io/write.c @@ -45,7 +45,8 @@ void run_io_mock_write_test(int* utest_result, io_mock_write_test_t t) { case IO_STEP_READ: case IO_STEP_SEEK: case IO_STEP_SIZE: - case IO_STEP_COPY: { + case IO_STEP_COPY: + case IO_STEP_PAD: { sp_unreachable_case(); } } From ed061a160b1d1590cdc193145953834e322056ee Mon Sep 17 00:00:00 2001 From: Thomas Spader Date: Tue, 12 May 2026 17:55:16 -0400 Subject: [PATCH 13/21] more --- test/io.c | 13 +++++++++++-- test/io/copy.c | 12 ------------ test/io/read.c | 25 +++++++++++++++++++++++++ test/io/write.c | 21 +++++++++++++++++++++ 4 files changed, 57 insertions(+), 14 deletions(-) diff --git a/test/io.c b/test/io.c index 00533cb..1eece0f 100644 --- a/test/io.c +++ b/test/io.c @@ -20,7 +20,13 @@ u64 io_get_num_results(const io_result_t* responses, u64 max) { sp_err_t io_mock_reader_read(sp_io_reader_t* r, void* ptr, u64 size, u64* bytes_read) { io_mock_reader_t* m = (io_mock_reader_t*)r; - sp_assert(m->cursor < m->num_results); + if (m->cursor >= m->num_results) { + // The wrapper called the backend more times than the test scripted. Report + // a distinctive failure rather than asserting, so the test framework can + // continue running other cases. + if (bytes_read) *bytes_read = 0; + return SP_ERR_IO_READ_FAILED; + } io_result_t* resp = &m->results[m->cursor++]; u64 n = sp_min(size, resp->bytes); if (n && resp->data) { @@ -32,7 +38,10 @@ sp_err_t io_mock_reader_read(sp_io_reader_t* r, void* ptr, u64 size, u64* bytes_ sp_err_t io_mock_writer_write(sp_io_writer_t* w, const void* ptr, u64 size, u64* bytes_written) { io_mock_writer_t* m = (io_mock_writer_t*)w; - sp_assert(m->cursor < m->num_results); + if (m->cursor >= m->num_results) { + if (bytes_written) *bytes_written = 0; + return SP_ERR_IO_WRITE_FAILED; + } io_result_t* resp = &m->results[m->cursor++]; u64 n = sp_min(size, resp->bytes); if (n) { diff --git a/test/io/copy.c b/test/io/copy.c index 7e9fed3..a72adfe 100644 --- a/test/io/copy.c +++ b/test/io/copy.c @@ -176,18 +176,6 @@ UTEST_F(io_copy, buffered_reader_one_fill_many_drains) { }); } -// Writer wrapper buffer is large enough to hold the entire copy. The -// backend must NOT be called: bytes stay buffered. Copy reports the full -// count, but received_len at the backend is 0. -UTEST_F(io_copy, buffered_writer_absorbs_entire_copy) { - run_io_mock_copy_writer_test(utest_result, (io_mock_copy_writer_test_t){ - .source = "01234567", - .responses = {0}, - .buffer = { .write = 16, .copy = 4 }, - .expect = { .err = SP_OK, .copied = 8, .received = "" }, - }); -} - // Writer wrapper buffer smaller than copy size. As the buffer fills, the // wrapper flushes to the backend. The tail of the data is left in the // buffer when copy returns because sp_io_copy_b does not flush at end. diff --git a/test/io/read.c b/test/io/read.c index 66327cc..3b96d9e 100644 --- a/test/io/read.c +++ b/test/io/read.c @@ -288,6 +288,31 @@ UTEST_F(io_read, buffered_short_fill) { }); } +// After a hard error, a subsequent successful read recovers. Pins that the +// wrapper does NOT latch the error: state lives in the backend. +UTEST_F(io_read, error_then_recovery) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .results = { + { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, + { .bytes = 3, .data = "abc", .err = SP_OK }, + }, + .steps = { + { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_READ_FAILED } }, + { .kind = IO_STEP_READ, .read = { 8, SP_OK, "abc" } }, + }, + }); +} + +// Unbuffered zero-byte request must not call the backend. +UTEST_F(io_read, zero_request) { + run_io_mock_read_test(utest_result, (io_mock_read_test_t){ + .results = {0}, + .steps = { + { .kind = IO_STEP_READ, .read = { 0, SP_OK } }, + }, + }); +} + // Zero-byte request must not call the backend. UTEST_F(io_read, buffered_zero_request) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ diff --git a/test/io/write.c b/test/io/write.c index 9607fc5..e6f1a2a 100644 --- a/test/io/write.c +++ b/test/io/write.c @@ -291,6 +291,27 @@ UTEST_F(io_write, buffered_flush_backend_error) { }); } +// When flush hits a backend that accepts a partial prefix then errors, the +// bytes that didn't make it are dropped: the wrapper buffer is cleared +// regardless of flush outcome. A subsequent write does NOT include the lost +// suffix. Pins "flush failure is non-recoverable; tail is lost" as deliberate. +UTEST_F(io_write, buffered_flush_partial_drops_tail) { + run_io_mock_write_test(utest_result, (io_mock_write_test_t){ + .results = { + { .bytes = 2, .err = SP_ERR_IO_WRITE_FAILED }, + { .bytes = 2, .err = SP_OK }, + }, + .buffer = 8, + .steps = { + { .kind = IO_STEP_WRITE, .write = { "abcd", SP_OK, 4 } }, + { .kind = IO_STEP_FLUSH, .flush = { SP_ERR_IO_WRITE_FAILED } }, + { .kind = IO_STEP_WRITE, .write = { "xy", SP_OK, 2 } }, + { .kind = IO_STEP_FLUSH, .flush = { SP_OK } }, + }, + .expect.received = "abxy", + }); +} + // Zero-length write is a no-op: no buffer change, no backend call. UTEST_F(io_write, buffered_zero_write) { run_io_mock_write_test(utest_result, (io_mock_write_test_t){ From f4122d4cb0be4168c83b86db8291b9b58d72f03f Mon Sep 17 00:00:00 2001 From: Thomas Spader Date: Tue, 12 May 2026 18:13:10 -0400 Subject: [PATCH 14/21] [the important one with the fixes] --- sp.h | 58 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/sp.h b/sp.h index e3c3f20..0cdba74 100644 --- a/sp.h +++ b/sp.h @@ -13205,12 +13205,25 @@ sp_err_t sp_io_copy(sp_io_writer_t* w, sp_io_reader_t* r, u64* bytes_copied) { sp_err_t sp_io_copy_b(sp_io_writer_t* w, sp_io_reader_t* r, u8* buffer, u64 n, u64* bytes_copied) { sp_err_t err = SP_OK; u64 total = 0; - u64 chunk = 0; while (true) { - sp_try_goto(sp_io_read(r, buffer, n, &chunk), err, done); - sp_try_goto(sp_io_write(w, buffer, chunk, SP_NULLPTR), err, done); - total += chunk; + // (bytes, error) is orthogonal on both sides. A read that produces bytes + // alongside an error must still have those bytes committed to the + // destination before we surface the error. A write that accepts a prefix + // before failing must have that prefix counted toward bytes_copied. The + // first error encountered wins; bytes_copied accurately reports what + // actually moved through. + u64 chunk = 0; + sp_err_t rerr = sp_io_read(r, buffer, n, &chunk); + + if (chunk) { + u64 wrote = 0; + sp_err_t werr = sp_io_write(w, buffer, chunk, &wrote); + total += wrote; + if (werr) { err = werr; goto done; } + } + + if (rerr) { err = rerr; goto done; } } done: @@ -13261,6 +13274,11 @@ sp_err_t sp_io_copy_b2(sp_io_writer_t* w, sp_io_reader_t* r, u8* buffer, u64 n, sp_err_t sp_io_read(sp_io_reader_t* reader, void* ptr, u64 size, u64* bytes_read) { sp_assert(reader); + if (!size) { + if (bytes_read) *bytes_read = 0; + return SP_OK; + } + if (!reader->buffer.data) { return reader->read(reader, ptr, size, bytes_read); } @@ -13452,17 +13470,18 @@ static sp_err_t sp_io_write_all(sp_io_writer_t* writer, const void* data, u64 si const u8* ptr = ((const u8*)data) + total; u64 remaining = size - total; + // Account for any partial progress BEFORE inspecting the error. The (bytes, + // error) pair is orthogonal: a backend that returns SP_ERR_IO_NO_SPACE with + // written=4 has committed those 4 bytes, and the caller deserves to know. + // // If write() returns 0 bytes written, but also does not report an error, we just // keep looping. If this keeps happening, though, you're stuck. Defensively, it // makes sense to just bail rather than risk *any* deadlock, but I think that doing // that would just hide the real breaking of an invariant. - // - // In other words: You asked to write some number of bytes. The backend failed to - // write anything, but somehow did not encounter an error. That doesn't make sense, - // and indicates a backend bug. u64 written = 0; - sp_try_goto(writer->write(writer, ptr, remaining, &written), result, done); + result = writer->write(writer, ptr, remaining, &written); total += written; + if (result) goto done; } done: @@ -13725,23 +13744,18 @@ void sp_free_a(sp_mem_t allocator, void* memory) { sp_err_t sp_io_mem_writer_write(sp_io_writer_t* writer, const void* ptr, u64 size, u64* bytes_written) { sp_io_mem_writer_t* w = (sp_io_mem_writer_t*)writer; sp_err_t result = SP_OK; - u64 written = 0; - // If you try a write that would overflow, write nothing. We could write what we're able to - // and return an error, but the general principle is to stop as soon as you know you're in - // an error state. And "I want to write 16 bytes into an 8 byte buffer" is an error state. I - // would rather end up in the same state every time (nothing written, get an error). + // Partial writes are part of the contract: write what fits, advance, and + // surface SP_ERR_IO_NO_SPACE alongside the partial count. Refusing the + // whole request would force callers to discover the boundary themselves. u64 available = w->len - w->pos; - if (size > available) { - result = SP_ERR_IO_NO_SPACE; - goto done; + u64 written = sp_min(size, available); + if (written) { + sp_mem_copy(w->ptr + w->pos, ptr, written); + w->pos += written; } + if (size > available) result = SP_ERR_IO_NO_SPACE; - sp_mem_copy(w->ptr + w->pos, ptr, size); - w->pos += size; - written = size; - -done: if (bytes_written) *bytes_written = written; return result; } From 934d6311b0598586027a7ae12273015d2895a319 Mon Sep 17 00:00:00 2001 From: Thomas Spader Date: Tue, 12 May 2026 23:38:08 -0400 Subject: [PATCH 15/21] sendfile? --- Makefile | 2 +- example/io_copy_perf.c | 137 +++++++++++++++++++++++++++++ sp.h | 189 +++++++++++++++++++++++++++++++++++++---- test/io/copy.c | 134 +++++++++++++++++++++++++++++ test/io/file.c | 91 ++++++++++++++++++++ 5 files changed, 535 insertions(+), 18 deletions(-) create mode 100644 example/io_copy_perf.c diff --git a/Makefile b/Makefile index d0c8bed..60ddc2d 100644 --- a/Makefile +++ b/Makefile @@ -46,7 +46,7 @@ CFLAGS = $(CFLAGS_LANG) -g -Werror=return-type -fsanitize=undefined,alignment -f EXE := $(if $(findstring windows,$(TRIPLE)),.exe,) EXE := $(if $(WASM),.wasm,$(EXE)) TESTS = amalg app array asset cv elf env format fmon fs glob ht io linkage math ps rb str thread time mem prompt leak -EXAMPLES = app array elf embed format hash_table io ls palette prompt prompt_fancy signal wc +EXAMPLES = app array elf embed format hash_table io io_copy_perf ls palette prompt prompt_fancy signal wc # TESTS = app amalg str format # EXAMPLES = app format hash_table TRIPLES = \ diff --git a/example/io_copy_perf.c b/example/io_copy_perf.c new file mode 100644 index 0000000..09da59b --- /dev/null +++ b/example/io_copy_perf.c @@ -0,0 +1,137 @@ +// Compare the userspace copy loop against the kernel-side fast path +// (copy_file_range on Linux) when copying a large file. +// +// sp_io_copy_b is the explicit-buffer form: it always loops read+write +// through the provided userspace buffer. sp_io_copy detects whether the +// (reader, writer) pair supports a kernel-to-kernel route and uses it if +// so, otherwise falling through to the generic loop. + +#define SP_IMPLEMENTATION +#include "sp.h" + +#define PERF_FILE_SIZE_MB 256u +#define PERF_NAIVE_BUFFER 4096u + +static void fill_random(u8* p, u64 n, u64 seed) { + // Cheap LCG. We just need bytes that vary enough that filesystems can't + // collapse the file with sparse / dedup heuristics. + u64 s = seed ? seed : 1; + for (u64 i = 0; i < n; i++) { + s = s * 6364136223846793005ull + 1442695040888963407ull; + p[i] = (u8)(s >> 56); + } +} + +static sp_err_t make_source(sp_str_t path, u64 size_bytes, sp_mem_t mem) { + u8* chunk = sp_alloc_n_a(mem, u8, 1u << 20); + fill_random(chunk, 1u << 20, 0xdeadbeefcafebabeull); + + sp_io_file_writer_t w = sp_zero; + sp_try(sp_io_file_writer_from_path(&w, path, SP_IO_WRITE_MODE_OVERWRITE)); + + u64 remaining = size_bytes; + while (remaining) { + u64 want = remaining < (1u << 20) ? remaining : (1u << 20); + sp_try(sp_io_write(&w.base, chunk, want, SP_NULLPTR)); + remaining -= want; + } + return sp_io_file_writer_close(&w); +} + +typedef struct { + u64 bytes; + u64 ns; +} run_t; + +static run_t copy_naive(sp_str_t src, sp_str_t dst, sp_mem_t mem) { + sp_io_file_reader_t r = sp_zero; + sp_io_file_writer_t w = sp_zero; + sp_io_file_reader_from_path(&r, src); + sp_io_file_writer_from_path(&w, dst, SP_IO_WRITE_MODE_OVERWRITE); + + u8* buf = sp_alloc_n_a(mem, u8, PERF_NAIVE_BUFFER); + u64 copied = 0; + + sp_tm_timer_t t = sp_tm_start_timer(); + // sp_io_copy_b never tries the kernel-side fast path — it is the explicit + // userspace-buffer form. Each PERF_NAIVE_BUFFER-sized chunk crosses the + // user/kernel boundary twice (read + write). + sp_io_copy_b(&w.base, &r.base, buf, PERF_NAIVE_BUFFER, &copied); + u64 ns = sp_tm_read_timer(&t); + + sp_io_file_reader_close(&r); + sp_io_file_writer_close(&w); + return (run_t){ .bytes = copied, .ns = ns }; +} + +static run_t copy_fast(sp_str_t src, sp_str_t dst) { + sp_io_file_reader_t r = sp_zero; + sp_io_file_writer_t w = sp_zero; + sp_io_file_reader_from_path(&r, src); + sp_io_file_writer_from_path(&w, dst, SP_IO_WRITE_MODE_OVERWRITE); + + u64 copied = 0; + sp_tm_timer_t t = sp_tm_start_timer(); + // sp_io_copy checks for the fast path. With a file reader (exposes its fd + // via .as_fd) and a file writer (consumes via .read_from -> copy_file_range + // on Linux), bytes never enter userspace. + sp_io_copy(&w.base, &r.base, &copied); + u64 ns = sp_tm_read_timer(&t); + + sp_io_file_reader_close(&r); + sp_io_file_writer_close(&w); + return (run_t){ .bytes = copied, .ns = ns }; +} + +static void report(const c8* label, run_t run) { + // MB/s = bytes / 1e6 / (ns / 1e9) = bytes * 1000 / ns. + u64 mb_per_s = run.ns ? (run.bytes * 1000u) / run.ns : 0; + sp_log_a( + " {}: {} bytes in {} us ({} MB/s)", + sp_fmt_cstr(label), + sp_fmt_uint(run.bytes), + sp_fmt_uint(run.ns / 1000u), + sp_fmt_uint(mb_per_s) + ); +} + +s32 run(s32 num_args, const c8** args) { + (void)num_args; (void)args; + sp_mem_t mem = sp_mem_arena_as_allocator(sp_mem_arena_new(sp_mem_os_new())); + + sp_str_t cwd = sp_fs_get_cwd_a(mem); + sp_str_t src = sp_str_concat_a(mem, cwd, sp_str_lit("/io_copy_perf.src")); + sp_str_t dst = sp_str_concat_a(mem, cwd, sp_str_lit("/io_copy_perf.dst")); + + u64 size_bytes = (u64)PERF_FILE_SIZE_MB * 1024u * 1024u; + sp_log_a("preparing {} MiB source at {}", sp_fmt_uint(PERF_FILE_SIZE_MB), sp_fmt_str(src)); + sp_err_t err = make_source(src, size_bytes, mem); + if (err) { + sp_log_a("failed to prepare source: err={}", sp_fmt_uint((u32)err)); + return 1; + } + + // Two trials of each so we can see cold-vs-warm cache effects. The first + // naive run typically pays for cold reads of the source; the second runs + // against a warm page cache. The fast-path run benefits from a warm cache + // too, but the dominant cost it skips is the userspace copy itself. + sp_log_a("running benchmark (two trials each)"); + run_t n1 = copy_naive(src, dst, mem); report("naive #1", n1); + run_t f1 = copy_fast (src, dst); report("fast #1", f1); + run_t n2 = copy_naive(src, dst, mem); report("naive #2", n2); + run_t f2 = copy_fast (src, dst); report("fast #2", f2); + + if (f2.ns) { + u64 speedup_x100 = (n2.ns * 100u) / f2.ns; + sp_log_a( + "warm-cache speedup: {}.{}x (naive #2 / fast #2)", + sp_fmt_uint(speedup_x100 / 100u), + sp_fmt_uint(speedup_x100 % 100u) + ); + } + + sp_fs_remove_file_a(src); + sp_fs_remove_file_a(dst); + return 0; +} +SP_MAIN(run) diff --git a/sp.h b/sp.h index 0cdba74..70c054d 100644 --- a/sp.h +++ b/sp.h @@ -730,6 +730,7 @@ typedef enum { SP_ERR_IO_NO_SPACE = 1009, SP_ERR_IO_EOF = 1010, SP_ERR_IO_INVALID_WRITE = 1011, + SP_ERR_IO_UNIMPLEMENTED = 1012, SP_ERR_FMT_TOO_MANY_RENDERERS = 1100, SP_ERR_FMT_WRONG_PARAM_KIND = 1101, SP_ERR_FMT_UNKNOWN_DIRECTIVE = 1102, @@ -3082,11 +3083,22 @@ typedef enum { SP_TYPEDEF_FN(sp_err_t, sp_io_reader_read_cb, sp_io_reader_t* r, void* ptr, u64 size, u64* bytes_read); SP_TYPEDEF_FN(sp_err_t, sp_io_seek_cb, sp_io_reader_t* r, s64 offset, sp_io_whence_t whence, s64* position); +// Optional. Returns the underlying OS handle if this reader has one. Used as +// the universal currency for kernel-to-kernel fast paths (sendfile, splice, +// copy_file_range). A reader that has no fd leaves this NULL and the fast +// path is unavailable. +SP_TYPEDEF_FN(sp_err_t, sp_io_reader_as_fd_cb, sp_io_reader_t* r, sp_io_file_t* fd); SP_TYPEDEF_FN(sp_err_t, sp_io_writer_write_cb, sp_io_writer_t* w, const void* ptr, u64 size, u64* bytes_written); +// Optional. The writer-side fast path. Implementations consume bytes from `r` +// however they like; in practice this means asking `r->as_fd` for an OS handle +// and routing through a kernel-side syscall. Return SP_ERR_IO_UNIMPLEMENTED to +// tell sp_io_copy that it should fall back to the generic loop. +SP_TYPEDEF_FN(sp_err_t, sp_io_writer_read_from_cb, sp_io_writer_t* w, sp_io_reader_t* r, u64* bytes_moved); struct sp_io_reader { sp_io_reader_read_cb read; + sp_io_reader_as_fd_cb as_fd; sp_mem_buffer_t buffer; u64 cursor; }; @@ -3110,6 +3122,7 @@ typedef struct { struct sp_io_writer { sp_io_writer_write_cb write; + sp_io_writer_read_from_cb read_from; sp_mem_buffer_t buffer; }; @@ -3133,45 +3146,54 @@ struct sp_io_dyn_mem_writer { u64 cursor; }; + SP_API sp_err_t sp_io_copy(sp_io_writer_t* dst, sp_io_reader_t* src, u64* bytes_copied); SP_API sp_err_t sp_io_copy_b(sp_io_writer_t* dst, sp_io_reader_t* src, u8* buffer, u64 n, u64* bytes_copied); + SP_API sp_err_t sp_io_read(sp_io_reader_t* reader, void* ptr, u64 size, u64* bytes_read); +SP_API sp_err_t sp_io_read_file_a(sp_mem_t mem, sp_str_t path, sp_str_t* content); SP_API void sp_io_reader_from_mem(sp_io_reader_t* reader, const void* ptr, u64 size); SP_API void sp_io_reader_set_buffer(sp_io_reader_t* reader, u8* buf, u64 capacity); + +SP_API sp_err_t sp_io_write(sp_io_writer_t* writer, const void* ptr, u64 size, u64* bytes_written); +SP_API sp_err_t sp_io_write_str(sp_io_writer_t* writer, sp_str_t str, u64* bytes_written); +SP_API sp_err_t sp_io_write_cstr(sp_io_writer_t* writer, const c8* cstr, u64* bytes_written); +SP_API sp_err_t sp_io_write_c8(sp_io_writer_t* writer, c8 c); +SP_API sp_err_t sp_io_pad(sp_io_writer_t* writer, u64 size, u64* bytes_written); +SP_API sp_err_t sp_io_flush(sp_io_writer_t* w); +SP_API sp_err_t sp_io_writer_set_buffer(sp_io_writer_t* writer, u8* buf, u64 capacity); + SP_API sp_err_t sp_io_seeking_reader_seek(sp_io_seeking_reader_t* r, s64 offset, sp_io_whence_t whence, s64* position); SP_API void sp_io_seeking_reader_from_reader(sp_io_seeking_reader_t* sr, sp_io_reader_t* r, sp_io_seek_cb seek); SP_API void sp_io_seeking_reader_from_mem(sp_io_seeking_reader_t* sr, sp_io_reader_t* backing, const void* ptr, u64 size); SP_API void sp_io_seeking_reader_from_file_reader(sp_io_seeking_reader_t* sr, sp_io_file_reader_t* fr); -SP_API sp_err_t sp_io_mem_seek(sp_io_reader_t* r, s64 offset, sp_io_whence_t whence, s64* position); + SP_API sp_err_t sp_io_file_reader_from_path(sp_io_file_reader_t* r, sp_str_t path); SP_API void sp_io_file_reader_from_file(sp_io_file_reader_t* r, sp_io_file_t file, sp_io_close_mode_t mode); SP_API sp_err_t sp_io_file_reader_seek(sp_io_file_reader_t* r, s64 offset, sp_io_whence_t whence, s64* position); SP_API sp_err_t sp_io_file_reader_size(sp_io_file_reader_t* r, u64* size); SP_API sp_err_t sp_io_file_reader_close(sp_io_file_reader_t* r); -SP_API void sp_io_pipe_reader_from_pipe(sp_io_pipe_reader_t* r, sp_io_pipe_t pipe, sp_io_close_mode_t mode); -SP_API sp_err_t sp_io_pipe_reader_close(sp_io_pipe_reader_t* r); -SP_API sp_err_t sp_io_write(sp_io_writer_t* writer, const void* ptr, u64 size, u64* bytes_written); -SP_API sp_err_t sp_io_write_str(sp_io_writer_t* writer, sp_str_t str, u64* bytes_written); -SP_API sp_err_t sp_io_write_cstr(sp_io_writer_t* writer, const c8* cstr, u64* bytes_written); -SP_API sp_err_t sp_io_write_c8(sp_io_writer_t* writer, c8 c); -SP_API sp_err_t sp_io_pad(sp_io_writer_t* writer, u64 size, u64* bytes_written); -SP_API sp_err_t sp_io_flush(sp_io_writer_t* w); -SP_API sp_err_t sp_io_writer_set_buffer(sp_io_writer_t* writer, u8* buf, u64 capacity); SP_API sp_err_t sp_io_file_writer_from_path(sp_io_file_writer_t* w, sp_str_t path, sp_io_write_mode_t mode); SP_API void sp_io_file_writer_from_fd(sp_io_file_writer_t* w, sp_sys_fd_t fd, sp_io_close_mode_t close_mode); SP_API sp_err_t sp_io_file_writer_seek(sp_io_file_writer_t* w, s64 offset, sp_io_whence_t whence, s64* position); SP_API sp_err_t sp_io_file_writer_size(sp_io_file_writer_t* w, u64* size); SP_API sp_err_t sp_io_file_writer_close(sp_io_file_writer_t* w); + +SP_API void sp_io_pipe_reader_from_pipe(sp_io_pipe_reader_t* r, sp_io_pipe_t pipe, sp_io_close_mode_t mode); +SP_API sp_err_t sp_io_pipe_reader_close(sp_io_pipe_reader_t* r); + +SP_API sp_err_t sp_io_mem_seek(sp_io_reader_t* r, s64 offset, sp_io_whence_t whence, s64* position); SP_API void sp_io_mem_writer_from_buffer(sp_io_mem_writer_t* w, void* ptr, u64 size); SP_API sp_err_t sp_io_mem_writer_seek(sp_io_mem_writer_t* w, s64 offset, sp_io_whence_t whence, s64* position); SP_API sp_err_t sp_io_mem_writer_size(sp_io_mem_writer_t* w, u64* size); SP_API sp_str_t sp_io_mem_writer_as_str(sp_io_mem_writer_t* w); + SP_API void sp_io_dyn_mem_writer_init_a(sp_mem_t mem, sp_io_dyn_mem_writer_t* w); SP_API sp_err_t sp_io_dyn_mem_writer_seek(sp_io_dyn_mem_writer_t* w, s64 offset, sp_io_whence_t whence, s64* position); SP_API sp_err_t sp_io_dyn_mem_writer_size(sp_io_dyn_mem_writer_t* w, u64* size); SP_API sp_err_t sp_io_dyn_mem_writer_close(sp_io_dyn_mem_writer_t* w); SP_API sp_str_t sp_io_dyn_mem_writer_as_str(sp_io_dyn_mem_writer_t* w); -SP_API sp_err_t sp_io_read_file_a(sp_mem_t mem, sp_str_t path, sp_str_t* content); + SP_API void sp_io_get_std_out(sp_io_file_writer_t* io); SP_API void sp_io_get_std_err(sp_io_file_writer_t* io); @@ -3849,6 +3871,8 @@ s32 errno; #define SP_SYSCALL_NUM_DUP2 33 #define SP_SYSCALL_NUM_NANOSLEEP 35 #define SP_SYSCALL_NUM_GETPID 39 + #define SP_SYSCALL_NUM_SENDFILE 40 + #define SP_SYSCALL_NUM_COPY_FILE_RANGE 326 #define SP_SYSCALL_NUM_CLONE 56 #define SP_SYSCALL_NUM_FORK 57 #define SP_SYSCALL_NUM_EXECVE 59 @@ -3931,6 +3955,8 @@ s32 errno; #define SP_SYSCALL_NUM_EXECVE 221 #define SP_SYSCALL_NUM_MMAP 222 #define SP_SYSCALL_NUM_WAIT4 260 + #define SP_SYSCALL_NUM_SENDFILE 71 + #define SP_SYSCALL_NUM_COPY_FILE_RANGE 285 #define SP_SYSCALL_NUM_OPEN SP_SYSCALL_NUM_OPENAT #define SP_SYSCALL_NUM_STAT SP_SYSCALL_NUM_NEWFSTATAT #define SP_SYSCALL_NUM_LSTAT SP_SYSCALL_NUM_NEWFSTATAT @@ -4149,6 +4175,7 @@ s32 sp_lx_inotify_init1(s32 flags); s32 sp_lx_inotify_add_watch(s32 fd, const c8* pathname, u32 mask); s32 sp_lx_inotify_rm_watch(s32 fd, s32 wd); s32 sp_lx_wait4(s32 pid, s32* status, s32 options, void* rusage); +s64 sp_lx_copy_file_range(s32 in_fd, s32 out_fd, u64 count); void sp_sys_exit(s32 code); s32 sp_lx_inotify_init1(s32 flags) { @@ -4167,6 +4194,16 @@ s32 sp_lx_wait4(s32 pid, s32* status, s32 options, void* rusage) { return (s32)sp_syscall(SP_SYSCALL_NUM_WAIT4, pid, status, options, rusage); } +// Kernel-to-kernel copy. NULL offset pointers means "use and advance the fd's +// own seek position", which is what we want for stream-style copies. +s64 sp_lx_copy_file_range(s32 in_fd, s32 out_fd, u64 count) { + s64 rc; + do { + rc = sp_syscall(SP_SYSCALL_NUM_COPY_FILE_RANGE, in_fd, 0, out_fd, 0, count, 0); + } while (rc == -1 && errno == SP_EINTR); + return rc; +} + ////////////// // PLATFORM // ////////////// @@ -13073,9 +13110,19 @@ sp_err_t sp_io_eof_read(sp_io_reader_t* r, void* ptr, u64 size, u64* bytes_read) return SP_ERR_IO_EOF; } +sp_err_t sp_io_file_reader_as_fd(sp_io_reader_t* r, sp_io_file_t* fd) { + sp_io_file_reader_t* fr = (sp_io_file_reader_t*)r; + if (fr->file == SP_SYS_INVALID_FD) return SP_ERR_IO; + *fd = fr->file; + return SP_OK; +} + void sp_io_file_reader_from_file(sp_io_file_reader_t* r, sp_io_file_t file, sp_io_close_mode_t mode) { *r = (sp_io_file_reader_t) { - .base = { .read = sp_io_file_reader_read }, + .base = { + .read = sp_io_file_reader_read, + .as_fd = sp_io_file_reader_as_fd, + }, .file = file, .close_mode = mode, }; @@ -13157,9 +13204,19 @@ sp_err_t sp_io_file_reader_close(sp_io_file_reader_t* r) { return SP_OK; } +sp_err_t sp_io_pipe_reader_as_fd(sp_io_reader_t* r, sp_io_file_t* fd) { + sp_io_pipe_reader_t* pr = (sp_io_pipe_reader_t*)r; + if (pr->pipe == SP_SYS_INVALID_FD) return SP_ERR_IO; + *fd = pr->pipe; + return SP_OK; +} + void sp_io_pipe_reader_from_pipe(sp_io_pipe_reader_t* r, sp_io_pipe_t pipe, sp_io_close_mode_t mode) { *r = (sp_io_pipe_reader_t) { - .base = { .read = sp_io_pipe_reader_read }, + .base = { + .read = sp_io_pipe_reader_read, + .as_fd = sp_io_pipe_reader_as_fd, + }, .pipe = pipe, .close_mode = mode, }; @@ -13198,8 +13255,48 @@ void sp_io_reader_set_buffer(sp_io_reader_t* reader, u8* buf, u64 capacity) { } sp_err_t sp_io_copy(sp_io_writer_t* w, sp_io_reader_t* r, u64* bytes_copied) { - u8 buffer[4096]; - return sp_io_copy_b(w, r, buffer, sizeof(buffer), bytes_copied); + u64 total = 0; + + // Fast path: writer supports a kernel-side bulk transfer and reader can + // expose its fd. If the reader has userspace-buffered bytes, drain them + // through the normal write path first — the kernel doesn't know about + // them. If the writer has its own buffer, flush it for the same reason. + if (w->read_from && r->as_fd) { + u64 buffered = r->buffer.len - r->cursor; + if (buffered) { + u64 wrote = 0; + sp_err_t err = sp_io_write(w, r->buffer.data + r->cursor, buffered, &wrote); + total += wrote; + r->cursor += wrote; + if (err) { + if (bytes_copied) *bytes_copied = total; + return err; + } + } + if (w->buffer.data && w->buffer.len) { + sp_err_t err = sp_io_flush(w); + if (err) { + if (bytes_copied) *bytes_copied = total; + return err; + } + } + + u64 moved = 0; + sp_err_t err = w->read_from(w, r, &moved); + total += moved; + if (err != SP_ERR_IO_UNIMPLEMENTED) { + if (bytes_copied) *bytes_copied = total; + return err; + } + // The fast path declined this pair. Fall through to the generic loop. + } + + u8 buf[4096]; + u64 slow_total = 0; + sp_err_t err = sp_io_copy_b(w, r, buf, sizeof(buf), &slow_total); + total += slow_total; + if (bytes_copied) *bytes_copied = total; + return err; } sp_err_t sp_io_copy_b(sp_io_writer_t* w, sp_io_reader_t* r, u8* buffer, u64 n, u64* bytes_copied) { @@ -13372,6 +13469,54 @@ sp_err_t sp_io_file_writer_write(sp_io_writer_t* writer, const void* ptr, u64 si return result; } +// Kernel-to-kernel fast path. Linux-only; on other platforms the read_from +// callback isn't wired up and sp_io_copy falls through to the generic loop. +// Asks the reader for an fd; if it has one, uses copy_file_range to move +// bytes without bouncing through userspace. On first-call failure returns +// SP_ERR_IO_UNIMPLEMENTED so the caller falls back. Partial progress + error +// is reported faithfully. +#if defined(SP_LINUX) +sp_err_t sp_io_file_writer_read_from(sp_io_writer_t* writer, sp_io_reader_t* r, u64* bytes_moved) { + sp_io_file_writer_t* w = (sp_io_file_writer_t*)writer; + u64 total = 0; + + if (!r->as_fd) { if (bytes_moved) *bytes_moved = 0; return SP_ERR_IO_UNIMPLEMENTED; } + + sp_io_file_t in_fd = SP_SYS_INVALID_FD; + if (r->as_fd(r, &in_fd) != SP_OK) { + if (bytes_moved) *bytes_moved = 0; + return SP_ERR_IO_UNIMPLEMENTED; + } + + // Cap each syscall at 1 GiB; Linux's copy_file_range historically caps at + // 0x7ffff000 internally anyway, and we want to bound the unit of progress + // we report. + const u64 chunk = (u64)1 << 30; + + while (true) { + s64 rc = sp_lx_copy_file_range(in_fd, w->fd, chunk); + if (rc < 0) { + if (total == 0) { + // First syscall failed. Source might not be a regular file + // (EINVAL), kernel might be too old (ENOSYS), or the pair might + // cross filesystems on a pre-5.3 kernel (EXDEV). Tell the caller + // to fall back to read+write. + if (bytes_moved) *bytes_moved = 0; + return SP_ERR_IO_UNIMPLEMENTED; + } + // We already made progress; surface as a write failure. + if (bytes_moved) *bytes_moved = total; + return SP_ERR_IO_WRITE_FAILED; + } + if (rc == 0) break; // EOF on source + total += (u64)rc; + } + + if (bytes_moved) *bytes_moved = total; + return SP_OK; +} +#endif + sp_err_t sp_io_file_writer_seek(sp_io_file_writer_t* w, s64 offset, sp_io_whence_t whence, s64* position) { sp_assert(w); sp_try(sp_io_flush(&w->base)); @@ -13436,7 +13581,12 @@ sp_err_t sp_io_file_writer_from_path(sp_io_file_writer_t* w, sp_str_t path, sp_i } *w = (sp_io_file_writer_t) { - .base = { .write = sp_io_file_writer_write }, + .base = { + .write = sp_io_file_writer_write, +#if defined(SP_LINUX) + .read_from = sp_io_file_writer_read_from, +#endif + }, .fd = fd, .close_mode = SP_IO_CLOSE_MODE_AUTO, }; @@ -13445,7 +13595,12 @@ sp_err_t sp_io_file_writer_from_path(sp_io_file_writer_t* w, sp_str_t path, sp_i void sp_io_file_writer_from_fd(sp_io_file_writer_t* w, sp_sys_fd_t fd, sp_io_close_mode_t close_mode) { *w = (sp_io_file_writer_t) { - .base = { .write = sp_io_file_writer_write }, + .base = { + .write = sp_io_file_writer_write, +#if defined(SP_LINUX) + .read_from = sp_io_file_writer_read_from, +#endif + }, .fd = fd, .close_mode = close_mode, }; diff --git a/test/io/copy.c b/test/io/copy.c index a72adfe..1d103c9 100644 --- a/test/io/copy.c +++ b/test/io/copy.c @@ -191,6 +191,140 @@ UTEST_F(io_copy, buffered_writer_overflow_flushes_partial) { }); } +/////////////////// +// FAST PATH // +/////////////////// +// Pin the read_from-on-writer negotiation contract. A tracking writer +// records whether read_from was invoked and how many bytes flowed through +// each path. We pair it with an exposes-fd reader (a file reader) or a +// no-fd reader (a mem reader) to exercise the dispatch table. + +typedef struct { + sp_io_writer_t base; + bool read_from_returns_unimpl; + u64 bytes_via_write; + u64 bytes_via_read_from; +} io_tracking_writer_t; + +static sp_err_t io_tracking_writer_write(sp_io_writer_t* w, const void* ptr, u64 size, u64* bytes_written) { + io_tracking_writer_t* t = (io_tracking_writer_t*)w; + (void)ptr; + t->bytes_via_write += size; + if (bytes_written) *bytes_written = size; + return SP_OK; +} + +static sp_err_t io_tracking_writer_read_from(sp_io_writer_t* w, sp_io_reader_t* r, u64* moved) { + io_tracking_writer_t* t = (io_tracking_writer_t*)w; + if (t->read_from_returns_unimpl) { + if (moved) *moved = 0; + return SP_ERR_IO_UNIMPLEMENTED; + } + // Drain the reader via .read so the test can observe a real byte count. + u8 buf [4096]; + u64 total = 0; + while (true) { + u64 chunk = 0; + sp_err_t err = sp_io_read(r, buf, sizeof(buf), &chunk); + total += chunk; + if (err == SP_ERR_IO_EOF) break; + if (err) { if (moved) *moved = total; return err; } + } + t->bytes_via_read_from = total; + if (moved) *moved = total; + return SP_OK; +} + +// Source has an fd (file reader) and writer advertises read_from. The fast +// path is taken; the byte-loop path is not touched. +UTEST_F(io_copy, fast_path_taken_when_both_sides_support) { + SKIP_ON_WASM() + sp_test_file_manager_t fm = sp_zero; + sp_test_file_manager_init(&fm); + sp_str_t path = sp_test_file_create_empty(&fm, sp_str_lit("fastpath_src.bin")); + + const c8* content = "0123456789abcdef"; + u64 n = sp_cstr_len(content); + { + sp_io_file_writer_t fw = sp_zero; + sp_io_file_writer_from_path(&fw, path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&fw.base, content, n, SP_NULLPTR); + sp_io_file_writer_close(&fw); + } + + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, path); + + io_tracking_writer_t w = sp_zero; + w.base.write = io_tracking_writer_write; + w.base.read_from = io_tracking_writer_read_from; + + u64 copied = 0; + EXPECT_EQ(sp_io_copy(&w.base, &r.base, &copied), SP_OK); + EXPECT_EQ(copied, n); + EXPECT_EQ(w.bytes_via_read_from, n); + EXPECT_EQ(w.bytes_via_write, 0); + + sp_io_file_reader_close(&r); + sp_test_file_manager_cleanup(&fm); +} + +// Source has no fd (mem reader). Fast path declines; we fall through to the +// byte-loop path. The tracking writer's read_from is never called. +UTEST_F(io_copy, fast_path_skipped_when_source_has_no_fd) { + const c8* content = "the-quick-brown-fox-jumps-over"; + u64 n = sp_cstr_len(content); + + sp_io_reader_t r = sp_zero; + sp_io_reader_from_mem(&r, content, n); + + io_tracking_writer_t w = sp_zero; + w.base.write = io_tracking_writer_write; + w.base.read_from = io_tracking_writer_read_from; + + u64 copied = 0; + EXPECT_EQ(sp_io_copy(&w.base, &r, &copied), SP_OK); + EXPECT_EQ(copied, n); + EXPECT_EQ(w.bytes_via_read_from, 0); + EXPECT_EQ(w.bytes_via_write, n); +} + +// Writer declines the fast path with SP_ERR_IO_UNIMPLEMENTED. Copy falls +// through to the byte loop without surfacing the unimplemented error to the +// caller. The end-to-end byte count is intact. +UTEST_F(io_copy, fast_path_unimplemented_falls_through) { + SKIP_ON_WASM() + sp_test_file_manager_t fm = sp_zero; + sp_test_file_manager_init(&fm); + sp_str_t path = sp_test_file_create_empty(&fm, sp_str_lit("fastpath_unimpl.bin")); + + const c8* content = "fallback-content-xyz"; + u64 n = sp_cstr_len(content); + { + sp_io_file_writer_t fw = sp_zero; + sp_io_file_writer_from_path(&fw, path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&fw.base, content, n, SP_NULLPTR); + sp_io_file_writer_close(&fw); + } + + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, path); + + io_tracking_writer_t w = sp_zero; + w.base.write = io_tracking_writer_write; + w.base.read_from = io_tracking_writer_read_from; + w.read_from_returns_unimpl = true; + + u64 copied = 0; + EXPECT_EQ(sp_io_copy(&w.base, &r.base, &copied), SP_OK); + EXPECT_EQ(copied, n); + EXPECT_EQ(w.bytes_via_read_from, 0); + EXPECT_EQ(w.bytes_via_write, n); + + sp_io_file_reader_close(&r); + sp_test_file_manager_cleanup(&fm); +} + // Writer buffer attached; backend errors on the flush that the wrapper // triggers when the buffer overflows. Copy surfaces the error. UTEST_F(io_copy, buffered_writer_flush_error) { diff --git a/test/io/file.c b/test/io/file.c index 2428e93..75ccf3a 100644 --- a/test/io/file.c +++ b/test/io/file.c @@ -318,6 +318,97 @@ UTEST_F(io, file_writer_pad) { sp_io_file_reader_close(&r); } +// File reader's as_fd callback returns the underlying fd. This is what the +// writer-side fast path keys off of. +UTEST_F(io, file_reader_as_fd) { + SKIP_ON_WASM() + sp_io_file_writer_t fw = sp_zero; + sp_io_file_writer_from_path(&fw, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_write(&fw.base, "x", 1, SP_NULLPTR); + sp_io_file_writer_close(&fw); + + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, ut.file_path); + + EXPECT_TRUE(r.base.as_fd != SP_NULLPTR); + sp_io_file_t fd = SP_SYS_INVALID_FD; + EXPECT_EQ(r.base.as_fd(&r.base, &fd), SP_OK); + EXPECT_EQ((s64)fd, (s64)r.file); + + sp_io_file_reader_close(&r); +} + +// File writer's read_from callback is set at construction. +UTEST_F(io, file_writer_read_from_set) { + SKIP_ON_WASM() + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + EXPECT_TRUE(w.base.read_from != SP_NULLPTR); + sp_io_file_writer_close(&w); +} + +// End-to-end: copy a non-trivial file via sp_io_copy and verify the +// destination matches. On Linux this exercises copy_file_range; elsewhere it +// falls back to the generic loop. Either way, the contents should be +// identical. +UTEST_F(io, file_to_file_copy) { + SKIP_ON_WASM() + // Produce 4 KiB of pseudo-random content so the kernel can't represent + // the source sparsely. + u8 source [4096]; + sp_for(i, sizeof(source)) source[i] = (u8)((i * 1103515245u + 12345u) >> 8); + + sp_io_file_writer_t sw = sp_zero; + sp_io_file_writer_from_path(&sw, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + EXPECT_EQ(sp_io_write(&sw.base, source, sizeof(source), SP_NULLPTR), SP_OK); + sp_io_file_writer_close(&sw); + + sp_str_t dst_path = sp_test_file_path(&ut.file_manager, sp_str_lit("file_to_file_copy.dst")); + + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, ut.file_path); + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, dst_path, SP_IO_WRITE_MODE_OVERWRITE); + + u64 copied = 0; + EXPECT_EQ(sp_io_copy(&w.base, &r.base, &copied), SP_OK); + EXPECT_EQ(copied, sizeof(source)); + + sp_io_file_reader_close(&r); + sp_io_file_writer_close(&w); + + sp_str_t loaded = sp_zero; + sp_io_read_file_a(ut.mem, dst_path, &loaded); + EXPECT_EQ(loaded.len, sizeof(source)); + sp_for(i, sizeof(source)) EXPECT_EQ((u8)loaded.data[i], source[i]); +} + +// When the source has no fd (e.g. an in-memory reader), the fast path +// declines and the generic loop produces the same result. +UTEST_F(io, file_copy_fast_path_falls_back_for_mem_source) { + SKIP_ON_WASM() + const c8* content = "abcdefghijklmnopqrstuvwxyz0123456789"; + u64 n = sp_cstr_len(content); + + sp_io_reader_t r = sp_zero; + sp_io_reader_from_mem(&r, content, n); + EXPECT_TRUE(r.as_fd == SP_NULLPTR); // Mem reader has no fd to hand out. + + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + + u64 copied = 0; + EXPECT_EQ(sp_io_copy(&w.base, &r, &copied), SP_OK); + EXPECT_EQ(copied, n); + + sp_io_file_writer_close(&w); + + sp_str_t loaded = sp_zero; + sp_io_read_file_a(ut.mem, ut.file_path, &loaded); + EXPECT_EQ(loaded.len, n); + sp_for(i, n) EXPECT_EQ(loaded.data[i], content[i]); +} + // Two file handles (writer then reader) operating on the same large offset; // doesn't fit the single-subject runner pattern. UTEST_F(io, file_seek_beyond_4gb) { From e5084c51898b07ab4cb2af73c84c2231f1f345e2 Mon Sep 17 00:00:00 2001 From: Thomas Spader Date: Wed, 13 May 2026 13:42:57 -0400 Subject: [PATCH 16/21] rename the _a --- example/app.c | 8 +- example/array.c | 2 +- example/elf.c | 14 +- example/embed.c | 30 +- example/format.c | 71 +- example/hash_table.c | 20 +- example/io.c | 26 +- example/io_copy_perf.c | 24 +- example/ls.c | 14 +- example/msvc.c | 40 +- example/palette.c | 10 +- example/prompt.c | 14 +- example/prompt_fancy.c | 32 +- example/signal.c | 8 +- example/wc.c | 18 +- sp.h | 934 ++++++++++++-------------- sp/sp_asset.h | 10 +- sp/sp_elf.h | 18 +- sp/sp_glob.h | 6 +- sp/sp_macho.h | 4 +- sp/sp_msvc.h | 66 +- sp/sp_prompt.h | 42 +- test/array.c | 10 +- test/asset.c | 28 +- test/bench/glob.c | 6 +- test/bench/ht.c | 50 +- test/elf.c | 28 +- test/env.c | 4 +- test/fmon.c | 64 +- test/format.c | 203 +++--- test/fs/canonicalize_path.c | 26 +- test/fs/collect.c | 18 +- test/fs/copy.c | 32 +- test/fs/copy_glob.c | 10 +- test/fs/create_dir.c | 24 +- test/fs/create_file.c | 32 +- test/fs/fs.h | 34 +- test/fs/get_cwd.c | 8 +- test/fs/get_exe_path.c | 14 +- test/fs/join_path.c | 2 +- test/fs/links.c | 32 +- test/fs/mod_time.c | 12 +- test/fs/normalize_path.c | 8 +- test/fs/predicates.c | 18 +- test/fs/remove.c | 8 +- test/fs/replace_ext.c | 2 +- test/fs/system_paths.c | 16 +- test/fs/windows/wtf8.c | 44 +- test/ht.c | 168 ++--- test/io/dyn.c | 4 +- test/io/file.c | 6 +- test/leak.c | 58 +- test/linkage.c | 134 ++-- test/mem/arena.c | 10 +- test/mem/builtin.c | 2 +- test/prompt.c | 18 +- test/ps.c | 108 +-- test/str.c | 132 ++-- test/time.c | 6 +- test/tools/features/main.c | 4 +- test/tools/linkage/format-bare-args.c | 4 +- test/tools/process/process.c | 20 +- test/tools/smoke.c | 2 +- test/tools/stress.c | 36 +- test/tools/test.h | 66 +- test/tools/unaligned.c | 4 +- tools/sp.c | 58 +- tools/utest.h | 30 +- 68 files changed, 1503 insertions(+), 1511 deletions(-) diff --git a/example/app.c b/example/app.c index db896e2..177bd19 100644 --- a/example/app.c +++ b/example/app.c @@ -8,7 +8,7 @@ typedef struct { sp_app_result_t on_init(sp_app_t* app) { state_t* s = (state_t*)app->user_data; - sp_log_a("on_init()"); + sp_log("on_init()"); s->timer = sp_tm_start_timer(); return SP_APP_CONTINUE; } @@ -16,7 +16,7 @@ sp_app_result_t on_init(sp_app_t* app) { sp_app_result_t on_update(sp_app_t* app) { state_t* s = (state_t*)app->user_data; u64 elapsed = sp_tm_read_timer(&s->timer); - sp_log_a("elapsed: {.gray .duration}", sp_fmt_uint(elapsed)); + sp_log("elapsed: {.gray .duration}", sp_fmt_uint(elapsed)); if (elapsed >= sp_tm_s_to_ns(1)) { return SP_APP_QUIT; }; @@ -24,12 +24,12 @@ sp_app_result_t on_update(sp_app_t* app) { } void on_deinit(sp_app_t* app) { - sp_log_a("on_deinit()"); + sp_log("on_deinit()"); } sp_app_config_t app_main(s32 num_args, const c8** args) { sp_mem_t a = sp_mem_os_new(); - state_t* state = sp_alloc_type_a(a, state_t); + state_t* state = sp_alloc_type(a, state_t); state->allocator = a; return (sp_app_config_t) { diff --git a/example/array.c b/example/array.c index 06708c0..03c662a 100644 --- a/example/array.c +++ b/example/array.c @@ -6,7 +6,7 @@ s32 run(s32 num_args, const c8** args) { sp_da_push(years, 1969); sp_da_push(years, 1972); sp_da_for(years, it) { - sp_log_a("year: {}", sp_fmt_int(years[it])); + sp_log("year: {}", sp_fmt_int(years[it])); } return 0; } diff --git a/example/elf.c b/example/elf.c index 7f0c939..b959394 100644 --- a/example/elf.c +++ b/example/elf.c @@ -10,12 +10,12 @@ typedef struct { } embed_header_t; static sp_str_t make_symbol_name(sp_str_t basename) { - return sp_str_concat_a(sp_mem_get_scratch(), sp_str_lit("embed_"), basename); + return sp_str_concat(sp_mem_get_scratch(), sp_str_lit("embed_"), basename); } s32 embed_main(s32 argc, const c8** argv) { if (argc < 3) { - sp_log_a("usage: embed [file2 ...]"); + sp_log("usage: embed [file2 ...]"); return 1; } @@ -24,7 +24,7 @@ s32 embed_main(s32 argc, const c8** argv) { sp_str_t output_path = sp_str_view(argv[1]); u32 num_files = argc - 2; - sp_log_a("embedding {.fg brightblack} files into {.fg cyan}", sp_fmt_uint(num_files), sp_fmt_str(output_path)); + sp_log("embedding {.fg brightblack} files into {.fg cyan}", sp_fmt_uint(num_files), sp_fmt_str(output_path)); sp_elf_t* elf = sp_elf_new_with_null_section(mem); @@ -49,7 +49,7 @@ s32 embed_main(s32 argc, const c8** argv) { } u32 header_size = sizeof(embed_header_t) * num_files; - embed_header_t* headers = sp_alloc_n_a(mem, embed_header_t, num_files); + embed_header_t* headers = sp_alloc_n(mem, embed_header_t, num_files); sp_elf_section_reserve_bytes(sp_elf_find_section_by_index(elf, data_sec_idx), header_size); symtab = sp_elf_find_section_by_name(elf, sp_str_lit(".symtab")); @@ -59,13 +59,13 @@ s32 embed_main(s32 argc, const c8** argv) { sp_str_t path = sp_str_view(argv[2 + i]); sp_str_t basename = sp_fs_get_name(path); sp_str_t content = sp_zero; - sp_io_read_file_a(mem, path, &content); + sp_io_read_file(mem, path, &content); if (sp_str_empty(content)) { return 1; } - sp_log_a("{.fg brightcyan} ({} bytes)", sp_fmt_str(basename), sp_fmt_uint(content.len)); + sp_log("{.fg brightcyan} ({} bytes)", sp_fmt_str(basename), sp_fmt_uint(content.len)); data_sec = sp_elf_find_section_by_index(elf, data_sec_idx); u32 data_offset = (u32)data_sec->buffer.size; @@ -87,7 +87,7 @@ s32 embed_main(s32 argc, const c8** argv) { sp_err_t err = sp_elf_write_to_file(elf, output_path); if (err != SP_OK) { - sp_log_a("error: failed to write output"); + sp_log("error: failed to write output"); return 1; } diff --git a/example/embed.c b/example/embed.c index 3d405ec..ca24b99 100644 --- a/example/embed.c +++ b/example/embed.c @@ -9,11 +9,11 @@ typedef struct { } embed_entry_t; sp_str_t symbol_from_path(sp_mem_t mem, sp_str_t path) { - sp_str_t symbol = sp_str_copy_a(mem, path); - symbol = sp_str_replace_c8_a(mem, symbol, '/', '_'); - symbol = sp_str_replace_c8_a(mem, symbol, '\\', '_'); - symbol = sp_str_replace_c8_a(mem, symbol, '.', '_'); - symbol = sp_str_replace_c8_a(mem, symbol, '-', '_'); + sp_str_t symbol = sp_str_copy(mem, path); + symbol = sp_str_replace_c8(mem, symbol, '/', '_'); + symbol = sp_str_replace_c8(mem, symbol, '\\', '_'); + symbol = sp_str_replace_c8(mem, symbol, '.', '_'); + symbol = sp_str_replace_c8(mem, symbol, '-', '_'); return symbol; } @@ -22,7 +22,7 @@ s32 embed_main(s32 argc, const c8** argv) { sp_mem_t mem = sp_mem_os_new(); if (argc < 4) { - sp_log_a("usage: embed [extra_files...]"); + sp_log("usage: embed [extra_files...]"); rc = 1; goto cleanup; } @@ -30,11 +30,11 @@ s32 embed_main(s32 argc, const c8** argv) { sp_str_t src_dir = sp_str_view(argv[1]); sp_str_t out_obj = sp_str_view(argv[2]); sp_str_t out_hdr = sp_str_view(argv[3]); - sp_log_a("scanning {}", sp_fmt_str(src_dir)); + sp_log("scanning {}", sp_fmt_str(src_dir)); - sp_da(sp_fs_entry_t) files = sp_fs_collect_recursive_a(mem, src_dir); + sp_da(sp_fs_entry_t) files = sp_fs_collect_recursive(mem, src_dir); if (sp_da_empty(files)) { - sp_log_a("no files found in {}", sp_fmt_str(src_dir)); + sp_log("no files found in {}", sp_fmt_str(src_dir)); rc = 1; goto cleanup; } @@ -58,7 +58,7 @@ s32 embed_main(s32 argc, const c8** argv) { sp_str_t symbol = symbol_from_path(mem, rel_path); sp_str_t content = sp_zero; - sp_io_read_file_a(mem, ent.path, &content); + sp_io_read_file(mem, ent.path, &content); u64 size = content.len; sp_elf_section_t* symtab = sp_elf_find_section_by_name(elf, sp_str_lit(".symtab")); @@ -88,7 +88,7 @@ s32 embed_main(s32 argc, const c8** argv) { sp_elf_add_symbol( symtab, elf, - sp_fmt_a(mem, "{}_size", sp_fmt_str(symbol)).value, + sp_fmt(mem, "{}_size", sp_fmt_str(symbol)).value, offset, sizeof(u64), STB_GLOBAL, STT_OBJECT, section->index @@ -108,7 +108,7 @@ s32 embed_main(s32 argc, const c8** argv) { sp_str_t path = sp_str_lit("include/spn.h"); sp_str_t content = sp_zero; - sp_io_read_file_a(mem, file_path, &content); + sp_io_read_file(mem, file_path, &content); u64 size = content.len; sp_elf_section_t* symtab = sp_elf_find_section_by_name(elf, sp_str_lit(".symtab")); @@ -138,7 +138,7 @@ s32 embed_main(s32 argc, const c8** argv) { sp_elf_add_symbol( symtab, elf, - sp_fmt_a(mem, "{}_size", sp_fmt_str(symbol)).value, + sp_fmt(mem, "{}_size", sp_fmt_str(symbol)).value, offset, sizeof(u64), STB_GLOBAL, STT_OBJECT, section->index @@ -154,7 +154,7 @@ s32 embed_main(s32 argc, const c8** argv) { sp_err_t err = sp_elf_write_to_file(elf, out_obj); if (err != SP_OK) { - sp_log_a("failed to write {}", sp_fmt_str(out_obj)); + sp_log("failed to write {}", sp_fmt_str(out_obj)); rc = 1; goto cleanup; } @@ -185,7 +185,7 @@ s32 embed_main(s32 argc, const c8** argv) { sp_io_write_str(&hdr.base, sp_str_lit("};\n"), SP_NULLPTR); sp_io_file_writer_close(&hdr); - sp_log_a("embedded {} files -> {} + {}", sp_fmt_uint(sp_da_size(entries)), sp_fmt_str(out_obj), sp_fmt_str(out_hdr)); + sp_log("embedded {} files -> {} + {}", sp_fmt_uint(sp_da_size(entries)), sp_fmt_str(out_obj), sp_fmt_str(out_hdr)); cleanup: return rc; diff --git a/example/format.c b/example/format.c index 3cb7c8e..109f73c 100644 --- a/example/format.c +++ b/example/format.c @@ -5,7 +5,7 @@ // the value-typed members of the union in sp_fmt_arg_t, you cast the pointer member. // // From there, you can append arbitrary bytes to the string builder being used for this -// call to sp_fmt_a(mem, ...).value. You can (and are encouraged to) use sp.h's internal helpers for +// call to sp_fmt(mem, ...).value. You can (and are encouraged to) use sp.h's internal helpers for // printing primitives, and you have access to the :specifier's width and precision. // // Then, to provide a convenient and type-safe call site, define the sp_fmt_*() macros that @@ -17,17 +17,17 @@ void format_point(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_ar point_t* point = (point_t*)arg->custom.ptr; u32 precision = sp_opt_is_null(arg->spec.precision) ? 2 : sp_opt_get(arg->spec.precision); sp_io_write_c8(io, '('); - sp_fmt_write_f64_a(io, point->x, precision); + sp_fmt_write_f64(io, point->x, precision); sp_io_write_cstr(io, ", ", SP_NULLPTR); - sp_fmt_write_f64_a(io, point->y, precision); + sp_fmt_write_f64(io, point->y, precision); sp_io_write_c8(io, ')'); } #define sp_fmt_point(p) sp_fmt_custom(point_t, format_point, (p)) #define sp_fmt_point_v(...) sp_fmt_custom_v(point_t, format_point, (point_t){__VA_ARGS__}) static void section(const c8* title) { - sp_log_a(""); - sp_log_a("{.gray .italic}", sp_fmt_cstr(title)); + sp_log(""); + sp_log("{.gray .italic}", sp_fmt_cstr(title)); } void specifier(s64 n, c8 fill, sp_fmt_align_t align, u8 width) { @@ -38,8 +38,15 @@ void specifier(s64 n, c8 fill, sp_fmt_align_t align, u8 width) { case SP_FMT_ALIGN_RIGHT: fmt = ":{.cyan}{.magenta}{.yellow} -> {:$>$}"; break; case SP_FMT_ALIGN_NONE: break; } - c8 aligner = sp_fmt_align_to_char(align); - sp_log_a( + + c8 aligner = 0; + switch (align) { + case SP_FMT_ALIGN_LEFT: aligner = '<'; + case SP_FMT_ALIGN_CENTER: aligner = '^'; + case SP_FMT_ALIGN_RIGHT: aligner = '>'; + case SP_FMT_ALIGN_NONE: aligner = 0; + } + sp_log( fmt, sp_fmt_char(fill), sp_fmt_char(aligner), sp_fmt_uint(width), @@ -51,42 +58,42 @@ void specifier(s64 n, c8 fill, sp_fmt_align_t align, u8 width) { s32 run(s32 num_args, const c8** args) { sp_mem_t mem = sp_mem_os_new(); - sp_log_a("{.green} has Zig/Rust style format specifiers (fill, align, width), plus named directives which may:", sp_fmt_cstr("sp.h")); - sp_log_a("- {} text from a format argument", sp_fmt_cstr("render")); - sp_log_a("- {.bold .cyan} the rendered text", sp_fmt_cstr("decorate")); - sp_log_a("- {.upper} the rendered text", sp_fmt_cstr("transform")); + sp_log("{.green} has Zig/Rust style format specifiers (fill, align, width), plus named directives which may:", sp_fmt_cstr("sp.h")); + sp_log("- {} text from a format argument", sp_fmt_cstr("render")); + sp_log("- {.bold .cyan} the rendered text", sp_fmt_cstr("decorate")); + sp_log("- {.upper} the rendered text", sp_fmt_cstr("transform")); section("decorators"); - sp_log_a("{:<14 .italic} -> hello, {.bold}", sp_fmt_cstr("hello, {.bold}"), sp_fmt_cstr("world")); - sp_log_a("{:<14 .italic} -> {.hyperlink}", sp_fmt_cstr("{.hyperlink}"), sp_fmt_cstr("https://spader.zone")); - sp_log_a("{:<14 .italic} -> {.quote}", sp_fmt_cstr("{.quote}"), sp_fmt_cstr("supposedly")); + sp_log("{:<14 .italic} -> hello, {.bold}", sp_fmt_cstr("hello, {.bold}"), sp_fmt_cstr("world")); + sp_log("{:<14 .italic} -> {.hyperlink}", sp_fmt_cstr("{.hyperlink}"), sp_fmt_cstr("https://spader.zone")); + sp_log("{:<14 .italic} -> {.quote}", sp_fmt_cstr("{.quote}"), sp_fmt_cstr("supposedly")); section("transformers"); - sp_log_a(".upper -> {.upper}", sp_fmt_cstr("hello world")); - sp_log_a(".redact -> {.redact}", sp_fmt_cstr("hunter2")); + sp_log(".upper -> {.upper}", sp_fmt_cstr("hello world")); + sp_log(".redact -> {.redact}", sp_fmt_cstr("hunter2")); section(".bytes"); u64 byte_samples[] = { 0ULL, 512ULL, 1536ULL, 10485760ULL, 5368709120ULL }; sp_carr_for(byte_samples, i) { - sp_log_a("{:<10} -> {.bytes}", sp_fmt_uint(byte_samples[i]), sp_fmt_uint(byte_samples[i])); + sp_log("{:<10} -> {.bytes}", sp_fmt_uint(byte_samples[i]), sp_fmt_uint(byte_samples[i])); } section(".iso"); u64 epoch_samples[] = { 0ULL, 1705330245ULL, 1735689599ULL }; sp_carr_for(epoch_samples, i) { - sp_log_a("{:<10} -> {.iso}", sp_fmt_uint(epoch_samples[i]), sp_fmt_uint(epoch_samples[i])); + sp_log("{:<10} -> {.iso}", sp_fmt_uint(epoch_samples[i]), sp_fmt_uint(epoch_samples[i])); } section(".ordinal"); s64 ord_samples[] = { 1, 2, 3, 4, 11, 12, 13, 21, 22, 23, 101, 102, 113 }; sp_carr_for(ord_samples, i) { - sp_log_a("{:<3} -> {.ordinal}", sp_fmt_int(ord_samples[i]), sp_fmt_int(ord_samples[i])); + sp_log("{:<3} -> {.ordinal}", sp_fmt_int(ord_samples[i]), sp_fmt_int(ord_samples[i])); } section(".duration"); u64 dur_samples[] = { 500ULL, 1500ULL, 2500000ULL, 3500000000ULL, 90000000000ULL }; sp_carr_for(dur_samples, i) { - sp_log_a("{:<11} -> {.duration}", sp_fmt_uint(dur_samples[i]), sp_fmt_uint(dur_samples[i])); + sp_log("{:<11} -> {.duration}", sp_fmt_uint(dur_samples[i]), sp_fmt_uint(dur_samples[i])); } section(":specifier"); @@ -100,19 +107,19 @@ s32 run(s32 num_args, const c8** args) { section("composition"); struct { const c8* name; sp_str_t str; } examples [] = { - { ".bold + .upper", sp_fmt_a(mem, "i never {.bold .upper} the kenosha kid", sp_fmt_cstr("did")).value }, - { ".italic + .bold", sp_fmt_a(mem, "i never {.italic .bold} the kenosha kid", sp_fmt_cstr("did")).value }, - { ".quote + .upper", sp_fmt_a(mem, "i never {.quote .upper} the kenosha kid", sp_fmt_cstr("did")).value }, - { ".redact + .bold", sp_fmt_a(mem, "i never {.redact .bold} the kenosha kid", sp_fmt_cstr("did")).value }, - { ".bold + .cyan", sp_fmt_a(mem, "i never {.bold .cyan} the kenosha kid", sp_fmt_cstr("did")).value }, - { "kitchen sink", sp_fmt_a(mem, "i never {.quote .bold .italic .upper .green} the kenosha kid", sp_fmt_cstr("did")).value }, + { ".bold + .upper", sp_fmt(mem, "i never {.bold .upper} the kenosha kid", sp_fmt_cstr("did")).value }, + { ".italic + .bold", sp_fmt(mem, "i never {.italic .bold} the kenosha kid", sp_fmt_cstr("did")).value }, + { ".quote + .upper", sp_fmt(mem, "i never {.quote .upper} the kenosha kid", sp_fmt_cstr("did")).value }, + { ".redact + .bold", sp_fmt(mem, "i never {.redact .bold} the kenosha kid", sp_fmt_cstr("did")).value }, + { ".bold + .cyan", sp_fmt(mem, "i never {.bold .cyan} the kenosha kid", sp_fmt_cstr("did")).value }, + { "kitchen sink", sp_fmt(mem, "i never {.quote .bold .italic .upper .green} the kenosha kid", sp_fmt_cstr("did")).value }, { ".bytes + :specifier", - sp_fmt_a(mem, "{} bytes is [{:^$ .bytes}]", sp_fmt_uint(1536), sp_fmt_uint(12), sp_fmt_uint(1536)).value + sp_fmt(mem, "{} bytes is [{:^$ .bytes}]", sp_fmt_uint(1536), sp_fmt_uint(12), sp_fmt_uint(1536)).value }, { ".duration + .bold", - sp_fmt_a(mem, "{.bold .duration}", sp_fmt_uint(2500000)).value + sp_fmt(mem, "{.bold .duration}", sp_fmt_uint(2500000)).value } }; @@ -122,7 +129,7 @@ s32 run(s32 num_args, const c8** args) { width = sp_max(width, sp_cstr_len(examples[it].name)); } sp_carr_for(examples, it) { - sp_log_a("{:<$} -> {}", sp_fmt_uint(width), sp_fmt_cstr(examples[it].name), sp_fmt_str(examples[it].str)); + sp_log("{:<$} -> {}", sp_fmt_uint(width), sp_fmt_cstr(examples[it].name), sp_fmt_str(examples[it].str)); } section("custom"); @@ -131,10 +138,10 @@ s32 run(s32 num_args, const c8** args) { // In C, you can use the &(foo_t) { 69 } syntax to take the address of a temporary and omit // the variable, which is pretty nice. But it's not possible in C++. #if !defined(SP_CPP) - sp_log_a("{}", sp_fmt_point(((point_t) { 69, 420 }))); - sp_log_a("{}", sp_fmt_point_v(69, 420)); + sp_log("{}", sp_fmt_point(((point_t) { 69, 420 }))); + sp_log("{}", sp_fmt_point_v(69, 420)); #endif - sp_log_a("{.cyan .bold}", sp_fmt_point(point)); + sp_log("{.cyan .bold}", sp_fmt_point(point)); return 0; } diff --git a/example/hash_table.c b/example/hash_table.c index dd5f21f..734496c 100644 --- a/example/hash_table.c +++ b/example/hash_table.c @@ -7,36 +7,36 @@ typedef struct { } ht_key_t; void header(const c8* key, const c8* value) { - sp_log_a("[{.fg brightcyan}] -> {.fg brightgreen}", sp_fmt_cstr(key), sp_fmt_cstr(value)); + sp_log("[{.fg brightcyan}] -> {.fg brightgreen}", sp_fmt_cstr(key), sp_fmt_cstr(value)); } s32 run(s32 num_args, const c8** args) { sp_mem_t a = sp_mem_os_new(); struct { - sp_ht_a(s32, u32) integer; - sp_ht_a(const c8*, u8) cstr; - sp_ht_a(ht_key_t, const c8*) key; + sp_ht(s32, u32) integer; + sp_ht(const c8*, u8) cstr; + sp_ht(ht_key_t, const c8*) key; } hts = sp_zero; - sp_ht_init_a(a, hts.integer); - sp_cstr_ht_init_a(a, hts.cstr); - sp_ht_init_a(a, hts.key); + sp_ht_init(a, hts.integer); + sp_cstr_ht_init(a, hts.cstr); + sp_ht_init(a, hts.key); header("const c8*", "u8"); sp_cstr_ht_insert(hts.cstr, "kram", 8); sp_cstr_ht_insert(hts.cstr, "qux", 69); sp_ht_for_kv(hts.cstr, it) { - sp_log_a("[{}] -> {}", sp_fmt_cstr(*it.key), sp_fmt_uint(*it.val)); + sp_log("[{}] -> {}", sp_fmt_cstr(*it.key), sp_fmt_uint(*it.val)); } - sp_log_a(""); + sp_log(""); header("ht_key_t", "const c8*"); ht_key_t key = { -1, 256 }; sp_ht_insert(hts.key, key, "first"); sp_ht_insert(hts.key, ((ht_key_t) { -69, 256 }), "second"); sp_ht_for_kv(hts.key, it) { - sp_log_a("[{}, {}] -> {} {.fg brightblack}", sp_fmt_int(it.key->foo), sp_fmt_uint(it.key->bar), sp_fmt_cstr(*it.val), sp_fmt_ptr(it.val)); + sp_log("[{}, {}] -> {} {.fg brightblack}", sp_fmt_int(it.key->foo), sp_fmt_uint(it.key->bar), sp_fmt_cstr(*it.val), sp_fmt_ptr(it.val)); } return 0; diff --git a/example/io.c b/example/io.c index 84865f8..291748e 100644 --- a/example/io.c +++ b/example/io.c @@ -7,15 +7,15 @@ s32 run(s32 num_args, const c8** args) { sp_io_file_reader_t r = sp_zero; sp_mem_buffer_t buffer = { - .data = sp_alloc_n_a(mem, u8, 64), + .data = sp_alloc_n(mem, u8, 64), .capacity = 64 }; - sp_str_t exe = sp_fs_get_exe_path_a(mem); + sp_str_t exe = sp_fs_get_exe_path(mem); // sp_io provides utilities for opening a file from a path sp_io_file_reader_from_path(&r, exe); sp_io_read(&r.base, buffer.data, buffer.capacity, &buffer.len); - sp_log_a("sp_io_file_reader_from_path: {}", sp_fmt_str(sp_mem_buffer_as_str(&buffer))); + sp_log("sp_io_file_reader_from_path: {}", sp_fmt_str(sp_mem_buffer_as_str(&buffer))); sp_io_file_reader_close(&r); sp_mem_zero(buffer.data, buffer.capacity); @@ -27,7 +27,7 @@ s32 run(s32 num_args, const c8** args) { sp_sys_fd_t fd = sp_sys_open_s(exe, SP_O_RDONLY | SP_O_BINARY, 0); sp_io_file_reader_from_file(&r, (sp_io_file_t)fd, SP_IO_CLOSE_MODE_AUTO); sp_io_read(&r.base, buffer.data, buffer.capacity, &buffer.len); - sp_log_a("sp_io_file_reader_from_file: {}", sp_fmt_str(sp_mem_buffer_as_str(&buffer))); + sp_log("sp_io_file_reader_from_file: {}", sp_fmt_str(sp_mem_buffer_as_str(&buffer))); sp_io_file_reader_close(&r); @@ -36,15 +36,23 @@ s32 run(s32 num_args, const c8** args) { c8 str [256] = sp_zero; sp_io_mem_writer_t mw = sp_zero; sp_io_mem_writer_from_buffer(&mw, str, 256); - sp_fmt_io(&mw.base, "hello, {}", sp_fmt_cstr("world")); - sp_log_a("sp_io_writer_from_mem: {}", sp_fmt_cstr(str)); + { + sp_mem_arena_marker_t s = sp_mem_begin_scratch(); + sp_fmt_io_a(&mw.base, s.mem, "hello, {}", sp_fmt_cstr("world")); + sp_mem_end_scratch(s); + } + sp_log("sp_io_writer_from_mem: {}", sp_fmt_cstr(str)); sp_mem_zero(str, 256); // You can also format directly to stdout sp_io_file_writer_t fw = sp_zero; sp_io_file_writer_from_fd(&fw, sp_sys_stdout, SP_IO_CLOSE_MODE_NONE); - sp_fmt_io(&fw.base, "hello, {.cyan}", sp_fmt_cstr("stdout")); + { + sp_mem_arena_marker_t s = sp_mem_begin_scratch(); + sp_fmt_io_a(&fw.base, s.mem, "hello, {.cyan}", sp_fmt_cstr("stdout")); + sp_mem_end_scratch(s); + } sp_io_write(&fw.base, "\n", 1, SP_NULLPTR); sp_io_file_writer_close(&fw); @@ -59,11 +67,11 @@ s32 run(s32 num_args, const c8** args) { total += bytes_read; if (err == SP_ERR_IO_EOF) break; if (err != SP_OK) { - sp_log_a("sp_io_read failed: {}", sp_fmt_uint((u32)err)); + sp_log("sp_io_read failed: {}", sp_fmt_uint((u32)err)); break; } } - sp_log_a("sp_io EOF: drained {} bytes", sp_fmt_uint(total)); + sp_log("sp_io EOF: drained {} bytes", sp_fmt_uint(total)); sp_io_file_reader_close(&r); return 0; diff --git a/example/io_copy_perf.c b/example/io_copy_perf.c index 09da59b..4f5ac00 100644 --- a/example/io_copy_perf.c +++ b/example/io_copy_perf.c @@ -23,7 +23,7 @@ static void fill_random(u8* p, u64 n, u64 seed) { } static sp_err_t make_source(sp_str_t path, u64 size_bytes, sp_mem_t mem) { - u8* chunk = sp_alloc_n_a(mem, u8, 1u << 20); + u8* chunk = sp_alloc_n(mem, u8, 1u << 20); fill_random(chunk, 1u << 20, 0xdeadbeefcafebabeull); sp_io_file_writer_t w = sp_zero; @@ -49,7 +49,7 @@ static run_t copy_naive(sp_str_t src, sp_str_t dst, sp_mem_t mem) { sp_io_file_reader_from_path(&r, src); sp_io_file_writer_from_path(&w, dst, SP_IO_WRITE_MODE_OVERWRITE); - u8* buf = sp_alloc_n_a(mem, u8, PERF_NAIVE_BUFFER); + u8* buf = sp_alloc_n(mem, u8, PERF_NAIVE_BUFFER); u64 copied = 0; sp_tm_timer_t t = sp_tm_start_timer(); @@ -86,7 +86,7 @@ static run_t copy_fast(sp_str_t src, sp_str_t dst) { static void report(const c8* label, run_t run) { // MB/s = bytes / 1e6 / (ns / 1e9) = bytes * 1000 / ns. u64 mb_per_s = run.ns ? (run.bytes * 1000u) / run.ns : 0; - sp_log_a( + sp_log( " {}: {} bytes in {} us ({} MB/s)", sp_fmt_cstr(label), sp_fmt_uint(run.bytes), @@ -99,15 +99,15 @@ s32 run(s32 num_args, const c8** args) { (void)num_args; (void)args; sp_mem_t mem = sp_mem_arena_as_allocator(sp_mem_arena_new(sp_mem_os_new())); - sp_str_t cwd = sp_fs_get_cwd_a(mem); - sp_str_t src = sp_str_concat_a(mem, cwd, sp_str_lit("/io_copy_perf.src")); - sp_str_t dst = sp_str_concat_a(mem, cwd, sp_str_lit("/io_copy_perf.dst")); + sp_str_t cwd = sp_fs_get_cwd(mem); + sp_str_t src = sp_str_concat(mem, cwd, sp_str_lit("/io_copy_perf.src")); + sp_str_t dst = sp_str_concat(mem, cwd, sp_str_lit("/io_copy_perf.dst")); u64 size_bytes = (u64)PERF_FILE_SIZE_MB * 1024u * 1024u; - sp_log_a("preparing {} MiB source at {}", sp_fmt_uint(PERF_FILE_SIZE_MB), sp_fmt_str(src)); + sp_log("preparing {} MiB source at {}", sp_fmt_uint(PERF_FILE_SIZE_MB), sp_fmt_str(src)); sp_err_t err = make_source(src, size_bytes, mem); if (err) { - sp_log_a("failed to prepare source: err={}", sp_fmt_uint((u32)err)); + sp_log("failed to prepare source: err={}", sp_fmt_uint((u32)err)); return 1; } @@ -115,7 +115,7 @@ s32 run(s32 num_args, const c8** args) { // naive run typically pays for cold reads of the source; the second runs // against a warm page cache. The fast-path run benefits from a warm cache // too, but the dominant cost it skips is the userspace copy itself. - sp_log_a("running benchmark (two trials each)"); + sp_log("running benchmark (two trials each)"); run_t n1 = copy_naive(src, dst, mem); report("naive #1", n1); run_t f1 = copy_fast (src, dst); report("fast #1", f1); run_t n2 = copy_naive(src, dst, mem); report("naive #2", n2); @@ -123,15 +123,15 @@ s32 run(s32 num_args, const c8** args) { if (f2.ns) { u64 speedup_x100 = (n2.ns * 100u) / f2.ns; - sp_log_a( + sp_log( "warm-cache speedup: {}.{}x (naive #2 / fast #2)", sp_fmt_uint(speedup_x100 / 100u), sp_fmt_uint(speedup_x100 % 100u) ); } - sp_fs_remove_file_a(src); - sp_fs_remove_file_a(dst); + sp_fs_remove_file(src); + sp_fs_remove_file(dst); return 0; } SP_MAIN(run) diff --git a/example/ls.c b/example/ls.c index 5c09c92..acba9e0 100644 --- a/example/ls.c +++ b/example/ls.c @@ -9,21 +9,21 @@ s32 compare_entries(const void* pa, const void* pb) { s32 run(s32 num_args, const c8** args) { sp_mem_t mem = sp_mem_os_new(); - sp_str_t cwd = sp_fs_get_cwd_a(mem); + sp_str_t cwd = sp_fs_get_cwd(mem); sp_str_t dir = cwd; - if (num_args == 2) dir = sp_fs_join_path_a(mem, cwd, sp_str_view(args[1])); + if (num_args == 2) dir = sp_fs_join_path(mem, cwd, sp_str_view(args[1])); - sp_da(sp_fs_entry_t) entries = sp_fs_collect_a(mem, dir); + sp_da(sp_fs_entry_t) entries = sp_fs_collect(mem, dir); sp_da_sort(entries, compare_entries); sp_da_for(entries, it) { sp_fs_entry_t* entry = &entries[it]; switch (entry->kind) { - case SP_FS_KIND_DIR: sp_log_a("{.fg blue}", sp_fmt_str(entry->name)); break; - case SP_FS_KIND_FILE: sp_log_a("{}", sp_fmt_str(entry->name)); break; + case SP_FS_KIND_DIR: sp_log("{.fg blue}", sp_fmt_str(entry->name)); break; + case SP_FS_KIND_FILE: sp_log("{}", sp_fmt_str(entry->name)); break; case SP_FS_KIND_SYMLINK: { - sp_str_t target = sp_fs_canonicalize_path_a(mem, entry->path); - sp_log_a("{.fg cyan} -> {}", sp_fmt_str(entry->name), sp_fmt_str(target)); + sp_str_t target = sp_fs_canonicalize_path(mem, entry->path); + sp_log("{.fg cyan} -> {}", sp_fmt_str(entry->name), sp_fmt_str(target)); break; } case SP_FS_KIND_NONE: break; diff --git a/example/msvc.c b/example/msvc.c index 1f02823..106e465 100644 --- a/example/msvc.c +++ b/example/msvc.c @@ -15,35 +15,35 @@ s32 msvc_main(s32 argc, const c8** argv) { sp_msvc_err_t err = sp_msvc_find(sp_mem_os_new(), SP_MSVC_ARCH_X64, &result); if (err) { - sp_log_a("sp_msvc_find failed: {}", sp_fmt_int(err)); + sp_log("sp_msvc_find failed: {}", sp_fmt_int(err)); return 1; } - sp_log_a("{.fg cyan} Visual Studio installation(s)", sp_fmt_uint(sp_da_size(result.installations))); + sp_log("{.fg cyan} Visual Studio installation(s)", sp_fmt_uint(sp_da_size(result.installations))); sp_da_for(result.installations, i) { sp_msvc_vs_t* vs = &result.installations[i]; - sp_log_a(""); - sp_log_a(" [{.fg yellow}] VS {.fg green}", sp_fmt_uint(i), sp_fmt_str(vs->version.product)); - sp_log_a(" build: {}", sp_fmt_str(vs->version.build.str)); - sp_log_a(" tools: {}", sp_fmt_str(vs->version.tools.str)); - sp_log_a(" path: {}", sp_fmt_str(vs->install_path)); - sp_log_a(" lib: {}", sp_fmt_str(vs->lib)); - sp_log_a(" include: {}", sp_fmt_str(vs->include)); - sp_log_a(" bin: {}", sp_fmt_str(vs->bin)); + sp_log(""); + sp_log(" [{.fg yellow}] VS {.fg green}", sp_fmt_uint(i), sp_fmt_str(vs->version.product)); + sp_log(" build: {}", sp_fmt_str(vs->version.build.str)); + sp_log(" tools: {}", sp_fmt_str(vs->version.tools.str)); + sp_log(" path: {}", sp_fmt_str(vs->install_path)); + sp_log(" lib: {}", sp_fmt_str(vs->lib)); + sp_log(" include: {}", sp_fmt_str(vs->include)); + sp_log(" bin: {}", sp_fmt_str(vs->bin)); } - sp_log_a(""); - sp_log_a("{.fg cyan} Windows SDK(s)", sp_fmt_uint(sp_da_size(result.sdks))); + sp_log(""); + sp_log("{.fg cyan} Windows SDK(s)", sp_fmt_uint(sp_da_size(result.sdks))); sp_da_for(result.sdks, i) { sp_msvc_sdk_t* sdk = &result.sdks[i]; - sp_log_a(""); - sp_log_a(" [{.fg yellow}] SDK {.fg green}", sp_fmt_uint(i), sp_fmt_str(sdk->version.str)); - sp_log_a(" root: {}", sp_fmt_str(sdk->root)); - sp_log_a(" lib_um: {}", sp_fmt_str(sdk->lib_um)); - sp_log_a(" lib_ucrt: {}", sp_fmt_str(sdk->lib_ucrt)); - sp_log_a(" inc_ucrt: {}", sp_fmt_str(sdk->include_ucrt)); - sp_log_a(" inc_um: {}", sp_fmt_str(sdk->include_um)); - sp_log_a(" inc_shared: {}", sp_fmt_str(sdk->include_shared)); + sp_log(""); + sp_log(" [{.fg yellow}] SDK {.fg green}", sp_fmt_uint(i), sp_fmt_str(sdk->version.str)); + sp_log(" root: {}", sp_fmt_str(sdk->root)); + sp_log(" lib_um: {}", sp_fmt_str(sdk->lib_um)); + sp_log(" lib_ucrt: {}", sp_fmt_str(sdk->lib_ucrt)); + sp_log(" inc_ucrt: {}", sp_fmt_str(sdk->include_ucrt)); + sp_log(" inc_um: {}", sp_fmt_str(sdk->include_um)); + sp_log(" inc_shared: {}", sp_fmt_str(sdk->include_shared)); } return 0; diff --git a/example/palette.c b/example/palette.c index 30d58cc..0efbc95 100644 --- a/example/palette.c +++ b/example/palette.c @@ -74,7 +74,7 @@ sp_str_t palette_color_to_ansi_bg(sp_mem_t mem, sp_color_t c) { u8 r = (u8)(c.r * 255.0f); u8 g = (u8)(c.g * 255.0f); u8 b = (u8)(c.b * 255.0f); - return sp_fmt_a(mem, "\033[48;2;{};{};{}m", sp_fmt_uint(r), sp_fmt_uint(g), sp_fmt_uint(b)).value; + return sp_fmt(mem, "\033[48;2;{};{};{}m", sp_fmt_uint(r), sp_fmt_uint(g), sp_fmt_uint(b)).value; } sp_str_t palette_color_to_hex(sp_mem_t mem, sp_color_t c) { @@ -82,7 +82,7 @@ sp_str_t palette_color_to_hex(sp_mem_t mem, sp_color_t c) { u8 g = (u8)(c.g * 255.0f); u8 b = (u8)(c.b * 255.0f); sp_io_dyn_mem_writer_t b_out = sp_zero; - sp_io_dyn_mem_writer_init_a(mem, &b_out); + sp_io_dyn_mem_writer_init(mem, &b_out); sp_io_write_c8(&b_out.base, '#'); for (s32 i = 0; i < 3; i++) { u8 v = (i == 0) ? r : (i == 1) ? g : b; @@ -98,7 +98,7 @@ void palette_render(app_t* app) { app->needs_redraw = false; sp_io_dyn_mem_writer_t out = sp_zero; - sp_io_dyn_mem_writer_init_a(app->mem, &out); + sp_io_dyn_mem_writer_init(app->mem, &out); sp_io_write_cstr(&out.base, "\033[H\033[2J", SP_NULLPTR); @@ -146,7 +146,7 @@ void palette_render(app_t* app) { void palette_print_results(app_t* app) { sp_da_for(app->saved_colors, i) { sp_str_t hex = palette_color_to_hex(app->mem, app->saved_colors[i]); - sp_log_a("{}", sp_fmt_str(hex)); + sp_log("{}", sp_fmt_str(hex)); } } @@ -236,7 +236,7 @@ sp_app_config_t app_main(s32 num_args, const c8** args) { if (num_args > 3) value = sp_parse_s32(sp_str_view(args[3])); sp_mem_t mem = sp_mem_os_new(); - app_t* app = sp_alloc_type_a(mem, app_t); + app_t* app = sp_alloc_type(mem, app_t); app->mem = mem; app->saved_colors = sp_da_new(app->mem, sp_color_t); diff --git a/example/prompt.c b/example/prompt.c index 0066a58..bbc03bd 100644 --- a/example/prompt.c +++ b/example/prompt.c @@ -148,8 +148,8 @@ s32 demo_select_filter(sp_prompt_ctx_t* ctx) { if (sp_prompt_submitted(ctx)) { const c8* selection = sp_prompt_get_str(ctx); - sp_str_t reaction = sp_fmt_a(ctx->mem, "{.quote}, eh? A childish response...", sp_fmt_cstr(selection)).value; - sp_prompt_note(ctx, sp_str_to_cstr_a(ctx->mem, reaction), "Selection"); + sp_str_t reaction = sp_fmt(ctx->mem, "{.quote}, eh? A childish response...", sp_fmt_cstr(selection)).value; + sp_prompt_note(ctx, sp_str_to_cstr(ctx->mem, reaction), "Selection"); } else if (sp_prompt_cancelled(ctx)) { sp_prompt_cancel(ctx, "You got cold feet..."); @@ -378,8 +378,8 @@ s32 prompt_main(s32 argc, const c8** argv) { sp_mem_t mem = sp_mem_os_new(); - sp_ht_a(const c8*, sp_prompt_demo_fn_t) demos = sp_zero; - sp_cstr_ht_init_a(mem, demos); + sp_ht(const c8*, sp_prompt_demo_fn_t) demos = sp_zero; + sp_cstr_ht_init(mem, demos); sp_carr_for(ordered, it) { sp_cstr_ht_insert(demos, ordered[it].name, ordered[it].fn); } @@ -387,10 +387,10 @@ s32 prompt_main(s32 argc, const c8** argv) { if (argc >= 2) { sp_prompt_demo_fn_t* fn = sp_cstr_ht_get(demos, argv[1]); if (!fn) { - sp_log_a("usage: prompt [program]"); - sp_log_a("programs:"); + sp_log("usage: prompt [program]"); + sp_log("programs:"); sp_carr_for(ordered, it) { - sp_log_a(" {}", sp_fmt_cstr(ordered[it].name)); + sp_log(" {}", sp_fmt_cstr(ordered[it].name)); } return SP_PROMPT_ERROR; diff --git a/example/prompt_fancy.c b/example/prompt_fancy.c index 86818e3..8895559 100644 --- a/example/prompt_fancy.c +++ b/example/prompt_fancy.c @@ -380,7 +380,7 @@ static void fancy_publish_render(sp_prompt_ctx_t* ctx) { fancy_render_rail(ctx, sp_str_lit("Build matrix"), fancy_style_ansi(SP_ANSI_FG_BRIGHT_WHITE_U8)); fancy_render_cstr(ctx, "│ ", fancy_style_ansi(SP_ANSI_FG_BRIGHT_BLACK_U8)); fancy_render_bar(ctx, copy.build_progress, 28, fancy_style_rgb(0x55, 0xaa, 0xff)); - fancy_render_str(ctx, sp_fmt_a(ctx->mem, " {}%", sp_fmt_uint((u32)(copy.build_progress * 100.0f))).value, sp_zero_s(sp_prompt_style_t)); + fancy_render_str(ctx, sp_fmt(ctx->mem, " {}%", sp_fmt_uint((u32)(copy.build_progress * 100.0f))).value, sp_zero_s(sp_prompt_style_t)); fancy_nl(ctx); fancy_render_rail(ctx, sp_str_lit(""), sp_zero_s(sp_prompt_style_t)); @@ -403,11 +403,11 @@ static void fancy_publish_render(sp_prompt_ctx_t* ctx) { fancy_render_rail(ctx, sp_str_lit("Uploads"), fancy_style_ansi(SP_ANSI_FG_BRIGHT_WHITE_U8)); fancy_render_cstr(ctx, "│ ", fancy_style_ansi(SP_ANSI_FG_BRIGHT_BLACK_U8)); fancy_render_bar(ctx, copy.upload_progress, 28, fancy_style_rgb(0x9b, 0xdb, 0x8d)); - fancy_render_str(ctx, sp_fmt_a(ctx->mem, " {}%", sp_fmt_uint((u32)(copy.upload_progress * 100.0f))).value, sp_zero_s(sp_prompt_style_t)); + fancy_render_str(ctx, sp_fmt(ctx->mem, " {}%", sp_fmt_uint((u32)(copy.upload_progress * 100.0f))).value, sp_zero_s(sp_prompt_style_t)); fancy_nl(ctx); - fancy_render_str(ctx, sp_fmt_a(ctx->mem, "│ latest: {}", sp_fmt_cstr(copy.latest)).value, fancy_style_ansi(SP_ANSI_FG_BRIGHT_BLACK_U8)); + fancy_render_str(ctx, sp_fmt(ctx->mem, "│ latest: {}", sp_fmt_cstr(copy.latest)).value, fancy_style_ansi(SP_ANSI_FG_BRIGHT_BLACK_U8)); fancy_nl(ctx); - fancy_render_str(ctx, sp_fmt_a(ctx->mem, "│ total: {}%", sp_fmt_uint((u32)(copy.overall_progress * 100.0f))).value, fancy_style_ansi(SP_ANSI_FG_BRIGHT_BLACK_U8)); + fancy_render_str(ctx, sp_fmt(ctx->mem, "│ total: {}%", sp_fmt_uint((u32)(copy.overall_progress * 100.0f))).value, fancy_style_ansi(SP_ANSI_FG_BRIGHT_BLACK_U8)); fancy_nl(ctx); } @@ -504,7 +504,8 @@ static bool fancy_has_arg(s32 argc, const c8** argv, const c8* arg) { static sp_str_t fancy_selected_changelog(sp_mem_t mem, fancy_changelog_item_t* items, u32 num_items) { sp_io_dyn_mem_writer_t builder = sp_zero; - sp_io_dyn_mem_writer_init_a(mem, &builder); + sp_io_dyn_mem_writer_init(mem, &builder); + sp_mem_arena_marker_t s = sp_mem_begin_scratch_for(mem); const c8* section = ""; u64 written = 0; sp_for(it, num_items) { @@ -518,28 +519,31 @@ static sp_str_t fancy_selected_changelog(sp_mem_t mem, fancy_changelog_item_t* i if (written) { sp_io_write_c8(&builder.base, '\n'); } - sp_fmt_io(&builder.base, "[{}]", sp_fmt_cstr(section)); + sp_fmt_io_a(&builder.base, s.mem, "[{}]", sp_fmt_cstr(section)); sp_io_write_c8(&builder.base, '\n'); } - sp_fmt_io(&builder.base, "- {}", sp_fmt_cstr(items[it].text)); + sp_fmt_io_a(&builder.base, s.mem, "- {}", sp_fmt_cstr(items[it].text)); sp_io_write_c8(&builder.base, '\n'); } + sp_mem_end_scratch(s); return sp_io_dyn_mem_writer_as_str(&builder); } static sp_str_t fancy_plan_note(sp_mem_t mem, const c8* kind, const c8* name, const c8* sections) { sp_io_dyn_mem_writer_t builder = sp_zero; - sp_io_dyn_mem_writer_init_a(mem, &builder); - sp_fmt_io(&builder.base, "version 0.13.3 ({})", sp_fmt_cstr(kind)); + sp_io_dyn_mem_writer_init(mem, &builder); + sp_mem_arena_marker_t s = sp_mem_begin_scratch_for(mem); + sp_fmt_io_a(&builder.base, s.mem, "version 0.13.3 ({})", sp_fmt_cstr(kind)); sp_io_write_c8(&builder.base, '\n'); - sp_fmt_io(&builder.base, "name {}", sp_fmt_cstr(name)); + sp_fmt_io_a(&builder.base, s.mem, "name {}", sp_fmt_cstr(name)); sp_io_write_c8(&builder.base, '\n'); sp_io_write_cstr(&builder.base, "tag v0.13.3", SP_NULLPTR); sp_io_write_c8(&builder.base, '\n'); sp_io_write_cstr(&builder.base, "artifacts linux, musl, freestanding, macos, windows", SP_NULLPTR); sp_io_write_c8(&builder.base, '\n'); - sp_fmt_io(&builder.base, "changelog {}", sp_fmt_cstr(sections)); + sp_fmt_io_a(&builder.base, s.mem, "changelog {}", sp_fmt_cstr(sections)); + sp_mem_end_scratch(s); return sp_io_dyn_mem_writer_as_str(&builder); } @@ -547,7 +551,7 @@ s32 fancy_main(s32 argc, const c8** argv) { bool scripted = fancy_has_arg(argc, argv, "--scripted"); sp_prompt_ctx_t* ctx = sp_prompt_begin(sp_mem_os_new()); if (ctx == SP_NULLPTR) { - sp_log_a("failed to initialize prompt"); + sp_log("failed to initialize prompt"); return 1; } @@ -665,7 +669,7 @@ s32 fancy_main(s32 argc, const c8** argv) { const c8* selected_sections = sp_prompt_join_selection(ctx, sections, sp_carr_len(sections)); sp_str_t plan = fancy_plan_note(ctx->mem, release_kind, release_name, selected_sections); - sp_prompt_note(ctx, sp_str_to_cstr_a(ctx->mem, plan), "Plan"); + sp_prompt_note(ctx, sp_str_to_cstr(ctx->mem, plan), "Plan"); if (scripted) { fancy_prime_enter(ctx); @@ -701,7 +705,7 @@ s32 fancy_main(s32 argc, const c8** argv) { } sp_str_t curated = fancy_selected_changelog(ctx->mem, changelog_items, sp_carr_len(changelog_items)); - sp_prompt_note(ctx, sp_str_to_cstr_a(ctx->mem, curated), "Published v0.13.3"); + sp_prompt_note(ctx, sp_str_to_cstr(ctx->mem, curated), "Published v0.13.3"); sp_prompt_success(ctx, "release published"); sp_prompt_outro(ctx, "done"); sp_prompt_end(ctx); diff --git a/example/signal.c b/example/signal.c index 5a117da..32c3815 100644 --- a/example/signal.c +++ b/example/signal.c @@ -2,23 +2,23 @@ #include "sp.h" void handle_interrupt(sp_os_signal_t signal, void* userdata) { - sp_log_a("received signal: {.fg brightcyan}", sp_fmt_int((s32)signal)); + sp_log("received signal: {.fg brightcyan}", sp_fmt_int((s32)signal)); sp_atomic_s32_set((sp_atomic_s32_t*)userdata, 1); } s32 run(s32 num_args, const c8** args) { sp_atomic_s32_t shutdown = sp_zero; - sp_log_a("hello, {.fg brightcyan}", sp_fmt_cstr("world")); + sp_log("hello, {.fg brightcyan}", sp_fmt_cstr("world")); sp_os_register_signal_handler(SP_OS_SIGNAL_INTERRUPT, handle_interrupt, &shutdown); - sp_log_a("handler registered, send SIGINT to test"); + sp_log("handler registered, send SIGINT to test"); /* spin so we can test ctrl+c */ while (!sp_atomic_s32_get(&shutdown)) { sp_sleep_ns(100000000); } - sp_log_a("shutting down"); + sp_log("shutting down"); return 0; } diff --git a/example/wc.c b/example/wc.c index 4e7378d..90b09fa 100644 --- a/example/wc.c +++ b/example/wc.c @@ -3,22 +3,22 @@ s32 run(s32 num_args, const c8** args) { if (num_args < 2) { - sp_log_a("usage: wc {.fg cyan}", sp_fmt_cstr("$file")); + sp_log("usage: wc {.fg cyan}", sp_fmt_cstr("$file")); return 1; } sp_mem_t mem = sp_mem_os_new(); - sp_str_t cwd = sp_fs_get_cwd_a(mem); - sp_str_t path = sp_fs_join_path_a(mem, cwd, sp_str_view(args[1])); + sp_str_t cwd = sp_fs_get_cwd(mem); + sp_str_t path = sp_fs_join_path(mem, cwd, sp_str_view(args[1])); sp_str_t content = sp_zero; - sp_io_read_file_a(mem, path, &content); + sp_io_read_file(mem, path, &content); - sp_ht_a(sp_str_t, u32) counts = sp_zero; - sp_str_ht_init_a(mem, counts); - sp_da(sp_str_t) lines = sp_str_split_c8_a(mem, content, '\n'); + sp_ht(sp_str_t, u32) counts = sp_zero; + sp_str_ht_init(mem, counts); + sp_da(sp_str_t) lines = sp_str_split_c8(mem, content, '\n'); sp_da_for(lines, i) { - sp_da(sp_str_t) words = sp_str_split_c8_a(mem, lines[i], ' '); + sp_da(sp_str_t) words = sp_str_split_c8(mem, lines[i], ' '); sp_da_for(words, j) { u32* count = sp_str_ht_get(counts, words[j]); @@ -31,7 +31,7 @@ s32 run(s32 num_args, const c8** args) { } sp_str_ht_for_kv(counts, it) { - sp_log_a("{} {}", sp_fmt_uint(*it.val), sp_fmt_str(*it.key)); + sp_log("{} {}", sp_fmt_uint(*it.val), sp_fmt_str(*it.key)); } return 0; } diff --git a/sp.h b/sp.h index 70c054d..905ae4f 100644 --- a/sp.h +++ b/sp.h @@ -394,7 +394,7 @@ #define SP_ASSERT(condition) sp_assert((condition)) #define sp_assert(x) sp_assert_f(sp_str_lit(__FILE__), sp_str_lit(SP_MACRO_STR(__LINE__)), sp_cstr_as_str(__func__), sp_str_lit(#x), (bool)(x)) -#define sp_fatal(FMT, ...) do { sp_log_a((FMT), ##__VA_ARGS__); sp_assert(false); } while (0) +#define sp_fatal(FMT, ...) do { sp_log((FMT), ##__VA_ARGS__); sp_assert(false); } while (0) #define SP_UNREACHABLE() SP_ASSERT(false) #define sp_unreachable() SP_UNREACHABLE() @@ -1249,6 +1249,7 @@ void* sp_sys_memmove(void* dest, const void* src, u64 n); void* sp_sys_memset(void* dest, u8 fill, u64 n); s32 sp_sys_memcmp(const void* a, const void* b, u64 n); void sp_sys_assert(bool cond); +void sp_sys_exit(s32 code); sp_sys_fd_t sp_sys_open_s(sp_str_t path, s32 flags, s32 mode); s32 sp_sys_stat_s(sp_str_t path, sp_sys_stat_t* st); s32 sp_sys_lstat_s(sp_str_t path, sp_sys_stat_t* st); @@ -1477,9 +1478,9 @@ SP_API void* sp_mem_os_on_alloc(void* ud, sp_mem_alloc_mode_t mo SP_API sp_mem_os_header_t* sp_mem_os_get_header(void* ptr); SP_API sp_mem_t sp_mem_os_new(); SP_API sp_mem_t sp_mem_arena_as_allocator(sp_mem_arena_t* arena); -SP_API void* sp_alloc_a(sp_mem_t mem, u64 size); -SP_API void* sp_realloc_a(sp_mem_t mem, void* memory, u64 size); -SP_API void sp_free_a(sp_mem_t mem, void* memory); +SP_API void* sp_alloc(sp_mem_t mem, u64 size); +SP_API void* sp_realloc(sp_mem_t mem, void* memory, u64 size); +SP_API void sp_free(sp_mem_t mem, void* memory); SP_API sp_mem_arena_t* sp_mem_arena_new(sp_mem_t mem); SP_API sp_mem_arena_t* sp_mem_arena_new_ex(sp_mem_t mem, u64 block_size, sp_mem_arena_mode_t mode, u8 alignment); SP_API void sp_mem_arena_clear(sp_mem_arena_t* arena); @@ -1511,8 +1512,8 @@ SP_API void sp_mem_end_scratch(sp_mem_arena_marker_t marker); #define sp_mem_allocator_alloc_type(a, T) sp_mem_allocator_alloc_n(a, T, 1) #define sp_mem_arena_alloc_n(a, T, n) (T*)sp_mem_arena_alloc((a), (n) * sizeof(T)) #define sp_mem_arena_alloc_type(a, T) sp_mem_arena_alloc_n(a, T, 1) -#define sp_alloc_n_a(a, T, n) (T*)sp_alloc_a(a, (n) * sizeof(T)) -#define sp_alloc_type_a(a, T) sp_alloc_n_a(a, T, 1) +#define sp_alloc_n(a, T, n) (T*)sp_alloc(a, (n) * sizeof(T)) +#define sp_alloc_type(a, T) sp_alloc_n(a, T, 1) typedef struct { @@ -1866,13 +1867,13 @@ typedef struct { #if defined(SP_CPP) SP_END_EXTERN_C() -template static T* sp_ht_alloc_type_a(sp_mem_t a, T* key, size_t size) { +template static T* sp_ht_alloc_type(sp_mem_t a, T* key, size_t size) { (void)key; return (T*)sp_mem_allocator_alloc(a, size); } SP_BEGIN_EXTERN_C() #else -#define sp_ht_alloc_type_a(a, key, size) sp_mem_allocator_alloc((a), (size)) +#define sp_ht_alloc_type(a, key, size) sp_mem_allocator_alloc((a), (size)) #endif #define __sp_ht_entry(__K, __V) \ @@ -1895,7 +1896,7 @@ SP_BEGIN_EXTERN_C() #define sp_ht_ex(__K, __V, __tag) \ struct __tag sp_ht_s(__K, __V)* -#define sp_ht_a(__K, __V) \ +#define sp_ht(__K, __V) \ struct sp_ht_s(__K, __V)* #define sp_ht_new(__K, __V) SP_NULLPTR @@ -1943,10 +1944,10 @@ SP_BEGIN_EXTERN_C() #define sp_ht_as_u8(ht) ((u8*)(ht)) #define sp_ht_field_offset_u8(ht, field) (sp_ht_field_as_u8(ht, field) - sp_ht_as_u8(ht)) -#define sp_ht_init_a(mem, ht) \ +#define sp_ht_init(mem, ht) \ do { \ - (ht) = sp_ht_alloc_type_a((mem), ht, sizeof(*(ht))); \ - (ht)->data = sp_ht_alloc_type_a((mem), (ht)->data, 2 * sizeof((ht)->data[0])); \ + (ht) = sp_ht_alloc_type((mem), ht, sizeof(*(ht))); \ + (ht)->data = sp_ht_alloc_type((mem), (ht)->data, 2 * sizeof((ht)->data[0])); \ (ht)->info.allocator = (mem); \ (ht)->size = 0; \ (ht)->capacity = 2; \ @@ -2062,9 +2063,9 @@ SP_BEGIN_EXTERN_C() #define sp_str_ht_ensure(ht) \ if (!(ht)) sp_str_ht_init(ht) -#define sp_str_ht_init_a(__a, ht) \ +#define sp_str_ht_init(__a, ht) \ do { \ - sp_ht_init_a((__a), ht); \ + sp_ht_init((__a), ht); \ (ht)->info.fn.hash = sp_ht_on_hash_str_key; \ (ht)->info.fn.compare = sp_ht_on_compare_str_key; \ } while (0) @@ -2097,9 +2098,9 @@ SP_BEGIN_EXTERN_C() #define sp_cstr_ht_ensure(ht) \ if (!(ht)) sp_cstr_ht_init(ht) -#define sp_cstr_ht_init_a(__a, ht) \ +#define sp_cstr_ht_init(__a, ht) \ do { \ - sp_ht_init_a((__a), ht); \ + sp_ht_init((__a), ht); \ (ht)->info.fn.hash = sp_ht_on_hash_cstr_key; \ (ht)->info.fn.compare = sp_ht_on_compare_cstr_key; \ } while (0) @@ -2204,18 +2205,18 @@ SP_TYPEDEF_FN(void, sp_str_reduce_fn_t, sp_str_reduce_context_t* context); #define SP_STR_NO_MATCH -1 SP_API sp_str_t sp_str(const c8* str, u32 len); -SP_API c8* sp_str_to_cstr_a(sp_mem_t mem, sp_str_t str); -SP_API sp_str_t sp_str_copy_a(sp_mem_t mem, sp_str_t str); -SP_API sp_str_t sp_str_concat_a(sp_mem_t mem, sp_str_t a, sp_str_t b); -SP_API sp_str_t sp_str_join_a(sp_mem_t mem, sp_str_t a, sp_str_t b, sp_str_t join); -SP_API sp_str_t sp_str_join_n_a(sp_mem_t mem, sp_str_t* strs, u32 n, sp_str_t joiner); -SP_API sp_str_t sp_str_reduce_a(sp_mem_t mem, sp_str_t* strs, u32 n, void* ud, sp_str_reduce_fn_t fn); -SP_API void sp_str_reduce_kernel_join_a(sp_str_reduce_context_t* context); -SP_API c8* sp_cstr_from_str_a(sp_mem_t mem, sp_str_t str); +SP_API c8* sp_str_to_cstr(sp_mem_t mem, sp_str_t str); +SP_API sp_str_t sp_str_copy(sp_mem_t mem, sp_str_t str); +SP_API sp_str_t sp_str_concat(sp_mem_t mem, sp_str_t a, sp_str_t b); +SP_API sp_str_t sp_str_join(sp_mem_t mem, sp_str_t a, sp_str_t b, sp_str_t join); +SP_API sp_str_t sp_str_join_n(sp_mem_t mem, sp_str_t* strs, u32 n, sp_str_t joiner); +SP_API sp_str_t sp_str_reduce(sp_mem_t mem, sp_str_t* strs, u32 n, void* ud, sp_str_reduce_fn_t fn); +SP_API void sp_str_reduce_kernel_join(sp_str_reduce_context_t* context); +SP_API c8* sp_cstr_from_str(sp_mem_t mem, sp_str_t str); SP_API void sp_str_copy_to(sp_str_t str, c8* buffer, u32 capacity); -SP_API sp_str_t sp_str_from_cstr_a(sp_mem_t mem, const c8* str); -SP_API sp_str_t sp_str_from_cstr_n_a(sp_mem_t mem, const c8* str, u32 len); -SP_API sp_str_t sp_str_alloc_a(sp_mem_t mem, u32 capacity); +SP_API sp_str_t sp_str_from_cstr(sp_mem_t mem, const c8* str); +SP_API sp_str_t sp_str_from_cstr_n(sp_mem_t mem, const c8* str, u32 len); +SP_API sp_str_t sp_str_alloc(sp_mem_t mem, u32 capacity); SP_API sp_str_t sp_str_view(const c8* cstr); SP_API bool sp_str_empty(sp_str_t); SP_API bool sp_str_equal(sp_str_t a, sp_str_t b); @@ -2235,24 +2236,24 @@ SP_API sp_str_t sp_str_prefix(sp_str_t str, s32 len); SP_API sp_str_t sp_str_suffix(sp_str_t str, s32 len); SP_API sp_str_t sp_str_sub(sp_str_t str, s32 index, s32 len); SP_API sp_str_t sp_str_sub_reverse(sp_str_t str, s32 index, s32 len); -SP_API sp_str_t sp_str_replace_c8_a(sp_mem_t mem, sp_str_t str, c8 from, c8 to); -SP_API sp_str_t sp_str_pad_a(sp_mem_t mem, sp_str_t str, u32 n); +SP_API sp_str_t sp_str_replace_c8(sp_mem_t mem, sp_str_t str, c8 from, c8 to); +SP_API sp_str_t sp_str_pad(sp_mem_t mem, sp_str_t str, u32 n); SP_API sp_str_t sp_str_trim_left(sp_str_t str); SP_API sp_str_t sp_str_trim_right(sp_str_t str); SP_API sp_str_t sp_str_trim(sp_str_t str); SP_API sp_str_t sp_str_strip_left(sp_str_t str, sp_str_t strip); SP_API sp_str_t sp_str_strip_right(sp_str_t str, sp_str_t strip); SP_API sp_str_t sp_str_strip(sp_str_t str, sp_str_t strip); -SP_API sp_str_t sp_str_truncate_a(sp_mem_t mem, sp_str_t str, u32 n, sp_str_t trailer); -SP_API sp_str_t sp_str_join_cstr_n_a(sp_mem_t mem, const c8** strings, u32 num_strings, sp_str_t join); -SP_API sp_str_t sp_str_to_upper_a(sp_mem_t mem, sp_str_t str); -SP_API sp_str_t sp_str_to_lower_a(sp_mem_t mem, sp_str_t str); -SP_API sp_str_t sp_str_to_pascal_case_a(sp_mem_t mem, sp_str_t str); +SP_API sp_str_t sp_str_truncate(sp_mem_t mem, sp_str_t str, u32 n, sp_str_t trailer); +SP_API sp_str_t sp_str_join_cstr_n(sp_mem_t mem, const c8** strings, u32 num_strings, sp_str_t join); +SP_API sp_str_t sp_str_to_upper(sp_mem_t mem, sp_str_t str); +SP_API sp_str_t sp_str_to_lower(sp_mem_t mem, sp_str_t str); +SP_API sp_str_t sp_str_to_pascal_case(sp_mem_t mem, sp_str_t str); SP_API sp_str_pair_t sp_str_cleave_c8(sp_str_t str, c8 delimiter); -SP_API sp_da(sp_str_t) sp_str_split_c8_a(sp_mem_t mem, sp_str_t str, c8 c); -SP_API sp_da(sp_str_t) sp_str_pad_to_longest_a(sp_mem_t mem, sp_str_t* strs, u32 n); -SP_API c8* sp_cstr_copy_a(sp_mem_t mem, const c8* cstr); -SP_API c8* sp_cstr_copy_n_a(sp_mem_t mem, const c8* str, u32 len); +SP_API sp_da(sp_str_t) sp_str_split_c8(sp_mem_t mem, sp_str_t str, c8 c); +SP_API sp_da(sp_str_t) sp_str_pad_to_longest(sp_mem_t mem, sp_str_t* strs, u32 n); +SP_API c8* sp_cstr_copy(sp_mem_t mem, const c8* cstr); +SP_API c8* sp_cstr_copy_n(sp_mem_t mem, const c8* str, u32 len); SP_API void sp_cstr_copy_to(const c8* str, c8* buffer, u32 buffer_len); SP_API void sp_cstr_copy_to_n(const c8* str, u32 len, c8* buffer, u32 buffer_len); SP_API bool sp_cstr_equal(const c8* a, const c8* b); @@ -2280,11 +2281,11 @@ SP_API u32 sp_utf8_to_lower(u32 codepoint); SP_API u32 sp_utf8_num_codepoints(sp_str_t str); SP_API sp_wide_str_t sp_wide_str(const u16* str, u32 len); SP_API bool sp_wtf8_validate(sp_str_t str); -SP_API sp_wide_str_t sp_wtf8_to_wtf16_a(sp_mem_t mem, sp_str_t wtf8); -SP_API sp_str_t sp_wtf16_to_wtf8_a(sp_mem_t mem, sp_wide_str_t wtf16); +SP_API sp_wide_str_t sp_wtf8_to_wtf16(sp_mem_t mem, sp_str_t wtf8); +SP_API sp_str_t sp_wtf16_to_wtf8(sp_mem_t mem, sp_wide_str_t wtf16); SP_API c8 sp_c8_to_upper(c8 c); SP_API c8 sp_c8_to_lower(c8 c); -SP_API sp_da(sp_str_t) sp_str_map_a(sp_mem_t mem, sp_str_t* s, u32 n, void* ud, sp_str_map_fn_t fn); +SP_API sp_da(sp_str_t) sp_str_map(sp_mem_t mem, sp_str_t* s, u32 n, void* ud, sp_str_map_fn_t fn); SP_API sp_str_t sp_str_map_kernel_prepend(sp_str_map_context_t* context); SP_API sp_str_t sp_str_map_kernel_append(sp_str_map_context_t* context); SP_API sp_str_t sp_str_map_kernel_prefix(sp_str_map_context_t* context); @@ -2306,12 +2307,12 @@ SP_API s32 sp_str_sort_kernel_alphabetical(const void* a, const void // ███████████ ░░░███████░ ░░█████████ // â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘ // @log -SP_API void sp_log_a(const c8* fmt, ...); -SP_API void sp_log_str_a(sp_str_t fmt, ...); -SP_API void sp_log_err_a(const c8* fmt, ...); -SP_API void sp_print_a(const c8* fmt, ...); -SP_API void sp_print_str_a(sp_str_t fmt, ...); -SP_API void sp_print_err_a(const c8* fmt, ...); +SP_API void sp_log(const c8* fmt, ...); +SP_API void sp_log_str(sp_str_t fmt, ...); +SP_API void sp_log_err(const c8* fmt, ...); +SP_API void sp_print(const c8* fmt, ...); +SP_API void sp_print_str(sp_str_t fmt, ...); +SP_API void sp_print_err(const c8* fmt, ...); // ██████████ ██████ █████ █████ █████ @@ -2328,7 +2329,7 @@ typedef struct { sp_str_t value; } sp_env_var_t; -typedef sp_ht_a(sp_str_t, sp_str_t) sp_env_table_t; +typedef sp_ht(sp_str_t, sp_str_t) sp_env_table_t; typedef struct { sp_mem_t mem; @@ -2443,7 +2444,7 @@ SP_API f64 sp_tm_us_to_ns_f(f64 us); SP_API f64 sp_tm_ns_to_s_f(f64 ns); SP_API f64 sp_tm_ns_to_ms_f(f64 ns); SP_API f64 sp_tm_ns_to_us_f(f64 ns); -SP_API sp_str_t sp_tm_epoch_to_iso8601_a(sp_mem_t mem, sp_tm_epoch_t time); +SP_API sp_str_t sp_tm_epoch_to_iso8601(sp_mem_t mem, sp_tm_epoch_t time); // ███████████ █████ █████ ██████████ █████████ █████ █████ █████████ ███████████ ██████████ ██████ ██████ @@ -2489,9 +2490,9 @@ typedef struct { } sp_fs_it_t; #define sp_fs_for(mem, dir, it) \ - for (sp_fs_it_t it = sp_fs_it_new_a(mem, dir); sp_fs_it_valid_a(&it); sp_fs_it_next_a(&it)) + for (sp_fs_it_t it = sp_fs_it_new(mem, dir); sp_fs_it_valid(&it); sp_fs_it_next(&it)) #define sp_fs_for_recursive(mem, dir, it) \ - for (sp_fs_it_t it = sp_fs_it_new_recursive_a(mem, dir); sp_fs_it_valid_a(&it); sp_fs_it_next_a(&it)) + for (sp_fs_it_t it = sp_fs_it_new_recursive(mem, dir); sp_fs_it_valid(&it); sp_fs_it_next(&it)) SP_API sp_str_t sp_fs_get_name(sp_str_t path); SP_API sp_str_t sp_fs_parent_path(sp_str_t path); @@ -2500,46 +2501,46 @@ SP_API sp_str_t sp_fs_get_ext(sp_str_t path); SP_API sp_str_t sp_fs_get_stem(sp_str_t path); SP_API bool sp_fs_is_root(sp_str_t path); SP_API bool sp_fs_is_glob(sp_str_t path); -SP_API sp_str_t sp_fs_normalize_path_a(sp_mem_t mem, sp_str_t path); -SP_API sp_str_t sp_fs_join_path_a(sp_mem_t mem, sp_str_t a, sp_str_t b); -SP_API sp_str_t sp_fs_replace_ext_a(sp_mem_t mem, sp_str_t path, sp_str_t ext); -SP_API sp_str_t sp_fs_canonicalize_path_a(sp_mem_t mem, sp_str_t path); -SP_API sp_str_t sp_fs_get_exe_path_a(sp_mem_t mem); -SP_API sp_str_t sp_fs_get_cwd_a(sp_mem_t mem); -SP_API sp_str_t sp_fs_get_storage_path_a(sp_mem_t mem); -SP_API sp_str_t sp_fs_get_config_path_a(sp_mem_t mem); -SP_API sp_da(sp_fs_entry_t) sp_fs_collect_a(sp_mem_t mem, sp_str_t path); -SP_API sp_da(sp_fs_entry_t) sp_fs_collect_recursive_a(sp_mem_t mem, sp_str_t path); -SP_API bool sp_fs_exists_a(sp_str_t path); -SP_API bool sp_fs_is_file_a(sp_str_t path); -SP_API bool sp_fs_is_dir_a(sp_str_t path); -SP_API bool sp_fs_is_symlink_a(sp_str_t path); -SP_API bool sp_fs_is_target_file_a(sp_str_t path); -SP_API bool sp_fs_is_target_dir_a(sp_str_t path); -SP_API sp_fs_kind_t sp_fs_get_kind_a(sp_str_t path); -SP_API sp_fs_kind_t sp_fs_get_target_kind_a(sp_str_t path); -SP_API sp_tm_epoch_t sp_fs_get_mod_time_a(sp_str_t path); -SP_API sp_err_t sp_fs_create_dir_a(sp_str_t path); -SP_API sp_err_t sp_fs_create_file_a(sp_str_t path); -SP_API sp_err_t sp_fs_create_file_str_a(sp_str_t path, sp_str_t str); -SP_API sp_err_t sp_fs_create_file_slice_a(sp_str_t path, sp_mem_slice_t slice); -SP_API sp_err_t sp_fs_create_file_cstr_a(sp_str_t path, const c8* str); -SP_API sp_err_t sp_fs_remove_dir_a(sp_str_t path); -SP_API sp_err_t sp_fs_remove_file_a(sp_str_t path); -SP_API sp_err_t sp_fs_create_hard_link_a(sp_str_t target, sp_str_t link_path); -SP_API sp_err_t sp_fs_create_sym_link_a(sp_str_t target, sp_str_t link_path); -SP_API sp_err_t sp_fs_link_a(sp_str_t from, sp_str_t to, sp_fs_link_kind_t kind); -SP_API sp_err_t sp_fs_copy_a(sp_str_t from, sp_str_t to); -SP_API void sp_fs_copy_file_a(sp_str_t from, sp_str_t to); -SP_API void sp_fs_copy_dir_a(sp_str_t from, sp_str_t to); -SP_API void sp_fs_copy_glob_a(sp_str_t from, sp_str_t glob, sp_str_t to); -SP_IMP sp_fs_it_t sp_fs_it_new_a(sp_mem_t mem, sp_str_t path); -SP_IMP sp_fs_it_t sp_fs_it_new_recursive_a(sp_mem_t mem, sp_str_t path); -SP_IMP void sp_fs_it_begin_a(sp_fs_it_t* it, sp_str_t path); -SP_IMP void sp_fs_it_next_a(sp_fs_it_t* it); -SP_IMP void sp_fs_it_push_a(sp_fs_it_t* it, sp_str_t path); -SP_IMP bool sp_fs_it_valid_a(sp_fs_it_t* it); -SP_IMP void sp_fs_it_deinit_a(sp_fs_it_t* it); +SP_API sp_str_t sp_fs_normalize_path(sp_mem_t mem, sp_str_t path); +SP_API sp_str_t sp_fs_join_path(sp_mem_t mem, sp_str_t a, sp_str_t b); +SP_API sp_str_t sp_fs_replace_ext(sp_mem_t mem, sp_str_t path, sp_str_t ext); +SP_API sp_str_t sp_fs_canonicalize_path(sp_mem_t mem, sp_str_t path); +SP_API sp_str_t sp_fs_get_exe_path(sp_mem_t mem); +SP_API sp_str_t sp_fs_get_cwd(sp_mem_t mem); +SP_API sp_str_t sp_fs_get_storage_path(sp_mem_t mem); +SP_API sp_str_t sp_fs_get_config_path(sp_mem_t mem); +SP_API sp_da(sp_fs_entry_t) sp_fs_collect(sp_mem_t mem, sp_str_t path); +SP_API sp_da(sp_fs_entry_t) sp_fs_collect_recursive(sp_mem_t mem, sp_str_t path); +SP_API bool sp_fs_exists(sp_str_t path); +SP_API bool sp_fs_is_file(sp_str_t path); +SP_API bool sp_fs_is_dir(sp_str_t path); +SP_API bool sp_fs_is_symlink(sp_str_t path); +SP_API bool sp_fs_is_target_file(sp_str_t path); +SP_API bool sp_fs_is_target_dir(sp_str_t path); +SP_API sp_fs_kind_t sp_fs_get_kind(sp_str_t path); +SP_API sp_fs_kind_t sp_fs_get_target_kind(sp_str_t path); +SP_API sp_tm_epoch_t sp_fs_get_mod_time(sp_str_t path); +SP_API sp_err_t sp_fs_create_dir(sp_str_t path); +SP_API sp_err_t sp_fs_create_file(sp_str_t path); +SP_API sp_err_t sp_fs_create_file_str(sp_str_t path, sp_str_t str); +SP_API sp_err_t sp_fs_create_file_slice(sp_str_t path, sp_mem_slice_t slice); +SP_API sp_err_t sp_fs_create_file_cstr(sp_str_t path, const c8* str); +SP_API sp_err_t sp_fs_remove_dir(sp_str_t path); +SP_API sp_err_t sp_fs_remove_file(sp_str_t path); +SP_API sp_err_t sp_fs_create_hard_link(sp_str_t target, sp_str_t link_path); +SP_API sp_err_t sp_fs_create_sym_link(sp_str_t target, sp_str_t link_path); +SP_API sp_err_t sp_fs_link(sp_str_t from, sp_str_t to, sp_fs_link_kind_t kind); +SP_API sp_err_t sp_fs_copy(sp_str_t from, sp_str_t to); +SP_API void sp_fs_copy_file(sp_str_t from, sp_str_t to); +SP_API void sp_fs_copy_dir(sp_str_t from, sp_str_t to); +SP_API void sp_fs_copy_glob(sp_str_t from, sp_str_t glob, sp_str_t to); +SP_IMP sp_fs_it_t sp_fs_it_new(sp_mem_t mem, sp_str_t path); +SP_IMP sp_fs_it_t sp_fs_it_new_recursive(sp_mem_t mem, sp_str_t path); +SP_IMP void sp_fs_it_begin(sp_fs_it_t* it, sp_str_t path); +SP_IMP void sp_fs_it_next(sp_fs_it_t* it); +SP_IMP void sp_fs_it_push(sp_fs_it_t* it, sp_str_t path); +SP_IMP bool sp_fs_it_valid(sp_fs_it_t* it); +SP_IMP void sp_fs_it_deinit(sp_fs_it_t* it); // ███████████ █████ █████ ███████████ ██████████ █████████ ██████████ █████ ██████ █████ █████████ @@ -2703,7 +2704,7 @@ SP_API sp_err_t sp_os_create_file(sp_str_t path); SP_API sp_err_t sp_os_create_dir(sp_str_t path); SP_API sp_err_t sp_os_create_hard_link(sp_str_t target, sp_str_t link_path); SP_API sp_err_t sp_os_create_sym_link(sp_str_t target, sp_str_t link_path); -SP_API sp_str_t sp_os_get_cwd_a(sp_mem_t mem); +SP_API sp_str_t sp_os_get_cwd(sp_mem_t mem); SP_API void sp_os_register_signal_handler(sp_os_signal_t, sp_os_signal_handler_t, void* userdata); SP_API bool sp_os_is_tty(sp_sys_fd_t fd); SP_API void sp_os_tty_size(sp_sys_fd_t fd, u32* cols, u32* rows); @@ -2875,11 +2876,10 @@ typedef struct { void sp_fmt_directive_register(const c8* name, sp_fmt_directive_t directive); -sp_err_t sp_fmt_io(sp_io_writer_t* io, const c8* fmt, ...); sp_err_t sp_fmt_io_a(sp_io_writer_t* io, sp_mem_t mem, const c8* fmt, ...); -SP_API sp_str_r sp_fmt_a(sp_mem_t mem, const c8* fmt, ...); -SP_API sp_err_t sp_fmt_v_a(sp_io_writer_t* io, sp_mem_t mem, sp_str_t fmt, va_list args); -SP_API void sp_fmt_render_default_a(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param); +SP_API sp_str_r sp_fmt(sp_mem_t mem, const c8* fmt, ...); +SP_API sp_err_t sp_fmt_v(sp_io_writer_t* io, sp_mem_t mem, sp_str_t fmt, va_list args); +SP_API void sp_fmt_render_default(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param); SP_API u8 sp_parse_u8(sp_str_t str); SP_API u16 sp_parse_u16(sp_str_t str); @@ -2963,7 +2963,7 @@ typedef struct { sp_mem_arena_t* scratch [2]; sp_env_t env; struct { - sp_ht_a(sp_str_t, sp_fmt_directive_t) directives; + sp_ht(sp_str_t, sp_fmt_directive_t) directives; } format; struct { sp_io_file_writer_t* out; @@ -3151,7 +3151,7 @@ SP_API sp_err_t sp_io_copy(sp_io_writer_t* dst, sp_io_reader_t* src, u64* SP_API sp_err_t sp_io_copy_b(sp_io_writer_t* dst, sp_io_reader_t* src, u8* buffer, u64 n, u64* bytes_copied); SP_API sp_err_t sp_io_read(sp_io_reader_t* reader, void* ptr, u64 size, u64* bytes_read); -SP_API sp_err_t sp_io_read_file_a(sp_mem_t mem, sp_str_t path, sp_str_t* content); +SP_API sp_err_t sp_io_read_file(sp_mem_t mem, sp_str_t path, sp_str_t* content); SP_API void sp_io_reader_from_mem(sp_io_reader_t* reader, const void* ptr, u64 size); SP_API void sp_io_reader_set_buffer(sp_io_reader_t* reader, u8* buf, u64 capacity); @@ -3188,7 +3188,7 @@ SP_API sp_err_t sp_io_mem_writer_seek(sp_io_mem_writer_t* w, s64 offset, s SP_API sp_err_t sp_io_mem_writer_size(sp_io_mem_writer_t* w, u64* size); SP_API sp_str_t sp_io_mem_writer_as_str(sp_io_mem_writer_t* w); -SP_API void sp_io_dyn_mem_writer_init_a(sp_mem_t mem, sp_io_dyn_mem_writer_t* w); +SP_API void sp_io_dyn_mem_writer_init(sp_mem_t mem, sp_io_dyn_mem_writer_t* w); SP_API sp_err_t sp_io_dyn_mem_writer_seek(sp_io_dyn_mem_writer_t* w, s64 offset, sp_io_whence_t whence, s64* position); SP_API sp_err_t sp_io_dyn_mem_writer_size(sp_io_dyn_mem_writer_t* w, u64* size); SP_API sp_err_t sp_io_dyn_mem_writer_close(sp_io_dyn_mem_writer_t* w); @@ -3312,10 +3312,10 @@ typedef struct { sp_mem_t mem; } sp_ps_t; -SP_API sp_ps_config_t sp_ps_config_copy_a(sp_mem_t mem, const sp_ps_config_t* src); -SP_API void sp_ps_config_add_arg_a(sp_mem_t mem, sp_ps_config_t* config, sp_str_t arg); -SP_API sp_ps_t sp_ps_create_a(sp_mem_t mem, sp_ps_config_t config); -SP_API sp_ps_output_t sp_ps_run_a(sp_mem_t mem, sp_ps_config_t config); +SP_API sp_ps_config_t sp_ps_config_copy(sp_mem_t mem, const sp_ps_config_t* src); +SP_API void sp_ps_config_add_arg(sp_mem_t mem, sp_ps_config_t* config, sp_str_t arg); +SP_API sp_ps_t sp_ps_create(sp_mem_t mem, sp_ps_config_t config); +SP_API sp_ps_output_t sp_ps_run(sp_mem_t mem, sp_ps_config_t config); SP_API sp_io_file_writer_t* sp_ps_io_in(sp_ps_t* ps); SP_API sp_io_reader_t* sp_ps_io_out(sp_ps_t* ps); SP_API sp_io_reader_t* sp_ps_io_err(sp_ps_t* ps); @@ -3443,7 +3443,7 @@ struct sp_fmon { sp_mem_t mem; }; -SP_API void sp_fmon_init_a(sp_mem_t mem, sp_fmon_t* m, sp_fmon_fn_t fn, sp_fmon_event_kind_t events, void* user_data); +SP_API void sp_fmon_init(sp_mem_t mem, sp_fmon_t* m, sp_fmon_fn_t fn, sp_fmon_event_kind_t events, void* user_data); SP_API void sp_fmon_deinit(sp_fmon_t* monitor); SP_API void sp_fmon_add_dir(sp_fmon_t* monitor, sp_str_t path); SP_API void sp_fmon_add_file(sp_fmon_t* monitor, sp_str_t file_path); @@ -3468,17 +3468,16 @@ typedef struct { } sp_fmt_parser_t; SP_IMP sp_fmt_directive_t* sp_fmt_directive_lookup(sp_str_t name); -SP_IMP void sp_fmt_directive_reset(); SP_IMP void sp_fmt_register_builtins(); SP_IMP c8* sp_fmt_uint_to_buf_dec(u64 value, c8* buf_end); SP_IMP c8* sp_fmt_uint_to_buf_hex_ex(u64 value, c8* buf_end, const c8* digits); SP_IMP c8* sp_fmt_uint_to_buf_hex(u64 value, c8* buf_end); -SP_IMP void sp_fmt_write_u64_a(sp_io_writer_t* io, u64 value); -SP_IMP void sp_fmt_write_s64_a(sp_io_writer_t* io, s64 value); -SP_IMP void sp_fmt_write_f64_a(sp_io_writer_t* io, f64 value, u32 precision); -SP_IMP void sp_fmt_write_ptr_a(sp_io_writer_t* io, void* value); -SP_IMP sp_err_t sp_fmt_render_a(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params); -SP_IMP void sp_fmt_apply_spec_a(sp_io_writer_t* io, sp_str_t pre, sp_str_t str, sp_str_t post, sp_fmt_spec_t spec); +SP_IMP void sp_fmt_write_u64(sp_io_writer_t* io, u64 value); +SP_IMP void sp_fmt_write_s64(sp_io_writer_t* io, s64 value); +SP_IMP void sp_fmt_write_f64(sp_io_writer_t* io, f64 value, u32 precision); +SP_IMP void sp_fmt_write_ptr(sp_io_writer_t* io, void* value); +SP_IMP sp_err_t sp_fmt_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params); +SP_IMP void sp_fmt_apply_spec(sp_io_writer_t* io, sp_str_t pre, sp_str_t str, sp_str_t post, sp_fmt_spec_t spec); SP_IMP sp_err_t sp_fmt_parse_specifier(sp_fmt_parser_t* p, sp_fmt_spec_t* spec); SP_IMP sp_str_t sp_fmt_color_to_ansi_fg(sp_str_t id); @@ -3499,10 +3498,9 @@ SP_IMP bool sp_utf8_is_bounds_ok(u32 codepoint, u8 len); SP_IMP u32 sp_utf8_mask(u8 byte, u8 mask, u8 shift); // @env -SP_IMP sp_env_var_t sp_os_env_parse_var(sp_str_t entry); SP_IMP bool sp_os_env_key_equal(sp_str_t a, sp_str_t b); SP_IMP void sp_os_env_it_set(sp_os_env_it_t* it); -SP_IMP c8** sp_env_to_posix_envp_a(sp_mem_t mem, sp_env_t* env); +SP_IMP c8** sp_env_to_posix_envp(sp_mem_t mem, sp_env_t* env); // @context SP_IMP sp_mem_arena_t* sp_tls_rt_get_scratch_arena(sp_tls_rt_t* tls); @@ -3514,8 +3512,8 @@ SP_IMP BOOL CALLBACK sp_tls_once_trampoline(PINIT_ONCE once, PVOID param, PVOI #endif // @fs -SP_IMP sp_fs_kind_t sp_fs_lstat_kind_a(sp_str_t path); -SP_IMP sp_fs_kind_t sp_fs_stat_kind_a(sp_str_t path); +SP_IMP sp_fs_kind_t sp_fs_lstat_kind(sp_str_t path); +SP_IMP sp_fs_kind_t sp_fs_stat_kind(sp_str_t path); // @io SP_IMP sp_err_t sp_io_file_reader_read(sp_io_reader_t* reader, void* ptr, u64 size, u64* bytes_read); @@ -3538,6 +3536,7 @@ SP_IMP void sp_fmon_os_add_file(sp_fmon_t* monitor, sp_str_t file_path); SP_IMP void sp_fmon_os_process_changes(sp_fmon_t* monitor); SP_IMP bool sp_win32_fmon_file_matches(sp_fmon_os_t* os, sp_str_t full_path); SP_IMP void sp_win32_fmon_add_change(sp_fmon_t* monitor, sp_str_t file_path, sp_str_t file_name, sp_fmon_event_kind_t events); +SP_IMP sp_env_var_t sp_os_env_parse_var(sp_str_t entry); #endif #if defined(SP_LINUX) SP_IMP bool sp_linux_fmon_file_matches(sp_fmon_os_t* os, sp_str_t full_path); @@ -3570,7 +3569,7 @@ SP_IMP void* sp_posix_thread_launch(void* args); #endif #if defined(SP_WIN32) SP_IMP void sp_ps_win32_append_quoted_arg(sp_io_writer_t* builder, sp_str_t arg); -SP_IMP c8* sp_ps_build_windows_cmdline_a(sp_mem_t mem, sp_ps_config_t* config); +SP_IMP c8* sp_ps_build_windows_cmdline(sp_mem_t mem, sp_ps_config_t* config); SP_IMP sp_win32_handle_t sp_ps_win32_open_null(sp_win32_dword_t access); SP_IMP sp_win32_handle_t sp_ps_win32_fd_to_handle(sp_sys_fd_t fd); SP_IMP sp_ps_status_t sp_ps_win32_finish_process(sp_ps_t* ps); @@ -3616,9 +3615,10 @@ s64 sp_syscall6(s64 n, s64 a1, s64 a2, s64 a3, s64 a4, s64 a5, s64 a6); // Typed wrappers for individual syscalls SP_IMP s32 sp_lx_inotify_init1(s32 flags); SP_IMP s32 sp_lx_inotify_add_watch(s32 fd, const c8* pathname, u32 mask); -SP_IMP s32 sp_lx_inotify_rm_watch(s32 fd, s32 wd); SP_IMP s32 sp_lx_wait4(s32 pid, s32* status, s32 options, void* rusage); SP_IMP s64 sp_lx_getdents64(s32 fd, void* buf, u64 count); +SP_IMP s64 sp_lx_copy_file_range(s32 in_fd, s32 out_fd, u64 count); + #endif #if defined(SP_WIN32) @@ -4170,14 +4170,6 @@ s64 sp_syscall6(s64 n, s64 a1, s64 a2, s64 a3, s64 a4, s64 a5, s64 a6) { ////////////////////// // SYSCALL WRAPPERS // ////////////////////// -s64 sp_lx_getdents64(s32 fd, void* buf, u64 count); -s32 sp_lx_inotify_init1(s32 flags); -s32 sp_lx_inotify_add_watch(s32 fd, const c8* pathname, u32 mask); -s32 sp_lx_inotify_rm_watch(s32 fd, s32 wd); -s32 sp_lx_wait4(s32 pid, s32* status, s32 options, void* rusage); -s64 sp_lx_copy_file_range(s32 in_fd, s32 out_fd, u64 count); -void sp_sys_exit(s32 code); - s32 sp_lx_inotify_init1(s32 flags) { return (s32)sp_syscall(SP_SYSCALL_NUM_INOTIFY_INIT1, flags); } @@ -4186,10 +4178,6 @@ s32 sp_lx_inotify_add_watch(s32 fd, const c8* pathname, u32 mask) { return (s32)sp_syscall(SP_SYSCALL_NUM_INOTIFY_ADD_WATCH, fd, pathname, mask); } -s32 sp_lx_inotify_rm_watch(s32 fd, s32 wd) { - return (s32)sp_syscall(SP_SYSCALL_NUM_INOTIFY_RM_WATCH, fd, wd); -} - s32 sp_lx_wait4(s32 pid, s32* status, s32 options, void* rusage) { return (s32)sp_syscall(SP_SYSCALL_NUM_WAIT4, pid, status, options, rusage); } @@ -4323,7 +4311,7 @@ sp_nt_status_t sp_sys_nt_path(sp_str_t utf8, sp_sys_nt_path_t* out) { *out = sp_zero_s(sp_sys_nt_path_t); if (sp_str_empty(utf8)) return SP_NT_STATUS_OBJECT_NAME_INVALID; - sp_wide_str_t wpath = sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), utf8); + sp_wide_str_t wpath = sp_wtf8_to_wtf16(sp_mem_get_scratch(), utf8); if (!wpath.data) return SP_NT_STATUS_OBJECT_NAME_INVALID; sp_nt_unicode_string_t nt = sp_zero; @@ -4508,7 +4496,7 @@ s32 sp_sys_rename(const c8* from, u32 from_len, const c8* to, u32 to_len) { u32 name_bytes = nt.name.Length; u32 info_bytes = sizeof(sp_nt_file_rename_information_t) + name_bytes - sizeof(u16); sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_nt_file_rename_information_t* info = (sp_nt_file_rename_information_t*)sp_alloc_a(s.mem, info_bytes); + sp_nt_file_rename_information_t* info = (sp_nt_file_rename_information_t*)sp_alloc(s.mem, info_bytes); *info = sp_zero_s(sp_nt_file_rename_information_t); info->ReplaceIfExists = 1; info->FileNameLength = name_bytes; @@ -5006,7 +4994,7 @@ static s64 sp_sys_xdg_or_home(sp_str_t xdg_var, sp_str_t home_suffix, c8* buf, u if (sp_str_empty(value)) { sp_str_t home = sp_os_env_get(sp_str_lit("HOME")); if (!sp_str_empty(home)) { - value = sp_str_join_a(sp_mem_get_scratch(), home, home_suffix, sp_str_lit("/")); + value = sp_str_join(sp_mem_get_scratch(), home, home_suffix, sp_str_lit("/")); } } @@ -5660,6 +5648,10 @@ void sp_sys_exit(s32 code) { void sp_sys_exit(s32 code) { __wasi_proc_exit((u32)code); } +#else +void sp_sys_exit(s32 code) { + exit(code); +} #endif ///////////////////// @@ -5926,7 +5918,7 @@ s32 sp_sys_unlink_s(sp_str_t path) { ////////////////// #if defined(SP_WIN32) s32 sp_sys_chdir(const c8* path, u32 len) { - sp_wide_str_t w = sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), sp_str(path, len)); + sp_wide_str_t w = sp_wtf8_to_wtf16(sp_mem_get_scratch(), sp_str(path, len)); if (!w.data) return -1; sp_nt_unicode_string_t us = { .Length = sp_cast(u16, w.len * sizeof(u16)), @@ -5968,7 +5960,7 @@ s64 sp_sys_getcwd(char* buf, u64 size) { sp_nt_unicode_string_t* cwd = (sp_nt_unicode_string_t*)(sp_nt_process_params() + 0x38); u32 wlen = cwd->Length / (u32)sizeof(u16); if (wlen && cwd->Buffer[wlen - 1] == '\\') wlen--; - sp_str_t utf8 = sp_wtf16_to_wtf8_a(sp_mem_get_scratch(), (sp_wide_str_t) { .data = cwd->Buffer, .len = wlen }); + sp_str_t utf8 = sp_wtf16_to_wtf8(sp_mem_get_scratch(), (sp_wide_str_t) { .data = cwd->Buffer, .len = wlen }); if (utf8.len >= size) return -1; sp_mem_copy(buf, utf8.data, utf8.len); buf[utf8.len] = 0; @@ -6017,7 +6009,7 @@ s32 sp_sys_link(const c8* existing, u32 existing_len, const c8* alias, u32 alias u32 name_bytes = nt.name.Length; u32 info_bytes = sizeof(sp_nt_file_link_information_t) + name_bytes - sizeof(u16); sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_nt_file_link_information_t* info = (sp_nt_file_link_information_t*)sp_alloc_a(s.mem, info_bytes); + sp_nt_file_link_information_t* info = (sp_nt_file_link_information_t*)sp_alloc(s.mem, info_bytes); *info = sp_zero_s(sp_nt_file_link_information_t); info->ReplaceIfExists = 0; info->FileNameLength = name_bytes; @@ -6074,8 +6066,8 @@ s32 sp_sys_link_s(sp_str_t existing, sp_str_t alias) { //////////////////// #if defined(SP_WIN32) s32 sp_sys_symlink(const c8* existing, u32 existing_len, const c8* alias, u32 alias_len) { - sp_wide_str_t wtarget = sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), sp_str(existing, existing_len)); - sp_wide_str_t wlink = sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), sp_str(alias, alias_len)); + sp_wide_str_t wtarget = sp_wtf8_to_wtf16(sp_mem_get_scratch(), sp_str(existing, existing_len)); + sp_wide_str_t wlink = sp_wtf8_to_wtf16(sp_mem_get_scratch(), sp_str(alias, alias_len)); if (!wtarget.data || !wlink.data) return -1; DWORD flags = 0; @@ -6203,7 +6195,7 @@ s64 sp_sys_canonicalize_path(const c8* path, u32 len, c8* buf, u64 size) { u32 skip = 0; if (wlen >= 4 && wbuf[0] == '\\' && wbuf[1] == '\\' && wbuf[2] == '?' && wbuf[3] == '\\') skip = 4; - sp_str_t utf8 = sp_wtf16_to_wtf8_a(sp_mem_get_scratch(), (sp_wide_str_t) { .data = wbuf + skip, .len = wlen - skip }); + sp_str_t utf8 = sp_wtf16_to_wtf8(sp_mem_get_scratch(), (sp_wide_str_t) { .data = wbuf + skip, .len = wlen - skip }); if (utf8.len >= size) return -1; sp_mem_copy(buf, utf8.data, utf8.len); buf[utf8.len] = 0; @@ -6219,7 +6211,9 @@ s64 sp_sys_canonicalize_path(const c8* path, u32 len, c8* buf, u64 size) { c8 proc [64] = sp_zero; sp_io_mem_writer_t io = sp_zero; sp_io_mem_writer_from_buffer(&io, proc, 64); - sp_fmt_io(&io.base, "/proc/self/fd/{}", sp_fmt_int(fd)); + sp_mem_arena_marker_t s = sp_mem_begin_scratch(); + sp_fmt_io_a(&io.base, s.mem, "/proc/self/fd/{}", sp_fmt_int(fd)); + sp_mem_end_scratch(s); s64 n = sp_syscall(SP_SYSCALL_NUM_READLINKAT, SP_AT_FDCWD, proc, buf, size); sp_sys_close(fd); @@ -6262,7 +6256,7 @@ s64 sp_sys_get_exe_path(c8* buf, u64 size) { sp_nt_unicode_string_t* image = (sp_nt_unicode_string_t*)(sp_nt_process_params() + 0x60); u32 wlen = image->Length / (u32)sizeof(u16); - sp_str_t utf8 = sp_wtf16_to_wtf8_a(sp_mem_get_scratch(), (sp_wide_str_t) { .data = image->Buffer, .len = wlen }); + sp_str_t utf8 = sp_wtf16_to_wtf8(sp_mem_get_scratch(), (sp_wide_str_t) { .data = image->Buffer, .len = wlen }); if (utf8.len >= size) return -1; sp_mem_copy(buf, utf8.data, utf8.len); buf[utf8.len] = 0; @@ -6499,7 +6493,7 @@ void sp_ht_resize_impl(void** data, u64 old_cap, u64 new_cap, sp_ht_info_t info) if (!data || new_cap <= old_cap) return; void* old_data = *data; - void* new_data = sp_alloc_a(info.allocator, new_cap * info.stride.entry); + void* new_data = sp_alloc(info.allocator, new_cap * info.stride.entry); for (u64 i = 0; i < old_cap; ++i) { u64 offset = i * info.stride.entry; @@ -6518,7 +6512,7 @@ void sp_ht_resize_impl(void** data, u64 old_cap, u64 new_cap, sp_ht_info_t info) *(sp_ht_entry_state*)((c8*)new_data + new_idx * info.stride.entry + info.stride.kv) = SP_HT_ENTRY_ACTIVE; } - sp_free_a(info.allocator, old_data); + sp_free(info.allocator, old_data); *data = new_data; } @@ -6609,7 +6603,7 @@ void* sp_da_resize(void* arr, u32 stride, u64 cap) { cap = sp_max(cap, 4); sp_da_header_t* header = sp_da_head(arr); - header = sp_cast(sp_da_header_t*, sp_realloc_a(header->allocator, header, cap * stride + sizeof(sp_da_header_t))); + header = sp_cast(sp_da_header_t*, sp_realloc(header->allocator, header, cap * stride + sizeof(sp_da_header_t))); if (!header) return SP_NULLPTR; @@ -6638,7 +6632,7 @@ void sp_da_push_ex(void** arr, void* val, u32 stride) { void* sp_da_init_ex(sp_mem_t mem, u32 stride) { u32 cap = 4; - sp_da_header_t* head = (sp_da_header_t*)sp_alloc_a(mem, cap * stride + sizeof(sp_da_header_t)); + sp_da_header_t* head = (sp_da_header_t*)sp_alloc(mem, cap * stride + sizeof(sp_da_header_t)); *head = (sp_da_header_t) { .size = 0, .capacity = cap, @@ -6660,7 +6654,7 @@ void* sp_da_init_ex(sp_mem_t mem, u32 stride) { // â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘ // @ring_buffer @rb void sp_rb_init_ex(sp_mem_t mem, void** arr, u32 stride, u32 capacity) { - sp_ring_buffer_t* rb = (sp_ring_buffer_t*)sp_alloc_a(mem, capacity * stride + sizeof(sp_ring_buffer_t)); + sp_ring_buffer_t* rb = (sp_ring_buffer_t*)sp_alloc(mem, capacity * stride + sizeof(sp_ring_buffer_t)); rb->head = 0; rb->size = 0; rb->capacity = capacity; @@ -6673,7 +6667,7 @@ void* sp_rb_grow_ex(void* arr, u32 stride, u32 capacity) { sp_assert(arr); sp_ring_buffer_t* old = sp_rb_head(arr); sp_mem_t mem = old->allocator; - sp_ring_buffer_t* rb = (sp_ring_buffer_t*)sp_alloc_a(mem, capacity * stride + sizeof(sp_ring_buffer_t)); + sp_ring_buffer_t* rb = (sp_ring_buffer_t*)sp_alloc(mem, capacity * stride + sizeof(sp_ring_buffer_t)); if (!rb) return SP_NULLPTR; rb->head = 0; @@ -6715,7 +6709,7 @@ void* sp_rb_grow_ex(void* arr, u32 stride, u32 capacity) { // @format void sp_fmt_directive_register(const c8* name, sp_fmt_directive_t directive) { sp_tls_rt_t* tls = sp_tls_rt_get(); - sp_str_t id = sp_str_from_cstr_a(tls->mem, name); + sp_str_t id = sp_str_from_cstr(tls->mem, name); sp_str_ht_insert(tls->format.directives, id, directive); } @@ -6725,13 +6719,6 @@ sp_fmt_directive_t* sp_fmt_directive_lookup(sp_str_t name) { return sp_str_ht_get_ex(tls->format.directives, name, index); } -void sp_fmt_directive_reset() { - sp_tls_rt_t* tls = sp_tls_rt_get(); - sp_str_ht_free(tls->format.directives); - sp_str_ht_init_a(tls->mem, tls->format.directives); - sp_fmt_register_builtins(); -} - static u8 sp_fmt_peek(sp_fmt_parser_t* p, u32 offset) { u32 idx = p->i + offset; if (idx >= p->str.len) return 0; @@ -6826,16 +6813,6 @@ static sp_fmt_align_t sp_fmt_align_from_char(u8 c) { return SP_FMT_ALIGN_NONE; } -static c8 sp_fmt_align_to_char(sp_fmt_align_t align) { - switch (align) { - case SP_FMT_ALIGN_LEFT: return '<'; - case SP_FMT_ALIGN_CENTER: return '^'; - case SP_FMT_ALIGN_RIGHT: return '>'; - case SP_FMT_ALIGN_NONE: return 0; - } - return 0; -} - static sp_err_t sp_fmt_parse_number(sp_fmt_parser_t* p, u32* out) { u32 acc = 0; sp_err_t err = SP_ERR; @@ -6956,20 +6933,10 @@ static sp_err_t sp_fmt_pull_int_arg(sp_fmt_arg_t a, s64* out) { return SP_OK; } -sp_err_t sp_fmt_io(sp_io_writer_t* io, const c8* fmt, ...) { - va_list args; - va_start(args, fmt); - sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_err_t result = sp_fmt_v_a(io, s.mem, sp_str_view(fmt), args); - sp_mem_end_scratch(s); - va_end(args); - return result; -} - sp_err_t sp_fmt_io_a(sp_io_writer_t* io, sp_mem_t mem, const c8* fmt, ...) { va_list args; va_start(args, fmt); - sp_err_t result = sp_fmt_v_a(io, mem, sp_str_view(fmt), args); + sp_err_t result = sp_fmt_v(io, mem, sp_str_view(fmt), args); va_end(args); return result; } @@ -7076,11 +7043,6 @@ static void sp_fmt_directive_hyperlink(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_ sp_io_write_cstr(io, "\033\\", SP_NULLPTR); } -static void sp_fmt_directive_hyperlink_after(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { - (void)arg; sp_unused(params); - sp_io_write_cstr(io, "\033]8;;\033\\", SP_NULLPTR); -} - static void sp_fmt_directive_quote(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { (void)arg; sp_unused(params); sp_io_write_c8(io, '"'); @@ -7115,7 +7077,7 @@ static void sp_fmt_directive_bytes_render(sp_io_writer_t* io, sp_mem_t mem, sp_f whole >>= 10; unit_idx++; } - sp_fmt_write_u64_a(io, whole); + sp_fmt_write_u64(io, whole); if (unit_idx > 0) { u32 tenths = (u32)((rem * 10) >> 10); if (tenths > 0) { @@ -7130,7 +7092,7 @@ static void sp_fmt_directive_bytes_render(sp_io_writer_t* io, sp_mem_t mem, sp_f static void sp_fmt_directive_iso_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(params); sp_tm_epoch_t epoch = SP_RVAL(sp_tm_epoch_t) { .s = arg->u, .ns = 0 }; - sp_io_write_str(io, sp_tm_epoch_to_iso8601_a(mem, epoch), SP_NULLPTR); + sp_io_write_str(io, sp_tm_epoch_to_iso8601(mem, epoch), SP_NULLPTR); } static void sp_fmt_directive_hex_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { @@ -7145,7 +7107,7 @@ static void sp_fmt_directive_hex_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt static void sp_fmt_directive_ordinal_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { s64 value = (arg->id == sp_fmt_id_s64) ? arg->i : (s64)arg->u; sp_unused(params); - sp_fmt_write_s64_a(io, value); + sp_fmt_write_s64(io, value); s64 abs = value < 0 ? -value : value; u32 mod100 = (u32)(abs % 100); u32 mod10 = (u32)(abs % 10); @@ -7162,7 +7124,7 @@ static void sp_fmt_directive_duration_render(sp_io_writer_t* io, sp_mem_t mem, s sp_unused(params); u64 ns = arg->u; if (ns < 1000) { - sp_fmt_write_u64_a(io, ns); + sp_fmt_write_u64(io, ns); sp_io_write_cstr(io, " ns", SP_NULLPTR); return; } @@ -7175,7 +7137,7 @@ static void sp_fmt_directive_duration_render(sp_io_writer_t* io, sp_mem_t mem, s whole /= 1000; unit_idx++; } - sp_fmt_write_u64_a(io, whole); + sp_fmt_write_u64(io, whole); if (rem >= 100) { sp_io_write_c8(io, '.'); sp_io_write_c8(io, (c8)('0' + rem / 100)); @@ -7673,11 +7635,11 @@ sp_tls_rt_t* sp_tls_rt_get() { sp_carr_for(tls->scratch, it) { tls->scratch[it] = sp_mem_arena_new(tls->mem); } - tls->std.out = sp_alloc_type_a(tls->mem, sp_io_file_writer_t); - tls->std.err = sp_alloc_type_a(tls->mem, sp_io_file_writer_t); + tls->std.out = sp_alloc_type(tls->mem, sp_io_file_writer_t); + tls->std.err = sp_alloc_type(tls->mem, sp_io_file_writer_t); sp_io_file_writer_from_fd(tls->std.out, sp_sys_stdout, SP_IO_CLOSE_MODE_NONE); sp_io_file_writer_from_fd(tls->std.err, sp_sys_stderr, SP_IO_CLOSE_MODE_NONE); - sp_str_ht_init_a(tls->mem, tls->format.directives); + sp_str_ht_init(tls->mem, tls->format.directives); sp_fmt_register_builtins(); sp_sys_tls_init(tls); } @@ -8223,16 +8185,16 @@ c8* sp_mem_buffer_as_cstr(sp_mem_buffer_t* buffer) { // ░░█████████ █████ █████ █████ █████ █████ ░░█████ ░░█████████ // â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘ // @string -c8* sp_cstr_copy_n_a(sp_mem_t mem, const c8* str, u32 len) { +c8* sp_cstr_copy_n(sp_mem_t mem, const c8* str, u32 len) { u32 capacity = len + 1; - c8* copy = sp_alloc_n_a(mem, c8, capacity); + c8* copy = sp_alloc_n(mem, c8, capacity); copy[0] = 0; sp_cstr_copy_to_n(str, len, copy, capacity); return copy; } -c8* sp_cstr_copy_a(sp_mem_t mem, const c8* str) { - return sp_cstr_copy_n_a(mem, str, sp_cstr_len(str)); +c8* sp_cstr_copy(sp_mem_t mem, const c8* str) { + return sp_cstr_copy_n(mem, str, sp_cstr_len(str)); } void sp_cstr_copy_to(const c8* str, c8* buffer, u32 buffer_length) { @@ -8489,11 +8451,11 @@ bool sp_wtf8_validate(sp_str_t str) { return true; } -sp_wide_str_t sp_wtf8_to_wtf16_a(sp_mem_t mem, sp_str_t wtf8) { +sp_wide_str_t sp_wtf8_to_wtf16(sp_mem_t mem, sp_str_t wtf8) { sp_wide_str_t result = sp_zero_s(sp_wide_str_t); if (sp_str_empty(wtf8)) return result; - u16* buf = sp_alloc_n_a(mem, u16, wtf8.len + 1); + u16* buf = sp_alloc_n(mem, u16, wtf8.len + 1); const c8* ptr = wtf8.data; u32 i = 0; u32 n = 0; @@ -8525,12 +8487,12 @@ sp_wide_str_t sp_wtf8_to_wtf16_a(sp_mem_t mem, sp_str_t wtf8) { return (sp_wide_str_t) { .data = buf, .len = n }; error: - sp_free_a(mem, buf); + sp_free(mem, buf); return result; } -sp_str_t sp_wtf16_to_wtf8_a(sp_mem_t mem, sp_wide_str_t wtf16) { - c8* buf = sp_alloc_n_a(mem, c8, wtf16.len * 3 + 1); +sp_str_t sp_wtf16_to_wtf8(sp_mem_t mem, sp_wide_str_t wtf16) { + c8* buf = sp_alloc_n(mem, c8, wtf16.len * 3 + 1); u32 offset = 0; sp_for(i, wtf16.len) { u16 u = wtf16.data[i]; @@ -8556,18 +8518,18 @@ sp_str_t sp_str(const c8* str, u32 len) { }; } -sp_str_t sp_str_copy_a(sp_mem_t mem, sp_str_t str) { +sp_str_t sp_str_copy(sp_mem_t mem, sp_str_t str) { if (!str.data || !str.len) return sp_zero_s(sp_str_t); - c8* buffer = sp_alloc_n_a(mem, c8, str.len); + c8* buffer = sp_alloc_n(mem, c8, str.len); sp_mem_copy(buffer, str.data, str.len); return sp_str(buffer, str.len); } -c8* sp_str_to_cstr_a(sp_mem_t mem, sp_str_t str) { - return sp_cstr_copy_n_a(mem, str.data, str.len); +c8* sp_str_to_cstr(sp_mem_t mem, sp_str_t str) { + return sp_cstr_copy_n(mem, str.data, str.len); } -sp_str_t sp_str_alloc_a(sp_mem_t mem, u32 capacity) { +sp_str_t sp_str_alloc(sp_mem_t mem, u32 capacity) { if (!capacity) return sp_zero_s(sp_str_t); return SP_RVAL(sp_str_t) { .data = (c8*)sp_mem_allocator_alloc(mem, capacity), @@ -8709,14 +8671,14 @@ sp_str_t sp_str_sub_reverse(sp_str_t str, s32 index, s32 len) { }; } -c8* sp_cstr_from_str_a(sp_mem_t mem, sp_str_t str) { +c8* sp_cstr_from_str(sp_mem_t mem, sp_str_t str) { c8* buffer = (c8*)sp_mem_allocator_alloc(mem, str.len + 1); if (str.len) sp_mem_copy(buffer, str.data, str.len); buffer[str.len] = '\0'; return buffer; } -sp_str_t sp_str_from_cstr_n_a(sp_mem_t mem, const c8* str, u32 length) { +sp_str_t sp_str_from_cstr_n(sp_mem_t mem, const c8* str, u32 length) { if (!str || !length) return sp_zero_s(sp_str_t); u32 len = sp_min(sp_cstr_len(str), length); if (!len) return sp_zero_s(sp_str_t); @@ -8726,11 +8688,11 @@ sp_str_t sp_str_from_cstr_n_a(sp_mem_t mem, const c8* str, u32 length) { return sp_str(buffer, len); } -sp_str_t sp_str_from_cstr_a(sp_mem_t mem, const c8* str) { +sp_str_t sp_str_from_cstr(sp_mem_t mem, const c8* str) { if (!str) return sp_zero_s(sp_str_t); u32 len = sp_cstr_len(str); if (!len) return sp_zero_s(sp_str_t); - c8* buffer = sp_alloc_n_a(mem, c8, len + 1); + c8* buffer = sp_alloc_n(mem, c8, len + 1); sp_mem_copy(buffer, str, len); return sp_str(buffer, len); @@ -8758,7 +8720,7 @@ void sp_str_it_next(sp_str_it_t* it) { it->c = sp_str_it_valid(it) ? it->str.data[it->index] : 0; } -sp_str_t sp_str_concat_a(sp_mem_t mem, sp_str_t a, sp_str_t b) { +sp_str_t sp_str_concat(sp_mem_t mem, sp_str_t a, sp_str_t b) { u32 len = a.len + b.len; if (!len) return sp_zero_s(sp_str_t); c8* buffer = (c8*)sp_mem_allocator_alloc(mem, len); @@ -8767,7 +8729,7 @@ sp_str_t sp_str_concat_a(sp_mem_t mem, sp_str_t a, sp_str_t b) { return sp_str(buffer, len); } -sp_str_t sp_str_join_a(sp_mem_t mem, sp_str_t a, sp_str_t b, sp_str_t join) { +sp_str_t sp_str_join(sp_mem_t mem, sp_str_t a, sp_str_t b, sp_str_t join) { u32 len = a.len + join.len + b.len; if (!len) return sp_zero_s(sp_str_t); c8* buffer = (c8*)sp_mem_allocator_alloc(mem, len); @@ -8778,10 +8740,10 @@ sp_str_t sp_str_join_a(sp_mem_t mem, sp_str_t a, sp_str_t b, sp_str_t join) { return sp_str(buffer, len); } -sp_str_t sp_str_join_cstr_n_a(sp_mem_t mem, const c8** strings, u32 num_strings, sp_str_t join) { +sp_str_t sp_str_join_cstr_n(sp_mem_t mem, const c8** strings, u32 num_strings, sp_str_t join) { if (!strings) num_strings = 0; sp_io_dyn_mem_writer_t builder = sp_zero; - sp_io_dyn_mem_writer_init_a(mem, &builder); + sp_io_dyn_mem_writer_init(mem, &builder); for (u32 index = 0; index < num_strings; index++) { sp_io_write_cstr(&builder.base, strings[index], SP_NULLPTR); @@ -8793,12 +8755,12 @@ sp_str_t sp_str_join_cstr_n_a(sp_mem_t mem, const c8** strings, u32 num_strings, return sp_io_dyn_mem_writer_as_str(&builder); } -sp_str_t sp_str_pad_a(sp_mem_t mem, sp_str_t str, u32 n) { +sp_str_t sp_str_pad(sp_mem_t mem, sp_str_t str, u32 n) { s32 delta = (s32)n - (s32)str.len; - if (delta <= 0) return sp_str_copy_a(mem, str); + if (delta <= 0) return sp_str_copy(mem, str); sp_mem_buffer_t buffer = { - .data = sp_alloc_n_a(mem, u8, n), + .data = sp_alloc_n(mem, u8, n), .len = n, .capacity = n, }; @@ -8808,10 +8770,10 @@ sp_str_t sp_str_pad_a(sp_mem_t mem, sp_str_t str, u32 n) { return sp_mem_buffer_as_str(&buffer); } -sp_str_t sp_str_replace_c8_a(sp_mem_t mem, sp_str_t str, c8 from, c8 to) { +sp_str_t sp_str_replace_c8(sp_mem_t mem, sp_str_t str, c8 from, c8 to) { if (sp_str_empty(str)) return sp_zero_s(sp_str_t); sp_mem_buffer_t buffer = { - .data = sp_alloc_n_a(mem, u8, str.len), + .data = sp_alloc_n(mem, u8, str.len), .len = str.len, .capacity = str.len, }; @@ -8824,7 +8786,7 @@ sp_str_t sp_str_replace_c8_a(sp_mem_t mem, sp_str_t str, c8 from, c8 to) { return sp_mem_buffer_as_str(&buffer); } -sp_da(sp_str_t) sp_str_split_c8_a(sp_mem_t mem, sp_str_t str, c8 delimiter) { +sp_da(sp_str_t) sp_str_split_c8(sp_mem_t mem, sp_str_t str, c8 delimiter) { if (sp_str_empty(str)) return SP_NULLPTR; sp_da(sp_str_t) result = sp_da_new(mem, sp_str_t); @@ -8923,10 +8885,10 @@ sp_str_t sp_str_strip(sp_str_t str, sp_str_t strip) { return sp_str_strip_right(result, strip); } -sp_str_t sp_str_to_upper_a(sp_mem_t mem, sp_str_t str) { +sp_str_t sp_str_to_upper(sp_mem_t mem, sp_str_t str) { if (sp_str_empty(str)) return sp_zero_s(sp_str_t); sp_mem_buffer_t buffer = { - .data = sp_alloc_n_a(mem, u8, str.len), + .data = sp_alloc_n(mem, u8, str.len), .capacity = str.len, }; @@ -8937,10 +8899,10 @@ sp_str_t sp_str_to_upper_a(sp_mem_t mem, sp_str_t str) { return sp_mem_buffer_as_str(&buffer); } -sp_str_t sp_str_to_lower_a(sp_mem_t mem, sp_str_t str) { +sp_str_t sp_str_to_lower(sp_mem_t mem, sp_str_t str) { if (sp_str_empty(str)) return sp_zero_s(sp_str_t); sp_mem_buffer_t buffer = { - .data = sp_alloc_n_a(mem, u8, str.len), + .data = sp_alloc_n(mem, u8, str.len), .capacity = str.len, }; @@ -8951,10 +8913,10 @@ sp_str_t sp_str_to_lower_a(sp_mem_t mem, sp_str_t str) { return sp_mem_buffer_as_str(&buffer); } -sp_str_t sp_str_to_pascal_case_a(sp_mem_t mem, sp_str_t str) { +sp_str_t sp_str_to_pascal_case(sp_mem_t mem, sp_str_t str) { if (sp_str_empty(str)) return sp_zero_s(sp_str_t); sp_mem_buffer_t buffer = { - .data = sp_alloc_n_a(mem, u8, str.len), + .data = sp_alloc_n(mem, u8, str.len), .capacity = str.len, }; bool word = true; @@ -8982,15 +8944,15 @@ sp_str_t sp_str_to_pascal_case_a(sp_mem_t mem, sp_str_t str) { return sp_mem_buffer_as_str(&buffer); } -sp_str_t sp_str_truncate_a(sp_mem_t mem, sp_str_t str, u32 max_len, sp_str_t trailer) { - if (!max_len) return sp_str_copy_a(mem, str); - if (str.len <= max_len) return sp_str_copy_a(mem, str); - if (trailer.len > max_len) return sp_str_copy_a(mem, str); +sp_str_t sp_str_truncate(sp_mem_t mem, sp_str_t str, u32 max_len, sp_str_t trailer) { + if (!max_len) return sp_str_copy(mem, str); + if (str.len <= max_len) return sp_str_copy(mem, str); + if (trailer.len > max_len) return sp_str_copy(mem, str); - return sp_str_concat_a(mem, sp_str_prefix(str, max_len - trailer.len), trailer); + return sp_str_concat(mem, sp_str_prefix(str, max_len - trailer.len), trailer); } -sp_da(sp_str_t) sp_str_map_a(sp_mem_t mem, sp_str_t* strs, u32 num_strs, void* user_data, sp_str_map_fn_t fn) { +sp_da(sp_str_t) sp_str_map(sp_mem_t mem, sp_str_t* strs, u32 num_strs, void* user_data, sp_str_map_fn_t fn) { sp_da(sp_str_t) results = sp_da_new(mem, sp_str_t); sp_for(it, num_strs) { @@ -9008,12 +8970,12 @@ sp_da(sp_str_t) sp_str_map_a(sp_mem_t mem, sp_str_t* strs, u32 num_strs, void* u sp_str_t sp_str_map_kernel_prepend(sp_str_map_context_t* context) { sp_str_t prefix = *(sp_str_t*)context->user_data; - return sp_str_concat_a(context->mem, prefix, context->str); + return sp_str_concat(context->mem, prefix, context->str); } sp_str_t sp_str_map_kernel_append(sp_str_map_context_t* context) { sp_str_t suffix = *(sp_str_t*)context->user_data; - return sp_str_concat_a(context->mem, context->str, suffix); + return sp_str_concat(context->mem, context->str, suffix); } sp_str_t sp_str_map_kernel_prefix(sp_str_map_context_t* context) { @@ -9025,7 +8987,7 @@ sp_str_t sp_str_map_kernel_prefix(sp_str_map_context_t* context) { sp_str_t sp_str_map_kernel_pad(sp_str_map_context_t* context) { u32 len; sp_mem_copy(&len, context->user_data, sizeof(len)); - return sp_str_pad_a(context->mem, context->str, len); + return sp_str_pad(context->mem, context->str, len); } sp_str_t sp_str_map_kernel_trim(sp_str_map_context_t* context) { @@ -9033,23 +8995,23 @@ sp_str_t sp_str_map_kernel_trim(sp_str_map_context_t* context) { } sp_str_t sp_str_map_kernel_to_upper(sp_str_map_context_t* context) { - return sp_str_to_upper_a(context->mem, context->str); + return sp_str_to_upper(context->mem, context->str); } sp_str_t sp_str_map_kernel_to_lower(sp_str_map_context_t* context) { - return sp_str_to_lower_a(context->mem, context->str); + return sp_str_to_lower(context->mem, context->str); } sp_str_t sp_str_map_kernel_pascal_case(sp_str_map_context_t* context) { - return sp_str_to_pascal_case_a(context->mem, context->str); + return sp_str_to_pascal_case(context->mem, context->str); } -sp_da(sp_str_t) sp_str_pad_to_longest_a(sp_mem_t mem, sp_str_t* strs, u32 n) { +sp_da(sp_str_t) sp_str_pad_to_longest(sp_mem_t mem, sp_str_t* strs, u32 n) { u32 max_len = 0; sp_for(i, n) { if (strs[i].len > max_len) max_len = strs[i].len; } - return sp_str_map_a(mem, strs, n, &max_len, sp_str_map_kernel_pad); + return sp_str_map(mem, strs, n, &max_len, sp_str_map_kernel_pad); } @@ -9100,7 +9062,7 @@ s32 sp_sys_fs_it_open(sp_sys_fs_it_t* it, const c8* path, u32 path_len, void* bu SP_ALIGNED u8 pattern_storage [SP_PATH_MAX]; sp_mem_fixed_t pattern_fixed = sp_mem_fixed(pattern_storage, sizeof(pattern_storage)); sp_mem_t pattern_mem = sp_mem_fixed_as_allocator(&pattern_fixed); - sp_str_t pattern = sp_fs_join_path_a(pattern_mem, sp_str(path, path_len), sp_str_lit("*")); + sp_str_t pattern = sp_fs_join_path(pattern_mem, sp_str(path, path_len), sp_str_lit("*")); sp_sys_nt_path_t nt = sp_zero; if (!SP_NT_SUCCESS(sp_sys_nt_path(pattern, &nt))) { @@ -9154,7 +9116,7 @@ s32 sp_sys_fs_it_next(sp_sys_fs_it_t* it, sp_sys_fs_entry_t* out) { out->kind = sp_sys_diriter_win32_attrs(fd->dwFileAttributes); out->len = sp_sys_diriter_win32_name_len(name); - sp_wtf16_to_wtf8_a(mem, sp_wide_str(name, out->len)); + sp_wtf16_to_wtf8(mem, sp_wide_str(name, out->len)); return SP_OK; } } @@ -9287,8 +9249,10 @@ void sp_assert_f(sp_str_t file, sp_str_t line, sp_str_t func, sp_str_t expr, boo sp_io_file_writer_t io = sp_zero; sp_io_file_writer_from_fd(&io, sp_sys_stderr, SP_IO_CLOSE_MODE_NONE); - sp_fmt_io( + sp_mem_arena_marker_t s = sp_mem_begin_scratch(); + sp_fmt_io_a( &io.base, + s.mem, "{.red} {}:{.gray}:{.yellow}{.yellow} {}", sp_fmt_cstr("assert"), sp_fmt_str(file), @@ -9297,6 +9261,7 @@ void sp_assert_f(sp_str_t file, sp_str_t line, sp_str_t func, sp_str_t expr, boo sp_fmt_cstr("()"), sp_fmt_str(expr) ); + sp_mem_end_scratch(s); sp_io_write_cstr(&io.base, "\n", SP_NULLPTR); sp_sys_assert(cond); @@ -9531,7 +9496,7 @@ sp_str_t sp_os_lib_kind_to_extension(sp_os_lib_kind_t kind) { } sp_str_t sp_os_lib_to_file_name(sp_mem_t mem, sp_str_t lib_name, sp_os_lib_kind_t kind) { - return sp_fmt_a(mem, "{}.{}", sp_fmt_str(lib_name), sp_fmt_str(sp_os_lib_kind_to_extension(kind))).value; + return sp_fmt(mem, "{}.{}", sp_fmt_str(lib_name), sp_fmt_str(sp_os_lib_kind_to_extension(kind))).value; } #endif @@ -9554,7 +9519,7 @@ sp_str_t sp_os_lib_kind_to_extension(sp_os_lib_kind_t kind) { } sp_str_t sp_os_lib_to_file_name(sp_mem_t mem, sp_str_t lib_name, sp_os_lib_kind_t kind) { - return sp_fmt_a(mem, "lib{}.{}", sp_fmt_str(lib_name), sp_fmt_str(sp_os_lib_kind_to_extension(kind))).value; + return sp_fmt(mem, "lib{}.{}", sp_fmt_str(lib_name), sp_fmt_str(sp_os_lib_kind_to_extension(kind))).value; } #endif @@ -9577,7 +9542,7 @@ sp_str_t sp_os_lib_kind_to_extension(sp_os_lib_kind_t kind) { } sp_str_t sp_os_lib_to_file_name(sp_mem_t mem, sp_str_t lib_name, sp_os_lib_kind_t kind) { - return sp_fmt_a(mem, "lib{}.{}", sp_fmt_str(lib_name), sp_fmt_str(sp_os_lib_kind_to_extension(kind))).value; + return sp_fmt(mem, "lib{}.{}", sp_fmt_str(lib_name), sp_fmt_str(sp_os_lib_kind_to_extension(kind))).value; } #endif @@ -9616,7 +9581,7 @@ sp_str_t sp_os_lib_kind_to_extension(sp_os_lib_kind_t kind) { } sp_str_t sp_os_lib_to_file_name(sp_mem_t mem, sp_str_t lib_name, sp_os_lib_kind_t kind) { - return sp_fmt_a(mem, "lib{}.{}", sp_fmt_str(lib_name), sp_fmt_str(sp_os_lib_kind_to_extension(kind))).value; + return sp_fmt(mem, "lib{}.{}", sp_fmt_str(lib_name), sp_fmt_str(sp_os_lib_kind_to_extension(kind))).value; } #endif @@ -9625,13 +9590,13 @@ sp_err_t sp_os_create_dir(sp_str_t path) { return sp_sys_mkdir_s(path, 0755) == 0 ? SP_OK : SP_ERR_OS; } -sp_str_t sp_os_get_cwd_a(sp_mem_t mem) { +sp_str_t sp_os_get_cwd(sp_mem_t mem) { c8 path[SP_PATH_MAX] = sp_zero; if (sp_sys_getcwd(path, SP_PATH_MAX - 1) < 0) { return sp_zero_s(sp_str_t); } - return sp_str_from_cstr_a(mem, path); + return sp_str_from_cstr(mem, path); } sp_err_t sp_os_create_file(sp_str_t path) { @@ -9771,7 +9736,7 @@ void sp_os_qsort(void *arr, u64 len, u64 stride, sp_qsort_fn_t cmp) { u8 *a = sp_void_cast(a, arr); u64 gap, i, j; sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - u8* tmp = sp_alloc_n_a(s.mem, u8, stride); + u8* tmp = sp_alloc_n(s.mem, u8, stride); for (gap = len / 3; gap > 0; gap /= 3 + 1) { for (i = gap; i < len; i++) { @@ -10095,13 +10060,12 @@ void sp_env_init(sp_mem_t mem, sp_env_t* env) { *env = sp_zero_s(sp_env_t); env->mem = mem; env->arena = sp_mem_arena_new_ex(mem, 4096, SP_MEM_ARENA_MODE_NO_REALLOC, SP_MEM_ALIGNMENT); - sp_str_ht_init_a(mem, env->vars); + sp_str_ht_init(mem, env->vars); } sp_env_t sp_env_capture(sp_mem_t mem) { sp_env_t env = sp_zero; sp_env_init(mem, &env); - sp_mem_t arena = sp_mem_arena_as_allocator(env.arena); for (sp_os_env_it_t it = sp_os_env_it_begin(); sp_os_env_it_valid(&it); sp_os_env_it_next(&it)) { sp_env_insert(&env, it.key, it.value); @@ -10137,7 +10101,7 @@ bool sp_env_contains(sp_env_t* env, sp_str_t name) { void sp_env_insert(sp_env_t* env, sp_str_t name, sp_str_t value) { sp_mem_t arena = sp_mem_arena_as_allocator(env->arena); - sp_str_ht_insert(env->vars, sp_str_copy_a(arena, name), sp_str_copy_a(arena, value)); + sp_str_ht_insert(env->vars, sp_str_copy(arena, name), sp_str_copy(arena, value)); } void sp_env_erase(sp_env_t* env, sp_str_t name) { @@ -10927,8 +10891,7 @@ struct sp_ps_os { SP_PRIVATE void sp_ps_set_cwd(posix_spawn_file_actions_t* fa, sp_str_t cwd); SP_PRIVATE bool sp_ps_create_pipes(s32 pipes [2]); -SP_PRIVATE sp_da(c8*) sp_ps_build_posix_args(sp_ps_config_t* config); -SP_PRIVATE void sp_ps_free_posix_args(c8** argv); +SP_PRIVATE sp_da(c8*) sp_ps_build_posix_args(sp_mem_t mem, sp_ps_config_t* config); SP_PRIVATE void sp_ps_set_nonblocking(s32 fd); SP_PRIVATE void sp_ps_set_blocking(s32 fd); @@ -10983,42 +10946,42 @@ void sp_ps_set_blocking(s32 fd) { fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK); } -sp_da(c8*) sp_ps_build_posix_args_a(sp_mem_t mem, sp_ps_config_t* config) { +sp_da(c8*) sp_ps_build_posix_args(sp_mem_t mem, sp_ps_config_t* config) { sp_da(c8*) args = sp_da_new(mem, c8*); - sp_da_push(args, sp_str_to_cstr_a(mem, config->command)); + sp_da_push(args, sp_str_to_cstr(mem, config->command)); sp_carr_for(config->args, it) { sp_str_t arg = config->args[it]; if (sp_str_empty(arg)) break; - sp_da_push(args, sp_str_to_cstr_a(mem, arg)); + sp_da_push(args, sp_str_to_cstr(mem, arg)); } sp_da_for(config->dyn_args, it) { - sp_da_push(args, sp_str_to_cstr_a(mem, config->dyn_args[it])); + sp_da_push(args, sp_str_to_cstr(mem, config->dyn_args[it])); } sp_da_push(args, SP_NULLPTR); return args; } -sp_ps_config_t sp_ps_config_copy_a(sp_mem_t mem, const sp_ps_config_t* src) { +sp_ps_config_t sp_ps_config_copy(sp_mem_t mem, const sp_ps_config_t* src) { sp_ps_config_t dst = sp_zero; - dst.command = sp_str_copy_a(mem, src->command); - dst.cwd = sp_str_copy_a(mem, src->cwd); + dst.command = sp_str_copy(mem, src->command); + dst.cwd = sp_str_copy(mem, src->cwd); for (u32 i = 0; i < SP_PS_MAX_ARGS; i++) { if (sp_str_empty(src->args[i])) break; - dst.args[i] = sp_str_copy_a(mem, src->args[i]); + dst.args[i] = sp_str_copy(mem, src->args[i]); } // Copy dynamic args if (src->dyn_args) { sp_da_init(mem, dst.dyn_args); sp_da_for(src->dyn_args, i) { - sp_da_push(dst.dyn_args, sp_str_copy_a(mem, src->dyn_args[i])); + sp_da_push(dst.dyn_args, sp_str_copy(mem, src->dyn_args[i])); } } @@ -11034,8 +10997,8 @@ sp_ps_config_t sp_ps_config_copy_a(sp_mem_t mem, const sp_ps_config_t* src) { for (u32 i = 0; i < SP_PS_MAX_ENV; i++) { if (sp_str_empty(src->env.extra[i].key)) break; - dst.env.extra[i].key = sp_str_copy_a(mem, src->env.extra[i].key); - dst.env.extra[i].value = sp_str_copy_a(mem, src->env.extra[i].value); + dst.env.extra[i].key = sp_str_copy(mem, src->env.extra[i].key); + dst.env.extra[i].value = sp_str_copy(mem, src->env.extra[i].value); } dst.io = src->io; @@ -11043,7 +11006,7 @@ sp_ps_config_t sp_ps_config_copy_a(sp_mem_t mem, const sp_ps_config_t* src) { return dst; } -void sp_ps_config_add_arg_a(sp_mem_t mem, sp_ps_config_t* config, sp_str_t arg) { +void sp_ps_config_add_arg(sp_mem_t mem, sp_ps_config_t* config, sp_str_t arg) { SP_ASSERT(config); if (!config->dyn_args) sp_da_init(mem, config->dyn_args); @@ -11110,7 +11073,7 @@ void sp_ps_configure_io_out(sp_ps_io_out_config_t* io, sp_ps_stdio_config_entry_ } } -c8** sp_env_to_posix_envp_a(sp_mem_t mem, sp_env_t* env) { +c8** sp_env_to_posix_envp(sp_mem_t mem, sp_env_t* env) { sp_da(c8*) envp = sp_da_new(mem, c8*); sp_str_ht_for(env->vars, it) { @@ -11118,7 +11081,7 @@ c8** sp_env_to_posix_envp_a(sp_mem_t mem, sp_env_t* env) { sp_str_t val = *sp_str_ht_it_getp(env->vars, it); u32 size = key.len + 1 + val.len + 1; - c8* entry = sp_alloc_n_a(mem, c8, size); + c8* entry = sp_alloc_n(mem, c8, size); sp_mem_copy(entry, key.data, key.len); entry[key.len] = '='; sp_mem_copy(entry + key.len + 1, val.data, val.len); @@ -11130,7 +11093,7 @@ c8** sp_env_to_posix_envp_a(sp_mem_t mem, sp_env_t* env) { return envp; } -sp_ps_t sp_ps_create_a(sp_mem_t mem, sp_ps_config_t config) { +sp_ps_t sp_ps_create(sp_mem_t mem, sp_ps_config_t config) { sp_ps_t proc = sp_zero_s(sp_ps_t); proc.mem = mem; proc.io = config.io; @@ -11138,9 +11101,9 @@ sp_ps_t sp_ps_create_a(sp_mem_t mem, sp_ps_config_t config) { SP_ASSERT(!sp_str_empty(config.command)); sp_mem_arena_marker_t scratch = sp_mem_begin_scratch_for(mem); - c8** argv = sp_ps_build_posix_args_a(scratch.mem, &config); + c8** argv = sp_ps_build_posix_args(scratch.mem, &config); sp_env_t env = sp_ps_build_env(&config.env, scratch.mem); - c8** envp = sp_env_to_posix_envp_a(scratch.mem, &env); + c8** envp = sp_env_to_posix_envp(scratch.mem, &env); posix_spawnattr_t attr; posix_spawn_file_actions_t fa; @@ -11201,7 +11164,7 @@ sp_ps_t sp_ps_create_a(sp_mem_t mem, sp_ps_config_t config) { return sp_zero_s(sp_ps_t); } - proc.os = sp_alloc_type_a(mem, sp_ps_os_t); + proc.os = sp_alloc_type(mem, sp_ps_os_t); proc.os->pid = pid; if (io.in.pipes.read >= 0) { @@ -11240,7 +11203,7 @@ sp_ps_t sp_ps_create_a(sp_mem_t mem, sp_ps_config_t config) { return proc; } -sp_ps_output_t sp_ps_run_a(sp_mem_t mem, sp_ps_config_t config) { +sp_ps_output_t sp_ps_run(sp_mem_t mem, sp_ps_config_t config) { if (config.io.out.mode == SP_PS_IO_MODE_EXISTING || config.io.out.mode == SP_PS_IO_MODE_REDIRECT) { return sp_zero_s(sp_ps_output_t); } @@ -11248,7 +11211,7 @@ sp_ps_output_t sp_ps_run_a(sp_mem_t mem, sp_ps_config_t config) { config.io.out = (sp_ps_io_out_config_t) { .mode = SP_PS_IO_MODE_CREATE }; - sp_ps_t ps = sp_ps_create_a(mem, config); + sp_ps_t ps = sp_ps_create(mem, config); if (ps.os) return sp_ps_output(&ps); return (sp_ps_output_t) { .status = { .state = SP_PS_STATE_DONE, .exit_code = -1 } }; } @@ -11282,7 +11245,7 @@ sp_io_file_writer_t* sp_ps_io_in(sp_ps_t* ps) { if (!ps) return SP_NULLPTR; if (!sp_ps_is_fd_valid(ps->io.in.fd)) return SP_NULLPTR; - sp_io_file_writer_t* writer = sp_alloc_type_a(ps->mem, sp_io_file_writer_t); + sp_io_file_writer_t* writer = sp_alloc_type(ps->mem, sp_io_file_writer_t); sp_io_file_writer_from_fd(writer, ps->io.in.fd, sp_ps_io_close_mode(ps->io.in.mode)); return writer; } @@ -11291,7 +11254,7 @@ sp_io_reader_t* sp_ps_io_out(sp_ps_t* ps) { if (!ps) return SP_NULLPTR; if (!sp_ps_is_fd_valid(ps->io.out.fd)) return SP_NULLPTR; - sp_io_pipe_reader_t* reader = sp_alloc_type_a(ps->mem, sp_io_pipe_reader_t); + sp_io_pipe_reader_t* reader = sp_alloc_type(ps->mem, sp_io_pipe_reader_t); sp_io_pipe_reader_from_pipe(reader, (sp_io_pipe_t)ps->io.out.fd, sp_ps_io_close_mode(ps->io.out.mode)); return &reader->base; } @@ -11300,7 +11263,7 @@ sp_io_reader_t* sp_ps_io_err(sp_ps_t* ps) { if (!ps) return SP_NULLPTR; if (!sp_ps_is_fd_valid(ps->io.err.fd)) return SP_NULLPTR; - sp_io_pipe_reader_t* reader = sp_alloc_type_a(ps->mem, sp_io_pipe_reader_t); + sp_io_pipe_reader_t* reader = sp_alloc_type(ps->mem, sp_io_pipe_reader_t); sp_io_pipe_reader_from_pipe(reader, (sp_io_pipe_t)ps->io.err.fd, sp_ps_io_close_mode(ps->io.err.mode)); return &reader->base; } @@ -11420,8 +11383,8 @@ sp_ps_output_t sp_ps_output(sp_ps_t* ps) { sp_io_dyn_mem_writer_t out; sp_io_dyn_mem_writer_t err; } write = sp_zero; - sp_io_dyn_mem_writer_init_a(ps->mem, &write.out); - sp_io_dyn_mem_writer_init_a(ps->mem, &write.err); + sp_io_dyn_mem_writer_init(ps->mem, &write.out); + sp_io_dyn_mem_writer_init(ps->mem, &write.err); sp_sys_fd_t fds[2]; u8 ready[2]; @@ -11481,14 +11444,14 @@ bool sp_ps_kill(sp_ps_t* ps) { void sp_ps_free(sp_ps_t* ps) { if (!ps || !ps->os) return; - sp_free_a(ps->mem, ps->os); + sp_free(ps->mem, ps->os); ps->os = SP_NULLPTR; } void sp_ps_output_free(sp_mem_t mem, sp_ps_output_t* output) { if (!output) return; - sp_free_a(mem, (void*)output->out.data); - sp_free_a(mem, (void*)output->err.data); + sp_free(mem, (void*)output->out.data); + sp_free(mem, (void*)output->err.data); *output = sp_zero_s(sp_ps_output_t); } @@ -11566,9 +11529,9 @@ void sp_ps_win32_append_quoted_arg(sp_io_writer_t* builder, sp_str_t arg) { sp_io_write_c8(builder, '"'); } -c8* sp_ps_build_windows_cmdline_a(sp_mem_t mem, sp_ps_config_t* config) { +c8* sp_ps_build_windows_cmdline(sp_mem_t mem, sp_ps_config_t* config) { sp_io_dyn_mem_writer_t builder = sp_zero; - sp_io_dyn_mem_writer_init_a(mem, &builder); + sp_io_dyn_mem_writer_init(mem, &builder); sp_ps_win32_append_quoted_arg(&builder.base, config->command); @@ -11748,23 +11711,23 @@ void sp_ps_win32_close_parent_fds(sp_ps_win32_stdio_t* io) { } } -sp_ps_config_t sp_ps_config_copy_a(sp_mem_t mem, const sp_ps_config_t* src) { +sp_ps_config_t sp_ps_config_copy(sp_mem_t mem, const sp_ps_config_t* src) { sp_ps_config_t dst = sp_zero; - dst.command = sp_str_copy_a(mem, src->command); - dst.cwd = sp_str_copy_a(mem, src->cwd); + dst.command = sp_str_copy(mem, src->command); + dst.cwd = sp_str_copy(mem, src->cwd); sp_for(i, SP_PS_MAX_ARGS) { if (sp_str_empty(src->args[i])) { break; } - dst.args[i] = sp_str_copy_a(mem, src->args[i]); + dst.args[i] = sp_str_copy(mem, src->args[i]); } if (src->dyn_args) { sp_da_init(mem, dst.dyn_args); sp_da_for(src->dyn_args, i) { - sp_da_push(dst.dyn_args, sp_str_copy_a(mem, src->dyn_args[i])); + sp_da_push(dst.dyn_args, sp_str_copy(mem, src->dyn_args[i])); } } @@ -11780,15 +11743,15 @@ sp_ps_config_t sp_ps_config_copy_a(sp_mem_t mem, const sp_ps_config_t* src) { if (sp_str_empty(src->env.extra[i].key)) { break; } - dst.env.extra[i].key = sp_str_copy_a(mem, src->env.extra[i].key); - dst.env.extra[i].value = sp_str_copy_a(mem, src->env.extra[i].value); + dst.env.extra[i].key = sp_str_copy(mem, src->env.extra[i].key); + dst.env.extra[i].value = sp_str_copy(mem, src->env.extra[i].value); } dst.io = src->io; return dst; } -void sp_ps_config_add_arg_a(sp_mem_t mem, sp_ps_config_t* config, sp_str_t arg) { +void sp_ps_config_add_arg(sp_mem_t mem, sp_ps_config_t* config, sp_str_t arg) { SP_ASSERT(config); if (!config->dyn_args) sp_da_init(mem, config->dyn_args); @@ -11797,7 +11760,7 @@ void sp_ps_config_add_arg_a(sp_mem_t mem, sp_ps_config_t* config, sp_str_t arg) } } -sp_ps_t sp_ps_create_a(sp_mem_t mem, sp_ps_config_t config) { +sp_ps_t sp_ps_create(sp_mem_t mem, sp_ps_config_t config) { sp_ps_t proc = sp_zero_s(sp_ps_t); proc.mem = mem; proc.io = config.io; @@ -11809,10 +11772,10 @@ sp_ps_t sp_ps_create_a(sp_mem_t mem, sp_ps_config_t config) { SP_ASSERT(!sp_str_empty(config.command)); sp_mem_arena_marker_t scratch = sp_mem_begin_scratch_for(mem); - c8* cmdline = sp_ps_build_windows_cmdline_a(scratch.mem, &config); + c8* cmdline = sp_ps_build_windows_cmdline(scratch.mem, &config); sp_io_dyn_mem_writer_t b = sp_zero; - sp_io_dyn_mem_writer_init_a(scratch.mem, &b); + sp_io_dyn_mem_writer_init(scratch.mem, &b); switch (config.env.mode) { case SP_PS_ENV_INHERIT: { @@ -11854,7 +11817,7 @@ sp_ps_t sp_ps_create_a(sp_mem_t mem, sp_ps_config_t config) { sp_str_t env = sp_io_dyn_mem_writer_as_str(&b); - c8* cwd = sp_str_empty(config.cwd) ? SP_NULLPTR : sp_str_to_cstr_a(scratch.mem, config.cwd); + c8* cwd = sp_str_empty(config.cwd) ? SP_NULLPTR : sp_str_to_cstr(scratch.mem, config.cwd); sp_ps_win32_stdio_t io = { .in = { .parent_fd = SP_SYS_INVALID_FD }, @@ -11896,7 +11859,7 @@ sp_ps_t sp_ps_create_a(sp_mem_t mem, sp_ps_config_t config) { sp_ps_win32_close_child_handles(&io); CloseHandle(process_info.hThread); - proc.os = sp_alloc_type_a(mem, sp_ps_os_t); + proc.os = sp_alloc_type(mem, sp_ps_os_t); proc.os->pid = process_info.hProcess; if (io.in.parent_fd != SP_SYS_INVALID_FD) { @@ -11918,11 +11881,11 @@ sp_ps_t sp_ps_create_a(sp_mem_t mem, sp_ps_config_t config) { return sp_zero_s(sp_ps_t); } -sp_ps_output_t sp_ps_run_a(sp_mem_t mem, sp_ps_config_t config) { +sp_ps_output_t sp_ps_run(sp_mem_t mem, sp_ps_config_t config) { config.io.out = (sp_ps_io_out_config_t) { .mode = SP_PS_IO_MODE_CREATE, }; - sp_ps_t ps = sp_ps_create_a(mem, config); + sp_ps_t ps = sp_ps_create(mem, config); if (ps.os) return sp_ps_output(&ps); return (sp_ps_output_t) { .status = { .state = SP_PS_STATE_DONE, .exit_code = -1 } }; } @@ -11931,7 +11894,7 @@ sp_io_file_writer_t* sp_ps_io_in(sp_ps_t* ps) { if (!ps) return SP_NULLPTR; if (!sp_ps_is_fd_valid(ps->io.in.fd)) return SP_NULLPTR; - sp_io_file_writer_t* writer = sp_alloc_type_a(ps->mem, sp_io_file_writer_t); + sp_io_file_writer_t* writer = sp_alloc_type(ps->mem, sp_io_file_writer_t); sp_io_file_writer_from_fd(writer, ps->io.in.fd, sp_ps_io_close_mode(ps->io.in.mode)); return writer; } @@ -11940,7 +11903,7 @@ sp_io_reader_t* sp_ps_io_out(sp_ps_t* ps) { if (!ps) return SP_NULLPTR; if (!sp_ps_is_fd_valid(ps->io.out.fd)) return SP_NULLPTR; - sp_io_pipe_reader_t* reader = sp_alloc_type_a(ps->mem, sp_io_pipe_reader_t); + sp_io_pipe_reader_t* reader = sp_alloc_type(ps->mem, sp_io_pipe_reader_t); sp_io_pipe_reader_from_pipe(reader, (sp_io_pipe_t)ps->io.out.fd, sp_ps_io_close_mode(ps->io.out.mode)); return &reader->base; } @@ -11949,7 +11912,7 @@ sp_io_reader_t* sp_ps_io_err(sp_ps_t* ps) { if (!ps) return SP_NULLPTR; if (!sp_ps_is_fd_valid(ps->io.err.fd)) return SP_NULLPTR; - sp_io_pipe_reader_t* reader = sp_alloc_type_a(ps->mem, sp_io_pipe_reader_t); + sp_io_pipe_reader_t* reader = sp_alloc_type(ps->mem, sp_io_pipe_reader_t); sp_io_pipe_reader_from_pipe(reader, (sp_io_pipe_t)ps->io.err.fd, sp_ps_io_close_mode(ps->io.err.mode)); return &reader->base; } @@ -12058,8 +12021,8 @@ sp_ps_output_t sp_ps_output(sp_ps_t* ps) { sp_io_dyn_mem_writer_t out = sp_zero; sp_io_dyn_mem_writer_t err = sp_zero; - sp_io_dyn_mem_writer_init_a(ps->mem, &out); - sp_io_dyn_mem_writer_init_a(ps->mem, &err); + sp_io_dyn_mem_writer_init(ps->mem, &out); + sp_io_dyn_mem_writer_init(ps->mem, &err); DWORD exit_code = 0; bool process_done = !ps->os; @@ -12127,14 +12090,14 @@ void sp_ps_free(sp_ps_t* ps) { CloseHandle(ps->os->pid); ps->os->pid = SP_NULLPTR; } - sp_free_a(ps->mem, ps->os); + sp_free(ps->mem, ps->os); ps->os = SP_NULLPTR; } void sp_ps_output_free(sp_mem_t mem, sp_ps_output_t* output) { if (!output) return; - sp_free_a(mem, (void*)output->out.data); - sp_free_a(mem, (void*)output->err.data); + sp_free(mem, (void*)output->out.data); + sp_free(mem, (void*)output->err.data); *output = sp_zero_s(sp_ps_output_t); } @@ -12143,13 +12106,13 @@ struct sp_ps_os { s32 dummy; }; -sp_ps_config_t sp_ps_config_copy_a(sp_mem_t mem, const sp_ps_config_t* src) { +sp_ps_config_t sp_ps_config_copy(sp_mem_t mem, const sp_ps_config_t* src) { SP_UNIMPLEMENTED(); SP_UNUSED(mem); return *src; } -void sp_ps_config_add_arg_a(sp_mem_t mem, sp_ps_config_t* config, sp_str_t arg) { +void sp_ps_config_add_arg(sp_mem_t mem, sp_ps_config_t* config, sp_str_t arg) { SP_UNIMPLEMENTED(); if (!config->dyn_args) sp_da_init(mem, config->dyn_args); if (!sp_str_empty(arg)) { @@ -12157,16 +12120,16 @@ void sp_ps_config_add_arg_a(sp_mem_t mem, sp_ps_config_t* config, sp_str_t arg) } } -sp_ps_t sp_ps_create_a(sp_mem_t mem, sp_ps_config_t config) { +sp_ps_t sp_ps_create(sp_mem_t mem, sp_ps_config_t config) { SP_UNIMPLEMENTED(); SP_UNUSED(mem); SP_UNUSED(config); return sp_zero_s(sp_ps_t); } -sp_ps_output_t sp_ps_run_a(sp_mem_t mem, sp_ps_config_t config) { +sp_ps_output_t sp_ps_run(sp_mem_t mem, sp_ps_config_t config) { SP_UNIMPLEMENTED(); - sp_ps_t ps = sp_ps_create_a(mem, config); + sp_ps_t ps = sp_ps_create(mem, config); return sp_ps_output(&ps); } @@ -12248,7 +12211,7 @@ SP_PRIVATE void sp_fmon_os_add_dir(sp_fmon_t* monitor, sp_str_t path); SP_PRIVATE void sp_fmon_os_add_file(sp_fmon_t* monitor, sp_str_t file_path); SP_PRIVATE void sp_fmon_os_process_changes(sp_fmon_t* monitor); -void sp_fmon_init_a(sp_mem_t mem, sp_fmon_t* monitor, sp_fmon_fn_t fn, sp_fmon_event_kind_t events, void* userdata) { +void sp_fmon_init(sp_mem_t mem, sp_fmon_t* monitor, sp_fmon_fn_t fn, sp_fmon_event_kind_t events, void* userdata) { monitor->mem = mem; monitor->callback = fn; monitor->events_to_watch = events; @@ -12303,7 +12266,7 @@ struct sp_fmon_os { struct sp_fmon_os { sp_da(s32) fds; sp_da(sp_str_t) paths; - sp_ht_a(sp_str_t, u8) files; + sp_ht(sp_str_t, u8) files; SP_ALIGNED u8 buffer[4096]; s32 fd; }; @@ -12344,7 +12307,7 @@ SP_PRIVATE void sp_win32_fmon_add_change(sp_fmon_t* monitor, sp_str_t file_path, SP_PRIVATE void sp_win32_fmon_issue_read(sp_fmon_t* monitor, sp_fmon_dir_t* info); void sp_fmon_os_init(sp_fmon_t* monitor) { - sp_fmon_os_t* os = sp_alloc_type_a(monitor->mem, sp_fmon_os_t); + sp_fmon_os_t* os = sp_alloc_type(monitor->mem, sp_fmon_os_t); sp_da_init(monitor->mem, os->dirs); sp_da_init(monitor->mem, os->watch_files); monitor->os = os; @@ -12363,12 +12326,12 @@ void sp_fmon_os_deinit(sp_fmon_t* monitor) { CloseHandle(info->overlapped.hEvent); } if (info->notify_information) { - sp_free_a(monitor->mem, info->notify_information); + sp_free(monitor->mem, info->notify_information); } } sp_da_free(os->dirs); sp_da_free(os->watch_files); - sp_free_a(monitor->mem, os); + sp_free(monitor->mem, os); monitor->os = NULL; } @@ -12395,8 +12358,8 @@ void sp_fmon_os_add_dir(sp_fmon_t* monitor, sp_str_t path) { sp_fmon_dir_t dir = sp_zero; dir.overlapped.hEvent = event; dir.handle = handle; - dir.path = sp_fs_canonicalize_path_a(monitor->mem, path); - dir.notify_information = sp_alloc_a(monitor->mem, SP_FILE_MONITOR_BUFFER_SIZE); + dir.path = sp_fs_canonicalize_path(monitor->mem, path); + dir.notify_information = sp_alloc(monitor->mem, SP_FILE_MONITOR_BUFFER_SIZE); sp_mem_zero(dir.notify_information, SP_FILE_MONITOR_BUFFER_SIZE); sp_da_push(os->dirs, dir); @@ -12406,14 +12369,14 @@ void sp_fmon_os_add_dir(sp_fmon_t* monitor, sp_str_t path) { void sp_fmon_os_add_file(sp_fmon_t* monitor, sp_str_t file_path) { sp_fmon_os_t* os = monitor->os; - sp_str_t canonical = sp_fs_canonicalize_path_a(monitor->mem, file_path); + sp_str_t canonical = sp_fs_canonicalize_path(monitor->mem, file_path); sp_da_push(os->watch_files, canonical); sp_str_t dir_path = sp_fs_parent_path(canonical); if (dir_path.len > 0) { bool found = false; sp_da_for(os->dirs, i) { - sp_str_t path = sp_fs_canonicalize_path_a(monitor->mem, os->dirs[i].path); // @spader + sp_str_t path = sp_fs_canonicalize_path(monitor->mem, os->dirs[i].path); // @spader if (sp_str_equal(path, dir_path)) { found = true; break; @@ -12471,7 +12434,7 @@ void sp_fmon_os_process_changes(sp_fmon_t* monitor) { if (events != SP_FILE_CHANGE_EVENT_NONE) { sp_str_t partial_path_str = sp_win32_utf16_to_utf8(¬ify->FileName[0], (s32)(notify->FileNameLength / sizeof(WCHAR))); - sp_str_t full_path = sp_fs_join_path_a(monitor->mem, info->path, partial_path_str); // @spader + sp_str_t full_path = sp_fs_join_path(monitor->mem, info->path, partial_path_str); // @spader if (sp_win32_fmon_file_matches(os, full_path)) { sp_str_t file_name = sp_fs_get_name(full_path); @@ -12490,7 +12453,7 @@ void sp_fmon_os_process_changes(sp_fmon_t* monitor) { } void sp_win32_fmon_add_change(sp_fmon_t* monitor, sp_str_t file_path, sp_str_t file_name, sp_fmon_event_kind_t events) { - if (sp_fs_is_dir_a(file_path)) return; + if (sp_fs_is_dir(file_path)) return; if (file_name.data && file_name.len > 0) { if (file_name.data[0] == '.' && file_name.len > 1 && file_name.data[1] == '#') return; @@ -12507,8 +12470,8 @@ void sp_win32_fmon_add_change(sp_fmon_t* monitor, sp_str_t file_path, sp_str_t f } sp_fmon_event_t change = { - .file_path = sp_str_copy_a(monitor->mem, file_path), - .file_name = sp_str_copy_a(monitor->mem, file_name), + .file_path = sp_str_copy(monitor->mem, file_path), + .file_name = sp_str_copy(monitor->mem, file_name), .events = events, }; sp_da_push(monitor->changes, change); @@ -12532,7 +12495,7 @@ void sp_win32_fmon_issue_read(sp_fmon_t* monitor, sp_fmon_dir_t* info) { #elif defined(SP_LINUX) void sp_fmon_os_init(sp_fmon_t* monitor) { - sp_fmon_os_t* linux_monitor = sp_alloc_type_a(monitor->mem, sp_fmon_os_t); + sp_fmon_os_t* linux_monitor = sp_alloc_type(monitor->mem, sp_fmon_os_t); linux_monitor->fd = sp_lx_inotify_init1(SP_IN_NONBLOCK | SP_IN_CLOEXEC); if (linux_monitor->fd == -1) { @@ -12542,7 +12505,7 @@ void sp_fmon_os_init(sp_fmon_t* monitor) { sp_da_init(monitor->mem, linux_monitor->fds); sp_da_init(monitor->mem, linux_monitor->paths); - sp_str_ht_init_a(monitor->mem, linux_monitor->files); + sp_str_ht_init(monitor->mem, linux_monitor->files); monitor->os = linux_monitor; } @@ -12571,7 +12534,7 @@ void sp_fmon_os_add_dir(sp_fmon_t* monitor, sp_str_t path) { if (wd != -1) { sp_da_push(os->fds, wd); - sp_da_push(os->paths, sp_str_copy_a(monitor->mem, path)); + sp_da_push(os->paths, sp_str_copy(monitor->mem, path)); } } @@ -12587,7 +12550,7 @@ void sp_fmon_os_deinit(sp_fmon_t* monitor) { void sp_fmon_os_add_file(sp_fmon_t* monitor, sp_str_t file_path) { sp_fmon_os_t* os = (sp_fmon_os_t*)monitor->os; - sp_str_t canonical = sp_fs_canonicalize_path_a(monitor->mem, file_path); + sp_str_t canonical = sp_fs_canonicalize_path(monitor->mem, file_path); sp_str_ht_insert(os->files, canonical, 1); sp_str_t dir_path = sp_fs_parent_path(canonical); @@ -12626,10 +12589,10 @@ void sp_fmon_os_process_changes(sp_fmon_t* monitor) { sp_str_t file_path = sp_zero_s(sp_str_t); if (event->len > 0 && event->name[0] != '\0') { - file_name = sp_str_from_cstr_a(monitor->mem, event->name); - file_path = sp_fs_join_path_a(monitor->mem, dir_path, file_name); + file_name = sp_str_from_cstr(monitor->mem, event->name); + file_path = sp_fs_join_path(monitor->mem, dir_path, file_name); } else { - file_path = sp_str_copy_a(monitor->mem, dir_path); + file_path = sp_str_copy(monitor->mem, dir_path); file_name = sp_fs_get_name(file_path); } @@ -12717,7 +12680,7 @@ SP_PRIVATE void sp_fmon_fsevents_callback( if (kind != SP_FILE_CHANGE_EVENT_NONE) { sp_fmon_event_t change = { - .file_path = sp_str_copy_a(os->mem.event, file_path), + .file_path = sp_str_copy(os->mem.event, file_path), .file_name = sp_fs_get_name(file_path), .events = kind, }; @@ -12751,11 +12714,11 @@ SP_PRIVATE void sp_fmon_fsevents_recreate_stream(sp_fmon_t* monitor) { sp_mem_arena_marker_t s = sp_mem_begin_scratch(); sp_mutex_lock(&os->mutex); - CFStringRef* cf_paths = sp_alloc_n_a(s.mem, CFStringRef, num_paths); + CFStringRef* cf_paths = sp_alloc_n(s.mem, CFStringRef, num_paths); sp_da_for(os->watch_paths, it) { cf_paths[it] = CFStringCreateWithCString( kCFAllocatorDefault, - sp_str_to_cstr_a(s.mem, os->watch_paths[it]), + sp_str_to_cstr(s.mem, os->watch_paths[it]), kCFStringEncodingUTF8 ); } @@ -12797,7 +12760,7 @@ SP_PRIVATE void sp_fmon_fsevents_recreate_stream(sp_fmon_t* monitor) { } void sp_fmon_os_init(sp_fmon_t* monitor) { - sp_fmon_os_t* os = sp_alloc_type_a(monitor->mem, sp_fmon_os_t); + sp_fmon_os_t* os = sp_alloc_type(monitor->mem, sp_fmon_os_t); os->queue = dispatch_queue_create("sp.fmon", DISPATCH_QUEUE_SERIAL); os->monitor = monitor; sp_mutex_init(&os->mutex, SP_MUTEX_PLAIN); @@ -12824,19 +12787,19 @@ void sp_fmon_os_deinit(sp_fmon_t* monitor) { sp_mutex_destroy(&os->mutex); sp_mem_arena_destroy(os->watch_arena); sp_mem_arena_destroy(os->event_arena); - sp_free_a(monitor->mem, os); + sp_free(monitor->mem, os); monitor->os = SP_NULLPTR; } void sp_fmon_os_push_dir(sp_fmon_os_t* os, sp_str_t dir) { sp_mutex_lock(&os->mutex); - sp_da_push(os->watch_paths, sp_str_copy_a(os->mem.watch, dir)); + sp_da_push(os->watch_paths, sp_str_copy(os->mem.watch, dir)); sp_mutex_unlock(&os->mutex); } void sp_fmon_os_push_file(sp_fmon_os_t* os, sp_str_t file) { sp_mutex_lock(&os->mutex); - sp_da_push(os->watch_files, sp_fs_canonicalize_path_a(os->mem.watch, file)); + sp_da_push(os->watch_files, sp_fs_canonicalize_path(os->mem.watch, file)); sp_mutex_unlock(&os->mutex); } @@ -12891,7 +12854,7 @@ void sp_fmon_os_process_changes(sp_fmon_t* monitor) { #include void sp_fmon_os_init(sp_fmon_t* monitor) { - sp_fmon_os_t* os = sp_alloc_type_a(monitor->mem, sp_fmon_os_t); + sp_fmon_os_t* os = sp_alloc_type(monitor->mem, sp_fmon_os_t); os->kq = kqueue(); if (os->kq == -1) { @@ -12942,7 +12905,7 @@ void sp_fmon_os_add_dir(sp_fmon_t* monitor, sp_str_t path) { if (kevent(os->kq, &change, 1, NULL, 0, NULL) != -1) { sp_da_push(os->fds, fd); - sp_da_push(os->watch_paths, sp_str_copy_a(monitor->mem, path)); + sp_da_push(os->watch_paths, sp_str_copy(monitor->mem, path)); } else { close(fd); } @@ -12972,7 +12935,7 @@ void sp_fmon_os_add_file(sp_fmon_t* monitor, sp_str_t path) { if (kevent(os->kq, &change, 1, NULL, 0, NULL) != -1) { sp_da_push(os->fds, fd); - sp_da_push(os->watch_paths, sp_str_copy_a(monitor->mem, path)); + sp_da_push(os->watch_paths, sp_str_copy(monitor->mem, path)); } else { close(fd); } @@ -13014,7 +12977,7 @@ void sp_fmon_os_process_changes(sp_fmon_t* monitor) { if (event_kind != SP_FILE_CHANGE_EVENT_NONE) { sp_fmon_event_t change = { - .file_path = sp_str_copy_a(monitor->mem, path), + .file_path = sp_str_copy(monitor->mem, path), .file_name = sp_fs_get_name(path), .events = event_kind, .time = 0 @@ -13363,7 +13326,6 @@ sp_err_t sp_io_copy_b2(sp_io_writer_t* w, sp_io_reader_t* r, u8* buffer, u64 n, } } -done: if (bytes_copied) *bytes_copied = total; return err.e; } @@ -13741,7 +13703,7 @@ void sp_io_get_std_err(sp_io_file_writer_t* io) { // APP // ///////// SP_API sp_app_t* sp_app_new(sp_mem_t mem, sp_app_config_t config) { - sp_app_t* app = sp_alloc_type_a(mem, sp_app_t); + sp_app_t* app = sp_alloc_type(mem, sp_app_t); *app = (sp_app_t) { .user_data = config.user_data, .on_init = config.on_init, @@ -13884,15 +13846,15 @@ sp_mem_t sp_mem_os_new() { return allocator; } -void* sp_alloc_a(sp_mem_t allocator, u64 size) { +void* sp_alloc(sp_mem_t allocator, u64 size) { return sp_mem_allocator_alloc(allocator, size); } -void* sp_realloc_a(sp_mem_t allocator, void* memory, u64 size) { +void* sp_realloc(sp_mem_t allocator, void* memory, u64 size) { return sp_mem_allocator_realloc(allocator, memory, size); } -void sp_free_a(sp_mem_t allocator, void* memory) { +void sp_free(sp_mem_t allocator, void* memory) { sp_mem_allocator_free(allocator, memory); } @@ -14036,7 +13998,7 @@ sp_err_t sp_io_dyn_mem_writer_close(sp_io_dyn_mem_writer_t* w) { return SP_OK; } -void sp_io_dyn_mem_writer_init_a(sp_mem_t mem, sp_io_dyn_mem_writer_t* w) { +void sp_io_dyn_mem_writer_init(sp_mem_t mem, sp_io_dyn_mem_writer_t* w) { *w = (sp_io_dyn_mem_writer_t) { .base = { .write = sp_io_dyn_mem_writer_write }, .allocator = mem, @@ -14051,14 +14013,14 @@ sp_str_t sp_io_dyn_mem_writer_as_str(sp_io_dyn_mem_writer_t* w) { return sp_mem_buffer_as_str(&w->storage); } -sp_str_r sp_fmt_a(sp_mem_t mem, const c8* fmt, ...) { +sp_str_r sp_fmt(sp_mem_t mem, const c8* fmt, ...) { va_list args; va_start(args, fmt); sp_io_dyn_mem_writer_t io = sp_zero; - sp_io_dyn_mem_writer_init_a(mem, &io); + sp_io_dyn_mem_writer_init(mem, &io); sp_mem_arena_marker_t s = sp_mem_begin_scratch_for(mem); - sp_err_t err = sp_fmt_v_a(&io.base, s.mem, sp_str_view(fmt), args); + sp_err_t err = sp_fmt_v(&io.base, s.mem, sp_str_view(fmt), args); sp_mem_end_scratch(s); va_end(args); @@ -14068,7 +14030,7 @@ sp_str_r sp_fmt_a(sp_mem_t mem, const c8* fmt, ...) { return result; } -sp_err_t sp_fmt_v_a(sp_io_writer_t* io, sp_mem_t mem, sp_str_t fmt, va_list args) { +sp_err_t sp_fmt_v(sp_io_writer_t* io, sp_mem_t mem, sp_str_t fmt, va_list args) { sp_fmt_parser_t p = { .str = fmt }; while (true) { @@ -14115,7 +14077,7 @@ sp_err_t sp_fmt_v_a(sp_io_writer_t* io, sp_mem_t mem, sp_str_t fmt, va_list args sp_fmt_arg_t arg = va_arg(args, sp_fmt_arg_t); arg.spec = spec; - sp_try(sp_fmt_render_a(io, mem, &arg, params)); + sp_try(sp_fmt_render(io, mem, &arg, params)); continue; } @@ -14136,7 +14098,7 @@ sp_err_t sp_fmt_v_a(sp_io_writer_t* io, sp_mem_t mem, sp_str_t fmt, va_list args return SP_OK; } -void sp_log_a(const c8* fmt, ...) { +void sp_log(const c8* fmt, ...) { u8 buffer [4096] = sp_zero; sp_io_mem_writer_t io = sp_zero; sp_io_mem_writer_from_buffer(&io, buffer, sizeof(buffer)); @@ -14144,7 +14106,7 @@ void sp_log_a(const c8* fmt, ...) { sp_mem_arena_marker_t s = sp_mem_begin_scratch(); va_list args; va_start(args, fmt); - sp_fmt_v_a(&io.base, s.mem, sp_str_view(fmt), args); + sp_fmt_v(&io.base, s.mem, sp_str_view(fmt), args); va_end(args); sp_mem_end_scratch(s); @@ -14154,7 +14116,7 @@ void sp_log_a(const c8* fmt, ...) { sp_io_write_cstr(&tls->std.out->base, "\n", SP_NULLPTR); } -void sp_log_str_a(sp_str_t fmt, ...) { +void sp_log_str(sp_str_t fmt, ...) { u8 buffer[4096] = sp_zero; sp_io_mem_writer_t io = sp_zero; sp_io_mem_writer_from_buffer(&io, buffer, sizeof(buffer)); @@ -14162,7 +14124,7 @@ void sp_log_str_a(sp_str_t fmt, ...) { sp_mem_arena_marker_t s = sp_mem_begin_scratch(); va_list args; va_start(args, fmt); - sp_fmt_v_a(&io.base, s.mem, fmt, args); + sp_fmt_v(&io.base, s.mem, fmt, args); va_end(args); sp_mem_end_scratch(s); @@ -14171,7 +14133,7 @@ void sp_log_str_a(sp_str_t fmt, ...) { sp_io_write_cstr(&tls->std.out->base, "\n", SP_NULLPTR); } -void sp_log_err_a(const c8* fmt, ...) { +void sp_log_err(const c8* fmt, ...) { u8 buffer[4096] = sp_zero; sp_io_mem_writer_t io = sp_zero; sp_io_mem_writer_from_buffer(&io, buffer, sizeof(buffer)); @@ -14179,7 +14141,7 @@ void sp_log_err_a(const c8* fmt, ...) { sp_mem_arena_marker_t s = sp_mem_begin_scratch(); va_list args; va_start(args, fmt); - sp_fmt_v_a(&io.base, s.mem, sp_str_view(fmt), args); + sp_fmt_v(&io.base, s.mem, sp_str_view(fmt), args); va_end(args); sp_mem_end_scratch(s); @@ -14188,7 +14150,7 @@ void sp_log_err_a(const c8* fmt, ...) { sp_io_write_cstr(&tls->std.out->base, "\n", SP_NULLPTR); } -void sp_print_a(const c8* fmt, ...) { +void sp_print(const c8* fmt, ...) { u8 buffer[4096] = sp_zero; sp_io_mem_writer_t io = sp_zero; sp_io_mem_writer_from_buffer(&io, buffer, sizeof(buffer)); @@ -14196,7 +14158,7 @@ void sp_print_a(const c8* fmt, ...) { sp_mem_arena_marker_t s = sp_mem_begin_scratch(); va_list args; va_start(args, fmt); - sp_fmt_v_a(&io.base, s.mem, sp_str_view(fmt), args); + sp_fmt_v(&io.base, s.mem, sp_str_view(fmt), args); va_end(args); sp_mem_end_scratch(s); @@ -14204,7 +14166,7 @@ void sp_print_a(const c8* fmt, ...) { sp_io_write_str(&tls->std.out->base, sp_io_mem_writer_as_str(&io), SP_NULLPTR); } -void sp_print_str_a(sp_str_t fmt, ...) { +void sp_print_str(sp_str_t fmt, ...) { u8 buffer[4096] = sp_zero; sp_io_mem_writer_t io = sp_zero; sp_io_mem_writer_from_buffer(&io, buffer, sizeof(buffer)); @@ -14212,7 +14174,7 @@ void sp_print_str_a(sp_str_t fmt, ...) { sp_mem_arena_marker_t s = sp_mem_begin_scratch(); va_list args; va_start(args, fmt); - sp_fmt_v_a(&io.base, s.mem, fmt, args); + sp_fmt_v(&io.base, s.mem, fmt, args); va_end(args); sp_mem_end_scratch(s); @@ -14220,7 +14182,7 @@ void sp_print_str_a(sp_str_t fmt, ...) { sp_io_write_str(&tls->std.out->base, sp_io_mem_writer_as_str(&io), SP_NULLPTR); } -void sp_print_err_a(const c8* fmt, ...) { +void sp_print_err(const c8* fmt, ...) { u8 buffer[4096] = sp_zero; sp_io_mem_writer_t io = sp_zero; sp_io_mem_writer_from_buffer(&io, buffer, sizeof(buffer)); @@ -14228,7 +14190,7 @@ void sp_print_err_a(const c8* fmt, ...) { sp_mem_arena_marker_t s = sp_mem_begin_scratch(); va_list args; va_start(args, fmt); - sp_fmt_v_a(&io.base, s.mem, sp_str_view(fmt), args); + sp_fmt_v(&io.base, s.mem, sp_str_view(fmt), args); va_end(args); sp_mem_end_scratch(s); @@ -14236,7 +14198,7 @@ void sp_print_err_a(const c8* fmt, ...) { sp_io_write_str(&tls->std.err->base, sp_io_mem_writer_as_str(&io), SP_NULLPTR); } -sp_err_t sp_fmt_render_a(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* directive_params) { +sp_err_t sp_fmt_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* directive_params) { sp_fmt_directive_t* directives[SP_FMT_MAX_DIRECTIVES]; u8 num_dirs = arg->spec.directive_count; @@ -14287,9 +14249,9 @@ sp_err_t sp_fmt_render_a(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp sp_io_dyn_mem_writer_t before_io = sp_zero; sp_io_dyn_mem_writer_t content_io = sp_zero; sp_io_dyn_mem_writer_t after_io = sp_zero; - sp_io_dyn_mem_writer_init_a(mem, &before_io); - sp_io_dyn_mem_writer_init_a(mem, &content_io); - sp_io_dyn_mem_writer_init_a(mem, &after_io); + sp_io_dyn_mem_writer_init(mem, &before_io); + sp_io_dyn_mem_writer_init(mem, &content_io); + sp_io_dyn_mem_writer_init(mem, &after_io); sp_for(it, num_dirs) { if (directives[it]->kind != sp_fmt_directive_decorator) continue; @@ -14319,7 +14281,7 @@ sp_err_t sp_fmt_render_a(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp render.param = params[it] ? &directive_params[it] : SP_NULLPTR; } } - render.fn = render.fn ? render.fn : sp_fmt_render_default_a; + render.fn = render.fn ? render.fn : sp_fmt_render_default; render.fn(&content_io.base, mem, arg, render.param); sp_str_t content = sp_io_dyn_mem_writer_as_str(&content_io); @@ -14329,7 +14291,7 @@ sp_err_t sp_fmt_render_a(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp if (directives[j]->kind != sp_fmt_directive_transformer) continue; sp_fmt_arg_t* p = params[j] ? &directive_params[j] : SP_NULLPTR; sp_io_dyn_mem_writer_t next = sp_zero; - sp_io_dyn_mem_writer_init_a(mem, &next); + sp_io_dyn_mem_writer_init(mem, &next); directives[j]->transformer(&next.base, mem, content, arg, p); content = sp_io_dyn_mem_writer_as_str(&next); } @@ -14346,11 +14308,11 @@ sp_err_t sp_fmt_render_a(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp sp_str_t before = sp_io_dyn_mem_writer_as_str(&before_io); sp_str_t after = sp_io_dyn_mem_writer_as_str(&after_io); - sp_fmt_apply_spec_a(io, before, content, after, arg->spec); + sp_fmt_apply_spec(io, before, content, after, arg->spec); return SP_OK; } -void sp_fmt_apply_spec_a(sp_io_writer_t* io, sp_str_t pre, sp_str_t str, sp_str_t post, sp_fmt_spec_t spec) { +void sp_fmt_apply_spec(sp_io_writer_t* io, sp_str_t pre, sp_str_t str, sp_str_t post, sp_fmt_spec_t spec) { u32 content_len = (u32)str.len; u32 width = spec.width > SP_FMT_WIDTH_MAX ? SP_FMT_WIDTH_MAX : spec.width; u32 pad = (width > content_len) ? (width - content_len) : 0; @@ -14431,14 +14393,14 @@ c8* sp_fmt_uint_to_buf_hex(u64 value, c8* buf_end) { return sp_fmt_uint_to_buf_hex_ex(value, buf_end, "0123456789abcdef"); } -void sp_fmt_write_u64_a(sp_io_writer_t* io, u64 value) { +void sp_fmt_write_u64(sp_io_writer_t* io, u64 value) { c8 buf[20]; c8* end = buf + sizeof(buf); c8* start = sp_fmt_uint_to_buf_dec(value, end); sp_io_write(io, start, (u64)(end - start), SP_NULLPTR); } -void sp_fmt_write_s64_a(sp_io_writer_t* io, s64 value) { +void sp_fmt_write_s64(sp_io_writer_t* io, s64 value) { c8 buf[21]; c8* end = buf + sizeof(buf); u64 abs = (value < 0) ? ((u64)(-(value + 1)) + 1) : (u64)value; @@ -14447,7 +14409,7 @@ void sp_fmt_write_s64_a(sp_io_writer_t* io, s64 value) { sp_io_write(io, start, (u64)(end - start), SP_NULLPTR); } -void sp_fmt_write_ptr_a(sp_io_writer_t* io, void* value) { +void sp_fmt_write_ptr(sp_io_writer_t* io, void* value) { c8 buf[18]; c8* end = buf + sizeof(buf); c8* start = sp_fmt_uint_to_buf_hex((u64)(uintptr_t)value, end); @@ -14456,7 +14418,7 @@ void sp_fmt_write_ptr_a(sp_io_writer_t* io, void* value) { sp_io_write(io, start, (u64)(end - start), SP_NULLPTR); } -void sp_fmt_write_f64_a(sp_io_writer_t* io, f64 value, u32 precision) { +void sp_fmt_write_f64(sp_io_writer_t* io, f64 value, u32 precision) { union { f64 f; u64 u; } bits; bits.f = value; u64 exponent = (bits.u >> 52) & 0x7ffULL; @@ -14501,7 +14463,7 @@ void sp_fmt_write_f64_a(sp_io_writer_t* io, f64 value, u32 precision) { frac_scaled -= scale; } - sp_fmt_write_u64_a(io, int_part); + sp_fmt_write_u64(io, int_part); if (precision > 0) { sp_io_write_c8(io, '.'); @@ -14515,18 +14477,18 @@ void sp_fmt_write_f64_a(sp_io_writer_t* io, f64 value, u32 precision) { } } -void sp_fmt_render_default_a(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { +void sp_fmt_render_default(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { sp_unused(param); switch (arg->id) { case sp_fmt_id_u64: - sp_fmt_write_u64_a(io, arg->u); + sp_fmt_write_u64(io, arg->u); break; case sp_fmt_id_s64: - sp_fmt_write_s64_a(io, arg->i); + sp_fmt_write_s64(io, arg->i); break; case sp_fmt_id_f64: { u32 p = sp_opt_is_null(arg->spec.precision) ? 6 : sp_opt_get(arg->spec.precision); - sp_fmt_write_f64_a(io, arg->f, p); + sp_fmt_write_f64(io, arg->f, p); break; } case sp_fmt_id_str: { @@ -14539,7 +14501,7 @@ void sp_fmt_render_default_a(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg break; } case sp_fmt_id_ptr: { - sp_fmt_write_ptr_a(io, arg->p); + sp_fmt_write_ptr(io, arg->p); break; } case sp_fmt_id_custom: { @@ -14631,7 +14593,7 @@ bool sp_fs_is_glob(sp_str_t path) { return sp_str_find_c8(path, '*') != SP_STR_NO_MATCH; } -sp_str_t sp_fs_normalize_path_a(sp_mem_t mem, sp_str_t path) { +sp_str_t sp_fs_normalize_path(sp_mem_t mem, sp_str_t path) { if (sp_str_back(path) == '/' || sp_str_back(path) == '\\') { path.len--; } @@ -14644,68 +14606,68 @@ sp_str_t sp_fs_normalize_path_a(sp_mem_t mem, sp_str_t path) { return (sp_str_t) { .data = buffer, .len = path.len }; } -sp_str_t sp_fs_join_path_a(sp_mem_t mem, sp_str_t a, sp_str_t b) { +sp_str_t sp_fs_join_path(sp_mem_t mem, sp_str_t a, sp_str_t b) { a = sp_fs_trim_path(a); b = sp_fs_trim_path(b); - if (sp_str_empty(a)) return sp_str_copy_a(mem, b); - if (sp_str_empty(b)) return sp_str_copy_a(mem, a); - return sp_str_join_a(mem, a, b, sp_str_lit("/")); + if (sp_str_empty(a)) return sp_str_copy(mem, b); + if (sp_str_empty(b)) return sp_str_copy(mem, a); + return sp_str_join(mem, a, b, sp_str_lit("/")); } -sp_str_t sp_fs_replace_ext_a(sp_mem_t mem, sp_str_t path, sp_str_t ext) { +sp_str_t sp_fs_replace_ext(sp_mem_t mem, sp_str_t path, sp_str_t ext) { sp_str_t stripped = sp_str_strip_right(path, sp_fs_get_ext(path)); return sp_str_empty(ext) ? - sp_str_copy_a(mem, stripped) : - sp_str_join_a(mem, path, ext, sp_str_lit(".")); + sp_str_copy(mem, stripped) : + sp_str_join(mem, path, ext, sp_str_lit(".")); } -sp_fs_kind_t sp_fs_lstat_kind_a(sp_str_t path) { +sp_fs_kind_t sp_fs_lstat_kind(sp_str_t path) { if (sp_str_empty(path)) return SP_FS_KIND_NONE; sp_sys_stat_t st = sp_zero; s32 rc = sp_sys_lstat_s(path, &st); return rc == 0 ? st.kind : SP_FS_KIND_NONE; } -sp_fs_kind_t sp_fs_stat_kind_a(sp_str_t path) { +sp_fs_kind_t sp_fs_stat_kind(sp_str_t path) { if (sp_str_empty(path)) return SP_FS_KIND_NONE; sp_sys_stat_t st = sp_zero; s32 rc = sp_sys_stat_s(path, &st); return rc == 0 ? st.kind : SP_FS_KIND_NONE; } -bool sp_fs_exists_a(sp_str_t path) { - return sp_fs_stat_kind_a(path) != SP_FS_KIND_NONE; +bool sp_fs_exists(sp_str_t path) { + return sp_fs_stat_kind(path) != SP_FS_KIND_NONE; } -bool sp_fs_is_file_a(sp_str_t path) { - return sp_fs_lstat_kind_a(path) == SP_FS_KIND_FILE; +bool sp_fs_is_file(sp_str_t path) { + return sp_fs_lstat_kind(path) == SP_FS_KIND_FILE; } -bool sp_fs_is_symlink_a(sp_str_t path) { - return sp_fs_lstat_kind_a(path) == SP_FS_KIND_SYMLINK; +bool sp_fs_is_symlink(sp_str_t path) { + return sp_fs_lstat_kind(path) == SP_FS_KIND_SYMLINK; } -bool sp_fs_is_dir_a(sp_str_t path) { - return sp_fs_lstat_kind_a(path) == SP_FS_KIND_DIR; +bool sp_fs_is_dir(sp_str_t path) { + return sp_fs_lstat_kind(path) == SP_FS_KIND_DIR; } -bool sp_fs_is_target_file_a(sp_str_t path) { - return sp_fs_stat_kind_a(path) == SP_FS_KIND_FILE; +bool sp_fs_is_target_file(sp_str_t path) { + return sp_fs_stat_kind(path) == SP_FS_KIND_FILE; } -bool sp_fs_is_target_dir_a(sp_str_t path) { - return sp_fs_stat_kind_a(path) == SP_FS_KIND_DIR; +bool sp_fs_is_target_dir(sp_str_t path) { + return sp_fs_stat_kind(path) == SP_FS_KIND_DIR; } -sp_fs_kind_t sp_fs_get_kind_a(sp_str_t path) { - return sp_fs_lstat_kind_a(path); +sp_fs_kind_t sp_fs_get_kind(sp_str_t path) { + return sp_fs_lstat_kind(path); } -sp_fs_kind_t sp_fs_get_target_kind_a(sp_str_t path) { - return sp_fs_stat_kind_a(path); +sp_fs_kind_t sp_fs_get_target_kind(sp_str_t path) { + return sp_fs_stat_kind(path); } -sp_tm_epoch_t sp_fs_get_mod_time_a(sp_str_t path) { +sp_tm_epoch_t sp_fs_get_mod_time(sp_str_t path) { sp_tm_epoch_t result = sp_zero_s(sp_tm_epoch_t); sp_sys_stat_t st; if (sp_sys_stat_s(path, &st) == 0) { @@ -14715,7 +14677,7 @@ sp_tm_epoch_t sp_fs_get_mod_time_a(sp_str_t path) { return result; } -sp_str_t sp_fs_canonicalize_path_a(sp_mem_t mem, sp_str_t path) { +sp_str_t sp_fs_canonicalize_path(sp_mem_t mem, sp_str_t path) { if (sp_str_empty(path)) return sp_zero_s(sp_str_t); c8 buf[SP_PATH_MAX]; @@ -14723,40 +14685,40 @@ sp_str_t sp_fs_canonicalize_path_a(sp_mem_t mem, sp_str_t path) { if (len <= 0) return sp_zero_s(sp_str_t); sp_str_t canonical = { .data = buf, .len = (u32)len }; - return sp_fs_normalize_path_a(mem, canonical); + return sp_fs_normalize_path(mem, canonical); } -sp_str_t sp_fs_get_exe_path_a(sp_mem_t mem) { +sp_str_t sp_fs_get_exe_path(sp_mem_t mem) { c8 buf[SP_PATH_MAX]; s64 len = sp_sys_get_exe_path(buf, SP_PATH_MAX); if (len <= 0) return sp_str_lit(""); - return sp_fs_normalize_path_a(mem, (sp_str_t){ .data = buf, .len = (u32)len }); + return sp_fs_normalize_path(mem, (sp_str_t){ .data = buf, .len = (u32)len }); } -sp_str_t sp_fs_get_cwd_a(sp_mem_t mem) { - sp_str_t cwd = sp_os_get_cwd_a(mem); - return sp_fs_normalize_path_a(mem, cwd); +sp_str_t sp_fs_get_cwd(sp_mem_t mem) { + sp_str_t cwd = sp_os_get_cwd(mem); + return sp_fs_normalize_path(mem, cwd); } -sp_str_t sp_fs_get_storage_path_a(sp_mem_t mem) { +sp_str_t sp_fs_get_storage_path(sp_mem_t mem) { c8 buf[SP_PATH_MAX]; s64 len = sp_sys_get_storage_path(buf, SP_PATH_MAX); if (len <= 0) return sp_zero_s(sp_str_t); - return sp_fs_normalize_path_a(mem, (sp_str_t){ .data = buf, .len = (u32)len }); + return sp_fs_normalize_path(mem, (sp_str_t){ .data = buf, .len = (u32)len }); } -sp_str_t sp_fs_get_config_path_a(sp_mem_t mem) { +sp_str_t sp_fs_get_config_path(sp_mem_t mem) { c8 buf[SP_PATH_MAX]; s64 len = sp_sys_get_config_path(buf, SP_PATH_MAX); if (len <= 0) return sp_zero_s(sp_str_t); - return sp_fs_normalize_path_a(mem, (sp_str_t){ .data = buf, .len = (u32)len }); + return sp_fs_normalize_path(mem, (sp_str_t){ .data = buf, .len = (u32)len }); } // // fs: io wrappers // -sp_err_t sp_io_read_file_a(sp_mem_t mem, sp_str_t path, sp_str_t* content) { +sp_err_t sp_io_read_file(sp_mem_t mem, sp_str_t path, sp_str_t* content) { sp_assert(content); sp_err_t err = SP_OK; c8* buffer = SP_NULLPTR; @@ -14769,7 +14731,7 @@ sp_err_t sp_io_read_file_a(sp_mem_t mem, sp_str_t path, sp_str_t* content) { sp_try_goto(sp_io_file_reader_size(&reader, &size), err, cleanup); if (!size) goto cleanup; - buffer = sp_alloc_n_a(mem, c8, size); + buffer = sp_alloc_n(mem, c8, size); sp_try_goto(sp_io_read(&reader.base, buffer, size, &bytes_read), err, cleanup); content->data = buffer; content->len = (u32)bytes_read; @@ -14781,10 +14743,10 @@ sp_err_t sp_io_read_file_a(sp_mem_t mem, sp_str_t path, sp_str_t* content) { return err; } -sp_err_t sp_fs_create_dir_a(sp_str_t path) { +sp_err_t sp_fs_create_dir(sp_str_t path) { if (sp_str_empty(path)) return SP_ERR_LAZY; - if (sp_fs_exists_a(path)) { - return sp_fs_is_dir_a(path) ? SP_OK : SP_ERR_LAZY; + if (sp_fs_exists(path)) { + return sp_fs_is_dir(path) ? SP_OK : SP_ERR_LAZY; } sp_err_t result = SP_OK; @@ -14793,7 +14755,7 @@ sp_err_t sp_fs_create_dir_a(sp_str_t path) { // Walk up, collecting intermediate paths that don't exist path = sp_fs_trim_path(path); - while (!sp_fs_is_root(path) && !sp_fs_exists_a(path)) { + while (!sp_fs_is_root(path) && !sp_fs_exists(path)) { sp_da_push(missing, path); path = sp_fs_parent_path(path); } @@ -14801,7 +14763,7 @@ sp_err_t sp_fs_create_dir_a(sp_str_t path) { // Walk back down and create each one sp_da_rfor(missing, it) { result = sp_os_create_dir(missing[it]); - if (result && !sp_fs_exists_a(missing[it])) goto cleanup; + if (result && !sp_fs_exists(missing[it])) goto cleanup; } cleanup: @@ -14809,11 +14771,11 @@ sp_err_t sp_fs_create_dir_a(sp_str_t path) { return result; } -sp_err_t sp_fs_create_file_a(sp_str_t path) { +sp_err_t sp_fs_create_file(sp_str_t path) { return sp_os_create_file(path); } -sp_err_t sp_fs_create_file_slice_a(sp_str_t path, sp_mem_slice_t slice) { +sp_err_t sp_fs_create_file_slice(sp_str_t path, sp_mem_slice_t slice) { sp_try(sp_os_create_file(path)); sp_io_file_writer_t io = sp_zero; sp_try(sp_io_file_writer_from_path(&io, path, SP_IO_WRITE_MODE_OVERWRITE)); @@ -14822,7 +14784,7 @@ sp_err_t sp_fs_create_file_slice_a(sp_str_t path, sp_mem_slice_t slice) { return SP_OK; } -sp_err_t sp_fs_create_file_str_a(sp_str_t path, sp_str_t str) { +sp_err_t sp_fs_create_file_str(sp_str_t path, sp_str_t str) { sp_try(sp_os_create_file(path)); sp_io_file_writer_t io = sp_zero; sp_try(sp_io_file_writer_from_path(&io, path, SP_IO_WRITE_MODE_OVERWRITE)); @@ -14831,57 +14793,57 @@ sp_err_t sp_fs_create_file_str_a(sp_str_t path, sp_str_t str) { return SP_OK; } -sp_err_t sp_fs_create_file_cstr_a(sp_str_t path, const c8* str) { - return sp_fs_create_file_str_a(path, sp_str_view(str)); +sp_err_t sp_fs_create_file_cstr(sp_str_t path, const c8* str) { + return sp_fs_create_file_str(path, sp_str_view(str)); } -sp_err_t sp_fs_create_hard_link_a(sp_str_t target, sp_str_t link_path) { +sp_err_t sp_fs_create_hard_link(sp_str_t target, sp_str_t link_path) { return sp_os_create_hard_link(target, link_path); } -sp_err_t sp_fs_create_sym_link_a(sp_str_t target, sp_str_t link_path) { +sp_err_t sp_fs_create_sym_link(sp_str_t target, sp_str_t link_path) { return sp_os_create_sym_link(target, link_path); } -sp_err_t sp_fs_link_a(sp_str_t from, sp_str_t to, sp_fs_link_kind_t kind) { +sp_err_t sp_fs_link(sp_str_t from, sp_str_t to, sp_fs_link_kind_t kind) { switch (kind) { - case SP_FS_LINK_HARD: return sp_fs_create_hard_link_a(from, to); - case SP_FS_LINK_SYMBOLIC: return sp_fs_create_sym_link_a(from, to); - case SP_FS_LINK_COPY: return sp_fs_copy_a(from, to); + case SP_FS_LINK_HARD: return sp_fs_create_hard_link(from, to); + case SP_FS_LINK_SYMBOLIC: return sp_fs_create_sym_link(from, to); + case SP_FS_LINK_COPY: return sp_fs_copy(from, to); } SP_UNREACHABLE_RETURN(SP_OK); } -sp_err_t sp_fs_remove_file_a(sp_str_t path) { +sp_err_t sp_fs_remove_file(sp_str_t path) { return sp_cast(sp_err_t, sp_sys_unlink_s(path)); } -void sp_fs_it_push_a(sp_fs_it_t* it, sp_str_t path) { +void sp_fs_it_push(sp_fs_it_t* it, sp_str_t path) { sp_fs_it_frame_t frame = sp_zero; if (sp_sys_fs_it_open_s(&frame.sys, path, sp_mem_slice(frame.buf, SP_FS_IT_BUF_SIZE)) < 0) return; - frame.path = sp_str_copy_a(it->mem, path); + frame.path = sp_str_copy(it->mem, path); sp_da_push(it->stack, frame); } -void sp_fs_it_begin_a(sp_fs_it_t* it, sp_str_t path) { - if (sp_str_empty(path) || !sp_fs_is_dir_a(path)) return; - sp_fs_it_push_a(it, path); - sp_fs_it_next_a(it); +void sp_fs_it_begin(sp_fs_it_t* it, sp_str_t path) { + if (sp_str_empty(path) || !sp_fs_is_dir(path)) return; + sp_fs_it_push(it, path); + sp_fs_it_next(it); } -void sp_fs_it_next_a(sp_fs_it_t* it) { +void sp_fs_it_next(sp_fs_it_t* it) { while (!sp_da_empty(it->stack)) { sp_fs_it_frame_t* top = sp_da_back(it->stack); top->sys.buf.data = top->buf; sp_sys_fs_entry_t d; if (sp_sys_fs_it_next(&top->sys, &d) == 0) { - it->entry.name = sp_str_from_cstr_n_a(it->mem, d.name, d.len); + it->entry.name = sp_str_from_cstr_n(it->mem, d.name, d.len); it->entry.kind = d.kind; - it->entry.path = sp_fs_join_path_a(it->mem, top->path, it->entry.name); + it->entry.path = sp_fs_join_path(it->mem, top->path, it->entry.name); if (it->recursive && it->entry.kind == SP_FS_KIND_DIR) { - sp_fs_it_push_a(it, it->entry.path); + sp_fs_it_push(it, it->entry.path); } return; } @@ -14891,18 +14853,18 @@ void sp_fs_it_next_a(sp_fs_it_t* it) { } } -bool sp_fs_it_valid_a(sp_fs_it_t* it) { +bool sp_fs_it_valid(sp_fs_it_t* it) { return !sp_da_empty(it->stack); } -void sp_fs_it_deinit_a(sp_fs_it_t* it) { +void sp_fs_it_deinit(sp_fs_it_t* it) { sp_da_for(it->stack, i) { sp_sys_fs_it_close(&it->stack[i].sys); } sp_da_free(it->stack); } -sp_da(sp_fs_entry_t) sp_fs_collect_a(sp_mem_t mem, sp_str_t path) { +sp_da(sp_fs_entry_t) sp_fs_collect(sp_mem_t mem, sp_str_t path) { sp_da(sp_fs_entry_t) entries = sp_da_new(mem, sp_fs_entry_t); sp_fs_for(mem, path, it) { @@ -14911,7 +14873,7 @@ sp_da(sp_fs_entry_t) sp_fs_collect_a(sp_mem_t mem, sp_str_t path) { return entries; } -sp_da(sp_fs_entry_t) sp_fs_collect_recursive_a(sp_mem_t mem, sp_str_t path) { +sp_da(sp_fs_entry_t) sp_fs_collect_recursive(sp_mem_t mem, sp_str_t path) { sp_da(sp_fs_entry_t) entries = sp_da_new(mem, sp_fs_entry_t); sp_fs_for_recursive(mem, path, it) { @@ -14920,17 +14882,17 @@ sp_da(sp_fs_entry_t) sp_fs_collect_recursive_a(sp_mem_t mem, sp_str_t path) { return entries; } -sp_err_t sp_fs_remove_dir_a(sp_str_t path) { +sp_err_t sp_fs_remove_dir(sp_str_t path) { sp_err_t err = SP_OK; sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_da(sp_fs_entry_t) entries = sp_fs_collect_a(s.mem, path); + sp_da(sp_fs_entry_t) entries = sp_fs_collect(s.mem, path); sp_da_for(entries, i) { sp_fs_entry_t* entry = &entries[i]; switch (entry->kind) { case SP_FS_KIND_FILE: - case SP_FS_KIND_SYMLINK: err = sp_fs_remove_file_a(entry->path); break; - case SP_FS_KIND_DIR: err = sp_fs_remove_dir_a(entry->path); break; + case SP_FS_KIND_SYMLINK: err = sp_fs_remove_file(entry->path); break; + case SP_FS_KIND_DIR: err = sp_fs_remove_dir(entry->path); break; case SP_FS_KIND_NONE: err = SP_ERR_OS; break; } if (err) goto done; @@ -14943,14 +14905,14 @@ sp_err_t sp_fs_remove_dir_a(sp_str_t path) { return err; } -void sp_fs_copy_file_a(sp_str_t from, sp_str_t to) { +void sp_fs_copy_file(sp_str_t from, sp_str_t to) { sp_mem_arena_marker_t s = sp_mem_begin_scratch(); sp_sys_stat_t st = sp_zero; sp_io_file_reader_t reader = sp_zero; sp_io_file_writer_t writer = sp_zero; - if (sp_fs_is_dir_a(to)) { - to = sp_fs_join_path_a(s.mem, to, sp_fs_get_name(from)); + if (sp_fs_is_dir(to)) { + to = sp_fs_join_path(s.mem, to, sp_fs_get_name(from)); } if (sp_sys_stat_s(from, &st)) goto done; @@ -14975,11 +14937,11 @@ void sp_fs_copy_file_a(sp_str_t from, sp_str_t to) { sp_mem_end_scratch(s); } -void sp_fs_copy_glob_a(sp_str_t from, sp_str_t glob, sp_str_t to) { - sp_fs_create_dir_a(to); +void sp_fs_copy_glob(sp_str_t from, sp_str_t glob, sp_str_t to) { + sp_fs_create_dir(to); sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_da(sp_fs_entry_t) entries = sp_fs_collect_a(s.mem, from); + sp_da(sp_fs_entry_t) entries = sp_fs_collect(s.mem, from); sp_da_for(entries, i) { sp_fs_entry_t* entry = &entries[i]; @@ -14989,58 +14951,58 @@ void sp_fs_copy_glob_a(sp_str_t from, sp_str_t glob, sp_str_t to) { if (!matches) matches = sp_str_equal(entry_name, glob); if (matches) { - sp_str_t entry_path = sp_fs_join_path_a(s.mem, from, entry_name); - sp_fs_copy_a(entry_path, to); + sp_str_t entry_path = sp_fs_join_path(s.mem, from, entry_name); + sp_fs_copy(entry_path, to); } } sp_mem_end_scratch(s); } -void sp_fs_copy_dir_a(sp_str_t from, sp_str_t to) { - if (sp_fs_is_dir_a(to)) { +void sp_fs_copy_dir(sp_str_t from, sp_str_t to) { + if (sp_fs_is_dir(to)) { sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - to = sp_fs_join_path_a(s.mem, to, sp_fs_get_name(from)); - sp_fs_copy_glob_a(from, sp_str_lit("*"), to); + to = sp_fs_join_path(s.mem, to, sp_fs_get_name(from)); + sp_fs_copy_glob(from, sp_str_lit("*"), to); sp_mem_end_scratch(s); return; } - sp_fs_copy_glob_a(from, sp_str_lit("*"), to); + sp_fs_copy_glob(from, sp_str_lit("*"), to); } -sp_err_t sp_fs_copy_a(sp_str_t from, sp_str_t to) { +sp_err_t sp_fs_copy(sp_str_t from, sp_str_t to) { if (sp_fs_is_glob(from)) { - sp_fs_copy_glob_a(sp_fs_parent_path(from), sp_fs_get_name(from), to); + sp_fs_copy_glob(sp_fs_parent_path(from), sp_fs_get_name(from), to); } - else if (sp_fs_is_target_dir_a(from)) { - SP_ASSERT(sp_fs_is_target_dir_a(to)); + else if (sp_fs_is_target_dir(from)) { + SP_ASSERT(sp_fs_is_target_dir(to)); sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_fs_copy_glob_a(from, sp_str_lit("*"), sp_fs_join_path_a(s.mem, to, sp_fs_get_name(from))); + sp_fs_copy_glob(from, sp_str_lit("*"), sp_fs_join_path(s.mem, to, sp_fs_get_name(from))); sp_mem_end_scratch(s); } - else if (sp_fs_is_target_file_a(from)) { - sp_fs_copy_file_a(from, to); + else if (sp_fs_is_target_file(from)) { + sp_fs_copy_file(from, to); } return SP_OK; } -sp_fs_it_t sp_fs_it_new_a(sp_mem_t mem, sp_str_t path) { +sp_fs_it_t sp_fs_it_new(sp_mem_t mem, sp_str_t path) { sp_fs_it_t it = { .mem = mem }; sp_da_init(mem, it.stack); - sp_fs_it_begin_a(&it, path); + sp_fs_it_begin(&it, path); return it; } -sp_fs_it_t sp_fs_it_new_recursive_a(sp_mem_t mem, sp_str_t path) { +sp_fs_it_t sp_fs_it_new_recursive(sp_mem_t mem, sp_str_t path) { sp_fs_it_t it = { .mem = mem, .recursive = true }; sp_da_init(mem, it.stack); - sp_fs_it_begin_a(&it, path); + sp_fs_it_begin(&it, path); return it; } // // time // -sp_str_t sp_tm_epoch_to_iso8601_a(sp_mem_t mem, sp_tm_epoch_t time) { +sp_str_t sp_tm_epoch_to_iso8601(sp_mem_t mem, sp_tm_epoch_t time) { sp_tm_date_time_t dt = sp_tm_epoch_to_date_time(time); c8* buf = (c8*)sp_mem_allocator_alloc(mem, 24); // YYYY-MM-DDTHH:MM:SS.mmmZ @@ -15072,9 +15034,9 @@ sp_str_t sp_tm_epoch_to_iso8601_a(sp_mem_t mem, sp_tm_epoch_t time) { } // @refactor -sp_str_t sp_str_reduce_a(sp_mem_t mem, sp_str_t* strings, u32 num_strings, void* user_data, sp_str_reduce_fn_t fn) { +sp_str_t sp_str_reduce(sp_mem_t mem, sp_str_t* strings, u32 num_strings, void* user_data, sp_str_reduce_fn_t fn) { sp_io_dyn_mem_writer_t io = sp_zero; - sp_io_dyn_mem_writer_init_a(mem, &io); + sp_io_dyn_mem_writer_init(mem, &io); sp_str_reduce_context_t context = { .user_data = user_data, @@ -15094,7 +15056,7 @@ sp_str_t sp_str_reduce_a(sp_mem_t mem, sp_str_t* strings, u32 num_strings, void* return sp_io_dyn_mem_writer_as_str(&io); } -void sp_str_reduce_kernel_join_a(sp_str_reduce_context_t* context) { +void sp_str_reduce_kernel_join(sp_str_reduce_context_t* context) { if (sp_str_empty(context->str)) return; sp_io_write_str(context->writer, context->str, SP_NULLPTR); @@ -15105,8 +15067,8 @@ void sp_str_reduce_kernel_join_a(sp_str_reduce_context_t* context) { } } -sp_str_t sp_str_join_n_a(sp_mem_t mem, sp_str_t* strings, u32 num_strings, sp_str_t joiner) { - return sp_str_reduce_a(mem, strings, num_strings, &joiner, sp_str_reduce_kernel_join_a); +sp_str_t sp_str_join_n(sp_mem_t mem, sp_str_t* strings, u32 num_strings, sp_str_t joiner) { + return sp_str_reduce(mem, strings, num_strings, &joiner, sp_str_reduce_kernel_join); } diff --git a/sp/sp_asset.h b/sp/sp_asset.h index 63688d8..c840a76 100644 --- a/sp/sp_asset.h +++ b/sp/sp_asset.h @@ -43,7 +43,7 @@ typedef struct { sp_asset_registry_t* registry; sp_asset_kind_t kind; void* fallback; - sp_ht_a(sp_str_t, sp_asset_t*) assets; + sp_ht(sp_str_t, sp_asset_t*) assets; } sp_asset_importer_t; struct sp_asset_import_context { @@ -127,7 +127,7 @@ void sp_asset_registry_init(sp_asset_registry_t* registry, sp_mem_t mem, sp_asse .fallback = cfg->fallback, .assets = SP_NULLPTR, }; - sp_str_ht_init_a(registry->mem, importer.assets); + sp_str_ht_init(registry->mem, importer.assets); sp_da_push(registry->importers, importer); } @@ -162,13 +162,13 @@ sp_asset_importer_t* sp_asset_registry_find_importer(sp_asset_registry_t* r, sp_ } static sp_asset_t* sp_asset_registry_alloc(sp_asset_registry_t* r, sp_asset_importer_t* importer, sp_str_t name, void* data) { - sp_asset_t* asset = sp_alloc_type_a(r->mem, sp_asset_t); + sp_asset_t* asset = sp_alloc_type(r->mem, sp_asset_t); asset->kind = importer->kind; asset->state = SP_ASSET_STATE_QUEUED; - asset->name = sp_str_copy_a(r->mem, name); + asset->name = sp_str_copy(r->mem, name); sp_atomic_ptr_set(&asset->data, data); - sp_str_ht_insert(importer->assets, sp_str_copy_a(r->mem, name), asset); + sp_str_ht_insert(importer->assets, sp_str_copy(r->mem, name), asset); return asset; } diff --git a/sp/sp_elf.h b/sp/sp_elf.h index aad2335..bda9a96 100644 --- a/sp/sp_elf.h +++ b/sp/sp_elf.h @@ -146,7 +146,7 @@ typedef struct { sp_mem_arena_t* arena; sp_mem_t mem; sp_da(sp_elf_section_t) sections; - sp_ht_a(sp_str_t, u32) section_map; + sp_ht(sp_str_t, u32) section_map; } sp_elf_t; sp_elf_t* sp_elf_new(sp_mem_t mem); @@ -196,7 +196,7 @@ sp_elf_t* sp_elf_new(sp_mem_t mem) { elf->arena = arena; elf->mem = am; sp_da_init(am, elf->sections); - sp_str_ht_init_a(am, elf->section_map); + sp_str_ht_init(am, elf->section_map); return elf; } @@ -224,7 +224,7 @@ sp_elf_section_t* sp_elf_add_section(sp_elf_t* elf, sp_str_t name, u32 kind, u64 sp_require_as_null(sp_elf_is_align_valid(alignment)); sp_elf_section_t section = { - .name = sp_str_copy_a(elf->mem, name), + .name = sp_str_copy(elf->mem, name), .index = sp_cast(u32, sp_da_size(elf->sections)), .type = kind, .alignment = alignment, @@ -394,7 +394,7 @@ sp_elf_section_t* sp_elf_rela_new(sp_elf_t* elf, sp_elf_section_t* target) { sp_elf_section_t* symtab = sp_elf_find_section_by_name(elf, sp_str_lit(".symtab")); if (!symtab) return SP_NULLPTR; - sp_str_t name = sp_fmt_a(elf->mem, ".rela{}", sp_fmt_str(target->name)).value; + sp_str_t name = sp_fmt(elf->mem, ".rela{}", sp_fmt_str(target->name)).value; sp_elf_section_t* rela = sp_elf_add_section(elf, name, SHT_RELA, 8); if (!rela) return SP_NULLPTR; rela->link = symtab->index; @@ -429,7 +429,7 @@ void sp_elf_symtab_sort(sp_elf_section_t* symtab, sp_elf_t* elf) { } Elf64_Sym* syms = (Elf64_Sym*)symtab->buffer.data; - Elf64_Sym* new_syms = sp_alloc_n_a(elf->mem, Elf64_Sym, count); + Elf64_Sym* new_syms = sp_alloc_n(elf->mem, Elf64_Sym, count); u32 local_idx = 0; SP_UNUSED(count); @@ -638,7 +638,7 @@ sp_elf_t* sp_elf_read(sp_mem_t mem, const u8* data, u64 size) { sp_elf_t* elf = sp_elf_new(mem); - Elf64_Shdr* section_headers = sp_alloc_n_a(elf->mem, Elf64_Shdr, num_sections); + Elf64_Shdr* section_headers = sp_alloc_n(elf->mem, Elf64_Shdr, num_sections); sp_mem_copy(section_headers, data + ehdr.e_shoff, sh_table_bytes); Elf64_Shdr* string_header = §ion_headers[ehdr.e_shstrndx]; @@ -649,7 +649,7 @@ sp_elf_t* sp_elf_read(sp_mem_t mem, const u8* data, u64 size) { sp_elf_free(elf); return SP_NULLPTR; } - string_table = sp_alloc_n_a(elf->mem, c8, string_header->sh_size); + string_table = sp_alloc_n(elf->mem, c8, string_header->sh_size); sp_mem_copy(string_table, data + string_header->sh_offset, string_header->sh_size); } @@ -703,11 +703,11 @@ sp_elf_t* sp_elf_read(sp_mem_t mem, const u8* data, u64 size) { sp_elf_t* sp_elf_read_from_file(sp_mem_t mem, sp_str_t path) { sp_str_t bytes = sp_zero; - if (sp_io_read_file_a(mem, path, &bytes)) { + if (sp_io_read_file(mem, path, &bytes)) { return SP_NULLPTR; } sp_elf_t* elf = sp_elf_read(mem, (const u8*)bytes.data, bytes.len); - sp_free_a(mem, (void*)bytes.data); + sp_free(mem, (void*)bytes.data); return elf; } diff --git a/sp/sp_glob.h b/sp/sp_glob.h index ac7f9c4..74b6d22 100644 --- a/sp/sp_glob.h +++ b/sp/sp_glob.h @@ -611,9 +611,9 @@ sp_glob_set_t* sp_glob_set_new(sp_mem_t mem) { sp_da_init(am, set->prefixes); sp_da_init(am, set->suffixes); sp_da_init(am, set->fallbacks); - sp_str_ht_init_a(am, set->literal); - sp_str_ht_init_a(am, set->base_name); - sp_str_ht_init_a(am, set->extension); + sp_str_ht_init(am, set->literal); + sp_str_ht_init(am, set->base_name); + sp_str_ht_init(am, set->extension); return set; } diff --git a/sp/sp_macho.h b/sp/sp_macho.h index 1a4b0ba..1034bea 100644 --- a/sp/sp_macho.h +++ b/sp/sp_macho.h @@ -73,7 +73,7 @@ u32 sp_macho_add_section(sp_macho_t* m, sp_str_t name, u64 align) { u32 index = sp_da_size(m->sections) + 1; sp_macho_section_t section = { - .name = sp_str_copy_a(m->mem, name), + .name = sp_str_copy(m->mem, name), .index = index, .align = align, .flags = 0, @@ -113,7 +113,7 @@ u32 sp_macho_add_symbol(sp_macho_t* m, sp_str_t name, u32 sect, u64 offset, sp_m u32 idx = sp_da_size(m->symbols); sp_macho_symbol_t sym = { - .name = sp_str_copy_a(m->mem, name), + .name = sp_str_copy(m->mem, name), .value = offset, .sect = (u8)sect, .bind = bind, diff --git a/sp/sp_msvc.h b/sp/sp_msvc.h index ed206ac..6a6e3bf 100644 --- a/sp/sp_msvc.h +++ b/sp/sp_msvc.h @@ -89,7 +89,7 @@ sp_str_t sp_msvc_bin_subdir(sp_msvc_arch_t arch) { sp_msvc_version_t sp_msvc_parse_version(sp_mem_t mem, sp_str_t str) { sp_msvc_version_t v = sp_zero; - v.str = sp_str_copy_a(mem, str); + v.str = sp_str_copy(mem, str); u32 parts[4] = sp_zero; u32 part = 0; @@ -123,7 +123,7 @@ bool sp_msvc_version_gt(sp_msvc_version_t a, sp_msvc_version_t b) { sp_str_t sp_msvc_json_get_str(sp_mem_t mem, sp_str_t json, sp_str_t key) { sp_mem_arena_marker_t scratch = sp_mem_begin_scratch_for(mem); - sp_str_t needle = sp_fmt_a(scratch.mem, "\"{}\":\"", sp_fmt_str(key)).value; + sp_str_t needle = sp_fmt(scratch.mem, "\"{}\":\"", sp_fmt_str(key)).value; s32 pos = sp_str_find(json, needle); if (pos == SP_STR_NO_MATCH) { @@ -143,7 +143,7 @@ sp_str_t sp_msvc_json_get_str(sp_mem_t mem, sp_str_t json, sp_str_t key) { sp_str_t raw = sp_str_sub(json, (s32)value_start, (s32)(value_end - value_start)); sp_io_dyn_mem_writer_t builder = sp_zero; - sp_io_dyn_mem_writer_init_a(mem, &builder); + sp_io_dyn_mem_writer_init(mem, &builder); sp_for(i, raw.len) { if (raw.data[i] == '\\' && i + 1 < raw.len && raw.data[i + 1] == '\\') { sp_io_write_c8(&builder.base, '\\'); @@ -179,8 +179,8 @@ sp_msvc_err_t sp_msvc_find_sdks(sp_msvc_t* msvc, sp_msvc_arch_t arch) { if (rc != ERROR_SUCCESS || type != REG_SZ) return SP_MSVC_ERR_SDK_NOT_FOUND; - sp_str_t sdk_root = sp_fs_normalize_path_a(mem, sp_str_view(root_buf)); - sp_str_t lib_dir = sp_fs_join_path_a(mem, sdk_root, sp_str_lit("Lib")); + sp_str_t sdk_root = sp_fs_normalize_path(mem, sp_str_view(root_buf)); + sp_str_t lib_dir = sp_fs_join_path(mem, sdk_root, sp_str_lit("Lib")); sp_str_t arch_str = sp_msvc_arch_str(arch); for (sp_fs_it_t it = sp_fs_it_new(lib_dir); sp_fs_it_valid(&it); sp_fs_it_next(&it)) { @@ -188,25 +188,25 @@ sp_msvc_err_t sp_msvc_find_sdks(sp_msvc_t* msvc, sp_msvc_arch_t arch) { sp_str_t name = it.entry.name; if (!sp_str_starts_with(name, sp_str_lit("10."))) continue; - sp_str_t ucrt_lib = sp_fs_join_path_a(mem, - sp_fs_join_path_a(mem, it.entry.path, sp_str_lit("ucrt")), arch_str + sp_str_t ucrt_lib = sp_fs_join_path(mem, + sp_fs_join_path(mem, it.entry.path, sp_str_lit("ucrt")), arch_str ); if (!sp_fs_is_dir(ucrt_lib)) continue; sp_str_t ver_root = it.entry.path; sp_msvc_sdk_t sdk = { .version = sp_msvc_parse_version(mem, name), - .root = sp_str_copy_a(mem, sdk_root), - .lib_um = sp_fs_join_path_a(mem, sp_fs_join_path_a(mem, ver_root, sp_str_lit("um")), arch_str), - .lib_ucrt = sp_str_copy_a(mem, ucrt_lib), - .include_ucrt = sp_fs_join_path_a(mem, - sp_fs_join_path_a(mem, sp_fs_join_path_a(mem, sdk_root, sp_str_lit("Include")), name), sp_str_lit("ucrt") + .root = sp_str_copy(mem, sdk_root), + .lib_um = sp_fs_join_path(mem, sp_fs_join_path(mem, ver_root, sp_str_lit("um")), arch_str), + .lib_ucrt = sp_str_copy(mem, ucrt_lib), + .include_ucrt = sp_fs_join_path(mem, + sp_fs_join_path(mem, sp_fs_join_path(mem, sdk_root, sp_str_lit("Include")), name), sp_str_lit("ucrt") ), - .include_um = sp_fs_join_path_a(mem, - sp_fs_join_path_a(mem, sp_fs_join_path_a(mem, sdk_root, sp_str_lit("Include")), name), sp_str_lit("um") + .include_um = sp_fs_join_path(mem, + sp_fs_join_path(mem, sp_fs_join_path(mem, sdk_root, sp_str_lit("Include")), name), sp_str_lit("um") ), - .include_shared = sp_fs_join_path_a(mem, - sp_fs_join_path_a(mem, sp_fs_join_path_a(mem, sdk_root, sp_str_lit("Include")), name), sp_str_lit("shared") + .include_shared = sp_fs_join_path(mem, + sp_fs_join_path(mem, sp_fs_join_path(mem, sdk_root, sp_str_lit("Include")), name), sp_str_lit("shared") ), }; sp_da_push(*out, sdk); @@ -232,7 +232,7 @@ sp_msvc_err_t sp_msvc_find_installations(sp_msvc_t* msvc, sp_msvc_arch_t arch) { sp_str_t program_data = sp_os_env_get(sp_str_lit("ProgramData")); if (!sp_str_valid(program_data)) return SP_MSVC_ERR_VS_NOT_FOUND; - sp_str_t instances_dir = sp_fs_join_path_a(mem, + sp_str_t instances_dir = sp_fs_join_path(mem, program_data, sp_str_lit("Microsoft/VisualStudio/Packages/_Instances") ); if (!sp_fs_is_dir(instances_dir)) return SP_MSVC_ERR_VS_NOT_FOUND; @@ -242,54 +242,54 @@ sp_msvc_err_t sp_msvc_find_installations(sp_msvc_t* msvc, sp_msvc_arch_t arch) { for (sp_fs_it_t it = sp_fs_it_new(instances_dir); sp_fs_it_valid(&it); sp_fs_it_next(&it)) { if (it.entry.kind != SP_FS_KIND_DIR) continue; - sp_str_t state_path = sp_fs_join_path_a(mem, it.entry.path, sp_str_lit("state.json")); + sp_str_t state_path = sp_fs_join_path(mem, it.entry.path, sp_str_lit("state.json")); if (!sp_fs_exists(state_path)) continue; sp_str_t json = sp_zero; - sp_io_read_file_a(mem, state_path, &json); + sp_io_read_file(mem, state_path, &json); if (sp_str_empty(json)) continue; sp_str_t install_path = sp_msvc_json_get_str(mem, json, sp_str_lit("installationPath")); if (sp_str_empty(install_path)) continue; - install_path = sp_fs_normalize_path_a(mem, install_path); + install_path = sp_fs_normalize_path(mem, install_path); sp_str_t build_version_str = sp_msvc_json_get_str(mem, json, sp_str_lit("buildVersion")); if (sp_str_empty(build_version_str)) continue; sp_str_t product_line = sp_msvc_json_get_str(mem, json, sp_str_lit("productLineVersion")); - sp_str_t tools_file = sp_fs_join_path_a(mem, + sp_str_t tools_file = sp_fs_join_path(mem, install_path, sp_str_lit("VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt") ); sp_str_t tools_version_str = sp_zero; - sp_io_read_file_a(mem, tools_file, &tools_version_str); + sp_io_read_file(mem, tools_file, &tools_version_str); if (sp_str_empty(tools_version_str)) continue; tools_version_str = sp_str_trim(tools_version_str); - sp_str_t tools_base = sp_fs_join_path_a(mem, - sp_fs_join_path_a(mem, install_path, sp_str_lit("VC/Tools/MSVC")), tools_version_str + sp_str_t tools_base = sp_fs_join_path(mem, + sp_fs_join_path(mem, install_path, sp_str_lit("VC/Tools/MSVC")), tools_version_str ); - sp_str_t lib_dir = sp_fs_join_path_a(mem, - sp_fs_join_path_a(mem, tools_base, sp_str_lit("Lib")), arch_str + sp_str_t lib_dir = sp_fs_join_path(mem, + sp_fs_join_path(mem, tools_base, sp_str_lit("Lib")), arch_str ); - sp_str_t vcruntime = sp_fs_join_path_a(mem, lib_dir, sp_str_lit("vcruntime.lib")); + sp_str_t vcruntime = sp_fs_join_path(mem, lib_dir, sp_str_lit("vcruntime.lib")); if (!sp_fs_exists(vcruntime)) continue; sp_msvc_vs_t vs = { .version = { - .product = sp_str_copy_a(mem, product_line), + .product = sp_str_copy(mem, product_line), .build = sp_msvc_parse_version(mem, build_version_str), .tools = sp_msvc_parse_version(mem, tools_version_str), }, - .install_path = sp_str_copy_a(mem, install_path), - .lib = sp_str_copy_a(mem, lib_dir), - .include = sp_fs_join_path_a(mem, tools_base, sp_str_lit("include")), - .bin = sp_fs_join_path_a(mem, - sp_fs_join_path_a(mem, tools_base, sp_str_lit("bin")), sp_msvc_bin_subdir(arch) + .install_path = sp_str_copy(mem, install_path), + .lib = sp_str_copy(mem, lib_dir), + .include = sp_fs_join_path(mem, tools_base, sp_str_lit("include")), + .bin = sp_fs_join_path(mem, + sp_fs_join_path(mem, tools_base, sp_str_lit("bin")), sp_msvc_bin_subdir(arch) ), }; sp_da_push(*out, vs); diff --git a/sp/sp_prompt.h b/sp/sp_prompt.h index 2e54d5b..03b6c8b 100644 --- a/sp/sp_prompt.h +++ b/sp/sp_prompt.h @@ -352,7 +352,7 @@ ////////// // ANSI // ////////// -// Static ANSI sequences. The _FMT variants are sp_fmt_a(ctx->mem, ).value templates ({} is the +// Static ANSI sequences. The _FMT variants are sp_fmt(ctx->mem, ).value templates ({} is the // placeholder). #define SP_ANSI_CURSOR_HOME "\r" #define SP_ANSI_CURSOR_UP "\x1b[A" @@ -943,7 +943,7 @@ static void sp_prompt_framebuffer_clear(sp_prompt_ctx_t* ctx) { } sp_prompt_ctx_t* sp_prompt_new(sp_mem_t mem) { - sp_prompt_ctx_t* ctx = sp_alloc_type_a(mem, sp_prompt_ctx_t); + sp_prompt_ctx_t* ctx = sp_alloc_type(mem, sp_prompt_ctx_t); u32 cols = 0; u32 rows = 0; if (sp_os_is_tty(sp_sys_stdout)) { @@ -958,7 +958,7 @@ sp_prompt_ctx_t* sp_prompt_new(sp_mem_t mem) { sp_prompt_ctx_t* sp_prompt_begin(sp_mem_t mem) { sp_prompt_ctx_t* ctx = sp_prompt_new(mem); if (sp_prompt_begin_ex(ctx)) { - sp_free_a(mem, ctx); + sp_free(mem, ctx); return SP_NULLPTR; } return ctx; @@ -1058,7 +1058,7 @@ const c8* sp_prompt_get_str(sp_prompt_ctx_t* ctx) { if (ctx->value.kind != SP_PROMPT_VALUE_STR) { return ""; } - return sp_str_to_cstr_a(ctx->mem, ctx->value.as.str); + return sp_str_to_cstr(ctx->mem, ctx->value.as.str); } bool sp_prompt_get_bool(sp_prompt_ctx_t* ctx) { @@ -1070,7 +1070,7 @@ bool sp_prompt_get_bool(sp_prompt_ctx_t* ctx) { const c8* sp_prompt_join_selection(sp_prompt_ctx_t* ctx, sp_prompt_select_option_t* options, u32 num_options) { sp_io_dyn_mem_writer_t builder = sp_zero; - sp_io_dyn_mem_writer_init_a(ctx->mem, &builder); + sp_io_dyn_mem_writer_init(ctx->mem, &builder); bool first = true; sp_for(it, num_options) { if (!options[it].selected) { @@ -1084,7 +1084,7 @@ const c8* sp_prompt_join_selection(sp_prompt_ctx_t* ctx, sp_prompt_select_option sp_io_write_str(&builder.base, sp_str_view(options[it].label), SP_NULLPTR); } - return sp_str_to_cstr_a(ctx->mem, sp_io_dyn_mem_writer_as_str(&builder)); + return sp_str_to_cstr(ctx->mem, sp_io_dyn_mem_writer_as_str(&builder)); } // Instead of just writing to the file descriptor that the main loop waits on for events, @@ -1163,7 +1163,7 @@ void sp_prompt_send_status_str(sp_prompt_ctx_t* ctx, sp_str_t text) { return; } - ctx->status.value = sp_str_copy_a(sp_mem_arena_as_allocator(ctx->channel.arena), text); + ctx->status.value = sp_str_copy(sp_mem_arena_as_allocator(ctx->channel.arena), text); ctx->status.dirty = true; sp_mutex_unlock(&ctx->channel.lock); @@ -1332,14 +1332,14 @@ static void sp_prompt_write_style(sp_prompt_ctx_t* ctx, sp_prompt_style_t style) } case SP_PROMPT_STYLE_ANSI: { sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_str_t esc = sp_fmt_a(s.mem, SP_ANSI_SGR_ANSI_FMT, sp_fmt_uint(style.ansi)).value; + sp_str_t esc = sp_fmt(s.mem, SP_ANSI_SGR_ANSI_FMT, sp_fmt_uint(style.ansi)).value; sp_prompt_emit_str(ctx, esc); sp_mem_end_scratch(s); break; } case SP_PROMPT_STYLE_RGB: { sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_str_t esc = sp_fmt_a(s.mem, SP_ANSI_SGR_RGB_FMT, sp_fmt_uint(style.rgb.r), sp_fmt_uint(style.rgb.g), sp_fmt_uint(style.rgb.b)).value; + sp_str_t esc = sp_fmt(s.mem, SP_ANSI_SGR_RGB_FMT, sp_fmt_uint(style.rgb.r), sp_fmt_uint(style.rgb.g), sp_fmt_uint(style.rgb.b)).value; sp_prompt_emit_str(ctx, esc); sp_mem_end_scratch(s); break; @@ -1572,7 +1572,7 @@ u32 sp_prompt_text_width(sp_str_t text) { sp_str_t sp_prompt_repeat(sp_prompt_ctx_t* ctx, u32 codepoint, u32 count) { SP_UNUSED(ctx); sp_io_dyn_mem_writer_t builder = sp_zero; - sp_io_dyn_mem_writer_init_a(sp_mem_begin_scratch().mem, &builder); + sp_io_dyn_mem_writer_init(sp_mem_begin_scratch().mem, &builder); c8 buf[4] = sp_zero; u8 len = sp_utf8_encode(codepoint, buf); sp_for(it, count) { @@ -1589,14 +1589,14 @@ static void sp_prompt_static_update(sp_prompt_ctx_t* ctx, sp_prompt_event_t even static void sp_prompt_intro_render(sp_prompt_ctx_t* ctx) { sp_prompt_intro_t* prompt = (sp_prompt_intro_t*)ctx->user_data; sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_prompt_line(ctx, sp_fmt_a(s.mem, "┌ {}", sp_fmt_str(prompt->text)).value); + sp_prompt_line(ctx, sp_fmt(s.mem, "┌ {}", sp_fmt_str(prompt->text)).value); sp_mem_end_scratch(s); } static void sp_prompt_outro_render(sp_prompt_ctx_t* ctx) { sp_prompt_outro_t* prompt = (sp_prompt_outro_t*)ctx->user_data; sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_prompt_line(ctx, sp_fmt_a(s.mem, "â”” {}", sp_fmt_str(prompt->text)).value); + sp_prompt_line(ctx, sp_fmt(s.mem, "â”” {}", sp_fmt_str(prompt->text)).value); sp_mem_end_scratch(s); } @@ -1604,7 +1604,7 @@ static void sp_prompt_note_render(sp_prompt_ctx_t* ctx) { sp_prompt_note_t* prompt = (sp_prompt_note_t*)ctx->user_data; sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_da(sp_str_t) message_lines = sp_str_split_c8_a(s.mem, prompt->message, '\n'); + sp_da(sp_str_t) message_lines = sp_str_split_c8(s.mem, prompt->message, '\n'); u32 title_width = sp_prompt_text_width(prompt->title); u32 max_line_width = 0; @@ -1630,18 +1630,18 @@ static void sp_prompt_note_render(sp_prompt_ctx_t* ctx) { sp_str_t spacer = sp_prompt_repeat(ctx, ' ', width); sp_str_t bottom = sp_prompt_repeat(ctx, 0x2500, width + 2); - sp_prompt_line(ctx, sp_fmt_a(s.mem, "â—‡ {} {}â•®", sp_fmt_str(prompt->title), sp_fmt_str(top_tail)).value); - sp_prompt_line(ctx, sp_fmt_a(s.mem, "│ {}│", sp_fmt_str(spacer)).value); + sp_prompt_line(ctx, sp_fmt(s.mem, "â—‡ {} {}â•®", sp_fmt_str(prompt->title), sp_fmt_str(top_tail)).value); + sp_prompt_line(ctx, sp_fmt(s.mem, "│ {}│", sp_fmt_str(spacer)).value); sp_da_for(message_lines, it) { sp_str_t line = message_lines[it]; u32 line_width = sp_prompt_text_width(line); sp_str_t pad = sp_prompt_repeat(ctx, ' ', width - line_width); - sp_prompt_line(ctx, sp_fmt_a(s.mem, "│ {}{}│", sp_fmt_str(line), sp_fmt_str(pad)).value); + sp_prompt_line(ctx, sp_fmt(s.mem, "│ {}{}│", sp_fmt_str(line), sp_fmt_str(pad)).value); } - sp_prompt_line(ctx, sp_fmt_a(s.mem, "│ {}│", sp_fmt_str(spacer)).value); - sp_prompt_line(ctx, sp_fmt_a(s.mem, "├{}╯", sp_fmt_str(bottom)).value); + sp_prompt_line(ctx, sp_fmt(s.mem, "│ {}│", sp_fmt_str(spacer)).value); + sp_prompt_line(ctx, sp_fmt(s.mem, "├{}╯", sp_fmt_str(bottom)).value); sp_mem_end_scratch(s); } @@ -1687,7 +1687,7 @@ static void sp_prompt_message_render(sp_prompt_ctx_t* ctx) { .ansi = prompt->ansi, }; sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_prompt_render_line(ctx, sp_fmt_a(s.mem, "{} ", sp_fmt_str(sp_prompt_repeat(ctx, prompt->symbol, 1))).value, style); + sp_prompt_render_line(ctx, sp_fmt(s.mem, "{} ", sp_fmt_str(sp_prompt_repeat(ctx, prompt->symbol, 1))).value, style); sp_prompt_render_line(ctx, prompt->text, sp_zero_s(sp_prompt_style_t)); sp_mem_end_scratch(s); ctx->cursor_col = 0; @@ -1765,7 +1765,7 @@ void sp_prompt_success(sp_prompt_ctx_t* ctx, const c8* message) { static void sp_prompt_str_append_codepoint(sp_prompt_ctx_t* ctx, sp_str_t* value, u32 codepoint) { sp_io_dyn_mem_writer_t builder = sp_zero; - sp_io_dyn_mem_writer_init_a(ctx->mem, &builder); + sp_io_dyn_mem_writer_init(ctx->mem, &builder); sp_io_write_str(&builder.base, *value, SP_NULLPTR); c8 buf[4] = sp_zero; u8 len = sp_utf8_encode(codepoint, buf); @@ -2998,7 +2998,7 @@ static void sp_prompt_progress_render(sp_prompt_ctx_t* ctx) { u32 percent = (u32)(ratio * SP_PROMPT_PROGRESS_PERCENT_SCALE + 0.5f); sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_prompt_render_line(ctx, sp_fmt_a(s.mem, " {}%", sp_fmt_uint(percent)).value, sp_zero_s(sp_prompt_style_t)); + sp_prompt_render_line(ctx, sp_fmt(s.mem, " {}%", sp_fmt_uint(percent)).value, sp_zero_s(sp_prompt_style_t)); sp_mem_end_scratch(s); ctx->cursor_col = 0; ctx->cursor_row++; diff --git a/test/array.c b/test/array.c index 26fa535..3764ada 100644 --- a/test/array.c +++ b/test/array.c @@ -110,7 +110,7 @@ UTEST_F(dyn_array, struct_type) { s.value = (float)i * 1.5f; sp_io_mem_writer_t io = sp_zero; sp_io_mem_writer_from_buffer(&io, s.name, sizeof(s.name)); - sp_io_write_str(&io.base, sp_fmt_a(sp_mem_get_scratch(), "Item_{}", sp_fmt_int(s.id)).value, SP_NULLPTR); + sp_io_write_str(&io.base, sp_fmt(sp_mem_get_scratch(), "Item_{}", sp_fmt_int(s.id)).value, SP_NULLPTR); sp_io_pad(&io.base, 1, SP_NULLPTR); sp_da_push(arr, s); } @@ -124,7 +124,7 @@ UTEST_F(dyn_array, struct_type) { char expected[32]; sp_io_mem_writer_t io = sp_zero; sp_io_mem_writer_from_buffer(&io, expected, sizeof(expected)); - sp_io_write_str(&io.base, sp_fmt_a(sp_mem_get_scratch(), "Item_{}", sp_fmt_int(i)).value, SP_NULLPTR); + sp_io_write_str(&io.base, sp_fmt(sp_mem_get_scratch(), "Item_{}", sp_fmt_int(i)).value, SP_NULLPTR); sp_io_pad(&io.base, 1, SP_NULLPTR); ASSERT_STREQ(arr[i].name, expected); @@ -139,7 +139,7 @@ UTEST_F(dyn_array, pointer_type) { const char* strings[] = {"Hello", "World", "Dynamic", "Array", "Test"}; for (s32 i = 0; i < 5; i++) { - c8* str = sp_cstr_copy_a(ut.mem, strings[i]); + c8* str = sp_cstr_copy(ut.mem, strings[i]); sp_da_push(arr, str); } @@ -150,7 +150,7 @@ UTEST_F(dyn_array, pointer_type) { } for (u64 i = 0; i < sp_da_size(arr); i++) { - sp_free_a(ut.mem, arr[i]); + sp_free(ut.mem, arr[i]); } sp_da_free(arr); @@ -456,7 +456,7 @@ UTEST_F(dyn_array, allocator_realloc_into_scratch_clobber) { { sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - u8* clobber = sp_alloc_n_a(s.mem, u8, num_bytes); + u8* clobber = sp_alloc_n(s.mem, u8, num_bytes); sp_mem_fill_u8(clobber, num_bytes, pattern.fill); sp_for(it, len) { diff --git a/test/asset.c b/test/asset.c index 2e87c2a..df62721 100644 --- a/test/asset.c +++ b/test/asset.c @@ -48,8 +48,8 @@ void sp_test_asset_noop_complete(sp_asset_import_context_t* context) { (void)con // Simple importer that just copies the data void sp_test_asset_import(sp_asset_import_context_t* context) { sp_test_asset_data_t* input = (sp_test_asset_data_t*)context->user_data; - sp_test_asset_data_t* data = sp_alloc_type_a(context->registry->mem, sp_test_asset_data_t); - data->content = sp_str_copy_a(context->registry->mem, input->content); + sp_test_asset_data_t* data = sp_alloc_type(context->registry->mem, sp_test_asset_data_t); + data->content = sp_str_copy(context->registry->mem, input->content); data->value = input->value; sp_atomic_ptr_set(&context->asset->data, data); @@ -83,7 +83,7 @@ UTEST_F(asset_registry, basic_add_and_find) { sp_asset_registry_init(®istry, ut.mem.tracking, config); // Add an asset - sp_test_asset_data_t* data1 = sp_alloc_type_a(ut.mem.arena, sp_test_asset_data_t); + sp_test_asset_data_t* data1 = sp_alloc_type(ut.mem.arena, sp_test_asset_data_t); data1->content = sp_str_lit("test content"); data1->value = 42; @@ -159,7 +159,7 @@ UTEST_F(asset_registry, string_copying) { // Create a temporary string c8 temp_buffer[32]; sp_cstr_copy_to("temp_asset", temp_buffer, sizeof(temp_buffer)); - sp_str_t temp_name = sp_str_from_cstr_a(sp_mem_get_scratch(), temp_buffer); + sp_str_t temp_name = sp_str_from_cstr(sp_mem_get_scratch(), temp_buffer); // Add asset with temporary name sp_asset_t* asset = sp_asset_registry_add(®istry, SP_ASSET_KIND_TEST, temp_name, SP_NULLPTR); @@ -169,7 +169,7 @@ UTEST_F(asset_registry, string_copying) { // The asset's name should still be intact ASSERT_TRUE(sp_str_equal(asset->name, sp_str_lit("temp_asset"))); - ASSERT_FALSE(sp_str_equal(asset->name, sp_str_from_cstr_a(sp_mem_get_scratch(), temp_buffer))); + ASSERT_FALSE(sp_str_equal(asset->name, sp_str_from_cstr(sp_mem_get_scratch(), temp_buffer))); // Should still be findable with original name sp_asset_t* found = sp_asset_registry_find(®istry, SP_ASSET_KIND_TEST, sp_str_lit("temp_asset")); @@ -342,14 +342,14 @@ UTEST_F(asset_registry, concurrent_find_during_import) { // Add some assets first for (s32 i = 0; i < 10; i++) { - sp_str_t name = sp_fmt_a(ut.mem.arena, "asset_{}", sp_fmt_int(i)).value; + sp_str_t name = sp_fmt(ut.mem.arena, "asset_{}", sp_fmt_int(i)).value; sp_asset_registry_add(®istry, SP_ASSET_KIND_TEST, name, (void*)(uintptr_t)i); } // Start importing more assets for (s32 i = 10; i < 20; i++) { - sp_str_t name = sp_fmt_a(ut.mem.arena, "asset_{}", sp_fmt_int(i)).value; - sp_test_asset_data_t* data = sp_alloc_type_a(ut.mem.arena, sp_test_asset_data_t); + sp_str_t name = sp_fmt(ut.mem.arena, "asset_{}", sp_fmt_int(i)).value; + sp_test_asset_data_t* data = sp_alloc_type(ut.mem.arena, sp_test_asset_data_t); data->content = name; data->value = i; sp_asset_registry_import(®istry, SP_ASSET_KIND_TEST, name, data); @@ -357,7 +357,7 @@ UTEST_F(asset_registry, concurrent_find_during_import) { // All assets should be immediately findable (sync ones with real data, async with default) for (s32 i = 0; i < 20; i++) { - sp_str_t name = sp_fmt_a(ut.mem.arena, "asset_{}", sp_fmt_int(i)).value; + sp_str_t name = sp_fmt(ut.mem.arena, "asset_{}", sp_fmt_int(i)).value; sp_asset_t* found = sp_asset_registry_find(®istry, SP_ASSET_KIND_TEST, name); ASSERT_NE(found, SP_NULLPTR); if (i < 10) { @@ -371,7 +371,7 @@ UTEST_F(asset_registry, concurrent_find_during_import) { // Now all should be completed for (s32 i = 0; i < 20; i++) { - sp_str_t name = sp_fmt_a(ut.mem.arena, "asset_{}", sp_fmt_int(i)).value; + sp_str_t name = sp_fmt(ut.mem.arena, "asset_{}", sp_fmt_int(i)).value; sp_asset_t* found = sp_asset_registry_find(®istry, SP_ASSET_KIND_TEST, name); ASSERT_NE(found, SP_NULLPTR); ASSERT_EQ(found->state, SP_ASSET_STATE_COMPLETED); @@ -397,13 +397,13 @@ UTEST_F(asset_registry, stress_many_assets) { // Add many assets for (s32 i = 0; i < ASSET_COUNT; i++) { - sp_str_t name = sp_fmt_a(ut.mem.arena, "stress_{}", sp_fmt_int(i)).value; + sp_str_t name = sp_fmt(ut.mem.arena, "stress_{}", sp_fmt_int(i)).value; sp_asset_registry_add(®istry, SP_ASSET_KIND_TEST, name, (void*)(uintptr_t)i); } // Verify all can be found for (s32 i = 0; i < ASSET_COUNT; i++) { - sp_str_t name = sp_fmt_a(ut.mem.arena, "stress_{}", sp_fmt_int(i)).value; + sp_str_t name = sp_fmt(ut.mem.arena, "stress_{}", sp_fmt_int(i)).value; sp_asset_t* found = sp_asset_registry_find(®istry, SP_ASSET_KIND_TEST, name); ASSERT_NE(found, SP_NULLPTR); ASSERT_EQ(sp_atomic_ptr_get(&found->data), (void*)(uintptr_t)i); @@ -412,7 +412,7 @@ UTEST_F(asset_registry, stress_many_assets) { // Random access pattern for (s32 iter = 0; iter < ASSET_COUNT * 2; iter++) { s32 id = (iter * 7919) % ASSET_COUNT; - sp_str_t name = sp_fmt_a(ut.mem.arena, "stress_{}", sp_fmt_int(id)).value; + sp_str_t name = sp_fmt(ut.mem.arena, "stress_{}", sp_fmt_int(id)).value; sp_asset_t* found = sp_asset_registry_find(®istry, SP_ASSET_KIND_TEST, name); ASSERT_NE(found, SP_NULLPTR); ASSERT_EQ(sp_atomic_ptr_get(&found->data), (void*)(uintptr_t)id); @@ -440,7 +440,7 @@ UTEST_F(asset_registry, stable_pointers) { // Insert many more assets for (s32 i = 0; i < 500; i++) { - sp_str_t name = sp_fmt_a(ut.mem.arena, "filler_{}", sp_fmt_int(i)).value; + sp_str_t name = sp_fmt(ut.mem.arena, "filler_{}", sp_fmt_int(i)).value; sp_asset_registry_add(®istry, SP_ASSET_KIND_TEST, name, (void*)(uintptr_t)i); } diff --git a/test/bench/glob.c b/test/bench/glob.c index 53515fe..8f237bb 100644 --- a/test/bench/glob.c +++ b/test/bench/glob.c @@ -115,7 +115,7 @@ int main(int argc, char** argv) { sp_str_t path = sp_str_view(bench_cases[i].path); SP_ASSERT(sp_glob_match(globs[i], path)); f64 ns = run_glob_bench(globs[i], path); - sp_str_t name = sp_fmt_a(sp_mem_get_scratch(), "{}_glob", sp_fmt_cstr(case_names[i])).value; + sp_str_t name = sp_fmt(sp_mem_get_scratch(), "{}_glob", sp_fmt_cstr(case_names[i])).value; sp_da_push(results, ((bench_result_t){.name = name, .ns_per_op = ns})); } @@ -124,7 +124,7 @@ int main(int argc, char** argv) { sp_str_t path = sp_str_view(bench_cases[i].path); SP_ASSERT(sp_glob_set_match(globsets[i], path)); f64 ns = run_glob_set_bench(globsets[i], path); - sp_str_t name = sp_fmt_a(sp_mem_get_scratch(), "{}_globset", sp_fmt_cstr(case_names[i])).value; + sp_str_t name = sp_fmt(sp_mem_get_scratch(), "{}_globset", sp_fmt_cstr(case_names[i])).value; sp_da_push(results, ((bench_result_t){.name = name, .ns_per_op = ns})); } @@ -137,7 +137,7 @@ int main(int argc, char** argv) { // Print space-separated pairs sp_da_for(results, i) { - sp_log_a("{} {}", sp_fmt_str(results[i].name), sp_fmt_float(results[i].ns_per_op)); + sp_log("{} {}", sp_fmt_str(results[i].name), sp_fmt_float(results[i].ns_per_op)); } return 0; diff --git a/test/bench/ht.c b/test/bench/ht.c index a74719a..9f01484 100644 --- a/test/bench/ht.c +++ b/test/bench/ht.c @@ -25,7 +25,7 @@ static u32 xorshift32(u32* state) { } static sp_str_t rgb_fg(u8 r, u8 g, u8 b) { - return sp_fmt_a(sp_mem_get_scratch(), "\x1b[38;2;{};{};{}m", sp_fmt_uint(r), sp_fmt_uint(g), sp_fmt_uint(b)).value; + return sp_fmt(sp_mem_get_scratch(), "\x1b[38;2;{};{};{}m", sp_fmt_uint(r), sp_fmt_uint(g), sp_fmt_uint(b)).value; } static sp_str_t color_for_ratio(f64 ratio) { @@ -48,7 +48,7 @@ typedef struct { } bench_params_t; typedef struct { - sp_ht_a(s32, u64) sp; + sp_ht(s32, u64) sp; kvp_t* stb; s32* random_keys; u32 random_keys_n; @@ -138,37 +138,38 @@ static void run_benchmarks(bench_t* benches, u32 num_benches) { u32 ratio_width = 6; sp_da_for(results, i) { if (results[i].name.len > max_name) max_name = results[i].name.len; - sp_str_t n_str = sp_fmt_a(sp_mem_get_scratch(), "{}", sp_fmt_uint(results[i].n)).value; + sp_str_t n_str = sp_fmt(sp_mem_get_scratch(), "{}", sp_fmt_uint(results[i].n)).value; if (n_str.len > max_n_width) max_n_width = n_str.len; } sp_io_writer_t sb = sp_zero; - sp_io_dyn_mem_writer_init_a(sp_mem_get_scratch(), &sb); - sp_fmt_io(&sb, "{}{} {} {} {} {}{}\n", + sp_io_dyn_mem_writer_init(sp_mem_get_scratch(), &sb); + sp_mem_arena_marker_t fmt_s = sp_mem_begin_scratch_for(sp_mem_get_scratch()); + sp_fmt_io_a(&sb, fmt_s.mem, "{}{} {} {} {} {}{}\n", sp_fmt_cstr(SP_ANSI_FG_BRIGHT_BLACK), - sp_fmt_str(sp_str_pad_a(sp_mem_get_scratch(), sp_str_lit("test"), max_name)), - sp_fmt_str(sp_str_pad_a(sp_mem_get_scratch(), sp_str_lit("n"), max_n_width)), - sp_fmt_str(sp_str_pad_a(sp_mem_get_scratch(), sp_str_lit("sp_ht"), time_width)), - sp_fmt_str(sp_str_pad_a(sp_mem_get_scratch(), sp_str_lit("stb_ds"), time_width)), - sp_fmt_str(sp_str_pad_a(sp_mem_get_scratch(), sp_str_lit("ratio"), ratio_width)), + sp_fmt_str(sp_str_pad(sp_mem_get_scratch(), sp_str_lit("test"), max_name)), + sp_fmt_str(sp_str_pad(sp_mem_get_scratch(), sp_str_lit("n"), max_n_width)), + sp_fmt_str(sp_str_pad(sp_mem_get_scratch(), sp_str_lit("sp_ht"), time_width)), + sp_fmt_str(sp_str_pad(sp_mem_get_scratch(), sp_str_lit("stb_ds"), time_width)), + sp_fmt_str(sp_str_pad(sp_mem_get_scratch(), sp_str_lit("ratio"), ratio_width)), sp_fmt_cstr(SP_ANSI_RESET)); sp_da_for(results, i) { bench_result_pair_t* r = &results[i]; - sp_str_t n_str = sp_fmt_a(sp_mem_get_scratch(), "{}", sp_fmt_uint(r->n)).value; + sp_str_t n_str = sp_fmt(sp_mem_get_scratch(), "{}", sp_fmt_uint(r->n)).value; f64 sp_ms = sp_tm_ns_to_ms_f((f64)r->sp_time_ns); f64 stb_ms = sp_tm_ns_to_ms_f((f64)r->stb_time_ns); f64 ratio = sp_ms / stb_ms; - sp_str_t sp_time_str = sp_str_pad_a(sp_mem_get_scratch(), sp_fmt_a(sp_mem_get_scratch(), "{}ms", sp_fmt_float(sp_ms)).value, time_width); - sp_str_t stb_time_str = sp_str_pad_a(sp_mem_get_scratch(), sp_fmt_a(sp_mem_get_scratch(), "{}ms", sp_fmt_float(stb_ms)).value, time_width); + sp_str_t sp_time_str = sp_str_pad(sp_mem_get_scratch(), sp_fmt(sp_mem_get_scratch(), "{}ms", sp_fmt_float(sp_ms)).value, time_width); + sp_str_t stb_time_str = sp_str_pad(sp_mem_get_scratch(), sp_fmt(sp_mem_get_scratch(), "{}ms", sp_fmt_float(stb_ms)).value, time_width); sp_str_t ratio_color = color_for_ratio(ratio); - sp_str_t ratio_str = sp_str_pad_a(sp_mem_get_scratch(), sp_fmt_a(sp_mem_get_scratch(), "{}x", sp_fmt_float(ratio)).value, ratio_width); + sp_str_t ratio_str = sp_str_pad(sp_mem_get_scratch(), sp_fmt(sp_mem_get_scratch(), "{}x", sp_fmt_float(ratio)).value, ratio_width); - sp_fmt_io(&sb, "{} {} {} {} {}{}{}\n", - sp_fmt_str(sp_str_pad_a(sp_mem_get_scratch(), r->name, max_name)), - sp_fmt_str(sp_str_pad_a(sp_mem_get_scratch(), n_str, max_n_width)), + sp_fmt_io_a(&sb, fmt_s.mem, "{} {} {} {} {}{}{}\n", + sp_fmt_str(sp_str_pad(sp_mem_get_scratch(), r->name, max_name)), + sp_fmt_str(sp_str_pad(sp_mem_get_scratch(), n_str, max_n_width)), sp_fmt_str(sp_time_str), sp_fmt_str(stb_time_str), sp_fmt_str(ratio_color), @@ -178,6 +179,7 @@ static void run_benchmarks(bench_t* benches, u32 num_benches) { sp_str_t output = sp_io_dyn_mem_writer_as_str(&sb); sp_os_print(output); + sp_mem_end_scratch(fmt_s); } static void kernel_deinit(bench_params_t p, bench_data_t* data) { @@ -199,7 +201,7 @@ static void kernel_deinit(bench_params_t p, bench_data_t* data) { static void kernel_seq_insert(bench_params_t p, bench_data_t* data) { switch (p.lib) { case BENCH_LIB_SP: { - sp_ht_init_a(sp_mem_os_new(), data->sp); + sp_ht_init(sp_mem_os_new(), data->sp); sp_for(i, p.n) { sp_ht_insert(data->sp, i, i); SP_COMPILER_BARRIER(); @@ -219,7 +221,7 @@ static void kernel_seq_insert(bench_params_t p, bench_data_t* data) { static void kernel_seq_lookup_init(bench_params_t p, bench_data_t* data) { switch (p.lib) { case BENCH_LIB_SP: { - sp_ht_init_a(sp_mem_os_new(), data->sp); + sp_ht_init(sp_mem_os_new(), data->sp); sp_for(i, p.n) { sp_ht_insert(data->sp, (s32)i, (u64)i); } @@ -259,7 +261,7 @@ static void kernel_seq_lookup(bench_params_t p, bench_data_t* data) { static void kernel_rnd_init(bench_params_t p, bench_data_t* data) { if (data->random_keys_n >= p.n) return; - data->random_keys = sp_alloc_a(sp_mem_os_new(), sizeof(s32) * p.n); + data->random_keys = sp_alloc(sp_mem_os_new(), sizeof(s32) * p.n); u32 state = 12345; sp_for(i, p.n) { data->random_keys[i] = (s32)xorshift32(&state); @@ -271,7 +273,7 @@ static void kernel_rnd_insert(bench_params_t p, bench_data_t* data) { kernel_rnd_init(p, data); switch (p.lib) { case BENCH_LIB_SP: { - sp_ht_init_a(sp_mem_os_new(), data->sp); + sp_ht_init(sp_mem_os_new(), data->sp); sp_for(i, p.n) { sp_ht_insert(data->sp, data->random_keys[i], (u64)i); SP_COMPILER_BARRIER(); @@ -292,7 +294,7 @@ static void kernel_rnd_lookup_init(bench_params_t p, bench_data_t* data) { kernel_rnd_init(p, data); switch (p.lib) { case BENCH_LIB_SP: { - sp_ht_init_a(sp_mem_os_new(), data->sp); + sp_ht_init(sp_mem_os_new(), data->sp); sp_for(i, p.n) { sp_ht_insert(data->sp, data->random_keys[i], (u64)i); } @@ -333,7 +335,7 @@ static void kernel_rnd_lookup(bench_params_t p, bench_data_t* data) { static void kernel_delete_init(bench_params_t p, bench_data_t* data) { switch (p.lib) { case BENCH_LIB_SP: { - sp_ht_init_a(sp_mem_os_new(), data->sp); + sp_ht_init(sp_mem_os_new(), data->sp); sp_for(i, p.n) { sp_ht_insert(data->sp, (s32)i, (u64)i); } @@ -371,7 +373,7 @@ static void kernel_mixed(bench_params_t p, bench_data_t* data) { u64 sum = 0; switch (p.lib) { case BENCH_LIB_SP: { - sp_ht_init_a(sp_mem_os_new(), data->sp); + sp_ht_init(sp_mem_os_new(), data->sp); sp_for(i, p.n) { sp_ht_insert(data->sp, (s32)i, (u64)i); if (i > 0) { diff --git a/test/elf.c b/test/elf.c index 21e0141..ed495ee 100644 --- a/test/elf.c +++ b/test/elf.c @@ -283,7 +283,7 @@ UTEST_F(elf, rela_info_encoding) { UTEST_F(elf, minimal_elf_format) { SKIP_ON_WASM() sp_elf_t* elf = sp_elf_new_with_null_section(ut.mem.tracking); - sp_io_dyn_mem_writer_t buf; sp_io_dyn_mem_writer_init_a(ut.mem.arena, &buf); + sp_io_dyn_mem_writer_t buf; sp_io_dyn_mem_writer_init(ut.mem.arena, &buf); sp_elf_write(elf, &buf.base); u8* data = buf.storage.data; u64 size = buf.storage.len; @@ -323,7 +323,7 @@ UTEST_F(elf, minimal_elf_format) { UTEST_F(elf, err_write_null_elf) { SKIP_ON_WASM() - sp_io_dyn_mem_writer_t buf; sp_io_dyn_mem_writer_init_a(ut.mem.arena, &buf); + sp_io_dyn_mem_writer_t buf; sp_io_dyn_mem_writer_init(ut.mem.arena, &buf); sp_err_t err = sp_elf_write(SP_NULLPTR, &buf.base); ASSERT_NE(err, SP_OK); sp_io_dyn_mem_writer_close(&buf); @@ -401,7 +401,7 @@ UTEST_F(elf, populated_section_alignment) { sp_elf_section_t* data = sp_elf_add_section(elf, sp_str_lit(".data"), SHT_PROGBITS, 8); sp_elf_section_reserve_bytes(data, 5); - sp_io_dyn_mem_writer_t buf; sp_io_dyn_mem_writer_init_a(ut.mem.arena, &buf); + sp_io_dyn_mem_writer_t buf; sp_io_dyn_mem_writer_init(ut.mem.arena, &buf); sp_elf_write(elf, &buf.base); u8* out_data = buf.storage.data; @@ -427,7 +427,7 @@ UTEST_F(elf, populated_sections_no_overlap) { sp_elf_section_t* data = sp_elf_add_section(elf, sp_str_lit(".data"), SHT_PROGBITS, 8); sp_elf_section_reserve_bytes(data, 50); - sp_io_dyn_mem_writer_t buf; sp_io_dyn_mem_writer_init_a(ut.mem.arena, &buf); + sp_io_dyn_mem_writer_t buf; sp_io_dyn_mem_writer_init(ut.mem.arena, &buf); sp_elf_write(elf, &buf.base); u8* out_data = buf.storage.data; @@ -459,7 +459,7 @@ UTEST_F(elf, populated_nobits_no_file_space) { sp_elf_section_t* bss = sp_elf_add_section(elf, sp_str_lit(".bss"), SHT_NOBITS, 8); bss->buffer.size = 1024; - sp_io_dyn_mem_writer_t buf; sp_io_dyn_mem_writer_init_a(ut.mem.arena, &buf); + sp_io_dyn_mem_writer_t buf; sp_io_dyn_mem_writer_init(ut.mem.arena, &buf); sp_elf_write(elf, &buf.base); u8* out_data = buf.storage.data; @@ -485,7 +485,7 @@ UTEST_F(elf, roundtrip_minimal) { SKIP_ON_WASM() sp_elf_t* elf = sp_elf_new_with_null_section(ut.mem.tracking); - sp_io_dyn_mem_writer_t writer; sp_io_dyn_mem_writer_init_a(ut.mem.arena, &writer); + sp_io_dyn_mem_writer_t writer; sp_io_dyn_mem_writer_init(ut.mem.arena, &writer); sp_elf_write(elf, &writer.base); sp_elf_t* read_elf = sp_elf_read(ut.mem.tracking, writer.storage.data, writer.storage.len); @@ -524,7 +524,7 @@ UTEST_F(elf, roundtrip_populated) { text = sp_elf_find_section_by_name(elf, sp_str_lit(".text")); sp_elf_add_symbol(symtab, elf, sp_str_lit("_start"), 0, sizeof(code), STB_GLOBAL, STT_FUNC, (u16)text->index); - sp_io_dyn_mem_writer_t writer; sp_io_dyn_mem_writer_init_a(ut.mem.arena, &writer); + sp_io_dyn_mem_writer_t writer; sp_io_dyn_mem_writer_init(ut.mem.arena, &writer); sp_elf_write(elf, &writer.base); sp_elf_t* read_elf = sp_elf_read(ut.mem.tracking, writer.storage.data, writer.storage.len); @@ -601,7 +601,7 @@ UTEST_F(elf, oracle_readelf_minimal) { sp_str_t path = sp_test_file_path(&ut.file_manager, sp_str_lit("minimal.o")); sp_elf_write_to_file(elf, path); - sp_ps_output_t ps = sp_ps_run_a(sp_mem_get_scratch(), (sp_ps_config_t){ + sp_ps_output_t ps = sp_ps_run(sp_mem_get_scratch(), (sp_ps_config_t){ .command = sp_str_lit("readelf"), .args = {sp_str_lit("-a"), path}, }); @@ -639,7 +639,7 @@ UTEST_F(elf, oracle_readelf_populated) { sp_str_t path = sp_test_file_path(&ut.file_manager, sp_str_lit("populated.o")); sp_elf_write_to_file(elf, path); - sp_ps_output_t ps = sp_ps_run_a(sp_mem_get_scratch(), (sp_ps_config_t){ + sp_ps_output_t ps = sp_ps_run(sp_mem_get_scratch(), (sp_ps_config_t){ .command = sp_str_lit("readelf"), .args = {sp_str_lit("-a"), path}, }); @@ -666,12 +666,12 @@ UTEST_F(elf, oracle_cc_read) { sp_io_file_writer_close(&c_file); sp_str_t fixture_path = sp_test_file_path(&ut.file_manager, sp_str_lit("minimal.o")); - sp_ps_output_t compile = sp_ps_run_a(sp_mem_get_scratch(), (sp_ps_config_t){ + sp_ps_output_t compile = sp_ps_run(sp_mem_get_scratch(), (sp_ps_config_t){ .command = sp_str_lit("cc"), .args = {sp_str_lit("-c"), c_path, sp_str_lit("-o"), fixture_path}, }); ASSERT_EQ(compile.status.exit_code, 0); - EXPECT_TRUE(sp_fs_exists_a(fixture_path)); + EXPECT_TRUE(sp_fs_exists(fixture_path)); sp_elf_t* read_elf = sp_elf_read_from_file(ut.mem.tracking, fixture_path); ASSERT_NE(read_elf, SP_NULLPTR); @@ -730,14 +730,14 @@ UTEST_F(elf, oracle_cc_link) { sp_io_file_writer_close(&f); sp_str_t bin_path = sp_test_file_path(&ut.file_manager, sp_str_lit("integration")); - sp_ps_output_t compile = sp_ps_run_a(sp_mem_get_scratch(), (sp_ps_config_t){ + sp_ps_output_t compile = sp_ps_run(sp_mem_get_scratch(), (sp_ps_config_t){ .command = sp_str_lit("cc"), .args = {c_path, obj_path, sp_str_lit("-o"), bin_path}, }); ASSERT_EQ(compile.status.exit_code, 0); - EXPECT_TRUE(sp_fs_exists_a(bin_path)); + EXPECT_TRUE(sp_fs_exists(bin_path)); - sp_ps_output_t run = sp_ps_run_a(sp_mem_get_scratch(), (sp_ps_config_t){ + sp_ps_output_t run = sp_ps_run(sp_mem_get_scratch(), (sp_ps_config_t){ .command = bin_path, }); ASSERT_EQ(run.status.exit_code, 0); diff --git a/test/env.c b/test/env.c index 25b8f1a..5a98ccd 100644 --- a/test/env.c +++ b/test/env.c @@ -206,7 +206,7 @@ UTEST_F(sp_env, to_posix_envp) { sp_env_insert(&env, sp_str_lit("AA"), sp_str_lit("11")); sp_env_insert(&env, sp_str_lit("BB"), sp_str_lit("22")); - c8** envp = sp_env_to_posix_envp_a(ut.mem, &env); + c8** envp = sp_env_to_posix_envp(ut.mem, &env); u32 count = 0; while (envp[count] != SP_NULLPTR) count++; @@ -229,7 +229,7 @@ UTEST_F(sp_env, to_posix_envp_empty) { sp_env_t env; sp_env_init(ut.mem, &env); - c8** envp = sp_env_to_posix_envp_a(ut.mem, &env); + c8** envp = sp_env_to_posix_envp(ut.mem, &env); EXPECT_EQ(envp[0], (c8*)SP_NULLPTR); sp_env_destroy(&env); diff --git a/test/fmon.c b/test/fmon.c index 9b7d9a8..2d4c941 100644 --- a/test/fmon.c +++ b/test/fmon.c @@ -13,7 +13,7 @@ static bool paths_equal(sp_str_t a, sp_str_t b) { sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - bool eq = sp_str_equal(sp_fs_normalize_path_a(s.mem, a), sp_fs_normalize_path_a(s.mem, b)); + bool eq = sp_str_equal(sp_fs_normalize_path(s.mem, a), sp_fs_normalize_path(s.mem, b)); sp_mem_end_scratch(s); return eq; } @@ -64,18 +64,18 @@ void fmon_callback(sp_fmon_t* monitor, sp_fmon_event_t* change, void* userdata) sp_test_file_monitor* fixture = (sp_test_file_monitor*)userdata; fixture->change_detected = true; fixture->last_event = change->events; - fixture->last_file_path = sp_str_copy_a(sp_mem_get_scratch(), change->file_path); + fixture->last_file_path = sp_str_copy(sp_mem_get_scratch(), change->file_path); if (fmon_history.count < SP_TEST_FMON_MAX_RECORDS) { sp_test_fmon_record_t* r = &fmon_history.records[fmon_history.count++]; r->events = change->events; - r->file_path = sp_str_copy_a(sp_mem_get_scratch(), change->file_path); + r->file_path = sp_str_copy(sp_mem_get_scratch(), change->file_path); } } UTEST_F(sp_test_file_monitor, init_and_cleanup) { SKIP_ON_WASM() - sp_fmon_init_a(ut.mem, &ut.monitor, fmon_callback, SP_FILE_CHANGE_EVENT_ALL, &ut); + sp_fmon_init(ut.mem, &ut.monitor, fmon_callback, SP_FILE_CHANGE_EVENT_ALL, &ut); EXPECT_NE(ut.monitor.os, SP_NULLPTR); } @@ -83,16 +83,16 @@ UTEST_F(sp_test_file_monitor, init_and_cleanup) { #if !defined(SP_MACOS) || defined(SP_FMON_MACOS_USE_FSEVENTS) UTEST_F(sp_test_file_monitor, detects_file_creation) { SKIP_ON_WASM() - sp_fmon_init_a(ut.mem, &ut.monitor, fmon_callback, SP_FILE_CHANGE_EVENT_ADDED, &ut); + sp_fmon_init(ut.mem, &ut.monitor, fmon_callback, SP_FILE_CHANGE_EVENT_ADDED, &ut); sp_str_t dir = sp_test_file_path(&ut.file_manager, sp_str_lit("watched.dir")); - sp_fs_create_dir_a(dir); + sp_fs_create_dir(dir); sp_fmon_add_dir(&ut.monitor, dir); sp_fmon_process_changes(&ut.monitor); ut.change_detected = false; - sp_str_t file = sp_fs_join_path_a(ut.mem, dir, sp_str_lit("new.file")); + sp_str_t file = sp_fs_join_path(ut.mem, dir, sp_str_lit("new.file")); sp_test_file_create_ex((sp_test_file_config_t) { .path = file, .content = sp_str_lit("spum"), @@ -116,12 +116,12 @@ UTEST_F(sp_test_file_monitor, detects_file_creation) { UTEST_F(sp_test_file_monitor, detects_file_modification) { SKIP_ON_WASM() - sp_fmon_init_a(ut.mem, &ut.monitor, fmon_callback, SP_FILE_CHANGE_EVENT_MODIFIED, &ut); + sp_fmon_init(ut.mem, &ut.monitor, fmon_callback, SP_FILE_CHANGE_EVENT_MODIFIED, &ut); sp_str_t test_dir = sp_test_file_path(&ut.file_manager, sp_str_lit("monitor_test")); - sp_fs_create_dir_a(test_dir); + sp_fs_create_dir(test_dir); - sp_str_t test_file = sp_fs_join_path_a(ut.mem, test_dir, sp_str_lit("modify_file.txt")); + sp_str_t test_file = sp_fs_join_path(ut.mem, test_dir, sp_str_lit("modify_file.txt")); sp_test_file_create_ex((sp_test_file_config_t) { .path = test_file, .content = sp_str_lit("initial content"), @@ -156,12 +156,12 @@ UTEST_F(sp_test_file_monitor, detects_file_modification) { UTEST_F(sp_test_file_monitor, detects_file_deletion) { SKIP_ON_WASM() - sp_fmon_init_a(ut.mem, &ut.monitor, fmon_callback, SP_FILE_CHANGE_EVENT_REMOVED, &ut); + sp_fmon_init(ut.mem, &ut.monitor, fmon_callback, SP_FILE_CHANGE_EVENT_REMOVED, &ut); sp_str_t test_dir = sp_test_file_path(&ut.file_manager, sp_str_lit("monitor_test")); - sp_fs_create_dir_a(test_dir); + sp_fs_create_dir(test_dir); - sp_str_t test_file = sp_fs_join_path_a(ut.mem, test_dir, sp_str_lit("delete_file.txt")); + sp_str_t test_file = sp_fs_join_path(ut.mem, test_dir, sp_str_lit("delete_file.txt")); sp_test_file_create_ex((sp_test_file_config_t) { .path = test_file, .content = sp_str_lit("to be deleted"), @@ -172,7 +172,7 @@ UTEST_F(sp_test_file_monitor, detects_file_deletion) { sp_fmon_process_changes(&ut.monitor); ut.change_detected = false; - sp_fs_remove_file_a(test_file); + sp_fs_remove_file(test_file); bool timed_out = true; sp_for_n(FMON_POLL_ITERATIONS) { @@ -192,13 +192,13 @@ UTEST_F(sp_test_file_monitor, detects_file_deletion) { #if !defined(SP_MACOS) || defined(SP_FMON_MACOS_USE_FSEVENTS) UTEST_F(sp_test_file_monitor, multiple_events_same_file) { SKIP_ON_WASM() - sp_fmon_init_a(ut.mem, &ut.monitor, fmon_callback, SP_FILE_CHANGE_EVENT_ALL, &ut); + sp_fmon_init(ut.mem, &ut.monitor, fmon_callback, SP_FILE_CHANGE_EVENT_ALL, &ut); sp_str_t test_dir = sp_test_file_path(&ut.file_manager, sp_str_lit("monitor_test")); - sp_fs_create_dir_a(test_dir); + sp_fs_create_dir(test_dir); sp_fmon_add_dir(&ut.monitor, test_dir); - sp_str_t test_file = sp_fs_join_path_a(ut.mem, test_dir, sp_str_lit("lifecycle.txt")); + sp_str_t test_file = sp_fs_join_path(ut.mem, test_dir, sp_str_lit("lifecycle.txt")); sp_fmon_process_changes(&ut.monitor); ut.change_detected = false; @@ -241,7 +241,7 @@ UTEST_F(sp_test_file_monitor, multiple_events_same_file) { EXPECT_TRUE((ut.last_event & SP_FILE_CHANGE_EVENT_MODIFIED) != 0); ut.change_detected = false; - sp_fs_remove_file_a(test_file); + sp_fs_remove_file(test_file); timed_out = true; sp_for_n(FMON_POLL_ITERATIONS) { @@ -261,12 +261,12 @@ UTEST_F(sp_test_file_monitor, multiple_events_same_file) { #if !defined(SP_MACOS) || defined(SP_FMON_MACOS_USE_FSEVENTS) UTEST_F(sp_test_file_monitor, event_filtering) { SKIP_ON_WASM() - sp_fmon_init_a(ut.mem, &ut.monitor, fmon_callback, SP_FILE_CHANGE_EVENT_REMOVED, &ut); + sp_fmon_init(ut.mem, &ut.monitor, fmon_callback, SP_FILE_CHANGE_EVENT_REMOVED, &ut); sp_str_t test_dir = sp_test_file_path(&ut.file_manager, sp_str_lit("filter_test")); - sp_fs_create_dir_a(test_dir); + sp_fs_create_dir(test_dir); - sp_str_t test_file = sp_fs_join_path_a(ut.mem, test_dir, sp_str_lit("filter.txt")); + sp_str_t test_file = sp_fs_join_path(ut.mem, test_dir, sp_str_lit("filter.txt")); sp_test_file_create_ex((sp_test_file_config_t) { .path = test_file, .content = sp_str_lit("hello"), @@ -293,7 +293,7 @@ UTEST_F(sp_test_file_monitor, event_filtering) { EXPECT_FALSE(ut.change_detected); // Delete it — should fire - sp_fs_remove_file_a(test_file); + sp_fs_remove_file(test_file); bool timed_out = true; sp_for_n(FMON_POLL_ITERATIONS) { @@ -312,13 +312,13 @@ UTEST_F(sp_test_file_monitor, event_filtering) { UTEST_F(sp_test_file_monitor, add_file_filtering) { SKIP_ON_WASM() - sp_fmon_init_a(ut.mem, &ut.monitor, fmon_callback, SP_FILE_CHANGE_EVENT_ALL, &ut); + sp_fmon_init(ut.mem, &ut.monitor, fmon_callback, SP_FILE_CHANGE_EVENT_ALL, &ut); sp_str_t test_dir = sp_test_file_path(&ut.file_manager, sp_str_lit("file_filter_test")); - sp_fs_create_dir_a(test_dir); + sp_fs_create_dir(test_dir); - sp_str_t watched_file = sp_fs_join_path_a(ut.mem, test_dir, sp_str_lit("watched.txt")); - sp_str_t ignored_file = sp_fs_join_path_a(ut.mem, test_dir, sp_str_lit("ignored.txt")); + sp_str_t watched_file = sp_fs_join_path(ut.mem, test_dir, sp_str_lit("watched.txt")); + sp_str_t ignored_file = sp_fs_join_path(ut.mem, test_dir, sp_str_lit("ignored.txt")); sp_test_file_create_ex((sp_test_file_config_t) { .path = watched_file, @@ -371,13 +371,13 @@ UTEST_F(sp_test_file_monitor, add_file_filtering) { #if !defined(SP_MACOS) || defined(SP_FMON_MACOS_USE_FSEVENTS) UTEST_F(sp_test_file_monitor, rename_file) { SKIP_ON_WASM() - sp_fmon_init_a(ut.mem, &ut.monitor, fmon_callback, SP_FILE_CHANGE_EVENT_ALL, &ut); + sp_fmon_init(ut.mem, &ut.monitor, fmon_callback, SP_FILE_CHANGE_EVENT_ALL, &ut); sp_str_t test_dir = sp_test_file_path(&ut.file_manager, sp_str_lit("rename_test")); - sp_fs_create_dir_a(test_dir); + sp_fs_create_dir(test_dir); - sp_str_t old_file = sp_fs_join_path_a(ut.mem, test_dir, sp_str_lit("before.txt")); - sp_str_t new_file = sp_fs_join_path_a(ut.mem, test_dir, sp_str_lit("after.txt")); + sp_str_t old_file = sp_fs_join_path(ut.mem, test_dir, sp_str_lit("before.txt")); + sp_str_t new_file = sp_fs_join_path(ut.mem, test_dir, sp_str_lit("after.txt")); sp_test_file_create_ex((sp_test_file_config_t) { .path = old_file, @@ -418,10 +418,10 @@ UTEST_F(sp_test_file_monitor, rename_file) { UTEST_F(sp_test_file_monitor, no_events_without_changes) { SKIP_ON_WASM() - sp_fmon_init_a(ut.mem, &ut.monitor, fmon_callback, SP_FILE_CHANGE_EVENT_ALL, &ut); + sp_fmon_init(ut.mem, &ut.monitor, fmon_callback, SP_FILE_CHANGE_EVENT_ALL, &ut); sp_str_t test_dir = sp_test_file_path(&ut.file_manager, sp_str_lit("monitor_test")); - sp_fs_create_dir_a(test_dir); + sp_fs_create_dir(test_dir); sp_fmon_add_dir(&ut.monitor, test_dir); sp_fmon_process_changes(&ut.monitor); diff --git a/test/format.c b/test/format.c index 4232769..71e2fc7 100644 --- a/test/format.c +++ b/test/format.c @@ -7,7 +7,14 @@ SP_TEST_MAIN() -static sp_ht_a(sp_str_t, sp_fmt_directive_t) sp_fmt_directives = SP_NULLPTR; +static sp_ht(sp_str_t, sp_fmt_directive_t) sp_fmt_directives = SP_NULLPTR; + +void sp_fmt_directive_reset() { + sp_tls_rt_t* tls = sp_tls_rt_get(); + sp_str_ht_free(tls->format.directives); + sp_str_ht_init(tls->mem, tls->format.directives); + sp_fmt_register_builtins(); +} typedef struct { const c8* str; @@ -309,8 +316,8 @@ UTEST(sp_fmt_parse_directive, no_width_just_directive) { static sp_str_t render_value_to_str(sp_fmt_arg_t arg) { sp_io_dyn_mem_writer_t io = sp_zero; - sp_io_dyn_mem_writer_init_a(sp_mem_get_scratch(), &io); - sp_fmt_render_default_a(&io.base, sp_mem_get_scratch(), &arg, SP_NULLPTR); + sp_io_dyn_mem_writer_init(sp_mem_get_scratch(), &io); + sp_fmt_render_default(&io.base, sp_mem_get_scratch(), &arg, SP_NULLPTR); return sp_io_dyn_mem_writer_as_str(&io); } @@ -421,8 +428,8 @@ UTEST(sp_fmt_render, f64_custom_precision_via_spec) { sp_fmt_arg_t arg = sp_fmt_float(3.14159); sp_opt_set(arg.spec.precision, 2); sp_io_dyn_mem_writer_t io = sp_zero; - sp_io_dyn_mem_writer_init_a(sp_mem_get_scratch(), &io); - sp_fmt_render_default_a(&io.base, sp_mem_get_scratch(), &arg, SP_NULLPTR); + sp_io_dyn_mem_writer_init(sp_mem_get_scratch(), &io); + sp_fmt_render_default(&io.base, sp_mem_get_scratch(), &arg, SP_NULLPTR); sp_str_t got = sp_io_dyn_mem_writer_as_str(&io); EXPECT_TRUE(sp_str_equal_cstr(got, "3.14")); } @@ -439,9 +446,9 @@ UTEST(sp_fmt_render, ptr_nonzero) { static sp_str_t apply_spec_to_str(const c8* content, sp_fmt_spec_t spec) { sp_io_dyn_mem_writer_t io = sp_zero; - sp_io_dyn_mem_writer_init_a(sp_mem_get_scratch(), &io); + sp_io_dyn_mem_writer_init(sp_mem_get_scratch(), &io); sp_str_t empty = sp_zero; - sp_fmt_apply_spec_a(&io.base, empty, sp_str_view(content), empty, spec); + sp_fmt_apply_spec(&io.base, empty, sp_str_view(content), empty, spec); return sp_io_dyn_mem_writer_as_str(&io); } @@ -483,8 +490,8 @@ UTEST(sp_fmt_pad, center_odd) { UTEST(sp_fmt_pad, wrapped_padding_outside) { sp_io_dyn_mem_writer_t io = sp_zero; - sp_io_dyn_mem_writer_init_a(sp_mem_get_scratch(), &io); - sp_fmt_apply_spec_a(&io.base, + sp_io_dyn_mem_writer_init(sp_mem_get_scratch(), &io); + sp_fmt_apply_spec(&io.base, sp_str_lit("<"), sp_str_lit("42"), sp_str_lit(">"), @@ -496,8 +503,8 @@ UTEST(sp_fmt_pad, wrapped_padding_outside) { UTEST(sp_fmt_pad, wrapped_center) { sp_io_dyn_mem_writer_t io = sp_zero; - sp_io_dyn_mem_writer_init_a(sp_mem_get_scratch(), &io); - sp_fmt_apply_spec_a(&io.base, + sp_io_dyn_mem_writer_init(sp_mem_get_scratch(), &io); + sp_fmt_apply_spec(&io.base, sp_str_lit("["), sp_str_lit("hi"), sp_str_lit("]"), @@ -574,7 +581,7 @@ UTEST(sp_fmt_directive, register_and_lookup) { EXPECT_TRUE(got->decorator.before == _test_before_lt); EXPECT_TRUE(got->decorator.after == _test_after_gt); - sp_str_t rendered = sp_fmt_a(sp_mem_get_scratch(), "{.wrap}", sp_fmt_cstr("ok")).value; + sp_str_t rendered = sp_fmt(sp_mem_get_scratch(), "{.wrap}", sp_fmt_cstr("ok")).value; EXPECT_TRUE(sp_str_equal_cstr(rendered, "")); sp_fmt_directive_reset(); } @@ -596,7 +603,7 @@ UTEST(sp_fmt_directive, reset_clears) { UTEST(sp_fmt_directive, wraps_content) { sp_fmt_directive_reset(); sp_fmt_register_decorator("wrap", _test_before_lt, _test_after_gt); - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.wrap}", sp_fmt_cstr("hi")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.wrap}", sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "")); sp_fmt_directive_reset(); } @@ -604,7 +611,7 @@ UTEST(sp_fmt_directive, wraps_content) { UTEST(sp_fmt_directive, render_replaces_value) { sp_fmt_directive_reset(); sp_fmt_register_renderer("x", _test_render_x, sp_fmt_id_none); - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.x}", sp_fmt_int(999)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.x}", sp_fmt_int(999)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "X")); sp_fmt_directive_reset(); } @@ -612,7 +619,7 @@ UTEST(sp_fmt_directive, render_replaces_value) { UTEST(sp_fmt_directive, transform_uppercase) { sp_fmt_directive_reset(); sp_fmt_register_transformer("upper", _test_transform_upper); - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.upper}", sp_fmt_cstr("hello")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.upper}", sp_fmt_cstr("hello")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "HELLO")); sp_fmt_directive_reset(); } @@ -620,7 +627,7 @@ UTEST(sp_fmt_directive, transform_uppercase) { UTEST(sp_fmt_directive, err_unknown_directive) { sp_fmt_directive_reset(); sp_str_t str = sp_zero; - EXPECT_EQ(sp_fmt_a(sp_mem_get_scratch(), "{.missing}", sp_fmt_int(42)).err, SP_ERR_FMT_UNKNOWN_DIRECTIVE); + EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "{.missing}", sp_fmt_int(42)).err, SP_ERR_FMT_UNKNOWN_DIRECTIVE); } UTEST(sp_fmt_directive, ordering_bracket_nested) { @@ -629,8 +636,8 @@ UTEST(sp_fmt_directive, ordering_bracket_nested) { sp_fmt_register_decorator("b", _test_before_b, _test_after_b); _test_log = (sp_io_dyn_mem_writer_t)sp_zero; - sp_io_dyn_mem_writer_init_a(sp_mem_get_scratch(), &_test_log); - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.a .b}", sp_fmt_cstr("x")).value; + sp_io_dyn_mem_writer_init(sp_mem_get_scratch(), &_test_log); + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.a .b}", sp_fmt_cstr("x")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "[a[bxb]a]")); sp_str_t log_str = sp_io_dyn_mem_writer_as_str(&_test_log); @@ -644,7 +651,7 @@ UTEST(sp_fmt_directive, err_multiple_renders) { sp_fmt_register_renderer("x", _test_render_x, sp_fmt_id_none); sp_fmt_register_renderer("y", _test_render_y, sp_fmt_id_none); sp_str_t str = sp_zero; - EXPECT_EQ(sp_fmt_a(sp_mem_get_scratch(), "{.x .y}", sp_fmt_int(0)).err, SP_ERR_FMT_TOO_MANY_RENDERERS); + EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "{.x .y}", sp_fmt_int(0)).err, SP_ERR_FMT_TOO_MANY_RENDERERS); EXPECT_EQ(_test_render_y_calls, 0); sp_fmt_directive_reset(); } @@ -652,7 +659,7 @@ UTEST(sp_fmt_directive, err_multiple_renders) { UTEST(sp_fmt_directive, padding_outside_wrappers) { sp_fmt_directive_reset(); sp_fmt_register_decorator("wrap", _test_before_lt, _test_after_gt); - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{:6 .wrap}", sp_fmt_int(42)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:6 .wrap}", sp_fmt_int(42)).value; EXPECT_TRUE(sp_str_equal_cstr(got, " <42>")); sp_fmt_directive_reset(); } @@ -660,64 +667,64 @@ UTEST(sp_fmt_directive, padding_outside_wrappers) { UTEST(sp_fmt_directive, padding_with_center_and_wrapper) { sp_fmt_directive_reset(); sp_fmt_register_decorator("wrap", _test_before_lt, _test_after_gt); - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{:*^8 .wrap}", sp_fmt_cstr("hi")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:*^8 .wrap}", sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "******")); sp_fmt_directive_reset(); } UTEST(sp_fmt_v, literal_only) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "hello, world").value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "hello, world").value; EXPECT_TRUE(sp_str_equal_cstr(got, "hello, world")); } UTEST(sp_fmt_v, empty_placeholder_int) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{}", sp_fmt_int(42)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{}", sp_fmt_int(42)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "42")); } UTEST(sp_fmt_v, empty_placeholder_str) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{}", sp_fmt_cstr("world")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{}", sp_fmt_cstr("world")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "world")); } UTEST(sp_fmt_v, multi_arg) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{} + {} = {}", + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{} + {} = {}", sp_fmt_int(2), sp_fmt_int(3), sp_fmt_int(5)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "2 + 3 = 5")); } UTEST(sp_fmt_v, literals_around_placeholder) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "hello, {}!", sp_fmt_cstr("thomas")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "hello, {}!", sp_fmt_cstr("thomas")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "hello, thomas!")); } UTEST(sp_fmt_v, width_right_align) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{:6}", sp_fmt_int(42)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:6}", sp_fmt_int(42)).value; EXPECT_TRUE(sp_str_equal_cstr(got, " 42")); } UTEST(sp_fmt_v, fill_center) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{:*^9}", sp_fmt_int(42)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:*^9}", sp_fmt_int(42)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "***42****")); } UTEST(sp_fmt_v, brace_escapes) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{{{}}}", sp_fmt_int(7)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{{{}}}", sp_fmt_int(7)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "{7}")); } UTEST(sp_fmt_v, close_brace_escape) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "hello }} world").value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "hello }} world").value; EXPECT_TRUE(sp_str_equal_cstr(got, "hello } world")); } UTEST(sp_fmt_v, err_lone_close_brace) { sp_str_t str = sp_zero; - EXPECT_EQ(sp_fmt_a(sp_mem_get_scratch(), "oops } here").err, SP_ERR_FMT_BAD_PLACEHOLDER); + EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "oops } here").err, SP_ERR_FMT_BAD_PLACEHOLDER); } UTEST(sp_fmt_v, str_with_padding) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "[{:->8}]", sp_fmt_cstr("hi")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "[{:->8}]", sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "[------hi]")); } @@ -726,7 +733,7 @@ UTEST(sp_fmt_directive, custom_fn_fallback) { sp_fmt_register_decorator("wrap", _test_before_lt, _test_after_gt); u32 value = 0; sp_fmt_arg_t arg = sp_fmt_custom(u32, _test_render_x, value); - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.wrap}", arg).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.wrap}", arg).value; EXPECT_TRUE(sp_str_equal_cstr(got, "")); sp_fmt_directive_reset(); } @@ -734,7 +741,7 @@ UTEST(sp_fmt_directive, custom_fn_fallback) { UTEST(sp_fmt_directive, default_render_with_wrappers_on_int) { sp_fmt_directive_reset(); sp_fmt_register_decorator("wrap", _test_before_lt, _test_after_gt); - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.wrap}", sp_fmt_int(42)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.wrap}", sp_fmt_int(42)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "<42>")); sp_fmt_directive_reset(); } @@ -742,7 +749,7 @@ UTEST(sp_fmt_directive, default_render_with_wrappers_on_int) { UTEST(sp_fmt_directive, content_wider_than_width_with_wrapper) { sp_fmt_directive_reset(); sp_fmt_register_decorator("wrap", _test_before_lt, _test_after_gt); - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{:3 .wrap}", sp_fmt_cstr("hello")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:3 .wrap}", sp_fmt_cstr("hello")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "")); sp_fmt_directive_reset(); } @@ -757,7 +764,7 @@ UTEST(sp_fmt_directive, before_render_then_transform) { sp_fmt_register_decorator("wrap", _test_before_lt, SP_NULLPTR); sp_fmt_register_renderer("prefix", _test_render_prefixed, sp_fmt_id_none); sp_fmt_register_transformer("upper", _test_transform_upper); - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.wrap .prefix .upper}", sp_fmt_int(0)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.wrap .prefix .upper}", sp_fmt_int(0)).value; EXPECT_TRUE(sp_str_equal_cstr(got, ""), @@ -781,22 +788,22 @@ UTEST(sp_fmt_pad, wrapped_content_overflow) { } UTEST(sp_fmt_v, escaped_braces_around_placeholder) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{{{:5}}}", sp_fmt_int(42)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{{{:5}}}", sp_fmt_int(42)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "{ 42}")); } UTEST(sp_fmt_v, dynamic_width) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{:$}", sp_fmt_int(6), sp_fmt_int(42)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:$}", sp_fmt_int(6), sp_fmt_int(42)).value; EXPECT_TRUE(sp_str_equal_cstr(got, " 42")); } UTEST(sp_fmt_v, dynamic_fill_center) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{:$^9}", sp_fmt_int('*'), sp_fmt_int(42)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:$^9}", sp_fmt_int('*'), sp_fmt_int(42)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "***42****")); } UTEST(sp_fmt_v, dynamic_fill_and_width) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{:$^$}", + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:$^$}", sp_fmt_int('-'), sp_fmt_int(8), sp_fmt_cstr("hi")).value; @@ -804,106 +811,106 @@ UTEST(sp_fmt_v, dynamic_fill_and_width) { } UTEST(sp_fmt_v, dynamic_precision) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{:.$}", sp_fmt_int(3), sp_fmt_float(3.14159)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:.$}", sp_fmt_int(3), sp_fmt_float(3.14159)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "3.142")); } UTEST(sp_fmt_v, dynamic_width_with_literal_precision) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{:$.2}", sp_fmt_int(8), sp_fmt_float(1.5)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:$.2}", sp_fmt_int(8), sp_fmt_float(1.5)).value; EXPECT_TRUE(sp_str_equal_cstr(got, " 1.50")); } UTEST(sp_fmt_v, err_parse_stops_formatting) { sp_str_t str = sp_zero; - EXPECT_EQ(sp_fmt_a(sp_mem_get_scratch(), "a {:5.} b {}", sp_fmt_int(99)).err, SP_ERR_FMT_BAD_PRECISION); + EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "a {:5.} b {}", sp_fmt_int(99)).err, SP_ERR_FMT_BAD_PRECISION); } UTEST(sp_fmt_v, err_unterminated_placeholder) { sp_str_t str = sp_zero; - EXPECT_EQ(sp_fmt_a(sp_mem_get_scratch(), "hi {nope", sp_fmt_int(1)).err, SP_ERR_FMT_BAD_PLACEHOLDER); + EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "hi {nope", sp_fmt_int(1)).err, SP_ERR_FMT_BAD_PLACEHOLDER); } UTEST(sp_fmt_v, err_dynamic_fill_wrong_kind) { sp_str_t str = sp_zero; - EXPECT_EQ(sp_fmt_a(sp_mem_get_scratch(), "{:$^5}", sp_fmt_float(1.0), sp_fmt_int(42)).err, SP_ERR_FMT_DIRECTIVE_ARG_WRONG_KIND); + EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "{:$^5}", sp_fmt_float(1.0), sp_fmt_int(42)).err, SP_ERR_FMT_DIRECTIVE_ARG_WRONG_KIND); } UTEST(sp_fmt_v, err_dynamic_width_wrong_kind) { sp_str_t str = sp_zero; - EXPECT_EQ(sp_fmt_a(sp_mem_get_scratch(), "{:$}", sp_fmt_cstr("oops"), sp_fmt_int(42)).err, SP_ERR_FMT_DIRECTIVE_ARG_WRONG_KIND); + EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "{:$}", sp_fmt_cstr("oops"), sp_fmt_int(42)).err, SP_ERR_FMT_DIRECTIVE_ARG_WRONG_KIND); } UTEST(sp_fmt_v, err_dynamic_precision_wrong_kind) { sp_str_t str = sp_zero; - EXPECT_EQ(sp_fmt_a(sp_mem_get_scratch(), "{:.$}", sp_fmt_float(3.0), sp_fmt_float(3.14)).err, SP_ERR_FMT_DIRECTIVE_ARG_WRONG_KIND); + EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "{:.$}", sp_fmt_float(3.0), sp_fmt_float(3.14)).err, SP_ERR_FMT_DIRECTIVE_ARG_WRONG_KIND); } UTEST(sp_fmt_v, err_stops_subsequent_placeholders) { sp_fmt_directive_reset(); sp_str_t str = sp_zero; - sp_err_t err = sp_fmt_a(sp_mem_get_scratch(), "{} {.nope} {}", sp_fmt_int(1), sp_fmt_int(2), sp_fmt_int(3)).err; + sp_err_t err = sp_fmt(sp_mem_get_scratch(), "{} {.nope} {}", sp_fmt_int(1), sp_fmt_int(2), sp_fmt_int(3)).err; EXPECT_EQ(err, SP_ERR_FMT_UNKNOWN_DIRECTIVE); } UTEST(sp_fmt_v, str_precision_truncates) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{:.3}", sp_fmt_cstr("hello")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:.3}", sp_fmt_cstr("hello")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "hel")); } UTEST(sp_fmt_v, str_precision_longer_than_string) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{:.10}", sp_fmt_cstr("hi")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:.10}", sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "hi")); } UTEST(sp_fmt_v, str_dynamic_precision_truncates) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{:.$}", sp_fmt_int(2), sp_fmt_cstr("hello")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:.$}", sp_fmt_int(2), sp_fmt_cstr("hello")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "he")); } UTEST(sp_fmt_v, str_precision_with_width) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "[{:>6.3}]", sp_fmt_cstr("hello")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "[{:>6.3}]", sp_fmt_cstr("hello")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "[ hel]")); } UTEST(sp_fmt_v, f64_precision_zero_means_zero) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{:.0}", sp_fmt_float(3.7)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:.0}", sp_fmt_float(3.7)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "4")); } UTEST(sp_fmt_v, f64_dynamic_precision_zero) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{:.$}", sp_fmt_int(0), sp_fmt_float(3.7)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:.$}", sp_fmt_int(0), sp_fmt_float(3.7)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "4")); } UTEST(sp_fmt_v, f64_no_precision_defaults_to_six) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{}", sp_fmt_float(1.5)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{}", sp_fmt_float(1.5)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "1.500000")); } UTEST(sp_fmt_v, width_clamped_literal) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{:99999}", sp_fmt_cstr("x")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:99999}", sp_fmt_cstr("x")).value; EXPECT_EQ(got.len, SP_FMT_WIDTH_MAX); } UTEST(sp_fmt_v, width_clamped_dynamic_huge) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{:$}", sp_fmt_uint(999999999ULL), sp_fmt_cstr("x")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:$}", sp_fmt_uint(999999999ULL), sp_fmt_cstr("x")).value; EXPECT_EQ(got.len, SP_FMT_WIDTH_MAX); } UTEST(sp_fmt_v, width_clamped_dynamic_negative) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{:$}", sp_fmt_int(-5), sp_fmt_cstr("hi")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:$}", sp_fmt_int(-5), sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "hi")); } static void _test_render_u64_only(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { (void)param; (void)mem; - sp_fmt_write_u64_a(io, arg->u); + sp_fmt_write_u64(io, arg->u); } UTEST(sp_fmt_directive, kinds_single_accepts_match) { sp_fmt_directive_reset(); sp_fmt_register_renderer("only_u64", _test_render_u64_only, sp_fmt_id_u64); - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.only_u64}", sp_fmt_uint(42)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.only_u64}", sp_fmt_uint(42)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "42")); sp_fmt_directive_reset(); } @@ -912,7 +919,7 @@ UTEST(sp_fmt_directive, kinds_single_rejects_mismatch) { sp_fmt_directive_reset(); sp_fmt_register_renderer("only_u64", _test_render_u64_only, sp_fmt_id_u64); sp_str_t str = sp_zero; - EXPECT_EQ(sp_fmt_a(sp_mem_get_scratch(), "{.only_u64}", sp_fmt_float(1.5)).err, SP_ERR_FMT_WRONG_PARAM_KIND); + EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "{.only_u64}", sp_fmt_float(1.5)).err, SP_ERR_FMT_WRONG_PARAM_KIND); sp_fmt_directive_reset(); } @@ -922,8 +929,8 @@ UTEST(sp_fmt_directive, kinds_multiple_accepts_either) { .kind = sp_fmt_directive_decorator, .arg_kinds = sp_cast(sp_fmt_arg_kind_t, sp_fmt_id_u64 | sp_fmt_id_s64), }); - sp_str_t a = sp_fmt_a(sp_mem_get_scratch(), "{.num}", sp_fmt_uint(7)).value; - sp_str_t b = sp_fmt_a(sp_mem_get_scratch(), "{.num}", sp_fmt_int(-3)).value; + sp_str_t a = sp_fmt(sp_mem_get_scratch(), "{.num}", sp_fmt_uint(7)).value; + sp_str_t b = sp_fmt(sp_mem_get_scratch(), "{.num}", sp_fmt_int(-3)).value; EXPECT_TRUE(sp_str_equal_cstr(a, "7")); EXPECT_TRUE(sp_str_equal_cstr(b, "-3")); sp_fmt_directive_reset(); @@ -936,16 +943,16 @@ UTEST(sp_fmt_directive, kinds_multiple_rejects_outsider) { .arg_kinds = sp_cast(sp_fmt_arg_kind_t, sp_fmt_id_u64 | sp_fmt_id_s64), }); sp_str_t str = sp_zero; - EXPECT_EQ(sp_fmt_a(sp_mem_get_scratch(), "{.num}", sp_fmt_cstr("nope")).err, SP_ERR_FMT_WRONG_PARAM_KIND); + EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "{.num}", sp_fmt_cstr("nope")).err, SP_ERR_FMT_WRONG_PARAM_KIND); sp_fmt_directive_reset(); } UTEST(sp_fmt_directive, kinds_unset_accepts_all) { sp_fmt_directive_reset(); sp_fmt_register_decorator("any", _test_before_lt, _test_after_gt); - sp_str_t a = sp_fmt_a(sp_mem_get_scratch(), "{.any}", sp_fmt_int(1)).value; - sp_str_t b = sp_fmt_a(sp_mem_get_scratch(), "{.any}", sp_fmt_cstr("hi")).value; - sp_str_t c = sp_fmt_a(sp_mem_get_scratch(), "{.any}", sp_fmt_float(2.0)).value; + sp_str_t a = sp_fmt(sp_mem_get_scratch(), "{.any}", sp_fmt_int(1)).value; + sp_str_t b = sp_fmt(sp_mem_get_scratch(), "{.any}", sp_fmt_cstr("hi")).value; + sp_str_t c = sp_fmt(sp_mem_get_scratch(), "{.any}", sp_fmt_float(2.0)).value; EXPECT_TRUE(sp_str_equal_cstr(a, "<1>")); EXPECT_TRUE(sp_str_equal_cstr(b, "")); EXPECT_TRUE(sp_str_equal_cstr(c, "<2.000000>")); @@ -1066,7 +1073,7 @@ static void _test_fg_after(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, UTEST(sp_fmt_directive_arg, literal_passed_as_str) { sp_fmt_directive_reset(); sp_fmt_register_decorator_p("fg", _test_fg_before, _test_fg_after, sp_fmt_id_str); - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.fg red}", sp_fmt_cstr("hello")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.fg red}", sp_fmt_cstr("hello")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "hello")); EXPECT_TRUE(_last_fg_had_param); EXPECT_TRUE(sp_str_equal_cstr(_last_fg_param, "red")); @@ -1076,7 +1083,7 @@ UTEST(sp_fmt_directive_arg, literal_passed_as_str) { UTEST(sp_fmt_directive_arg, dynamic_passed_as_str) { sp_fmt_directive_reset(); sp_fmt_register_decorator_p("fg", _test_fg_before, _test_fg_after, sp_fmt_id_str); - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.fg $}", sp_fmt_cstr("blue"), sp_fmt_cstr("hello")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.fg $}", sp_fmt_cstr("blue"), sp_fmt_cstr("hello")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "hello")); sp_fmt_directive_reset(); } @@ -1084,7 +1091,7 @@ UTEST(sp_fmt_directive_arg, dynamic_passed_as_str) { UTEST(sp_fmt_directive_arg, dynamic_accepts_u64_with_mask) { sp_fmt_directive_reset(); sp_fmt_register_decorator_p("fg", _test_fg_before, _test_fg_after, sp_fmt_id_str | sp_fmt_id_u64); - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.fg $}", sp_fmt_uint(31), sp_fmt_cstr("x")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.fg $}", sp_fmt_uint(31), sp_fmt_cstr("x")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "x")); sp_fmt_directive_reset(); } @@ -1093,7 +1100,7 @@ UTEST(sp_fmt_directive_arg, err_missing_arg) { sp_fmt_directive_reset(); sp_fmt_register_decorator_p("fg", _test_fg_before, _test_fg_after, sp_fmt_id_str); sp_str_t str = sp_zero; - EXPECT_EQ(sp_fmt_a(sp_mem_get_scratch(), "{.fg}", sp_fmt_cstr("hi")).err, SP_ERR_FMT_DIRECTIVE_ARG_MISSING); + EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "{.fg}", sp_fmt_cstr("hi")).err, SP_ERR_FMT_DIRECTIVE_ARG_MISSING); sp_fmt_directive_reset(); } @@ -1101,7 +1108,7 @@ UTEST(sp_fmt_directive_arg, err_unexpected_arg) { sp_fmt_directive_reset(); sp_fmt_register_decorator("bold", _test_before_lt, _test_after_gt); sp_str_t str = sp_zero; - EXPECT_EQ(sp_fmt_a(sp_mem_get_scratch(), "{.bold red}", sp_fmt_cstr("hi")).err, SP_ERR_FMT_DIRECTIVE_ARG_UNEXPECTED); + EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "{.bold red}", sp_fmt_cstr("hi")).err, SP_ERR_FMT_DIRECTIVE_ARG_UNEXPECTED); sp_fmt_directive_reset(); } @@ -1109,7 +1116,7 @@ UTEST(sp_fmt_directive_arg, err_wrong_literal_kind) { sp_fmt_directive_reset(); sp_fmt_register_decorator_p("numpad", _test_before_lt, _test_after_gt, sp_fmt_id_u64); sp_str_t str = sp_zero; - EXPECT_EQ(sp_fmt_a(sp_mem_get_scratch(), "{.numpad abc}", sp_fmt_cstr("hi")).err, SP_ERR_FMT_DIRECTIVE_ARG_WRONG_KIND); + EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "{.numpad abc}", sp_fmt_cstr("hi")).err, SP_ERR_FMT_DIRECTIVE_ARG_WRONG_KIND); sp_fmt_directive_reset(); } @@ -1117,7 +1124,7 @@ UTEST(sp_fmt_directive_arg, err_wrong_dynamic_kind) { sp_fmt_directive_reset(); sp_fmt_register_decorator_p("fg", _test_fg_before, _test_fg_after, sp_fmt_id_str); sp_str_t str = sp_zero; - EXPECT_EQ(sp_fmt_a(sp_mem_get_scratch(), "{.fg $}", sp_fmt_uint(5), sp_fmt_cstr("hi")).err, SP_ERR_FMT_DIRECTIVE_ARG_WRONG_KIND); + EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "{.fg $}", sp_fmt_uint(5), sp_fmt_cstr("hi")).err, SP_ERR_FMT_DIRECTIVE_ARG_WRONG_KIND); sp_fmt_directive_reset(); } @@ -1125,40 +1132,40 @@ UTEST(sp_fmt_directive_arg, chain_of_literal_and_dynamic) { sp_fmt_directive_reset(); sp_fmt_register_decorator_p("fg", _test_fg_before, _test_fg_after, sp_fmt_id_str); sp_fmt_register_decorator("bold", _test_before_lt, _test_after_gt); - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.fg $ .bold}", sp_fmt_cstr("cyan"), sp_fmt_cstr("hi")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.fg $ .bold}", sp_fmt_cstr("cyan"), sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "")); sp_fmt_directive_reset(); } UTEST(sp_fmt_builtin_fg, literal_color) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.fg red}", sp_fmt_cstr("hi")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.fg red}", sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "\033[31mhi\033[0m")); } UTEST(sp_fmt_builtin_fg, literal_bright_cyan) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.fg brightcyan}", sp_fmt_cstr("hi")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.fg brightcyan}", sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "\033[96mhi\033[0m")); } UTEST(sp_fmt_builtin_fg, dynamic_color) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.fg $}", sp_fmt_cstr("green"), sp_fmt_cstr("hi")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.fg $}", sp_fmt_cstr("green"), sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "\033[32mhi\033[0m")); } UTEST(sp_fmt_builtin_fg, composes_with_padding) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{:*^6 .fg red}", sp_fmt_cstr("hi")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:*^6 .fg red}", sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "**\033[31mhi\033[0m**")); } UTEST(sp_fmt_builtin_fg, err_missing_arg) { sp_str_t str = sp_zero; - EXPECT_EQ(sp_fmt_a(sp_mem_get_scratch(), "{.fg}", sp_fmt_cstr("hi")).err, SP_ERR_FMT_DIRECTIVE_ARG_MISSING); + EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "{.fg}", sp_fmt_cstr("hi")).err, SP_ERR_FMT_DIRECTIVE_ARG_MISSING); } UTEST(sp_fmt_directive_arg, dynamic_param_interleaved_with_width) { sp_fmt_directive_reset(); sp_fmt_register_decorator_p("fg", _test_fg_before, _test_fg_after, sp_fmt_id_str); - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{:$ .fg $}", sp_fmt_int(4), sp_fmt_cstr("red"), sp_fmt_cstr("hi")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:$ .fg $}", sp_fmt_int(4), sp_fmt_cstr("red"), sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got, " hi")); sp_fmt_directive_reset(); } @@ -1172,7 +1179,7 @@ UTEST(sp_fmt_transform, composes_with_wrappers) { sp_fmt_directive_reset(); sp_fmt_register_decorator("wrap", _test_before_lt, _test_after_gt); sp_fmt_register_transformer("upper", _test_transform_upper); - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.wrap .upper}", sp_fmt_cstr("hi")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.wrap .upper}", sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "")); sp_fmt_directive_reset(); } @@ -1181,9 +1188,9 @@ UTEST(sp_fmt_transform, stacked_innermost_first) { sp_fmt_directive_reset(); sp_fmt_register_transformer("upper", _test_transform_upper); sp_fmt_register_transformer("redact", _test_transform_redact); - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.upper .redact}", sp_fmt_cstr("hi")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.upper .redact}", sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "**")); - sp_str_t got2 = sp_fmt_a(sp_mem_get_scratch(), "{.redact .upper}", sp_fmt_cstr("hi")).value; + sp_str_t got2 = sp_fmt(sp_mem_get_scratch(), "{.redact .upper}", sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got2, "**")); sp_fmt_directive_reset(); } @@ -1191,7 +1198,7 @@ UTEST(sp_fmt_transform, stacked_innermost_first) { UTEST(sp_fmt_transform, measures_post_transform_width) { sp_fmt_directive_reset(); sp_fmt_register_transformer("redact", _test_transform_redact); - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{:10 .redact}", sp_fmt_cstr("hi")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:10 .redact}", sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got, " **")); sp_fmt_directive_reset(); } @@ -1204,19 +1211,21 @@ UTEST(sp_fmt_transform, into_writer_backed_builder) { sp_io_mem_writer_t w = sp_zero; sp_io_mem_writer_from_buffer(&w, buf, sizeof(buf)); - sp_fmt_io(&w.base, "{.upper}", sp_fmt_cstr("hello")); + sp_mem_arena_marker_t s = sp_mem_begin_scratch(); + sp_fmt_io_a(&w.base, s.mem, "{.upper}", sp_fmt_cstr("hello")); + sp_mem_end_scratch(s); sp_str_t got = { .data = buf, .len = 5 }; EXPECT_TRUE(sp_str_equal_cstr(got, "HELLO")); sp_fmt_directive_reset(); } UTEST(sp_fmt_builtin_transform, upper) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.upper}", sp_fmt_cstr("hello")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.upper}", sp_fmt_cstr("hello")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "HELLO")); } UTEST(sp_fmt_builtin_transform, redact) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.redact}", sp_fmt_cstr("secret")).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.redact}", sp_fmt_cstr("secret")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "******")); } @@ -1509,37 +1518,37 @@ UTEST(fmt, parse_edge_cases) { } UTEST(fmt, hex_zero) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.hex}", sp_fmt_uint(0)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.hex}", sp_fmt_uint(0)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "0x0")); } UTEST(fmt, hex_small) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.hex}", sp_fmt_uint(0xa)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.hex}", sp_fmt_uint(0xa)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "0xA")); } UTEST(fmt, hex_no_pad) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.hex}", sp_fmt_uint(0xa5)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.hex}", sp_fmt_uint(0xa5)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "0xA5")); } UTEST(fmt, hex_word) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.hex}", sp_fmt_uint(0xdeadbeef)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.hex}", sp_fmt_uint(0xdeadbeef)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "0xDEADBEEF")); } UTEST(fmt, hex_u64_max) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.hex}", sp_fmt_uint(0xffffffffffffffffULL)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.hex}", sp_fmt_uint(0xffffffffffffffffULL)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "0xFFFFFFFFFFFFFFFF")); } UTEST(fmt, hex_signed_negative) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.hex}", sp_fmt_int(-1)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.hex}", sp_fmt_int(-1)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "0xFFFFFFFFFFFFFFFF")); } UTEST(fmt, hex_mixed_digits) { - sp_str_t got = sp_fmt_a(sp_mem_get_scratch(), "{.hex}", sp_fmt_uint(0x1234abcd)).value; + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.hex}", sp_fmt_uint(0x1234abcd)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "0x1234ABCD")); } diff --git a/test/fs/canonicalize_path.c b/test/fs/canonicalize_path.c index bfdec66..6a3f760 100644 --- a/test/fs/canonicalize_path.c +++ b/test/fs/canonicalize_path.c @@ -17,14 +17,14 @@ static void run_canon_test(s32* utest_result, sp_test_file_manager_t* fm, canon_ if (t.setup[0].path) { sp_str_t sandbox = sp_test_file_path(fm, sp_str_view(t.label)); - sp_fs_create_dir_a(sandbox); + sp_fs_create_dir(sandbox); fs_apply_setup(utest_result, fm, sandbox, t.setup); - input = sp_fs_join_path_a(fm->mem, sandbox, sp_str_view(t.input)); + input = sp_fs_join_path(fm->mem, sandbox, sp_str_view(t.input)); } else { input = sp_str_view(t.input); } - sp_str_t result = sp_fs_canonicalize_path_a(fm->mem, input); + sp_str_t result = sp_fs_canonicalize_path(fm->mem, input); if (t.expect_nonempty && result.len == 0) { SP_TEST_REPORT("{}: expected nonempty", sp_fmt_cstr(t.label)); @@ -54,7 +54,7 @@ static void run_canon_test(s32* utest_result, sp_test_file_manager_t* fm, canon_ SP_FAIL(); } } - if (t.expect_exists && !sp_fs_exists_a(result)) { + if (t.expect_exists && !sp_fs_exists(result)) { SP_TEST_REPORT("{}: expected result to exist", sp_fmt_cstr(t.label)); SP_FAIL(); } @@ -284,17 +284,17 @@ UTEST_F(fs, canon_idempotent) { // run a second pass: canonicalizing an already-canonical path should be the same sp_str_t sandbox = sp_test_file_path(&ut.file_manager, sp_str_lit("canon_idempotent")); - sp_str_t path = sp_fs_join_path_a(a, sandbox, sp_str_lit("stable.txt")); - sp_str_t first = sp_fs_canonicalize_path_a(a, path); - sp_str_t second = sp_fs_canonicalize_path_a(a, first); + sp_str_t path = sp_fs_join_path(a, sandbox, sp_str_lit("stable.txt")); + sp_str_t first = sp_fs_canonicalize_path(a, path); + sp_str_t second = sp_fs_canonicalize_path(a, first); SP_EXPECT_STR_EQ(first, second); } UTEST_F(fs, canon_exe_idempotent) { SKIP_ON_WASM() sp_mem_t a = ut.file_manager.mem; - sp_str_t exe = sp_fs_get_exe_path_a(a); - sp_str_t canonical = sp_fs_canonicalize_path_a(a, exe); + sp_str_t exe = sp_fs_get_exe_path(a); + sp_str_t canonical = sp_fs_canonicalize_path(a, exe); SP_EXPECT_STR_EQ(canonical, exe); } @@ -303,13 +303,13 @@ UTEST_F(fs, canon_exe_idempotent) { UTEST_F(fs, canon_cwd_matches_dot) { SKIP_ON_WASM() sp_mem_t a = ut.file_manager.mem; - sp_str_t old_cwd = sp_fs_get_cwd_a(a); + sp_str_t old_cwd = sp_fs_get_cwd(a); sp_str_t sandbox = sp_test_file_path(&ut.file_manager, sp_str_lit("canon_cwd")); - sp_fs_create_dir_a(sandbox); + sp_fs_create_dir(sandbox); ASSERT_EQ(sp_sys_chdir_s(sandbox), 0); - sp_str_t cwd = sp_fs_get_cwd_a(a); - sp_str_t canonical_dot = sp_fs_canonicalize_path_a(a, sp_str_lit(".")); + sp_str_t cwd = sp_fs_get_cwd(a); + sp_str_t canonical_dot = sp_fs_canonicalize_path(a, sp_str_lit(".")); SP_EXPECT_STR_EQ(cwd, canonical_dot); ASSERT_EQ(sp_sys_chdir_s(old_cwd), 0); } diff --git a/test/fs/collect.c b/test/fs/collect.c index 7fd76ed..9d897a0 100644 --- a/test/fs/collect.c +++ b/test/fs/collect.c @@ -45,23 +45,23 @@ UTEST_F_TEARDOWN(fs_collect) { static void add_entry(sp_test_file_manager_t* tmp, const c8* label, collect_entry_t entry) { sp_str_t root = sp_test_file_path(tmp, sp_str_view(label)); - sp_str_t path = sp_fs_join_path_a(tmp->mem, root, sp_str_view(entry.path)); + sp_str_t path = sp_fs_join_path(tmp->mem, root, sp_str_view(entry.path)); switch (entry.kind) { case COLLECT_ENT_FILE: { sp_str_t parent = sp_fs_parent_path(path); - if (!sp_str_empty(parent) && !sp_fs_exists_a(parent)) { - sp_fs_create_dir_a(parent); + if (!sp_str_empty(parent) && !sp_fs_exists(parent)) { + sp_fs_create_dir(parent); } sp_test_file_create_ex((sp_test_file_config_t) { .path = path }); break; } case COLLECT_ENT_DIR: { - sp_fs_create_dir_a(path); + sp_fs_create_dir(path); break; } case COLLECT_ENT_SYMLINK: { - sp_str_t target = sp_fs_join_path_a(tmp->mem, root, sp_str_view(entry.target)); - sp_fs_create_sym_link_a(target, path); + sp_str_t target = sp_fs_join_path(tmp->mem, root, sp_str_view(entry.target)); + sp_fs_create_sym_link(target, path); break; } case COLLECT_ENT_MISSING: { @@ -80,8 +80,8 @@ static void run_collect_test(s32* utest_result, sp_test_file_manager_t* tmp, col sp_str_t root = sp_test_file_path(tmp, sp_str_view(test.label)); sp_da(sp_fs_entry_t) results = test.recursive - ? sp_fs_collect_recursive_a(tmp->mem, root) - : sp_fs_collect_a(tmp->mem, root); + ? sp_fs_collect_recursive(tmp->mem, root) + : sp_fs_collect(tmp->mem, root); u32 num_expected = 0; sp_carr_for(test.expect, it) { @@ -93,7 +93,7 @@ static void run_collect_test(s32* utest_result, sp_test_file_manager_t* tmp, col sp_for(i, num_expected) { collect_expect_t exp = test.expect[i]; - sp_str_t expected_path = sp_fs_join_path_a(tmp->mem, root, sp_str_view(exp.name)); + sp_str_t expected_path = sp_fs_join_path(tmp->mem, root, sp_str_view(exp.name)); bool found = false; sp_da_for(results, n) { diff --git a/test/fs/copy.c b/test/fs/copy.c index 6e019ec..2580760 100644 --- a/test/fs/copy.c +++ b/test/fs/copy.c @@ -24,31 +24,31 @@ typedef struct { static void run_copy_test(s32* utest_result, sp_test_file_manager_t* fm, copy_test_t t) { sp_str_t sandbox = sp_test_file_path(fm, sp_str_view(t.label)); - sp_fs_create_dir_a(sandbox); + sp_fs_create_dir(sandbox); fs_apply_setup(utest_result, fm, sandbox, t.setup); - sp_str_t src = sp_fs_join_path_a(fm->mem, sandbox, sp_str_view(t.src)); - sp_str_t dst = sp_fs_join_path_a(fm->mem, sandbox, sp_str_view(t.dst)); + sp_str_t src = sp_fs_join_path(fm->mem, sandbox, sp_str_view(t.src)); + sp_str_t dst = sp_fs_join_path(fm->mem, sandbox, sp_str_view(t.dst)); switch (t.action) { - case COPY_FILE: sp_fs_copy_file_a(src, dst); break; - case COPY_DIR: sp_fs_copy_a(src, dst); break; - case COPY_LINK: sp_fs_link_a(src, dst, SP_FS_LINK_COPY); break; + case COPY_FILE: sp_fs_copy_file(src, dst); break; + case COPY_DIR: sp_fs_copy(src, dst); break; + case COPY_LINK: sp_fs_link(src, dst, SP_FS_LINK_COPY); break; } sp_for(i, 16) { copy_expect_t* exp = &t.expect[i]; if (!exp->path) break; - sp_str_t path = sp_fs_join_path_a(fm->mem, sandbox, sp_str_view(exp->path)); - bool exists = sp_fs_exists_a(path); + sp_str_t path = sp_fs_join_path(fm->mem, sandbox, sp_str_view(exp->path)); + bool exists = sp_fs_exists(path); fs_expect_bool(utest_result, path, "exists", exists, exp->exists); if (exp->exists && exists) { - fs_expect_attr(utest_result, path, sp_fs_get_kind_a(path), exp->attr); + fs_expect_attr(utest_result, path, sp_fs_get_kind(path), exp->attr); if (exp->content) { sp_str_t file_content = sp_zero; - sp_io_read_file_a(fm->mem, path, &file_content); + sp_io_read_file(fm->mem, path, &file_content); SP_EXPECT_STR_EQ(file_content, sp_str_view(exp->content)); } } @@ -177,22 +177,22 @@ UTEST_F(fs, copy_preserves_file_attributes) { .content = sp_str_lit("preserved content"), }); - ASSERT_EQ(chmod(sp_cstr_from_str_a(a, source_file), 0755), 0); + ASSERT_EQ(chmod(sp_cstr_from_str(a, source_file), 0755), 0); struct stat original_stat = {0}; - ASSERT_EQ(stat(sp_cstr_from_str_a(a, source_file), &original_stat), 0); + ASSERT_EQ(stat(sp_cstr_from_str(a, source_file), &original_stat), 0); sp_str_t copy_file = sp_test_file_path(&ut.file_manager, sp_str_lit("copy_attrs.txt")); - ASSERT_EQ(sp_fs_copy_a(source_file, copy_file), SP_OK); - ASSERT_TRUE(sp_fs_is_file_a(copy_file)); + ASSERT_EQ(sp_fs_copy(source_file, copy_file), SP_OK); + ASSERT_TRUE(sp_fs_is_file(copy_file)); struct stat copy_stat = {0}; - ASSERT_EQ(stat(sp_cstr_from_str_a(a, copy_file), ©_stat), 0); + ASSERT_EQ(stat(sp_cstr_from_str(a, copy_file), ©_stat), 0); ASSERT_EQ(original_stat.st_mode, copy_stat.st_mode); ASSERT_EQ(original_stat.st_size, copy_stat.st_size); sp_str_t preserved = sp_zero; - sp_io_read_file_a(a, copy_file, &preserved); + sp_io_read_file(a, copy_file, &preserved); SP_EXPECT_STR_EQ(preserved, sp_str_lit("preserved content")); } #endif diff --git a/test/fs/copy_glob.c b/test/fs/copy_glob.c index 8402150..6d249b7 100644 --- a/test/fs/copy_glob.c +++ b/test/fs/copy_glob.c @@ -9,13 +9,13 @@ typedef struct { static void run_copy_glob_test(s32* utest_result, sp_test_file_manager_t* fm, copy_glob_test_t t) { sp_str_t sandbox = sp_test_file_path(fm, sp_str_view(t.label)); - sp_str_t src = sp_fs_join_path_a(fm->mem, sandbox, sp_str_lit("src")); - sp_str_t dst = sp_fs_join_path_a(fm->mem, sandbox, sp_str_lit("dst")); - sp_fs_create_dir_a(src); - sp_fs_create_dir_a(dst); + sp_str_t src = sp_fs_join_path(fm->mem, sandbox, sp_str_lit("src")); + sp_str_t dst = sp_fs_join_path(fm->mem, sandbox, sp_str_lit("dst")); + sp_fs_create_dir(src); + sp_fs_create_dir(dst); fs_apply_setup(utest_result, fm, src, t.src); - sp_fs_copy_glob_a(src, sp_str_view(t.glob), dst); + sp_fs_copy_glob(src, sp_str_view(t.glob), dst); fs_expect_paths(utest_result, fm, dst, t.expect); } diff --git a/test/fs/create_dir.c b/test/fs/create_dir.c index 7374967..e09442a 100644 --- a/test/fs/create_dir.c +++ b/test/fs/create_dir.c @@ -46,17 +46,17 @@ UTEST_F_TEARDOWN(fs_create_dir) { static void run_create_dir_test(s32* utest_result, sp_test_file_manager_t* fm, create_dir_test_t t) { sp_str_t sandbox = sp_test_file_path(fm, sp_str_view(t.label)); - sp_fs_create_dir_a(sandbox); + sp_fs_create_dir(sandbox); u32 setup_count = 0; while (setup_count < 16 && t.setup[setup_count].path) setup_count++; sp_for(i, setup_count) { create_dir_setup_ent_t* s = &t.setup[i]; - sp_str_t full = sp_fs_join_path_a(fm->mem, sandbox, sp_str_view(s->path)); + sp_str_t full = sp_fs_join_path(fm->mem, sandbox, sp_str_view(s->path)); sp_str_t parent = sp_fs_parent_path(full); - if (!sp_str_empty(parent) && !sp_fs_exists_a(parent)) { - sp_fs_create_dir_a(parent); + if (!sp_str_empty(parent) && !sp_fs_exists(parent)) { + sp_fs_create_dir(parent); } switch (s->kind) { @@ -65,19 +65,19 @@ static void run_create_dir_test(s32* utest_result, sp_test_file_manager_t* fm, c break; } case CREATE_DIR_ENT_DIR: { - sp_fs_create_dir_a(full); + sp_fs_create_dir(full); break; } case CREATE_DIR_ENT_SYMLINK: { - sp_str_t target = sp_fs_join_path_a(fm->mem, sandbox, sp_str_view(s->symlink_target)); - ASSERT_EQ(sp_fs_create_sym_link_a(target, full), SP_OK); + sp_str_t target = sp_fs_join_path(fm->mem, sandbox, sp_str_view(s->symlink_target)); + ASSERT_EQ(sp_fs_create_sym_link(target, full), SP_OK); break; } } } - sp_str_t target = sp_fs_join_path_a(fm->mem, sandbox, sp_str_view(t.target)); - sp_err_t result = sp_fs_create_dir_a(target); + sp_str_t target = sp_fs_join_path(fm->mem, sandbox, sp_str_view(t.target)); + sp_err_t result = sp_fs_create_dir(target); if (t.expect_ok && result) { SP_TEST_REPORT("{} does not exist with code {}", sp_fmt_str(target), sp_fmt_int(result)); @@ -92,8 +92,8 @@ static void run_create_dir_test(s32* utest_result, sp_test_file_manager_t* fm, c sp_for(i, expected_count) { create_dir_expected_ent_t* exp = &t.expected[i]; - sp_str_t expected_path = sp_fs_join_path_a(fm->mem, sandbox, sp_str_view(exp->path)); - bool exists = sp_fs_exists_a(expected_path); + sp_str_t expected_path = sp_fs_join_path(fm->mem, sandbox, sp_str_view(exp->path)); + bool exists = sp_fs_exists(expected_path); if (exists != exp->exists) { if (exp->exists) { SP_TEST_REPORT("expected {} to exist", sp_fmt_str(expected_path)); @@ -104,7 +104,7 @@ static void run_create_dir_test(s32* utest_result, sp_test_file_manager_t* fm, c } if (exp->exists) { - sp_fs_kind_t attr = sp_fs_get_kind_a(expected_path); + sp_fs_kind_t attr = sp_fs_get_kind(expected_path); if (attr != exp->attr) { SP_TEST_REPORT( "{} had attr {} but expected {}", diff --git a/test/fs/create_file.c b/test/fs/create_file.c index 49c9479..05ac7bc 100644 --- a/test/fs/create_file.c +++ b/test/fs/create_file.c @@ -10,11 +10,11 @@ typedef struct { static void run_create_file_test(s32* utest_result, sp_test_file_manager_t* fm, create_file_test_t t) { sp_str_t sandbox = sp_test_file_path(fm, sp_str_view(t.label)); - sp_fs_create_dir_a(sandbox); + sp_fs_create_dir(sandbox); fs_apply_setup(utest_result, fm, sandbox, t.setup); - sp_str_t path = sp_fs_join_path_a(fm->mem, sandbox, sp_str_view(t.path)); - sp_fs_create_file_a(path); + sp_str_t path = sp_fs_join_path(fm->mem, sandbox, sp_str_view(t.path)); + sp_fs_create_file(path); fs_expect_paths(utest_result, fm, sandbox, t.expect); } @@ -59,32 +59,32 @@ UTEST_F(fs, create_file_with_content) { SKIP_ON_WASM() sp_mem_t a = ut.file_manager.mem; sp_str_t sandbox = sp_test_file_path(&ut.file_manager, sp_str_lit("create_file_with_content")); - sp_fs_create_dir_a(sandbox); + sp_fs_create_dir(sandbox); u8 buffer [] = { 's', 'p', 'u', 'm', 0 }; sp_str_t path = sp_zero; sp_str_t content = sp_zero; sp_err_t result = SP_OK; - path = sp_fs_join_path_a(a, sandbox, sp_str_lit("slice.file")); - result = sp_fs_create_file_slice_a(path, (sp_mem_slice_t) { buffer, 4 }); - sp_io_read_file_a(a, path, &content); + path = sp_fs_join_path(a, sandbox, sp_str_lit("slice.file")); + result = sp_fs_create_file_slice(path, (sp_mem_slice_t) { buffer, 4 }); + sp_io_read_file(a, path, &content); EXPECT_EQ(result, SP_OK); - EXPECT_TRUE(sp_fs_exists_a(path)); + EXPECT_TRUE(sp_fs_exists(path)); SP_EXPECT_STR_EQ_CSTR(content, "spum"); - path = sp_fs_join_path_a(a, sandbox, sp_str_lit("str.file")); - result = sp_fs_create_file_str_a(path, (sp_str_t) { (c8*)buffer, 4 }); - sp_io_read_file_a(a, path, &content); + path = sp_fs_join_path(a, sandbox, sp_str_lit("str.file")); + result = sp_fs_create_file_str(path, (sp_str_t) { (c8*)buffer, 4 }); + sp_io_read_file(a, path, &content); EXPECT_EQ(result, SP_OK); - EXPECT_TRUE(sp_fs_exists_a(path)); + EXPECT_TRUE(sp_fs_exists(path)); SP_EXPECT_STR_EQ_CSTR(content, "spum"); - path = sp_fs_join_path_a(a, sandbox, sp_str_lit("cstr.file")); - result = sp_fs_create_file_cstr_a(path, (const c8*)buffer); - sp_io_read_file_a(a, path, &content); + path = sp_fs_join_path(a, sandbox, sp_str_lit("cstr.file")); + result = sp_fs_create_file_cstr(path, (const c8*)buffer); + sp_io_read_file(a, path, &content); EXPECT_EQ(result, SP_OK); - EXPECT_TRUE(sp_fs_exists_a(path)); + EXPECT_TRUE(sp_fs_exists(path)); SP_EXPECT_STR_EQ_CSTR(content, "spum"); } diff --git a/test/fs/fs.h b/test/fs/fs.h index 894bb60..edc32f9 100644 --- a/test/fs/fs.h +++ b/test/fs/fs.h @@ -16,12 +16,12 @@ static void probe_symlinks(sp_mem_t a, sp_str_t test_dir) { if (probed) return; probed = true; - sp_str_t target = sp_fs_join_path_a(a, test_dir, sp_str_lit(".symlink_probe_target")); - sp_str_t link = sp_fs_join_path_a(a, test_dir, sp_str_lit(".symlink_probe_link")); - sp_fs_create_file_a(target); - are_symlinks_available = sp_fs_create_sym_link_a(target, link) == SP_OK; - if (are_symlinks_available) sp_fs_remove_file_a(link); - sp_fs_remove_file_a(target); + sp_str_t target = sp_fs_join_path(a, test_dir, sp_str_lit(".symlink_probe_target")); + sp_str_t link = sp_fs_join_path(a, test_dir, sp_str_lit(".symlink_probe_link")); + sp_fs_create_file(target); + are_symlinks_available = sp_fs_create_sym_link(target, link) == SP_OK; + if (are_symlinks_available) sp_fs_remove_file(link); + sp_fs_remove_file(target); } #define FS_EXPECT_EXIST true @@ -114,8 +114,8 @@ static void fs_expect_paths(s32* utest_result, sp_test_file_manager_t* fm, sp_st u32 expected_count = fs_count_expected_paths(expected); sp_for(i, expected_count) { fs_expected_path_t* exp = &expected[i]; - sp_str_t path = sp_fs_join_path_a(fm->mem, sandbox, sp_str_view(exp->path)); - bool exists = sp_fs_exists_a(path); + sp_str_t path = sp_fs_join_path(fm->mem, sandbox, sp_str_view(exp->path)); + bool exists = sp_fs_exists(path); if (exists != exp->exists) { if (exp->exists) { SP_TEST_REPORT("expected {} to exist", sp_fmt_str(path)); @@ -126,7 +126,7 @@ static void fs_expect_paths(s32* utest_result, sp_test_file_manager_t* fm, sp_st } if (exp->exists) { - fs_expect_attr(utest_result, path, sp_fs_get_kind_a(path), exp->attr); + fs_expect_attr(utest_result, path, sp_fs_get_kind(path), exp->attr); } } } @@ -135,11 +135,11 @@ static void fs_apply_setup(s32* utest_result, sp_test_file_manager_t* fm, sp_str u32 setup_count = fs_count_setup(setup); sp_for(i, setup_count) { fs_setup_t* ent = &setup[i]; - sp_str_t path = sp_fs_join_path_a(fm->mem, sandbox, sp_str_view(ent->path)); + sp_str_t path = sp_fs_join_path(fm->mem, sandbox, sp_str_view(ent->path)); sp_str_t parent = sp_fs_parent_path(path); - if (!sp_str_empty(parent) && !sp_str_equal(parent, path) && !sp_fs_exists_a(parent)) { - sp_fs_create_dir_a(parent); + if (!sp_str_empty(parent) && !sp_str_equal(parent, path) && !sp_fs_exists(parent)) { + sp_fs_create_dir(parent); } switch (ent->kind) { @@ -151,20 +151,20 @@ static void fs_apply_setup(s32* utest_result, sp_test_file_manager_t* fm, sp_str break; } case FS_SETUP_DIR: { - sp_fs_create_dir_a(path); + sp_fs_create_dir(path); break; } case FS_SETUP_SYMLINK: { - sp_str_t target = sp_fs_join_path_a(fm->mem, sandbox, sp_str_view(ent->target)); - if (sp_fs_create_sym_link_a(target, path) != SP_OK) { + sp_str_t target = sp_fs_join_path(fm->mem, sandbox, sp_str_view(ent->target)); + if (sp_fs_create_sym_link(target, path) != SP_OK) { SP_TEST_REPORT("failed to create symlink {} -> {}", sp_fmt_str(path), sp_fmt_str(target)); SP_FAIL(); } break; } case FS_SETUP_HARD_LINK: { - sp_str_t target = sp_fs_join_path_a(fm->mem, sandbox, sp_str_view(ent->target)); - if (sp_fs_create_hard_link_a(target, path) != SP_OK) { + sp_str_t target = sp_fs_join_path(fm->mem, sandbox, sp_str_view(ent->target)); + if (sp_fs_create_hard_link(target, path) != SP_OK) { SP_TEST_REPORT("failed to create hard link {} -> {}", sp_fmt_str(path), sp_fmt_str(target)); SP_FAIL(); } diff --git a/test/fs/get_cwd.c b/test/fs/get_cwd.c index d2c6271..808b25d 100644 --- a/test/fs/get_cwd.c +++ b/test/fs/get_cwd.c @@ -3,16 +3,16 @@ UTEST(fs_get_cwd, basic_properties) { SKIP_ON_WASM() sp_mem_t a = sp_mem_os_new(); - sp_str_t cwd = sp_fs_get_cwd_a(a); + sp_str_t cwd = sp_fs_get_cwd(a); ASSERT_GT(cwd.len, 0); - ASSERT_TRUE(sp_fs_is_dir_a(cwd)); + ASSERT_TRUE(sp_fs_is_dir(cwd)); ASSERT_NE(cwd.data[cwd.len - 1], '/'); } UTEST(fs_get_cwd, is_normalized) { SKIP_ON_WASM() sp_mem_t a = sp_mem_os_new(); - sp_str_t cwd = sp_fs_get_cwd_a(a); + sp_str_t cwd = sp_fs_get_cwd(a); // no backslashes sp_for(i, cwd.len) { ASSERT_NE(cwd.data[i], '\\'); @@ -22,7 +22,7 @@ UTEST(fs_get_cwd, is_normalized) { UTEST(fs_get_cwd, is_absolute) { SKIP_ON_WASM() sp_mem_t a = sp_mem_os_new(); - sp_str_t cwd = sp_fs_get_cwd_a(a); + sp_str_t cwd = sp_fs_get_cwd(a); bool is_absolute = (cwd.data[0] == '/') || (cwd.len >= 2 && cwd.data[1] == ':'); ASSERT_TRUE(is_absolute); } diff --git a/test/fs/get_exe_path.c b/test/fs/get_exe_path.c index 79be645..8519144 100644 --- a/test/fs/get_exe_path.c +++ b/test/fs/get_exe_path.c @@ -3,7 +3,7 @@ UTEST(fs_get_exe_path, basic_properties) { SKIP_ON_WASM() sp_mem_t a = sp_mem_os_new(); - sp_str_t exe = sp_fs_get_exe_path_a(a); + sp_str_t exe = sp_fs_get_exe_path(a); ASSERT_GT(exe.len, 0); // normalized: no backslashes @@ -22,7 +22,7 @@ UTEST(fs_get_exe_path, basic_properties) { UTEST(fs_get_exe_path, is_absolute) { SKIP_ON_WASM() sp_mem_t a = sp_mem_os_new(); - sp_str_t exe = sp_fs_get_exe_path_a(a); + sp_str_t exe = sp_fs_get_exe_path(a); // absolute: starts with / on POSIX, or X: on Windows bool is_absolute = (exe.data[0] == '/') || (exe.len >= 2 && exe.data[1] == ':'); ASSERT_TRUE(is_absolute); @@ -31,23 +31,23 @@ UTEST(fs_get_exe_path, is_absolute) { UTEST(fs_get_exe_path, exists_on_disk) { SKIP_ON_WASM() sp_mem_t a = sp_mem_os_new(); - sp_str_t exe = sp_fs_get_exe_path_a(a); - ASSERT_TRUE(sp_fs_exists_a(exe)); + sp_str_t exe = sp_fs_get_exe_path(a); + ASSERT_TRUE(sp_fs_exists(exe)); } UTEST(fs_get_exe_path, is_canonical) { SKIP_ON_WASM() // canonicalizing the exe path should be a no-op sp_mem_t a = sp_mem_os_new(); - sp_str_t exe = sp_fs_get_exe_path_a(a); - sp_str_t canonical = sp_fs_canonicalize_path_a(a, exe); + sp_str_t exe = sp_fs_get_exe_path(a); + sp_str_t canonical = sp_fs_canonicalize_path(a, exe); SP_EXPECT_STR_EQ(canonical, exe); } UTEST(fs_get_exe_path, no_dotdot) { SKIP_ON_WASM() sp_mem_t a = sp_mem_os_new(); - sp_str_t exe = sp_fs_get_exe_path_a(a); + sp_str_t exe = sp_fs_get_exe_path(a); ASSERT_FALSE(sp_str_contains(exe, sp_str_lit(".."))); } diff --git a/test/fs/join_path.c b/test/fs/join_path.c index f25d8c6..e59c2eb 100644 --- a/test/fs/join_path.c +++ b/test/fs/join_path.c @@ -21,7 +21,7 @@ UTEST(fs_join_path, cases) { }; SP_CARR_FOR(cases, i) { - sp_str_t result = sp_fs_join_path_a(a, sp_str_view(cases[i].a), sp_str_view(cases[i].b)); + sp_str_t result = sp_fs_join_path(a, sp_str_view(cases[i].a), sp_str_view(cases[i].b)); SP_EXPECT_STR_EQ_CSTR(result, cases[i].expected); } } diff --git a/test/fs/links.c b/test/fs/links.c index 3068ba2..59e1e30 100644 --- a/test/fs/links.c +++ b/test/fs/links.c @@ -12,15 +12,15 @@ typedef struct { static void run_link_test(s32* utest_result, sp_test_file_manager_t* fm, link_test_t t) { sp_str_t sandbox = sp_test_file_path(fm, sp_str_view(t.label)); - sp_fs_create_dir_a(sandbox); + sp_fs_create_dir(sandbox); fs_apply_setup(utest_result, fm, sandbox, t.setup); - sp_str_t target = sp_fs_join_path_a(fm->mem, sandbox, sp_str_view(t.target)); - sp_str_t link_path = sp_fs_join_path_a(fm->mem, sandbox, sp_str_view(t.link_path)); + sp_str_t target = sp_fs_join_path(fm->mem, sandbox, sp_str_view(t.target)); + sp_str_t link_path = sp_fs_join_path(fm->mem, sandbox, sp_str_view(t.link_path)); sp_err_t result = t.symlink - ? sp_fs_create_sym_link_a(target, link_path) - : sp_fs_create_hard_link_a(target, link_path); + ? sp_fs_create_sym_link(target, link_path) + : sp_fs_create_hard_link(target, link_path); fs_expect_bool(utest_result, link_path, "link_ok", result == SP_OK, t.expect_ok); fs_expect_paths(utest_result, fm, sandbox, t.expected); @@ -44,15 +44,15 @@ UTEST_F(fs, create_hard_link_file) { }); sp_str_t sandbox = sp_test_file_path(&ut.file_manager, sp_str_lit("create_hard_link_file")); - sp_str_t source = sp_fs_join_path_a(a, sandbox, sp_str_lit("file.txt")); - sp_str_t link = sp_fs_join_path_a(a, sandbox, sp_str_lit("file.hard")); + sp_str_t source = sp_fs_join_path(a, sandbox, sp_str_lit("file.txt")); + sp_str_t link = sp_fs_join_path(a, sandbox, sp_str_lit("file.hard")); sp_io_file_writer_t writer = sp_zero; sp_io_file_writer_from_path(&writer, source, SP_IO_WRITE_MODE_OVERWRITE); sp_io_write_str(&writer.base, sp_str_lit("updated"), SP_NULLPTR); sp_io_file_writer_close(&writer); sp_str_t link_content = sp_zero; - sp_io_read_file_a(a, link, &link_content); + sp_io_read_file(a, link, &link_content); SP_EXPECT_STR_EQ(link_content, sp_str_lit("updated")); } @@ -111,9 +111,9 @@ UTEST_F(fs, create_symlink_file) { }); sp_str_t sandbox = sp_test_file_path(&ut.file_manager, sp_str_lit("create_symlink_file")); - sp_str_t link = sp_fs_join_path_a(a, sandbox, sp_str_lit("file.link")); + sp_str_t link = sp_fs_join_path(a, sandbox, sp_str_lit("file.link")); sp_str_t symlink_content = sp_zero; - sp_io_read_file_a(a, link, &symlink_content); + sp_io_read_file(a, link, &symlink_content); SP_EXPECT_STR_EQ(symlink_content, sp_str_lit("hello")); } @@ -162,16 +162,16 @@ UTEST_F(fs, canonicalize_through_symlink) { SKIP_IF_NO_SYMLINKS(); sp_mem_t a = ut.file_manager.mem; sp_str_t sandbox = sp_test_file_path(&ut.file_manager, sp_str_lit("canon_through_symlink")); - sp_fs_create_dir_a(sandbox); + sp_fs_create_dir(sandbox); - sp_str_t real = sp_fs_join_path_a(a, sandbox, sp_str_lit("real.txt")); - sp_str_t link = sp_fs_join_path_a(a, sandbox, sp_str_lit("link.txt")); + sp_str_t real = sp_fs_join_path(a, sandbox, sp_str_lit("real.txt")); + sp_str_t link = sp_fs_join_path(a, sandbox, sp_str_lit("link.txt")); sp_test_file_create_ex((sp_test_file_config_t) { .path = real, .content = sp_str_lit("data") }); - ASSERT_EQ(sp_fs_create_sym_link_a(real, link), SP_OK); + ASSERT_EQ(sp_fs_create_sym_link(real, link), SP_OK); - sp_str_t canon_link = sp_fs_canonicalize_path_a(a, link); - sp_str_t canon_real = sp_fs_canonicalize_path_a(a, real); + sp_str_t canon_link = sp_fs_canonicalize_path(a, link); + sp_str_t canon_real = sp_fs_canonicalize_path(a, real); SP_EXPECT_STR_EQ(canon_link, canon_real); } diff --git a/test/fs/mod_time.c b/test/fs/mod_time.c index 30c9083..e9472ee 100644 --- a/test/fs/mod_time.c +++ b/test/fs/mod_time.c @@ -4,11 +4,11 @@ UTEST_F(fs, mod_time_nonzero) { SKIP_ON_WASM() sp_str_t file = sp_test_file_path(&ut.file_manager, sp_str_lit("mod_time.file")); sp_str_t dir = sp_test_file_path(&ut.file_manager, sp_str_lit("mod_time.dir")); - sp_fs_create_file_a(file); - sp_fs_create_dir_a(dir); + sp_fs_create_file(file); + sp_fs_create_dir(dir); - EXPECT_TRUE(sp_fs_get_mod_time_a(file).s > 0); - EXPECT_TRUE(sp_fs_get_mod_time_a(dir).s > 0); + EXPECT_TRUE(sp_fs_get_mod_time(file).s > 0); + EXPECT_TRUE(sp_fs_get_mod_time(dir).s > 0); } UTEST_F(fs, mod_time_updates_after_write) { @@ -19,7 +19,7 @@ UTEST_F(fs, mod_time_updates_after_write) { .content = sp_str_lit("a"), }); - sp_tm_epoch_t before = sp_fs_get_mod_time_a(file); + sp_tm_epoch_t before = sp_fs_get_mod_time(file); sp_os_sleep_ms(100); sp_io_file_writer_t writer = sp_zero; @@ -27,7 +27,7 @@ UTEST_F(fs, mod_time_updates_after_write) { sp_io_write_str(&writer.base, sp_str_lit("b"), SP_NULLPTR); sp_io_file_writer_close(&writer); - sp_tm_epoch_t after = sp_fs_get_mod_time_a(file); + sp_tm_epoch_t after = sp_fs_get_mod_time(file); ASSERT_TRUE(after.s > before.s || (after.s == before.s && after.ns > before.ns)); } diff --git a/test/fs/normalize_path.c b/test/fs/normalize_path.c index 3c511aa..6ca7cf0 100644 --- a/test/fs/normalize_path.c +++ b/test/fs/normalize_path.c @@ -26,7 +26,7 @@ UTEST(fs_normalize_path, cases) { }; SP_CARR_FOR(cases, i) { - sp_str_t result = sp_fs_normalize_path_a(a, sp_str_view(cases[i].input)); + sp_str_t result = sp_fs_normalize_path(a, sp_str_view(cases[i].input)); SP_EXPECT_STR_EQ_CSTR(result, cases[i].expected); } } @@ -35,7 +35,7 @@ UTEST(fs_normalize_path, preserves_dotdot) { SKIP_ON_WASM() sp_mem_t a = sp_mem_os_new(); SP_EXPECT_STR_EQ_CSTR( - sp_fs_normalize_path_a(a, sp_str_lit("a\\b\\..\\c")), + sp_fs_normalize_path(a, sp_str_lit("a\\b\\..\\c")), "a/b/../c" ); } @@ -44,7 +44,7 @@ UTEST(fs_normalize_path, preserves_dot) { SKIP_ON_WASM() sp_mem_t a = sp_mem_os_new(); SP_EXPECT_STR_EQ_CSTR( - sp_fs_normalize_path_a(a, sp_str_lit("a\\.\\b")), + sp_fs_normalize_path(a, sp_str_lit("a\\.\\b")), "a/./b" ); } @@ -53,7 +53,7 @@ UTEST(fs_normalize_path, nonexistent_path) { SKIP_ON_WASM() sp_mem_t a = sp_mem_os_new(); SP_EXPECT_STR_EQ_CSTR( - sp_fs_normalize_path_a(a, sp_str_lit("C:\\no\\such\\path\\file.txt")), + sp_fs_normalize_path(a, sp_str_lit("C:\\no\\such\\path\\file.txt")), "C:/no/such/path/file.txt" ); } diff --git a/test/fs/predicates.c b/test/fs/predicates.c index 7f9bd6d..712938c 100644 --- a/test/fs/predicates.c +++ b/test/fs/predicates.c @@ -25,21 +25,21 @@ static u32 fs_count_predicates(fs_predicate_expected_t* expected) { static void run_fs_predicate_test(s32* utest_result, sp_test_file_manager_t* fm, fs_predicate_test_t t) { sp_str_t sandbox = sp_test_file_path(fm, sp_str_view(t.label)); - sp_fs_create_dir_a(sandbox); + sp_fs_create_dir(sandbox); fs_apply_setup(utest_result, fm, sandbox, t.setup); u32 expected_count = fs_count_predicates(t.expected); sp_for(i, expected_count) { fs_predicate_expected_t* exp = &t.expected[i]; - sp_str_t path = sp_fs_join_path_a(fm->mem, sandbox, sp_str_view(exp->path)); + sp_str_t path = sp_fs_join_path(fm->mem, sandbox, sp_str_view(exp->path)); - fs_expect_bool(utest_result, path, "exists", sp_fs_exists_a(path), exp->exists); - fs_expect_bool(utest_result, path, "is_regular_file", sp_fs_is_file_a(path), exp->is_regular_file); - fs_expect_bool(utest_result, path, "is_dir", sp_fs_is_dir_a(path), exp->is_dir); - fs_expect_bool(utest_result, path, "is_symlink", sp_fs_is_symlink_a(path), exp->is_symlink); - fs_expect_bool(utest_result, path, "is_target_regular_file", sp_fs_is_target_file_a(path), exp->is_target_regular_file); - fs_expect_bool(utest_result, path, "is_target_dir", sp_fs_is_target_dir_a(path), exp->is_target_dir); - fs_expect_attr(utest_result, path, sp_fs_get_kind_a(path), exp->attr); + fs_expect_bool(utest_result, path, "exists", sp_fs_exists(path), exp->exists); + fs_expect_bool(utest_result, path, "is_regular_file", sp_fs_is_file(path), exp->is_regular_file); + fs_expect_bool(utest_result, path, "is_dir", sp_fs_is_dir(path), exp->is_dir); + fs_expect_bool(utest_result, path, "is_symlink", sp_fs_is_symlink(path), exp->is_symlink); + fs_expect_bool(utest_result, path, "is_target_regular_file", sp_fs_is_target_file(path), exp->is_target_regular_file); + fs_expect_bool(utest_result, path, "is_target_dir", sp_fs_is_target_dir(path), exp->is_target_dir); + fs_expect_attr(utest_result, path, sp_fs_get_kind(path), exp->attr); } } diff --git a/test/fs/remove.c b/test/fs/remove.c index 0e201aa..4e0d448 100644 --- a/test/fs/remove.c +++ b/test/fs/remove.c @@ -10,14 +10,14 @@ typedef struct { static void run_remove_test(s32* utest_result, sp_test_file_manager_t* fm, remove_test_t t) { sp_str_t sandbox = sp_test_file_path(fm, sp_str_view(t.label)); - sp_fs_create_dir_a(sandbox); + sp_fs_create_dir(sandbox); fs_apply_setup(utest_result, fm, sandbox, t.setup); - sp_str_t path = sp_fs_join_path_a(fm->mem, sandbox, sp_str_view(t.remove_path)); + sp_str_t path = sp_fs_join_path(fm->mem, sandbox, sp_str_view(t.remove_path)); if (t.remove_dir) { - sp_fs_remove_dir_a(path); + sp_fs_remove_dir(path); } else { - sp_fs_remove_file_a(path); + sp_fs_remove_file(path); } fs_expect_paths(utest_result, fm, sandbox, t.expected); diff --git a/test/fs/replace_ext.c b/test/fs/replace_ext.c index c147f76..86b9a8c 100644 --- a/test/fs/replace_ext.c +++ b/test/fs/replace_ext.c @@ -19,7 +19,7 @@ UTEST(fs_replace_ext, cases) { }; SP_CARR_FOR(cases, i) { - sp_str_t result = sp_fs_replace_ext_a(a, sp_str_view(cases[i].path), sp_str_view(cases[i].ext)); + sp_str_t result = sp_fs_replace_ext(a, sp_str_view(cases[i].path), sp_str_view(cases[i].ext)); SP_EXPECT_STR_EQ_CSTR(result, cases[i].expected); } } diff --git a/test/fs/system_paths.c b/test/fs/system_paths.c index 0cfa9f7..ba1e978 100644 --- a/test/fs/system_paths.c +++ b/test/fs/system_paths.c @@ -17,36 +17,36 @@ static void assert_normalized(s32* utest_result, sp_str_t path, const c8* label) UTEST(fs_system_paths, nonempty) { SKIP_ON_WASM() sp_mem_t a = sp_mem_os_new(); - ASSERT_GT(sp_fs_get_storage_path_a(a).len, 0); - ASSERT_GT(sp_fs_get_config_path_a(a).len, 0); + ASSERT_GT(sp_fs_get_storage_path(a).len, 0); + ASSERT_GT(sp_fs_get_config_path(a).len, 0); } UTEST(fs_system_paths, storage_path_normalized) { SKIP_ON_WASM() sp_mem_t a = sp_mem_os_new(); - sp_str_t path = sp_fs_get_storage_path_a(a); + sp_str_t path = sp_fs_get_storage_path(a); assert_normalized(&ur, path, "storage_path"); } UTEST(fs_system_paths, config_path_normalized) { SKIP_ON_WASM() sp_mem_t a = sp_mem_os_new(); - sp_str_t path = sp_fs_get_config_path_a(a); + sp_str_t path = sp_fs_get_config_path(a); assert_normalized(&ur, path, "config_path"); } UTEST(fs_system_paths, storage_path_exists) { SKIP_ON_WASM() sp_mem_t a = sp_mem_os_new(); - sp_str_t path = sp_fs_get_storage_path_a(a); - ASSERT_TRUE(sp_fs_is_dir_a(path)); + sp_str_t path = sp_fs_get_storage_path(a); + ASSERT_TRUE(sp_fs_is_dir(path)); } UTEST(fs_system_paths, config_path_exists) { SKIP_ON_WASM() sp_mem_t a = sp_mem_os_new(); - sp_str_t path = sp_fs_get_config_path_a(a); - ASSERT_TRUE(sp_fs_is_dir_a(path)); + sp_str_t path = sp_fs_get_config_path(a); + ASSERT_TRUE(sp_fs_is_dir(path)); } diff --git a/test/fs/windows/wtf8.c b/test/fs/windows/wtf8.c index 6cacbc7..f1e4742 100644 --- a/test/fs/windows/wtf8.c +++ b/test/fs/windows/wtf8.c @@ -28,7 +28,7 @@ UTEST(wtf8, to_wtf16_cases) { SP_CARR_FOR(cases, i) { wtf8_case_t c = cases[i]; - sp_wide_str_t str = sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), c.utf8); + sp_wide_str_t str = sp_wtf8_to_wtf16(sp_mem_get_scratch(), c.utf8); EXPECT_EQ(str.len, c.wtf16_len); sp_for(j, c.wtf16_len) { if (str.data[j] != c.wtf16[j]) { @@ -43,7 +43,7 @@ UTEST(wtf8, to_wtf16_cases) { UTEST(wtf8, to_wtf16_alloc_null_terminates) { SKIP_ON_WASM() - sp_wide_str_t w = sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), sp_str_lit("hi")); + sp_wide_str_t w = sp_wtf8_to_wtf16(sp_mem_get_scratch(), sp_str_lit("hi")); EXPECT_EQ(w.len, (u32)2); EXPECT_EQ(w.data[0], (u16)'h'); EXPECT_EQ(w.data[1], (u16)'i'); @@ -52,72 +52,72 @@ UTEST(wtf8, to_wtf16_alloc_null_terminates) { UTEST(wtf8, to_wtf16_alloc_empty_returns_null) { SKIP_ON_WASM() - sp_wide_str_t w = sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), sp_str_lit("")); + sp_wide_str_t w = sp_wtf8_to_wtf16(sp_mem_get_scratch(), sp_str_lit("")); EXPECT_EQ(w.data, (u16*)SP_NULLPTR); EXPECT_EQ(w.len, 0u); } UTEST(wtf8, to_wtf16_alloc_rejects_invalid) { SKIP_ON_WASM() - EXPECT_EQ(sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), sp_str_lit("\xC3")).data, (u16*)SP_NULLPTR); - EXPECT_EQ(sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), sp_str_lit("\xC3\x28")).data, (u16*)SP_NULLPTR); - EXPECT_EQ(sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), sp_str_lit("\xC0\x80")).data, (u16*)SP_NULLPTR); - EXPECT_EQ(sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), sp_str_lit("\xE0\x80\x80")).data, (u16*)SP_NULLPTR); + EXPECT_EQ(sp_wtf8_to_wtf16(sp_mem_get_scratch(), sp_str_lit("\xC3")).data, (u16*)SP_NULLPTR); + EXPECT_EQ(sp_wtf8_to_wtf16(sp_mem_get_scratch(), sp_str_lit("\xC3\x28")).data, (u16*)SP_NULLPTR); + EXPECT_EQ(sp_wtf8_to_wtf16(sp_mem_get_scratch(), sp_str_lit("\xC0\x80")).data, (u16*)SP_NULLPTR); + EXPECT_EQ(sp_wtf8_to_wtf16(sp_mem_get_scratch(), sp_str_lit("\xE0\x80\x80")).data, (u16*)SP_NULLPTR); } UTEST(wtf8, reject_invalid) { SKIP_ON_WASM() - EXPECT_EQ(sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), sp_str_lit("\xC0")).len, 0u); - EXPECT_EQ(sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), sp_str_lit("\xC3")).len, 0u); - EXPECT_EQ(sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), sp_str_lit("\xC3\x28")).len, 0u); - EXPECT_EQ(sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), sp_str_lit("\xE2\x82")).len, 0u); - EXPECT_EQ(sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), sp_str_lit("\xC0\x80")).len, 0u); - EXPECT_EQ(sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), sp_str_lit("\xE0\x80\x80")).len, 0u); - EXPECT_EQ(sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), sp_str_lit("\xF0\x80\x80\x80")).len, 0u); + EXPECT_EQ(sp_wtf8_to_wtf16(sp_mem_get_scratch(), sp_str_lit("\xC0")).len, 0u); + EXPECT_EQ(sp_wtf8_to_wtf16(sp_mem_get_scratch(), sp_str_lit("\xC3")).len, 0u); + EXPECT_EQ(sp_wtf8_to_wtf16(sp_mem_get_scratch(), sp_str_lit("\xC3\x28")).len, 0u); + EXPECT_EQ(sp_wtf8_to_wtf16(sp_mem_get_scratch(), sp_str_lit("\xE2\x82")).len, 0u); + EXPECT_EQ(sp_wtf8_to_wtf16(sp_mem_get_scratch(), sp_str_lit("\xC0\x80")).len, 0u); + EXPECT_EQ(sp_wtf8_to_wtf16(sp_mem_get_scratch(), sp_str_lit("\xE0\x80\x80")).len, 0u); + EXPECT_EQ(sp_wtf8_to_wtf16(sp_mem_get_scratch(), sp_str_lit("\xF0\x80\x80\x80")).len, 0u); } UTEST(wtf8, roundtrip_ascii) { SKIP_ON_WASM() sp_str_t input = sp_str_lit("hello/world.txt"); - sp_str_t back = sp_wtf16_to_wtf8_a(sp_mem_get_scratch(), sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), input)); + sp_str_t back = sp_wtf16_to_wtf8(sp_mem_get_scratch(), sp_wtf8_to_wtf16(sp_mem_get_scratch(), input)); SP_EXPECT_STR_EQ(back, input); } UTEST(wtf8, roundtrip_non_ascii) { SKIP_ON_WASM() sp_str_t input = sp_str_lit("caf\xC3\xA9/\xE2\x82\xAC"); - sp_str_t back = sp_wtf16_to_wtf8_a(sp_mem_get_scratch(), sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), input)); + sp_str_t back = sp_wtf16_to_wtf8(sp_mem_get_scratch(), sp_wtf8_to_wtf16(sp_mem_get_scratch(), input)); SP_EXPECT_STR_EQ(back, input); } UTEST(wtf8, roundtrip_4byte_via_surrogate_pair) { SKIP_ON_WASM() sp_str_t input = sp_str_lit("\xF0\x9F\x98\x80"); - sp_wide_str_t w = sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), input); + sp_wide_str_t w = sp_wtf8_to_wtf16(sp_mem_get_scratch(), input); EXPECT_EQ(w.len, (u32)2); EXPECT_EQ(w.data[0], (u16)0xD83D); EXPECT_EQ(w.data[1], (u16)0xDE00); - sp_str_t back = sp_wtf16_to_wtf8_a(sp_mem_get_scratch(), w); + sp_str_t back = sp_wtf16_to_wtf8(sp_mem_get_scratch(), w); SP_EXPECT_STR_EQ(back, input); } UTEST(wtf8, roundtrip_unpaired_high_surrogate) { SKIP_ON_WASM() sp_str_t input = sp_str_lit("\xED\xA0\x80"); - sp_wide_str_t w = sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), input); + sp_wide_str_t w = sp_wtf8_to_wtf16(sp_mem_get_scratch(), input); EXPECT_EQ(w.len, (u32)1); EXPECT_EQ(w.data[0], (u16)0xD800); - sp_str_t back = sp_wtf16_to_wtf8_a(sp_mem_get_scratch(), w); + sp_str_t back = sp_wtf16_to_wtf8(sp_mem_get_scratch(), w); SP_EXPECT_STR_EQ(back, input); } UTEST(wtf8, roundtrip_unpaired_low_surrogate) { SKIP_ON_WASM() sp_str_t input = sp_str_lit("\xED\xBF\xBF"); - sp_wide_str_t w = sp_wtf8_to_wtf16_a(sp_mem_get_scratch(), input); + sp_wide_str_t w = sp_wtf8_to_wtf16(sp_mem_get_scratch(), input); EXPECT_EQ(w.len, (u32)1); EXPECT_EQ(w.data[0], (u16)0xDFFF); - sp_str_t back = sp_wtf16_to_wtf8_a(sp_mem_get_scratch(), w); + sp_str_t back = sp_wtf16_to_wtf8(sp_mem_get_scratch(), w); SP_EXPECT_STR_EQ(back, input); } diff --git a/test/ht.c b/test/ht.c index ec4bb72..262bbdc 100644 --- a/test/ht.c +++ b/test/ht.c @@ -45,8 +45,8 @@ typedef struct { } compound_key_t; UTEST_F(sp_ht, basic_operations) { - sp_ht_a(int, float) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(int, float) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); EXPECT_EQ(sp_ht_size(ht), 0u); EXPECT_TRUE(sp_ht_empty(ht)); @@ -84,8 +84,8 @@ UTEST_F(sp_ht, basic_operations) { } UTEST_F(sp_ht, pointer_retrieval) { - sp_ht_a(u32, double) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(u32, double) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); sp_ht_insert(ht, 100, 123.456); sp_ht_insert(ht, 200, 789.012); @@ -104,8 +104,8 @@ UTEST_F(sp_ht, pointer_retrieval) { } UTEST_F(sp_ht, struct_values) { - sp_ht_a(int, vec3_t) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(int, vec3_t) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); vec3_t v1 = {1.0f, 2.0f, 3.0f}; vec3_t v2 = {4.0f, 5.0f, 6.0f}; @@ -124,8 +124,8 @@ UTEST_F(sp_ht, struct_values) { } UTEST_F(sp_ht, struct_keys) { - sp_ht_a(compound_key_t, const char*) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(compound_key_t, const char*) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); compound_key_t k1 = {100, 1}; compound_key_t k2 = {200, 2}; @@ -149,8 +149,8 @@ UTEST_F(sp_ht, struct_keys) { } UTEST_F(sp_ht, string_keys) { - sp_ht_a(u64, int) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(u64, int) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); const char* s1 = "apple"; const char* s2 = "banana"; @@ -175,8 +175,8 @@ UTEST_F(sp_ht, string_keys) { } UTEST_F(sp_ht, collision_handling) { - sp_ht_a(int, int) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(int, int) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); for (s32 i = 0; i < 100; i++) { sp_ht_insert(ht, i, i * 100); @@ -206,8 +206,8 @@ UTEST_F(sp_ht, collision_handling) { } UTEST_F(sp_ht, iteration) { - sp_ht_a(s32, s32) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(s32, s32) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); for (s32 i = 0; i < 10; i++) { sp_ht_insert(ht, i * 10, i); @@ -230,8 +230,8 @@ UTEST_F(sp_ht, iteration) { } UTEST_F(sp_ht, edge_cases) { - sp_ht_a(int, int) ht1 = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht1); + sp_ht(int, int) ht1 = SP_NULLPTR; + sp_ht_init(ut.mem, ht1); EXPECT_EQ(sp_ht_size(ht1), 0u); EXPECT_TRUE(sp_ht_empty(ht1)); EXPECT_FALSE(sp_ht_getp(ht1, 42)); @@ -239,15 +239,15 @@ UTEST_F(sp_ht, edge_cases) { sp_ht_clear(ht1); sp_ht_free(ht1); - sp_ht_a(int, int) ht2 = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht2); + sp_ht(int, int) ht2 = SP_NULLPTR; + sp_ht_init(ut.mem, ht2); sp_ht_insert(ht2, 1, 100); sp_ht_erase(ht2, 1); EXPECT_EQ(sp_ht_size(ht2), 0u); sp_ht_free(ht2); - sp_ht_a(int, int) ht3 = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht3); + sp_ht(int, int) ht3 = SP_NULLPTR; + sp_ht_init(ut.mem, ht3); sp_ht_insert(ht3, 1, 100); sp_ht_erase(ht3, 999); EXPECT_EQ(sp_ht_size(ht3), 1u); @@ -255,8 +255,8 @@ UTEST_F(sp_ht, edge_cases) { } UTEST_F(sp_ht, pathological_all_same_hash) { - sp_ht_a(u32, u32) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(u32, u32) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); u64 cap = sp_ht_capacity(ht); if (cap < 2) { @@ -278,8 +278,8 @@ UTEST_F(sp_ht, pathological_all_same_hash) { } UTEST_F(sp_ht, duplicate_key_insert_size_bug) { - sp_ht_a(u32, u32) table = SP_NULLPTR; - sp_ht_init_a(ut.mem, table); + sp_ht(u32, u32) table = SP_NULLPTR; + sp_ht_init(ut.mem, table); sp_ht_insert(table, 42, 100); EXPECT_EQ(sp_ht_size(table), 1u); @@ -298,8 +298,8 @@ UTEST_F(sp_ht, duplicate_key_insert_size_bug) { } UTEST_F(sp_ht, iterator_yields_inactive_entry_at_slot_zero) { - sp_ht_a(u64, u64) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(u64, u64) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); sp_ht_insert(ht, 0, 999); sp_ht_erase(ht, 0); @@ -317,8 +317,8 @@ UTEST_F(sp_ht, iterator_yields_inactive_entry_at_slot_zero) { } UTEST_F(sp_ht, collision) { - sp_ht_a(s32, s32) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(s32, s32) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); for (u32 i = 0; i < 8; i++) { sp_ht_insert(ht, i, i); @@ -389,8 +389,8 @@ UTEST_F(sp_ht, collision) { } UTEST_F(sp_ht, iterator_returns_zero_entries_for_populated_table) { - sp_ht_a(u64, u64) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(u64, u64) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); sp_ht_insert(ht, 1, 100); sp_ht_insert(ht, 2, 200); @@ -406,7 +406,7 @@ UTEST_F(sp_ht, iterator_returns_zero_entries_for_populated_table) { } UTEST_F(sp_ht, null_safety) { - sp_ht_a(s32, s32) null_ht = NULL; + sp_ht(s32, s32) null_ht = NULL; EXPECT_EQ(sp_ht_size(null_ht), 0u); EXPECT_EQ(sp_ht_capacity(null_ht), 0u); @@ -431,7 +431,7 @@ UTEST_F(sp_ht, null_safety) { sp_ht_erase(null_ht, 42); sp_ht_free(null_ht); - sp_ht_init_a(ut.mem, null_ht); + sp_ht_init(ut.mem, null_ht); sp_ht_insert(null_ht, 42, 100); EXPECT_NE(null_ht, SP_NULLPTR); EXPECT_EQ(sp_ht_size(null_ht), 1u); @@ -443,13 +443,13 @@ UTEST_F(sp_ht, null_safety) { UTEST_F(sp_ht, string_key_custom_hash) { sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_ht_a(sp_str_t, int) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(sp_str_t, int) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); sp_ht_set_fns(ht, sp_ht_on_hash_str_key, sp_ht_on_compare_str_key); - sp_str_t ka = sp_str_copy_a(s.mem, sp_str_lit("hello")); - sp_str_t kb = sp_str_copy_a(s.mem, sp_str_lit("world")); - sp_str_t kc = sp_str_copy_a(s.mem, sp_str_lit("test")); + sp_str_t ka = sp_str_copy(s.mem, sp_str_lit("hello")); + sp_str_t kb = sp_str_copy(s.mem, sp_str_lit("world")); + sp_str_t kc = sp_str_copy(s.mem, sp_str_lit("test")); sp_ht_insert(ht, ka, 100); sp_ht_insert(ht, kb, 200); @@ -463,8 +463,8 @@ UTEST_F(sp_ht, string_key_custom_hash) { EXPECT_EQ(*sp_ht_getp(ht, kb), 200); EXPECT_EQ(*sp_ht_getp(ht, kc), 300); - sp_str_t ka_copy = sp_str_copy_a(s.mem, sp_str_lit("hello")); - sp_str_t kb_copy = sp_str_copy_a(s.mem, sp_str_lit("world")); + sp_str_t ka_copy = sp_str_copy(s.mem, sp_str_lit("hello")); + sp_str_t kb_copy = sp_str_copy(s.mem, sp_str_lit("world")); EXPECT_TRUE(sp_ht_getp(ht, ka_copy)); EXPECT_TRUE(sp_ht_getp(ht, kb_copy)); @@ -472,7 +472,7 @@ UTEST_F(sp_ht, string_key_custom_hash) { EXPECT_EQ(*sp_ht_getp(ht, ka_copy), 100); EXPECT_EQ(*sp_ht_getp(ht, kb_copy), 200); - sp_str_t kd = sp_str_copy_a(s.mem, sp_str_lit("missing")); + sp_str_t kd = sp_str_copy(s.mem, sp_str_lit("missing")); EXPECT_FALSE(sp_ht_getp(ht, kd)); EXPECT_EQ(sp_ht_getp(ht, kd), SP_NULLPTR); @@ -483,12 +483,12 @@ UTEST_F(sp_ht, string_key_custom_hash) { UTEST_F(sp_ht, str_key) { sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_ht_a(sp_str_t, s32) ht = SP_NULLPTR; - sp_str_ht_init_a(ut.mem, ht); + sp_ht(sp_str_t, s32) ht = SP_NULLPTR; + sp_str_ht_init(ut.mem, ht); - sp_str_t ka = sp_str_copy_a(s.mem, sp_str_lit("hello")); - sp_str_t kb = sp_str_copy_a(s.mem, sp_str_lit("world")); - sp_str_t kc = sp_str_copy_a(s.mem, sp_str_lit("test")); + sp_str_t ka = sp_str_copy(s.mem, sp_str_lit("hello")); + sp_str_t kb = sp_str_copy(s.mem, sp_str_lit("world")); + sp_str_t kc = sp_str_copy(s.mem, sp_str_lit("test")); sp_str_ht_insert(ht, ka, 100); sp_str_ht_insert(ht, kb, 200); @@ -502,8 +502,8 @@ UTEST_F(sp_ht, str_key) { EXPECT_EQ(*sp_str_ht_get(ht, kb), 200); EXPECT_EQ(*sp_str_ht_get(ht, kc), 300); - sp_str_t ka_copy = sp_str_copy_a(s.mem, sp_str_lit("hello")); - sp_str_t kb_copy = sp_str_copy_a(s.mem, sp_str_lit("world")); + sp_str_t ka_copy = sp_str_copy(s.mem, sp_str_lit("hello")); + sp_str_t kb_copy = sp_str_copy(s.mem, sp_str_lit("world")); EXPECT_TRUE(sp_str_ht_get(ht, ka_copy)); EXPECT_TRUE(sp_str_ht_get(ht, kb_copy)); @@ -511,7 +511,7 @@ UTEST_F(sp_ht, str_key) { EXPECT_EQ(*sp_str_ht_get(ht, ka_copy), 100); EXPECT_EQ(*sp_str_ht_get(ht, kb_copy), 200); - sp_str_t kd = sp_str_copy_a(s.mem, sp_str_lit("missing")); + sp_str_t kd = sp_str_copy(s.mem, sp_str_lit("missing")); EXPECT_FALSE(sp_str_ht_get(ht, kd)); EXPECT_EQ(sp_str_ht_get(ht, kd), SP_NULLPTR); @@ -520,8 +520,8 @@ UTEST_F(sp_ht, str_key) { } UTEST_F(sp_ht, cstr_key) { - sp_ht_a(const c8*, s32) ht = SP_NULLPTR; - sp_cstr_ht_init_a(ut.mem, ht); + sp_ht(const c8*, s32) ht = SP_NULLPTR; + sp_cstr_ht_init(ut.mem, ht); sp_cstr_ht_insert(ht, "hello", 100); sp_cstr_ht_insert(ht, "world", 200); @@ -586,7 +586,7 @@ UTEST_F(siphash, collision_resistance) { u64 seed = 0x31415926; const s32 count = 1000; - u64* hashes = sp_alloc_n_a(ut.mem, u64, count); + u64* hashes = sp_alloc_n(ut.mem, u64, count); for (s32 i = 0; i < count; i++) { hashes[i] = sp_hash_bytes(&i, sizeof(i), seed); @@ -603,13 +603,13 @@ UTEST_F(siphash, collision_resistance) { EXPECT_EQ(collisions, 0); - sp_free_a(ut.mem, hashes); + sp_free(ut.mem, hashes); } UTEST_F(sp_ht, hash_table_with_dyn_array_values) { typedef int* int_array; - sp_ht_a(int, int_array) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(int, int_array) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); for (s32 i = 0; i < 5; i++) { sp_da(int) arr = sp_da_new(ut.mem, int); @@ -641,8 +641,8 @@ UTEST_F(sp_ht, hash_table_with_dyn_array_values) { } UTEST_F(sp_ht, multiple_arrays_in_hash_table) { - sp_ht_a(int, void*) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(int, void*) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); for (s32 key = 0; key < 5; key++) { sp_da(int) arr = sp_da_new(ut.mem, int); @@ -674,20 +674,20 @@ UTEST_F(sp_ht, multiple_arrays_in_hash_table) { } UTEST_F(sp_ht, front_null_table) { - sp_ht_a(int, int) ht = NULL; + sp_ht(int, int) ht = NULL; EXPECT_EQ(sp_ht_front(ht), SP_NULLPTR); } UTEST_F(sp_ht, front_empty_table) { - sp_ht_a(int, int) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(int, int) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); EXPECT_EQ(sp_ht_front(ht), SP_NULLPTR); sp_ht_free(ht); } UTEST_F(sp_ht, front_single_item) { - sp_ht_a(int, int) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(int, int) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); sp_ht_insert(ht, 42, 100); int* front = sp_ht_front(ht); @@ -698,8 +698,8 @@ UTEST_F(sp_ht, front_single_item) { } UTEST_F(sp_ht, front_multiple_items) { - sp_ht_a(int, int) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(int, int) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); sp_ht_insert(ht, 1, 10); sp_ht_insert(ht, 2, 20); sp_ht_insert(ht, 3, 30); @@ -720,8 +720,8 @@ UTEST_F(sp_ht, front_multiple_items) { } UTEST_F(sp_ht, front_after_erase) { - sp_ht_a(int, int) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(int, int) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); sp_ht_insert(ht, 1, 10); sp_ht_insert(ht, 2, 20); @@ -746,10 +746,10 @@ UTEST_F(sp_ht, front_after_erase) { } UTEST_F(sp_ht, for_kv_iteration) { - sp_ht_a(s32, s32) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); - sp_ht_a(s32, bool) visited = SP_NULLPTR; - sp_ht_init_a(ut.mem, visited); + sp_ht(s32, s32) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); + sp_ht(s32, bool) visited = SP_NULLPTR; + sp_ht_init(ut.mem, visited); sp_for(it, 10) { sp_ht_insert(ht, it * 10, it); @@ -779,12 +779,12 @@ UTEST_F(sp_ht, for_kv_iteration) { } UTEST_F(sp_ht, for_kv_string_keys) { - sp_ht_a(sp_str_t, s32) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(sp_str_t, s32) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); sp_ht_set_fns(ht, sp_ht_on_hash_str_key, sp_ht_on_compare_str_key); - sp_ht_a(sp_str_t, bool) visited = SP_NULLPTR; - sp_ht_init_a(ut.mem, visited); + sp_ht(sp_str_t, bool) visited = SP_NULLPTR; + sp_ht_init(ut.mem, visited); sp_ht_set_fns(visited, sp_ht_on_hash_str_key, sp_ht_on_compare_str_key); sp_ht_insert(ht, sp_str_lit("one"), 1); @@ -812,8 +812,8 @@ UTEST_F(sp_ht, for_kv_string_keys) { } UTEST_F(sp_ht, for_kv_empty_table) { - sp_ht_a(s32, s32) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(s32, s32) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); s32 count = 0; sp_ht_for_kv(ht, it) { @@ -827,7 +827,7 @@ UTEST_F(sp_ht, for_kv_empty_table) { } UTEST_F(sp_ht, for_kv_null_table) { - sp_ht_a(s32, s32) ht = SP_NULLPTR; + sp_ht(s32, s32) ht = SP_NULLPTR; s32 count = 0; sp_ht_for_kv(ht, it) { @@ -854,8 +854,8 @@ static bool cmp_s32(void* ka, void* kb, u64 size) { // Hash collision: insert uses hash equality instead of key equality for probing. // Constant hash forces all keys to same bucket; different keys overwrite each other. UTEST_F(sp_ht, issue_hash_collision_overwrites_different_keys) { - sp_ht_a(s32, s32) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(s32, s32) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); sp_ht_set_fns(ht, hash_constant, cmp_s32); sp_ht_insert(ht, 100, 1); @@ -892,8 +892,8 @@ static sp_hash_t sp_test_identity_hash(void* key, u64 size) { // Rehash: resize extends array but doesn't rehash existing entries. // Insert a key whose slot changes after resize, verify it moves correctly. UTEST_F(sp_ht, issue_rehash_breaks_lookups_after_resize) { - sp_ht_a(s32, s32) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(s32, s32) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); sp_ht_set_fns(ht, sp_test_identity_hash, cmp_s32); sp_ht_insert(ht, 1, 0); @@ -934,8 +934,8 @@ static bool sp_test_counting_compare(void* ka, void* kb, u64 size) { // Linear scan: lookup probes entire capacity instead of stopping at INACTIVE. // With 1 element in cap=64 table, missing key lookup should be O(1), not O(N). UTEST_F(sp_ht, issue_linear_scan_calls_hash_too_many_times) { - sp_ht_a(s32, s32) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(s32, s32) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); sp_ht_set_fns(ht, sp_test_counting_hash, sp_test_counting_compare); for (s32 i = 0; i < 32; i++) { @@ -982,8 +982,8 @@ static s32 eval_val(void) { } UTEST_F(sp_ht, issue_insert_evaluates_args_multiple_times) { - sp_ht_a(s32, s32) ht = SP_NULLPTR; - sp_ht_init_a(ut.mem, ht); + sp_ht(s32, s32) ht = SP_NULLPTR; + sp_ht_init(ut.mem, ht); g_key_eval_count = 0; g_val_eval_count = 0; diff --git a/test/io/dyn.c b/test/io/dyn.c index a00391c..b27092c 100644 --- a/test/io/dyn.c +++ b/test/io/dyn.c @@ -12,7 +12,7 @@ typedef struct { //////////// void run_io_dyn_test(int* utest_result, sp_mem_t mem, io_dyn_test_t t) { sp_io_dyn_mem_writer_t w; - sp_io_dyn_mem_writer_init_a(mem, &w); + sp_io_dyn_mem_writer_init(mem, &w); sp_carr_for(t.steps, j) { const io_step_t* step = &t.steps[j]; @@ -98,7 +98,7 @@ UTEST_F(io, dyn_seek) { UTEST_F(io, dyn_grows) { SKIP_ON_WASM() sp_io_dyn_mem_writer_t w; - sp_io_dyn_mem_writer_init_a(ut.mem, &w); + sp_io_dyn_mem_writer_init(ut.mem, &w); u8 data [256]; sp_for(i, 256) data[i] = (u8)i; diff --git a/test/io/file.c b/test/io/file.c index 75ccf3a..9cc25be 100644 --- a/test/io/file.c +++ b/test/io/file.c @@ -211,7 +211,7 @@ void run_io_file_writer_test(int* utest_result, sp_mem_t mem, sp_str_t path, io_ if (t.expect.content) { sp_str_t loaded = sp_zero; - sp_io_read_file_a(mem, path, &loaded); + sp_io_read_file(mem, path, &loaded); u64 n = sp_cstr_len(t.expect.content); EXPECT_EQ(loaded.len, n); sp_for(it, n) { @@ -378,7 +378,7 @@ UTEST_F(io, file_to_file_copy) { sp_io_file_writer_close(&w); sp_str_t loaded = sp_zero; - sp_io_read_file_a(ut.mem, dst_path, &loaded); + sp_io_read_file(ut.mem, dst_path, &loaded); EXPECT_EQ(loaded.len, sizeof(source)); sp_for(i, sizeof(source)) EXPECT_EQ((u8)loaded.data[i], source[i]); } @@ -404,7 +404,7 @@ UTEST_F(io, file_copy_fast_path_falls_back_for_mem_source) { sp_io_file_writer_close(&w); sp_str_t loaded = sp_zero; - sp_io_read_file_a(ut.mem, ut.file_path, &loaded); + sp_io_read_file(ut.mem, ut.file_path, &loaded); EXPECT_EQ(loaded.len, n); sp_for(i, n) EXPECT_EQ(loaded.data[i], content[i]); } diff --git a/test/leak.c b/test/leak.c index 325e192..38a74ae 100644 --- a/test/leak.c +++ b/test/leak.c @@ -33,12 +33,12 @@ UTEST(tracking, alloc_free_balance) { sp_mem_tracking_init(&t); sp_mem_t mem = sp_mem_tracking_as_allocator(&t); - void* p = sp_alloc_a(mem, 64); + void* p = sp_alloc(mem, 64); EXPECT_NE(p, SP_NULLPTR); EXPECT_EQ(t.live_count, 1u); EXPECT_EQ(t.live_bytes, 64u); - sp_free_a(mem, p); + sp_free(mem, p); EXPECT_EQ(t.live_count, 0u); EXPECT_EQ(t.live_bytes, 0u); EXPECT_EQ(t.double_frees, 0u); @@ -52,10 +52,10 @@ UTEST(tracking, detects_leak) { sp_mem_tracking_init(&t); sp_mem_t mem = sp_mem_tracking_as_allocator(&t); - sp_alloc_a(mem, 16); - sp_alloc_a(mem, 32); - void* freed = sp_alloc_a(mem, 8); - sp_free_a(mem, freed); + sp_alloc(mem, 16); + sp_alloc(mem, 32); + void* freed = sp_alloc(mem, 8); + sp_free(mem, freed); EXPECT_EQ(t.live_count, 2u); EXPECT_EQ(t.live_bytes, 48u); @@ -68,10 +68,10 @@ UTEST(tracking, detects_double_free) { sp_mem_tracking_init(&t); sp_mem_t mem = sp_mem_tracking_as_allocator(&t); - void* p = sp_alloc_a(mem, 64); - sp_free_a(mem, p); - sp_free_a(mem, p); - sp_free_a(mem, p); + void* p = sp_alloc(mem, 64); + sp_free(mem, p); + sp_free(mem, p); + sp_free(mem, p); EXPECT_EQ(t.double_frees, 2u); EXPECT_EQ(t.live_count, 0u); @@ -90,7 +90,7 @@ UTEST(tracking, detects_wild_free) { // large enough for the back-step, and the bytes there won't match either // sentinel (we zero-init the buffer). static u8 buf[256] = {0}; - sp_free_a(mem, &buf[200]); + sp_free(mem, &buf[200]); EXPECT_EQ(t.wild_frees, 1u); EXPECT_EQ(t.double_frees, 0u); @@ -104,7 +104,7 @@ UTEST(tracking, free_null_is_noop) { sp_mem_tracking_init(&t); sp_mem_t mem = sp_mem_tracking_as_allocator(&t); - sp_free_a(mem, SP_NULLPTR); + sp_free(mem, SP_NULLPTR); EXPECT_EQ(t.double_frees, 0u); EXPECT_EQ(t.wild_frees, 0u); @@ -118,17 +118,17 @@ UTEST(tracking, realloc_grows_and_preserves) { sp_mem_tracking_init(&t); sp_mem_t mem = sp_mem_tracking_as_allocator(&t); - u8* p = sp_alloc_n_a(mem, u8, 4); + u8* p = sp_alloc_n(mem, u8, 4); sp_for(i, 4) p[i] = (u8)(i + 1); - u8* g = sp_void_cast(g, sp_realloc_a(mem, p, 64)); + u8* g = sp_void_cast(g, sp_realloc(mem, p, 64)); EXPECT_NE(g, SP_NULLPTR); sp_for(i, 4) EXPECT_EQ(g[i], (u8)(i + 1)); EXPECT_EQ(t.live_count, 1u); EXPECT_EQ(t.live_bytes, 64u); - sp_free_a(mem, g); + sp_free(mem, g); EXPECT_EQ(t.live_count, 0u); sp_mem_tracking_deinit(&t); @@ -139,12 +139,12 @@ UTEST(tracking, realloc_null_is_alloc) { sp_mem_tracking_init(&t); sp_mem_t mem = sp_mem_tracking_as_allocator(&t); - void* p = sp_realloc_a(mem, SP_NULLPTR, 32); + void* p = sp_realloc(mem, SP_NULLPTR, 32); EXPECT_NE(p, SP_NULLPTR); EXPECT_EQ(t.live_count, 1u); EXPECT_EQ(t.live_bytes, 32u); - sp_free_a(mem, p); + sp_free(mem, p); sp_mem_tracking_deinit(&t); } @@ -153,8 +153,8 @@ UTEST(tracking, realloc_zero_is_free) { sp_mem_tracking_init(&t); sp_mem_t mem = sp_mem_tracking_as_allocator(&t); - void* p = sp_alloc_a(mem, 32); - void* r = sp_realloc_a(mem, p, 0); + void* p = sp_alloc(mem, 32); + void* r = sp_realloc(mem, p, 0); EXPECT_EQ(r, SP_NULLPTR); EXPECT_EQ(t.live_count, 0u); EXPECT_EQ(t.live_bytes, 0u); @@ -166,9 +166,9 @@ UTEST(tracking, realloc_zero_is_free) { // SP_PS LEAK // ////////////// static sp_str_t leak_ps_get_process_path(sp_mem_t mem) { - sp_str_t exe = sp_fs_parent_path(sp_fs_get_exe_path_a(mem)); - sp_str_t process = sp_fs_join_path_a(mem, exe, sp_str_lit("process")); - return sp_fs_replace_ext_a(mem, process, sp_os_get_executable_ext()); + sp_str_t exe = sp_fs_parent_path(sp_fs_get_exe_path(mem)); + sp_str_t process = sp_fs_join_path(mem, exe, sp_str_lit("process")); + return sp_fs_replace_ext(mem, process, sp_os_get_executable_ext()); } UTEST_F(leak, ps_create_wait_free_balances) { @@ -184,7 +184,7 @@ UTEST_F(leak, ps_create_wait_free_balances) { }, }; - sp_ps_t ps = sp_ps_create_a(ut.mem, config); + sp_ps_t ps = sp_ps_create(ut.mem, config); EXPECT_NE(ps.os, SP_NULLPTR); sp_ps_status_t status = sp_ps_wait(&ps); @@ -194,17 +194,17 @@ UTEST_F(leak, ps_create_wait_free_balances) { } UTEST_F(leak, multiple_allocs_independent) { - void* a = sp_alloc_a(ut.mem, 8); - void* b = sp_alloc_a(ut.mem, 16); - void* c = sp_alloc_a(ut.mem, 32); + void* a = sp_alloc(ut.mem, 8); + void* b = sp_alloc(ut.mem, 16); + void* c = sp_alloc(ut.mem, 32); EXPECT_EQ(ut.tracker.live_count, 3u); EXPECT_EQ(ut.tracker.live_bytes, 56u); - sp_free_a(ut.mem, b); + sp_free(ut.mem, b); EXPECT_EQ(ut.tracker.live_count, 2u); EXPECT_EQ(ut.tracker.live_bytes, 40u); - sp_free_a(ut.mem, a); - sp_free_a(ut.mem, c); + sp_free(ut.mem, a); + sp_free(ut.mem, c); EXPECT_EQ(ut.tracker.live_count, 0u); } diff --git a/test/linkage.c b/test/linkage.c index 34c37fe..3605845 100644 --- a/test/linkage.c +++ b/test/linkage.c @@ -17,7 +17,7 @@ typedef struct linkage { #if defined(SP_WIN32) bool check_path(sp_str_t program) { - return SearchPathA(SP_NULLPTR, sp_str_to_cstr_a(sp_mem_get_scratch(), program), SP_NULLPTR, 0, SP_NULLPTR, SP_NULLPTR) > 0; + return SearchPathA(SP_NULLPTR, sp_str_to_cstr(sp_mem_get_scratch(), program), SP_NULLPTR, 0, SP_NULLPTR, SP_NULLPTR) > 0; } #else bool check_path(sp_str_t program) { @@ -32,7 +32,7 @@ UTEST_F_SETUP(linkage) { sp_test_file_manager_init(&ut.files); ut.root = ut.files.paths.root; - ut.source = sp_fs_join_path_a(ut.mem, ut.root, sp_str_lit("test/tools/linkage")); + ut.source = sp_fs_join_path(ut.mem, ut.root, sp_str_lit("test/tools/linkage")); if (!ut.has_compiler.some) { sp_opt_set(ut.has_compiler, check_path(sp_str_lit("cl"))); @@ -51,7 +51,7 @@ UTEST_F_TEARDOWN(linkage) { sp_str_t linkage_exe(sp_str_t stem) { #if defined(SP_WIN32) - return sp_fmt_a(sp_mem_get_scratch(), "{}.exe", sp_fmt_str(stem)).value; + return sp_fmt(sp_mem_get_scratch(), "{}.exe", sp_fmt_str(stem)).value; #else return stem; #endif @@ -59,32 +59,32 @@ sp_str_t linkage_exe(sp_str_t stem) { sp_str_t linkage_obj(sp_str_t stem) { #if defined(SP_WIN32) - return sp_fmt_a(sp_mem_get_scratch(), "{}.obj", sp_fmt_str(stem)).value; + return sp_fmt(sp_mem_get_scratch(), "{}.obj", sp_fmt_str(stem)).value; #else - return sp_fmt_a(sp_mem_get_scratch(), "{}.o", sp_fmt_str(stem)).value; + return sp_fmt(sp_mem_get_scratch(), "{}.o", sp_fmt_str(stem)).value; #endif } sp_str_t linkage_shared(sp_str_t stem) { #if defined(SP_WIN32) - return sp_fmt_a(sp_mem_get_scratch(), "{}.dll", sp_fmt_str(stem)).value; + return sp_fmt(sp_mem_get_scratch(), "{}.dll", sp_fmt_str(stem)).value; #else - return sp_fmt_a(sp_mem_get_scratch(), "{}.so", sp_fmt_str(stem)).value; + return sp_fmt(sp_mem_get_scratch(), "{}.so", sp_fmt_str(stem)).value; #endif } sp_str_t linkage_static(sp_str_t stem) { #if defined(SP_WIN32) - return sp_fmt_a(sp_mem_get_scratch(), "{}.lib", sp_fmt_str(stem)).value; + return sp_fmt(sp_mem_get_scratch(), "{}.lib", sp_fmt_str(stem)).value; #else - return sp_fmt_a(sp_mem_get_scratch(), "{}.a", sp_fmt_str(stem)).value; + return sp_fmt(sp_mem_get_scratch(), "{}.a", sp_fmt_str(stem)).value; #endif } void linkage_add_win32_link_libs(sp_ps_config_t* cfg) { #if defined(SP_WIN32) - sp_ps_config_add_arg_a(sp_mem_get_scratch(), cfg, sp_str_lit("/link")); - sp_ps_config_add_arg_a(sp_mem_get_scratch(), cfg, sp_str_lit("shell32.lib")); + sp_ps_config_add_arg(sp_mem_get_scratch(), cfg, sp_str_lit("/link")); + sp_ps_config_add_arg(sp_mem_get_scratch(), cfg, sp_str_lit("shell32.lib")); #else SP_UNUSED(cfg); #endif @@ -97,22 +97,22 @@ bool compile_to_exe(linkage* ctx, const c8* file, sp_str_t output) { .args = { sp_str_lit("/nologo"), sp_str_lit("/TC"), - sp_fmt_a(sp_mem_get_scratch(), "/I{}", sp_fmt_str(ctx->root)).value, - sp_fs_join_path_a(sp_mem_get_scratch(), ctx->source, sp_str_view(file)), - sp_fmt_a(sp_mem_get_scratch(), "/Fe:{}", sp_fmt_str(output)).value, + sp_fmt(sp_mem_get_scratch(), "/I{}", sp_fmt_str(ctx->root)).value, + sp_fs_join_path(sp_mem_get_scratch(), ctx->source, sp_str_view(file)), + sp_fmt(sp_mem_get_scratch(), "/Fe:{}", sp_fmt_str(output)).value, }, }; linkage_add_win32_link_libs(&cfg); - sp_ps_output_t out = sp_ps_run_a(sp_mem_get_scratch(), cfg); + sp_ps_output_t out = sp_ps_run(sp_mem_get_scratch(), cfg); return !out.status.exit_code; #else - sp_ps_output_t out = sp_ps_run_a(sp_mem_get_scratch(), (sp_ps_config_t) { + sp_ps_output_t out = sp_ps_run(sp_mem_get_scratch(), (sp_ps_config_t) { .command = sp_str_lit("cc"), .args = { - sp_fs_join_path_a(sp_mem_get_scratch(), ctx->source, sp_str_view(file)), - sp_fmt_a(sp_mem_get_scratch(), "-I{}", sp_fmt_str(ctx->root)).value, + sp_fs_join_path(sp_mem_get_scratch(), ctx->source, sp_str_view(file)), + sp_fmt(sp_mem_get_scratch(), "-I{}", sp_fmt_str(ctx->root)).value, sp_str_lit("-o"), output, sp_str_lit("-g"), }, @@ -124,26 +124,26 @@ bool compile_to_exe(linkage* ctx, const c8* file, sp_str_t output) { bool compile_to_object(linkage* ctx, const c8* file, sp_str_t output) { #if defined(SP_WIN32) - sp_ps_output_t out = sp_ps_run_a(sp_mem_get_scratch(), (sp_ps_config_t) { + sp_ps_output_t out = sp_ps_run(sp_mem_get_scratch(), (sp_ps_config_t) { .command = sp_str_lit("cl"), .args = { sp_str_lit("/nologo"), sp_str_lit("/TC"), sp_str_lit("/c"), - sp_fmt_a(sp_mem_get_scratch(), "/I{}", sp_fmt_str(ctx->root)).value, - sp_fs_join_path_a(sp_mem_get_scratch(), ctx->source, sp_str_view(file)), - sp_fmt_a(sp_mem_get_scratch(), "/Fo:{}", sp_fmt_str(output)).value, + sp_fmt(sp_mem_get_scratch(), "/I{}", sp_fmt_str(ctx->root)).value, + sp_fs_join_path(sp_mem_get_scratch(), ctx->source, sp_str_view(file)), + sp_fmt(sp_mem_get_scratch(), "/Fo:{}", sp_fmt_str(output)).value, }, }); return !out.status.exit_code; #else - sp_ps_output_t out = sp_ps_run_a(sp_mem_get_scratch(), (sp_ps_config_t) { + sp_ps_output_t out = sp_ps_run(sp_mem_get_scratch(), (sp_ps_config_t) { .command = sp_str_lit("cc"), .args = { sp_str_lit("-c"), - sp_fs_join_path_a(sp_mem_get_scratch(), ctx->source, sp_str_view(file)), - sp_fmt_a(sp_mem_get_scratch(), "-I{}", sp_fmt_str(ctx->root)).value, + sp_fs_join_path(sp_mem_get_scratch(), ctx->source, sp_str_view(file)), + sp_fmt(sp_mem_get_scratch(), "-I{}", sp_fmt_str(ctx->root)).value, sp_str_lit("-o"), output, sp_str_lit("-g"), }, @@ -157,16 +157,16 @@ bool compile_objects_to_exe(sp_str_t output, sp_str_t* objs, u32 len) { #if defined(SP_WIN32) sp_ps_config_t cfg = { .command = sp_str_lit("cl"), - .args = { sp_str_lit("/nologo"), sp_fmt_a(sp_mem_get_scratch(), "/Fe:{}", sp_fmt_str(output)).value }, + .args = { sp_str_lit("/nologo"), sp_fmt(sp_mem_get_scratch(), "/Fe:{}", sp_fmt_str(output)).value }, }; sp_for(it, len) { - sp_ps_config_add_arg_a(sp_mem_get_scratch(), &cfg, objs[it]); + sp_ps_config_add_arg(sp_mem_get_scratch(), &cfg, objs[it]); } linkage_add_win32_link_libs(&cfg); - sp_ps_output_t out = sp_ps_run_a(sp_mem_get_scratch(), cfg); + sp_ps_output_t out = sp_ps_run(sp_mem_get_scratch(), cfg); return !out.status.exit_code; #else sp_ps_config_t cfg = { @@ -175,10 +175,10 @@ bool compile_objects_to_exe(sp_str_t output, sp_str_t* objs, u32 len) { }; for (u32 it = 0; it < len; it++) { - sp_ps_config_add_arg_a(sp_mem_get_scratch(), &cfg, objs[it]); + sp_ps_config_add_arg(sp_mem_get_scratch(), &cfg, objs[it]); } - sp_ps_output_t out = sp_ps_run_a(sp_mem_get_scratch(), cfg); + sp_ps_output_t out = sp_ps_run(sp_mem_get_scratch(), cfg); return !out.status.exit_code; #endif } @@ -190,28 +190,28 @@ bool compile_to_linked_exe(linkage* ctx, const c8* file, sp_str_t output, sp_str .args = { sp_str_lit("/nologo"), sp_str_lit("/TC"), - sp_fs_join_path_a(sp_mem_get_scratch(), ctx->source, sp_str_view(file)), - sp_fmt_a(sp_mem_get_scratch(), "/I{}", sp_fmt_str(ctx->root)).value, - sp_fmt_a(sp_mem_get_scratch(), "/Fe:{}", sp_fmt_str(output)).value, + sp_fs_join_path(sp_mem_get_scratch(), ctx->source, sp_str_view(file)), + sp_fmt(sp_mem_get_scratch(), "/I{}", sp_fmt_str(ctx->root)).value, + sp_fmt(sp_mem_get_scratch(), "/Fe:{}", sp_fmt_str(output)).value, }, }; - sp_ps_config_add_arg_a(sp_mem_get_scratch(), &cfg, sp_str_lit("/link")); + sp_ps_config_add_arg(sp_mem_get_scratch(), &cfg, sp_str_lit("/link")); sp_for(it, len) { - sp_ps_config_add_arg_a(sp_mem_get_scratch(), &cfg, libs[it]); + sp_ps_config_add_arg(sp_mem_get_scratch(), &cfg, libs[it]); } - sp_ps_config_add_arg_a(sp_mem_get_scratch(), &cfg, sp_str_lit("shell32.lib")); + sp_ps_config_add_arg(sp_mem_get_scratch(), &cfg, sp_str_lit("shell32.lib")); - sp_ps_output_t out = sp_ps_run_a(sp_mem_get_scratch(), cfg); + sp_ps_output_t out = sp_ps_run(sp_mem_get_scratch(), cfg); return !out.status.exit_code; #else sp_ps_config_t cfg = { .command = sp_str_lit("cc"), .args = { - sp_fs_join_path_a(sp_mem_get_scratch(), ctx->source, sp_str_view(file)), - sp_fmt_a(sp_mem_get_scratch(), "-I{}", sp_fmt_str(ctx->root)).value, + sp_fs_join_path(sp_mem_get_scratch(), ctx->source, sp_str_view(file)), + sp_fmt(sp_mem_get_scratch(), "-I{}", sp_fmt_str(ctx->root)).value, sp_str_lit("-o"), output, sp_str_lit("-g"), sp_str_lit("-lpthread"), sp_str_lit("-lm") @@ -219,10 +219,10 @@ bool compile_to_linked_exe(linkage* ctx, const c8* file, sp_str_t output, sp_str }; for (u32 it = 0; it < len; it++) { - sp_ps_config_add_arg_a(sp_mem_get_scratch(), &cfg, libs[it]); + sp_ps_config_add_arg(sp_mem_get_scratch(), &cfg, libs[it]); } - sp_ps_output_t out = sp_ps_run_a(sp_mem_get_scratch(), cfg); + sp_ps_output_t out = sp_ps_run(sp_mem_get_scratch(), cfg); return !out.status.exit_code; #endif } @@ -234,15 +234,15 @@ bool create_archive(linkage* ctx, sp_str_t archive, sp_str_t* objs, u32 len) { .command = sp_str_lit("lib"), .args = { sp_str_lit("/nologo"), - sp_fmt_a(sp_mem_get_scratch(), "/OUT:{}", sp_fmt_str(archive)).value, + sp_fmt(sp_mem_get_scratch(), "/OUT:{}", sp_fmt_str(archive)).value, }, }; sp_for(it, len) { - sp_ps_config_add_arg_a(sp_mem_get_scratch(), &cfg, objs[it]); + sp_ps_config_add_arg(sp_mem_get_scratch(), &cfg, objs[it]); } - sp_ps_output_t out = sp_ps_run_a(sp_mem_get_scratch(), cfg); + sp_ps_output_t out = sp_ps_run(sp_mem_get_scratch(), cfg); return !out.status.exit_code; #else sp_ps_config_t cfg = { @@ -252,28 +252,28 @@ bool create_archive(linkage* ctx, sp_str_t archive, sp_str_t* objs, u32 len) { }, }; - sp_ps_config_add_arg_a(sp_mem_get_scratch(), &cfg, archive); + sp_ps_config_add_arg(sp_mem_get_scratch(), &cfg, archive); for (u32 it = 0; it < len; it++) { - sp_ps_config_add_arg_a(sp_mem_get_scratch(), &cfg, objs[it]); + sp_ps_config_add_arg(sp_mem_get_scratch(), &cfg, objs[it]); } - sp_ps_output_t out = sp_ps_run_a(sp_mem_get_scratch(), cfg); + sp_ps_output_t out = sp_ps_run(sp_mem_get_scratch(), cfg); return !out.status.exit_code; #endif } bool is_symbol_in_binary(sp_str_t binary, sp_str_t symbol) { #if defined(SP_WIN32) - HMODULE module = LoadLibraryA(sp_str_to_cstr_a(sp_mem_get_scratch(), binary)); + HMODULE module = LoadLibraryA(sp_str_to_cstr(sp_mem_get_scratch(), binary)); if (!module) { return false; } - bool found = GetProcAddress(module, sp_str_to_cstr_a(sp_mem_get_scratch(), symbol)) != SP_NULLPTR; + bool found = GetProcAddress(module, sp_str_to_cstr(sp_mem_get_scratch(), symbol)) != SP_NULLPTR; FreeLibrary(module); return found; #else - sp_ps_output_t nm = sp_ps_run_a(sp_mem_get_scratch(), (sp_ps_config_t){ + sp_ps_output_t nm = sp_ps_run(sp_mem_get_scratch(), (sp_ps_config_t){ .command = sp_str_lit("nm"), .args = { sp_str_lit("-g"), binary }, }); @@ -287,7 +287,7 @@ UTEST_F(linkage, single_tu) { sp_str_t bin = linkage_exe(sp_test_file_path(&ut.files, sp_str_lit("header-single"))); EXPECT_TRUE(compile_to_exe(&ut, "main.c", bin)); - sp_ps_output_t run = sp_ps_run_a(ut.mem, (sp_ps_config_t){ .command = bin }); + sp_ps_output_t run = sp_ps_run(ut.mem, (sp_ps_config_t){ .command = bin }); ASSERT_EQ(run.status.exit_code, 0); } @@ -310,7 +310,7 @@ UTEST_F(linkage, multi_tu) { sp_str_t bin = linkage_exe(sp_test_file_path(&ut.files, sp_str_lit("header-multi"))); EXPECT_TRUE(compile_objects_to_exe(bin, objs, sp_carr_len(objs))); - sp_ps_output_t run = sp_ps_run_a(ut.mem, (sp_ps_config_t){ .command = bin }); + sp_ps_output_t run = sp_ps_run(ut.mem, (sp_ps_config_t){ .command = bin }); EXPECT_EQ(run.status.exit_code, 0); } @@ -319,27 +319,27 @@ UTEST_F(linkage, shared_lib) { sp_str_t so = linkage_shared(sp_test_file_path(&ut.files, sp_str_lit("shared"))); #if defined(SP_WIN32) - sp_ps_output_t out = sp_ps_run_a(ut.mem, (sp_ps_config_t){ + sp_ps_output_t out = sp_ps_run(ut.mem, (sp_ps_config_t){ .command = sp_str_lit("cl"), .args = { sp_str_lit("/nologo"), sp_str_lit("/LD"), sp_str_lit("/TC"), sp_str_lit("/DSP_SHARED_LIB"), - sp_fs_join_path_a(ut.mem, ut.source, sp_str_lit("lib-impl.c")), - sp_fmt_a(ut.mem, "/I{}", sp_fmt_str(ut.root)).value, - sp_fmt_a(ut.mem, "/Fe:{}", sp_fmt_str(so)).value, + sp_fs_join_path(ut.mem, ut.source, sp_str_lit("lib-impl.c")), + sp_fmt(ut.mem, "/I{}", sp_fmt_str(ut.root)).value, + sp_fmt(ut.mem, "/Fe:{}", sp_fmt_str(so)).value, sp_str_lit("/link"), sp_str_lit("shell32.lib"), }, }); #else - sp_ps_output_t out = sp_ps_run_a(ut.mem, (sp_ps_config_t){ + sp_ps_output_t out = sp_ps_run(ut.mem, (sp_ps_config_t){ .command = sp_str_lit("cc"), .args = { sp_str_lit("-shared"), sp_str_lit("-fPIC"), - sp_fs_join_path_a(ut.mem, ut.source, sp_str_lit("lib-impl.c")), - sp_fmt_a(ut.mem, "-I{}", sp_fmt_str(ut.root)).value, + sp_fs_join_path(ut.mem, ut.source, sp_str_lit("lib-impl.c")), + sp_fmt(ut.mem, "-I{}", sp_fmt_str(ut.root)).value, sp_str_lit("-o"), so, sp_str_lit("-lpthread"), sp_str_lit("-lm") }, @@ -360,7 +360,7 @@ UTEST_F(linkage, static_lib) { sp_str_t bin = linkage_exe(sp_test_file_path(&ut.files, sp_str_lit("static-single"))); EXPECT_TRUE(compile_to_linked_exe(&ut, "main-decl.c", bin, &archive, 1)); - sp_ps_output_t run = sp_ps_run_a(ut.mem, (sp_ps_config_t){ .command = bin }); + sp_ps_output_t run = sp_ps_run(ut.mem, (sp_ps_config_t){ .command = bin }); ASSERT_EQ(run.status.exit_code, 0); } @@ -369,27 +369,27 @@ UTEST_F(linkage, cpp_compat) { sp_str_t obj = linkage_obj(sp_test_file_path(&ut.files, sp_str_lit("cpp-main"))); #if defined(SP_WIN32) - sp_ps_output_t out = sp_ps_run_a(ut.mem, (sp_ps_config_t){ + sp_ps_output_t out = sp_ps_run(ut.mem, (sp_ps_config_t){ .command = sp_str_lit("cl"), .args = { sp_str_lit("/nologo"), sp_str_lit("/TP"), sp_str_lit("/std:c++20"), sp_str_lit("/c"), - sp_fs_join_path_a(ut.mem, ut.source, sp_str_lit("cpp-compat.c")), - sp_fmt_a(ut.mem, "/I{}", sp_fmt_str(ut.root)).value, - sp_fmt_a(ut.mem, "/Fo:{}", sp_fmt_str(obj)).value, + sp_fs_join_path(ut.mem, ut.source, sp_str_lit("cpp-compat.c")), + sp_fmt(ut.mem, "/I{}", sp_fmt_str(ut.root)).value, + sp_fmt(ut.mem, "/Fo:{}", sp_fmt_str(obj)).value, sp_str_lit("/WX"), }, }); #else - sp_ps_output_t out = sp_ps_run_a(ut.mem, (sp_ps_config_t){ + sp_ps_output_t out = sp_ps_run(ut.mem, (sp_ps_config_t){ .command = sp_str_lit("c++"), .args = { sp_str_lit("-c"), sp_str_lit("-x"), sp_str_lit("c++"), - sp_fs_join_path_a(ut.mem, ut.source, sp_str_lit("cpp-compat.c")), - sp_fmt_a(ut.mem, "-I{}", sp_fmt_str(ut.root)).value, + sp_fs_join_path(ut.mem, ut.source, sp_str_lit("cpp-compat.c")), + sp_fmt(ut.mem, "-I{}", sp_fmt_str(ut.root)).value, sp_str_lit("-o"), obj, sp_str_lit("-g"), sp_str_lit("-Werror"), diff --git a/test/mem/arena.c b/test/mem/arena.c index 46aeaf2..bb99e5c 100644 --- a/test/mem/arena.c +++ b/test/mem/arena.c @@ -3,9 +3,9 @@ UTEST_F(mem, arena_padding_after_byte) { sp_mem_arena_marker_t scratch = sp_mem_begin_scratch(); - u8* byte = sp_void_cast(byte, sp_alloc_a(scratch.mem, 1)); + u8* byte = sp_void_cast(byte, sp_alloc(scratch.mem, 1)); - u8* word = sp_void_cast(word, sp_alloc_a(scratch.mem, 8)); + u8* word = sp_void_cast(word, sp_alloc(scratch.mem, 8)); EXPECT_ALIGNED(byte); EXPECT_ALIGNED(word); @@ -17,9 +17,9 @@ UTEST_F(mem, arena_padding_after_byte) { UTEST_F(mem, arena_padding_mixed_sizes) { sp_mem_arena_marker_t scratch = sp_mem_begin_scratch(); - u8* p1 = sp_void_cast(p1, sp_alloc_a(scratch.mem, 3)); - u8* p2 = sp_void_cast(p2, sp_alloc_a(scratch.mem, 13)); - u8* p3 = sp_void_cast(p3, sp_alloc_a(scratch.mem, 8)); + u8* p1 = sp_void_cast(p1, sp_alloc(scratch.mem, 3)); + u8* p2 = sp_void_cast(p2, sp_alloc(scratch.mem, 13)); + u8* p3 = sp_void_cast(p3, sp_alloc(scratch.mem, 8)); EXPECT_ALIGNED(p1); EXPECT_ALIGNED(p2); diff --git a/test/mem/builtin.c b/test/mem/builtin.c index 71e8a5f..a18e276 100644 --- a/test/mem/builtin.c +++ b/test/mem/builtin.c @@ -356,7 +356,7 @@ UTEST_F(mem, alloc_preserves_u64_size) { }; u64 requested = (u64)5 * 1024 * 1024 * 1024; - sp_alloc_a(mock, requested); + sp_alloc(mock, requested); EXPECT_EQ(recorded_size, requested); } diff --git a/test/prompt.c b/test/prompt.c index e0e43f5..e73bff4 100644 --- a/test/prompt.c +++ b/test/prompt.c @@ -48,7 +48,7 @@ UTEST_F_SETUP(prompt) { ut.ctx = sp_zero_s(sp_prompt_ctx_t); ut.app = SP_NULLPTR; sp_prompt_ctx_init(&ut.ctx, ut.mem.tracking, 80, 20); - sp_io_dyn_mem_writer_init_a(ut.mem.arena, &ut.writer); + sp_io_dyn_mem_writer_init(ut.mem.arena, &ut.writer); ut.ctx.writer = &ut.writer.base; } @@ -71,7 +71,7 @@ static u32 count_expected_lines(const c8* lines[32]) { static sp_str_t trim_framebuffer_row(sp_mem_t mem, sp_prompt_cell_t* row, u32 cols) { sp_io_dyn_mem_writer_t builder = sp_zero; - sp_io_dyn_mem_writer_init_a(mem, &builder); + sp_io_dyn_mem_writer_init(mem, &builder); sp_for(col, cols) { c8 buf[4] = sp_zero; u8 len = sp_utf8_encode(row[col].codepoint, buf); @@ -226,7 +226,7 @@ static sp_da(sp_str_t) sp_prompt_vt_render_lines(sp_mem_t mem, sp_str_t bytes) { sp_da(sp_str_t) lines = sp_da_new(mem, sp_str_t); sp_for_range(row, 0, vt.max_row + 1) { sp_io_dyn_mem_writer_t builder = sp_zero; - sp_io_dyn_mem_writer_init_a(mem, &builder); + sp_io_dyn_mem_writer_init(mem, &builder); sp_for(col, 256) { c8 buf[4] = sp_zero; u8 len = sp_utf8_encode(vt.cells[row][col], buf); @@ -2370,13 +2370,13 @@ UTEST_F(prompt, abort_drives_widget_to_cancel) { } UTEST_F(prompt, prompt_end_frees_channel) { - sp_prompt_ctx_t* ctx = sp_alloc_type_a(ut.mem.tracking, sp_prompt_ctx_t); + sp_prompt_ctx_t* ctx = sp_alloc_type(ut.mem.tracking, sp_prompt_ctx_t); sp_prompt_ctx_init(ctx, ut.mem.tracking, 80, 20); sp_prompt_send_progress_f32(ctx, 0.5f); sp_prompt_send_status(ctx, "halfway"); sp_prompt_complete(ctx); sp_prompt_end(ctx); - sp_free_a(ut.mem.tracking, ctx); + sp_free(ut.mem.tracking, ctx); } typedef struct { @@ -2541,7 +2541,7 @@ UTEST_F(prompt, progress_widget_renders_active_two_rows_plus_rail) { } UTEST_F(prompt, progress_widget_quarter_full_uses_partial_blocks) { - sp_prompt_progress_widget_t* p = sp_alloc_type_a(ut.mem.arena, sp_prompt_progress_widget_t); + sp_prompt_progress_widget_t* p = sp_alloc_type(ut.mem.arena, sp_prompt_progress_widget_t); *p = sp_zero_s(sp_prompt_progress_widget_t); p->config.prompt = "Loading"; p->config.width = 8; @@ -2573,7 +2573,7 @@ UTEST_F(prompt, progress_widget_clamps_out_of_range_inputs) { probe_state_t observer = sp_zero; SP_UNUSED(observer); - sp_prompt_progress_widget_t* p = sp_alloc_type_a(ut.mem.arena, sp_prompt_progress_widget_t); + sp_prompt_progress_widget_t* p = sp_alloc_type(ut.mem.arena, sp_prompt_progress_widget_t); *p = sp_zero_s(sp_prompt_progress_widget_t); p->config.prompt = "clamp"; p->config.width = 4; @@ -2667,7 +2667,7 @@ UTEST_F(prompt, status_after_complete_does_not_block_submit) { } UTEST_F(prompt, progress_widget_renders_status_below_bar) { - sp_prompt_progress_widget_t* p = sp_alloc_type_a(ut.mem.arena, sp_prompt_progress_widget_t); + sp_prompt_progress_widget_t* p = sp_alloc_type(ut.mem.arena, sp_prompt_progress_widget_t); *p = sp_zero_s(sp_prompt_progress_widget_t); p->config.prompt = "Installing"; p->config.width = 8; @@ -2698,7 +2698,7 @@ UTEST_F(prompt, progress_widget_renders_status_below_bar) { } UTEST_F(prompt, progress_widget_omits_status_when_empty) { - sp_prompt_progress_widget_t* p = sp_alloc_type_a(ut.mem.arena, sp_prompt_progress_widget_t); + sp_prompt_progress_widget_t* p = sp_alloc_type(ut.mem.arena, sp_prompt_progress_widget_t); *p = sp_zero_s(sp_prompt_progress_widget_t); p->config.prompt = "Loading"; p->config.width = 4; diff --git a/test/ps.c b/test/ps.c index 6c3e740..83efbd3 100644 --- a/test/ps.c +++ b/test/ps.c @@ -53,9 +53,9 @@ typedef struct { // FIXTURES // ////////////// sp_str_t get_process_path(sp_mem_t mem) { - sp_str_t exe = sp_fs_parent_path(sp_fs_get_exe_path_a(mem)); - sp_str_t process = sp_fs_join_path_a(mem, exe, sp_str_lit("process")); - process = sp_fs_replace_ext_a(mem, process, sp_os_get_executable_ext()); + sp_str_t exe = sp_fs_parent_path(sp_fs_get_exe_path(mem)); + sp_str_t process = sp_fs_join_path(mem, exe, sp_str_lit("process")); + process = sp_fs_replace_ext(mem, process, sp_os_get_executable_ext()); return process; } @@ -70,12 +70,12 @@ UTEST_F_SETUP(ps) { ut.arena = sp_mem_arena_new(sp_mem_os_new()); ut.mem = sp_mem_arena_as_allocator(ut.arena); sp_str_t process = get_process_path(ut.mem); - EXPECT_TRUE(sp_fs_exists_a(process)); + EXPECT_TRUE(sp_fs_exists(process)); sp_test_file_manager_init(&ut.file_manager); ut.buffer = (sp_byte_buffer_t) { .len = 1024, - .data = (u8*)sp_alloc_a(ut.mem, 1024) + .data = (u8*)sp_alloc(ut.mem, 1024) }; } @@ -118,7 +118,7 @@ void sp_test_proc_collect_stream(sp_test_proc_stream_context_t* ctx) { if (total_read != ctx->expected_len) { if (ctx->mode == SP_TEST_PROC_READ_EXACT) { if (total_read != ctx->expected_len) { - sp_log_a("expected to read {}, but got {}", sp_fmt_uint(total_read), sp_fmt_uint(ctx->expected_len)); + sp_log("expected to read {}, but got {}", sp_fmt_uint(total_read), sp_fmt_uint(ctx->expected_len)); sp_assert(total_read == ctx->expected_len); } } @@ -149,13 +149,13 @@ void sp_test_proc_io(sp_ps* utest_fixture, s32* utest_result, sp_test_proc_io_co }; if (test.output.out.enabled) { - sp_ps_config_add_arg_a(ut.mem, &config, sp_str_lit("--stdout")); + sp_ps_config_add_arg(ut.mem, &config, sp_str_lit("--stdout")); } if (test.output.err.enabled) { - sp_ps_config_add_arg_a(ut.mem, &config, sp_str_lit("--stderr")); + sp_ps_config_add_arg(ut.mem, &config, sp_str_lit("--stderr")); } - sp_ps_t ps = sp_ps_create_a(ut.mem, config); + sp_ps_t ps = sp_ps_create(ut.mem, config); SP_ASSERT(ps.os); sp_io_file_writer_t* in = sp_ps_io_in(&ps); @@ -166,7 +166,7 @@ void sp_test_proc_io(sp_ps* utest_fixture, s32* utest_result, sp_test_proc_io_co u64 bytes_written = 0; sp_io_write(&in->base, test.input.data, test.input.len, &bytes_written); if (!(bytes_written == test.input.len)) { - sp_log_a( + sp_log( "stdin: tried to write {} ({}), but {.fg yellow} returned {}", sp_fmt_str(test.input), sp_fmt_uint(test.input.len), @@ -182,7 +182,7 @@ void sp_test_proc_io(sp_ps* utest_fixture, s32* utest_result, sp_test_proc_io_co if (!sp_str_empty(test.output.out.expected)) { sp_test_proc_stream_context_t check = { .stream = out, - .buffer = { .len = 1024, .data = (u8*)sp_alloc_a(ut.mem, 1024) }, + .buffer = { .len = 1024, .data = (u8*)sp_alloc(ut.mem, 1024) }, .expected = test.output.out.expected, .mode = SP_TEST_PROC_READ_EXACT, .expected_len = test.output.out.expected.len, @@ -190,7 +190,7 @@ void sp_test_proc_io(sp_ps* utest_fixture, s32* utest_result, sp_test_proc_io_co sp_test_proc_check_stream(&check); if (check.result != SP_TEST_PS_OUTPUT_MATCH) { - sp_log_a("stdout: expected {.quote}, but got {.quote}", sp_fmt_str(check.expected), sp_fmt_cstr((c8*)check.buffer.data)); + sp_log("stdout: expected {.quote}, but got {.quote}", sp_fmt_str(check.expected), sp_fmt_cstr((c8*)check.buffer.data)); sp_assert(check.result == SP_TEST_PS_OUTPUT_MATCH); } } @@ -198,7 +198,7 @@ void sp_test_proc_io(sp_ps* utest_fixture, s32* utest_result, sp_test_proc_io_co if (!sp_str_empty(test.output.err.expected)) { sp_test_proc_stream_context_t check = { .stream = err, - .buffer = { .len = 1024, .data = (u8*)sp_alloc_a(ut.mem, 1024) }, + .buffer = { .len = 1024, .data = (u8*)sp_alloc(ut.mem, 1024) }, .expected = test.output.err.expected, .mode = SP_TEST_PROC_READ_EXACT, .expected_len = test.output.err.expected.len, @@ -206,7 +206,7 @@ void sp_test_proc_io(sp_ps* utest_fixture, s32* utest_result, sp_test_proc_io_co sp_test_proc_check_stream(&check); if (check.result != SP_TEST_PS_OUTPUT_MATCH) { - sp_log_a("stderr: expected {.quote}, but got {.quote}", sp_fmt_str(check.expected), sp_fmt_cstr((c8*)check.buffer.data)); + sp_log("stderr: expected {.quote}, but got {.quote}", sp_fmt_str(check.expected), sp_fmt_cstr((c8*)check.buffer.data)); sp_assert(check.result == SP_TEST_PS_OUTPUT_MATCH); } } @@ -459,7 +459,7 @@ void sp_test_proc_env_verify(sp_ps* utest_fixture, s32* utest_result, sp_test_pr }, }; - sp_ps_t ps = sp_ps_create_a(ut.mem, config); + sp_ps_t ps = sp_ps_create(ut.mem, config); SP_ASSERT(ps.os); sp_io_file_writer_t* in = sp_ps_io_in(&ps); @@ -480,7 +480,7 @@ void sp_test_proc_env_verify(sp_ps* utest_fixture, s32* utest_result, sp_test_pr .stream = out, .buffer = { .len = 1024, - .data = sp_alloc_n_a(ut.mem, u8, 1024), + .data = sp_alloc_n(ut.mem, u8, 1024), }, .mode = SP_TEST_PROC_READ_UNTIL_DONE, }; @@ -618,7 +618,7 @@ UTEST_F(ps, empty_env_var) { // SP_PS_WAIT // ////////////////// UTEST_F(ps, wait_after_process_complete) { - sp_ps_t ps = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("exit_code"), @@ -634,7 +634,7 @@ UTEST_F(ps, wait_after_process_complete) { } UTEST_F(ps, wait_while_process_running) { - sp_ps_t ps = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("wait"), @@ -648,7 +648,7 @@ UTEST_F(ps, wait_while_process_running) { } UTEST_F(ps, run) { - sp_ps_output_t result = sp_ps_run_a(ut.mem, (sp_ps_config_t) { + sp_ps_output_t result = sp_ps_run(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("print"), @@ -660,7 +660,7 @@ UTEST_F(ps, run) { } UTEST_F(ps, poll_while_process_running) { - sp_ps_t ps = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("wait"), @@ -676,7 +676,7 @@ UTEST_F(ps, poll_while_process_running) { } UTEST_F(ps, process_complete_during_poll) { - sp_ps_t ps = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("wait"), @@ -690,7 +690,7 @@ UTEST_F(ps, process_complete_during_poll) { } UTEST_F(ps, poll_after_process_complete) { - sp_ps_t ps = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("exit_code"), @@ -706,7 +706,7 @@ UTEST_F(ps, poll_after_process_complete) { } UTEST_F(ps, poll_with_timeout_after_process_complete) { - sp_ps_t ps = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("exit_code"), @@ -722,7 +722,7 @@ UTEST_F(ps, poll_with_timeout_after_process_complete) { } UTEST_F(ps, wait_twice_while_process_running) { - sp_ps_t ps = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("exit_code"), @@ -740,7 +740,7 @@ UTEST_F(ps, wait_twice_while_process_running) { } UTEST_F(ps, poll_then_wait) { - sp_ps_t ps = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("wait"), @@ -757,7 +757,7 @@ UTEST_F(ps, poll_then_wait) { } UTEST_F(ps, poll_multiple) { - sp_ps_t ps = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("wait"), @@ -779,7 +779,7 @@ UTEST_F(ps, poll_multiple) { } UTEST_F(ps, wait_with_output) { - sp_ps_t ps = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("print"), @@ -803,7 +803,7 @@ UTEST_F(ps, wait_with_output) { } UTEST_F(ps, poll_with_io) { - sp_ps_t ps = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("wait"), @@ -827,7 +827,7 @@ UTEST_F(ps, poll_with_io) { } UTEST_F(ps, interleaved_read_write) { - sp_ps_t ps = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("echo_line"), @@ -847,7 +847,7 @@ UTEST_F(ps, interleaved_read_write) { EXPECT_NE(out, SP_NULLPTR); for (u32 i = 0; i < 4; i++) { - sp_str_t input = sp_fmt_a(ut.mem, "line {}\n", sp_fmt_uint(i)).value; + sp_str_t input = sp_fmt(ut.mem, "line {}\n", sp_fmt_uint(i)).value; u64 written = 0; sp_io_write_str(&in->base, input, &written); @@ -856,7 +856,7 @@ UTEST_F(ps, interleaved_read_write) { sp_os_sleep_ms(50); u64 bytes_read = 0; sp_io_read(out, ut.buffer.data, ut.buffer.len, &bytes_read); - sp_str_t expected = sp_fmt_a(ut.mem, "echo: line {}\n", sp_fmt_uint(i)).value; + sp_str_t expected = sp_fmt(ut.mem, "echo: line {}\n", sp_fmt_uint(i)).value; EXPECT_EQ(bytes_read, expected.len); EXPECT_TRUE(sp_mem_is_equal(ut.buffer.data, expected.data, expected.len)); } @@ -869,7 +869,7 @@ UTEST_F(ps, interleaved_read_write) { } UTEST_F(ps, incremental_nonblocking_read) { - sp_ps_t ps = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("slow_write"), @@ -914,7 +914,7 @@ UTEST_F(ps, incremental_nonblocking_read) { } UTEST_F(ps, output) { - sp_ps_t ps = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("print"), @@ -936,7 +936,7 @@ UTEST_F(ps, output) { } UTEST_F(ps, redirect_stderr_to_stdout) { - sp_ps_t ps = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("print"), @@ -955,7 +955,7 @@ UTEST_F(ps, redirect_stderr_to_stdout) { EXPECT_EQ(output.status.state, SP_PS_STATE_DONE); EXPECT_EQ(output.status.exit_code, 0); - sp_str_t expected = sp_fmt_a(ut.mem, "{}{}", sp_fmt_str(sp_test_ps_canary), sp_fmt_str(sp_test_ps_canary)).value; + sp_str_t expected = sp_fmt(ut.mem, "{}{}", sp_fmt_str(sp_test_ps_canary), sp_fmt_str(sp_test_ps_canary)).value; EXPECT_TRUE(sp_str_equal(output.out, expected)); EXPECT_TRUE(sp_str_empty(output.err)); @@ -964,7 +964,7 @@ UTEST_F(ps, redirect_stderr_to_stdout) { } UTEST_F(ps, redirect_stdout_to_stderr) { - sp_ps_t ps = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("print"), @@ -983,7 +983,7 @@ UTEST_F(ps, redirect_stdout_to_stderr) { EXPECT_EQ(output.status.state, SP_PS_STATE_DONE); EXPECT_EQ(output.status.exit_code, 0); - sp_str_t expected = sp_fmt_a(ut.mem, "{}{}", sp_fmt_str(sp_test_ps_canary), sp_fmt_str(sp_test_ps_canary)).value; + sp_str_t expected = sp_fmt(ut.mem, "{}{}", sp_fmt_str(sp_test_ps_canary), sp_fmt_str(sp_test_ps_canary)).value; EXPECT_TRUE(sp_str_empty(output.out)); EXPECT_TRUE(sp_str_equal(output.err, expected)); @@ -992,7 +992,7 @@ UTEST_F(ps, redirect_stdout_to_stderr) { } UTEST_F(ps, output_large_stdout_stderr_deadlock) { - sp_ps_t ps = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("flood"), @@ -1017,7 +1017,7 @@ UTEST_F(ps, output_large_stdout_stderr_deadlock) { } UTEST_F(ps, output_large_stdout_deadlock) { - sp_ps_t ps = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("flood"), @@ -1091,7 +1091,7 @@ UTEST_F(ps, concurrent_existing_fd_small_writes) { const s32 write_size = 100; const s32 write_count = 50; - sp_ps_t ps_a = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps_a = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("pattern"), @@ -1107,7 +1107,7 @@ UTEST_F(ps, concurrent_existing_fd_small_writes) { } }); - sp_ps_t ps_b = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps_b = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("pattern"), @@ -1132,7 +1132,7 @@ UTEST_F(ps, concurrent_existing_fd_small_writes) { sp_ps_wait(&ps_b); const u32 expected_total = write_size * write_count * 2; - u8* buffer = (u8*)sp_alloc_a(ut.mem, expected_total + 1024); + u8* buffer = (u8*)sp_alloc(ut.mem, expected_total + 1024); u32 total_read = 0; while (total_read < expected_total) { @@ -1164,10 +1164,10 @@ UTEST_F(ps, concurrent_existing_fd_large_writes) { const s32 write_size = 8192; const s32 write_count = 10; - sp_str_t size_str = sp_fmt_a(ut.mem, "{}", sp_fmt_int(write_size)).value; - sp_str_t count_str = sp_fmt_a(ut.mem, "{}", sp_fmt_int(write_count)).value; + sp_str_t size_str = sp_fmt(ut.mem, "{}", sp_fmt_int(write_size)).value; + sp_str_t count_str = sp_fmt(ut.mem, "{}", sp_fmt_int(write_count)).value; - sp_ps_t ps_a = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps_a = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("pattern"), @@ -1183,7 +1183,7 @@ UTEST_F(ps, concurrent_existing_fd_large_writes) { } }); - sp_ps_t ps_b = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps_b = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = get_process_path(ut.mem), .args = { sp_str_lit("--fn"), sp_str_lit("pattern"), @@ -1205,7 +1205,7 @@ UTEST_F(ps, concurrent_existing_fd_large_writes) { sp_sys_close(pipes[1]); const u32 expected_total = write_size * write_count * 2; - u8* buffer = (u8*)sp_alloc_a(ut.mem, expected_total + 1024); + u8* buffer = (u8*)sp_alloc(ut.mem, expected_total + 1024); u32 total_read = 0; fcntl(pipes[0], SP_F_SETFL, fcntl(pipes[0], SP_F_GETFL) | SP_O_NONBLOCK); @@ -1241,10 +1241,10 @@ UTEST_F(ps, concurrent_existing_fd_large_writes) { sp_test_concurrent_analysis_t analysis = sp_test_analyze_concurrent_output(buffer, total_read, write_size); - sp_log_a("large writes ({}B x {} per process, > PIPE_BUF):", sp_fmt_int(write_size), sp_fmt_int(write_count)); - sp_log_a(" total bytes: {} (A={}, B={})", sp_fmt_uint(total_read), sp_fmt_uint(analysis.a_bytes), sp_fmt_uint(analysis.b_bytes)); - sp_log_a(" transitions: {}", sp_fmt_uint(analysis.transitions)); - sp_log_a(" interleaved: {}", sp_fmt_cstr(analysis.interleaved ? "true" : "false")); + sp_log("large writes ({}B x {} per process, > PIPE_BUF):", sp_fmt_int(write_size), sp_fmt_int(write_count)); + sp_log(" total bytes: {} (A={}, B={})", sp_fmt_uint(total_read), sp_fmt_uint(analysis.a_bytes), sp_fmt_uint(analysis.b_bytes)); + sp_log(" transitions: {}", sp_fmt_uint(analysis.transitions)); + sp_log(" interleaved: {}", sp_fmt_cstr(analysis.interleaved ? "true" : "false")); EXPECT_EQ(analysis.a_bytes, write_size * write_count); EXPECT_EQ(analysis.b_bytes, write_size * write_count); @@ -1252,14 +1252,14 @@ UTEST_F(ps, concurrent_existing_fd_large_writes) { #endif // !_WIN32 UTEST_F(ps, create_nonexistent_binary) { - sp_ps_t ps = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = sp_str_lit("/usr/bin/this_binary_does_not_exist_at_all") }); EXPECT_EQ(ps.os, SP_NULLPTR); } UTEST_F(ps, wait_nonexistent_binary) { - sp_ps_t ps = sp_ps_create_a(ut.mem, (sp_ps_config_t) { + sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { .command = sp_str_lit("/usr/bin/this_binary_does_not_exist_at_all") }); EXPECT_EQ(ps.os, SP_NULLPTR); @@ -1270,7 +1270,7 @@ UTEST_F(ps, wait_nonexistent_binary) { } UTEST_F(ps, run_nonexistent_binary) { - sp_ps_output_t result = sp_ps_run_a(ut.mem, (sp_ps_config_t) { + sp_ps_output_t result = sp_ps_run(ut.mem, (sp_ps_config_t) { .command = sp_str_lit("/usr/bin/this_binary_does_not_exist_at_all") }); EXPECT_EQ(result.status.exit_code, -1); diff --git a/test/str.c b/test/str.c index 4605b3a..19531c8 100644 --- a/test/str.c +++ b/test/str.c @@ -7,18 +7,18 @@ SP_TEST_MAIN() UTEST(cstr, all_variations) { const c8* original = "Hello World"; - c8* copy = sp_cstr_copy_a(sp_mem_get_scratch(), original); + c8* copy = sp_cstr_copy(sp_mem_get_scratch(), original); ASSERT_TRUE(sp_cstr_equal(copy, original)); ASSERT_NE(copy, original); - c8* partial = sp_cstr_copy_n_a(sp_mem_get_scratch(), original, 5); + c8* partial = sp_cstr_copy_n(sp_mem_get_scratch(), original, 5); ASSERT_TRUE(sp_cstr_equal(partial, "Hello")); const c8* empty = ""; - c8* empty_copy = sp_cstr_copy_a(sp_mem_get_scratch(), empty); + c8* empty_copy = sp_cstr_copy(sp_mem_get_scratch(), empty); ASSERT_TRUE(sp_cstr_equal(empty_copy, "")); - c8* null_copy = sp_cstr_copy_a(sp_mem_get_scratch(), SP_NULLPTR); + c8* null_copy = sp_cstr_copy(sp_mem_get_scratch(), SP_NULLPTR); ASSERT_EQ(null_copy[0], '\0'); } @@ -84,26 +84,26 @@ UTEST(cstr, length_tests) { UTEST(str, conversion_functions) { sp_str_t str = sp_str_lit("Hello World"); - c8* cstr = sp_str_to_cstr_a(sp_mem_get_scratch(), str); + c8* cstr = sp_str_to_cstr(sp_mem_get_scratch(), str); ASSERT_TRUE(sp_cstr_equal(cstr, "Hello World")); sp_str_t empty = sp_str_lit(""); - c8* empty_cstr = sp_str_to_cstr_a(sp_mem_get_scratch(), empty); + c8* empty_cstr = sp_str_to_cstr(sp_mem_get_scratch(), empty); ASSERT_TRUE(sp_cstr_equal(empty_cstr, "")); } UTEST(str, string_copy_operations) { sp_str_t original = sp_str_lit("Hello World"); - sp_str_t copy = sp_str_copy_a(sp_mem_get_scratch(), original); + sp_str_t copy = sp_str_copy(sp_mem_get_scratch(), original); ASSERT_EQ(copy.len, original.len); ASSERT_TRUE(sp_str_equal(copy, original)); ASSERT_NE(copy.data, original.data); - sp_str_t from_cstr = sp_str_from_cstr_a(sp_mem_get_scratch(), "Test String"); + sp_str_t from_cstr = sp_str_from_cstr(sp_mem_get_scratch(), "Test String"); ASSERT_EQ(from_cstr.len, 11); SP_EXPECT_STR_EQ_CSTR(from_cstr, "Test String"); - sp_str_t partial = sp_str_from_cstr_n_a(sp_mem_get_scratch(), "Hello World", 5); + sp_str_t partial = sp_str_from_cstr_n(sp_mem_get_scratch(), "Hello World", 5); ASSERT_EQ(partial.len, 5); SP_EXPECT_STR_EQ_CSTR(partial, "Hello"); @@ -132,7 +132,7 @@ UTEST(str, string_creation) { ASSERT_EQ(str3.len, 7); SP_EXPECT_STR_EQ_CSTR(str3, "Dynamic"); - sp_str_t allocated = sp_str_alloc_a(sp_mem_get_scratch(), 100); + sp_str_t allocated = sp_str_alloc(sp_mem_get_scratch(), 100); ASSERT_EQ(allocated.len, 0); ASSERT_NE(allocated.data, SP_NULLPTR); } @@ -192,23 +192,23 @@ UTEST(str, sorting_tests) { } sp_str_t sp_test_map_band_member(sp_str_map_context_t* context) { - return sp_str_concat_a(sp_mem_get_scratch(), context->str, sp_str_lit(" is in the band")); + return sp_str_concat(sp_mem_get_scratch(), context->str, sp_str_lit(" is in the band")); } UTEST(str, map_reduce) { sp_str_t band [] = { sp_str_lit("jerry"), sp_str_lit("bobby"), sp_str_lit("phil") }; - sp_da(sp_str_t) result = sp_str_map_a(sp_mem_get_scratch(), &band[0], SP_CARR_LEN(band), SP_NULLPTR, sp_test_map_band_member); + sp_da(sp_str_t) result = sp_str_map(sp_mem_get_scratch(), &band[0], SP_CARR_LEN(band), SP_NULLPTR, sp_test_map_band_member); SP_EXPECT_STR_EQ_CSTR(result[0], "jerry is in the band"); SP_EXPECT_STR_EQ_CSTR(result[1], "bobby is in the band"); SP_EXPECT_STR_EQ_CSTR(result[2], "phil is in the band"); - sp_str_t joined = sp_str_join_n_a(sp_mem_get_scratch(), band, SP_CARR_LEN(band), sp_str_lit(" and ")); + sp_str_t joined = sp_str_join_n(sp_mem_get_scratch(), band, SP_CARR_LEN(band), sp_str_lit(" and ")); SP_EXPECT_STR_EQ_CSTR(joined, "jerry and bobby and phil"); u32 len = 3; - sp_da(sp_str_t) clipped = sp_str_map_a(sp_mem_get_scratch(), &band[0], SP_CARR_LEN(band), &len, sp_str_map_kernel_prefix); + sp_da(sp_str_t) clipped = sp_str_map(sp_mem_get_scratch(), &band[0], SP_CARR_LEN(band), &len, sp_str_map_kernel_prefix); SP_EXPECT_STR_EQ_CSTR(clipped[0], "jer"); SP_EXPECT_STR_EQ_CSTR(clipped[1], "bob"); SP_EXPECT_STR_EQ_CSTR(clipped[2], "phi"); @@ -247,45 +247,45 @@ UTEST(str, valid_and_at) { UTEST(str, to_upper_and_replace) { { sp_str_t lowercase = sp_str_lit("hello world!"); - sp_str_t uppercase = sp_str_to_upper_a(sp_mem_get_scratch(), lowercase); + sp_str_t uppercase = sp_str_to_upper(sp_mem_get_scratch(), lowercase); SP_EXPECT_STR_EQ_CSTR(uppercase, "HELLO WORLD!"); }; { sp_str_t lower = sp_str_lit("caf\xC3\xA9 123 {[]}"); - SP_EXPECT_STR_EQ_CSTR(sp_str_to_upper_a(sp_mem_get_scratch(), lower), "CAF\xC3\xA9 123 {[]}"); + SP_EXPECT_STR_EQ_CSTR(sp_str_to_upper(sp_mem_get_scratch(), lower), "CAF\xC3\xA9 123 {[]}"); } { sp_str_t upper = sp_str_lit("CAF\xC3\xA9 123 {[]}"); - SP_EXPECT_STR_EQ_CSTR(sp_str_to_lower_a(sp_mem_get_scratch(), upper), "caf\xC3\xA9 123 {[]}"); + SP_EXPECT_STR_EQ_CSTR(sp_str_to_lower(sp_mem_get_scratch(), upper), "caf\xC3\xA9 123 {[]}"); } sp_str_t mixed = sp_str_lit("HeLLo WoRLd!"); - sp_str_t upper_mixed = sp_str_to_upper_a(sp_mem_get_scratch(), mixed); + sp_str_t upper_mixed = sp_str_to_upper(sp_mem_get_scratch(), mixed); SP_EXPECT_STR_EQ_CSTR(upper_mixed, "HELLO WORLD!"); sp_str_t original = sp_str_lit("hello world"); - sp_str_t replaced = sp_str_replace_c8_a(sp_mem_get_scratch(), original, 'l', 'X'); + sp_str_t replaced = sp_str_replace_c8(sp_mem_get_scratch(), original, 'l', 'X'); SP_EXPECT_STR_EQ_CSTR(replaced, "heXXo worXd"); - sp_str_t no_match = sp_str_replace_c8_a(sp_mem_get_scratch(), original, 'z', 'X'); + sp_str_t no_match = sp_str_replace_c8(sp_mem_get_scratch(), original, 'z', 'X'); SP_EXPECT_STR_EQ_CSTR(no_match, "hello world"); // all same char - SP_EXPECT_STR_EQ_CSTR(sp_str_replace_c8_a(sp_mem_get_scratch(), sp_str_lit("aaa"), 'a', 'b'), "bbb"); + SP_EXPECT_STR_EQ_CSTR(sp_str_replace_c8(sp_mem_get_scratch(), sp_str_lit("aaa"), 'a', 'b'), "bbb"); // empty string - SP_EXPECT_STR_EQ_CSTR(sp_str_replace_c8_a(sp_mem_get_scratch(), sp_str_lit(""), 'a', 'b'), ""); + SP_EXPECT_STR_EQ_CSTR(sp_str_replace_c8(sp_mem_get_scratch(), sp_str_lit(""), 'a', 'b'), ""); // same from/to (no-op) - SP_EXPECT_STR_EQ_CSTR(sp_str_replace_c8_a(sp_mem_get_scratch(), sp_str_lit("abc"), 'a', 'a'), "abc"); + SP_EXPECT_STR_EQ_CSTR(sp_str_replace_c8(sp_mem_get_scratch(), sp_str_lit("abc"), 'a', 'a'), "abc"); // single char string - SP_EXPECT_STR_EQ_CSTR(sp_str_replace_c8_a(sp_mem_get_scratch(), sp_str_lit("x"), 'x', 'y'), "y"); + SP_EXPECT_STR_EQ_CSTR(sp_str_replace_c8(sp_mem_get_scratch(), sp_str_lit("x"), 'x', 'y'), "y"); // first and last positions - SP_EXPECT_STR_EQ_CSTR(sp_str_replace_c8_a(sp_mem_get_scratch(), sp_str_lit("/path/to/file/"), '/', '\\'), "\\path\\to\\file\\"); + SP_EXPECT_STR_EQ_CSTR(sp_str_replace_c8(sp_mem_get_scratch(), sp_str_lit("/path/to/file/"), '/', '\\'), "\\path\\to\\file\\"); } UTEST(str, ends_with) { @@ -297,19 +297,19 @@ UTEST(str, ends_with) { } UTEST(str, concat) { - SP_EXPECT_STR_EQ_CSTR(sp_str_concat_a(sp_mem_get_scratch(), sp_str_lit("Jerry"), sp_str_lit("Garcia")), "JerryGarcia"); - SP_EXPECT_STR_EQ_CSTR(sp_str_concat_a(sp_mem_get_scratch(), sp_str_lit("Jerry"), sp_str_lit("")), "Jerry"); - SP_EXPECT_STR_EQ_CSTR(sp_str_concat_a(sp_mem_get_scratch(), sp_str_lit(""), sp_str_lit("Jerry")), "Jerry"); + SP_EXPECT_STR_EQ_CSTR(sp_str_concat(sp_mem_get_scratch(), sp_str_lit("Jerry"), sp_str_lit("Garcia")), "JerryGarcia"); + SP_EXPECT_STR_EQ_CSTR(sp_str_concat(sp_mem_get_scratch(), sp_str_lit("Jerry"), sp_str_lit("")), "Jerry"); + SP_EXPECT_STR_EQ_CSTR(sp_str_concat(sp_mem_get_scratch(), sp_str_lit(""), sp_str_lit("Jerry")), "Jerry"); } UTEST(str, join_operations) { - SP_EXPECT_STR_EQ_CSTR(sp_str_join_a(sp_mem_get_scratch(), sp_str_lit("hello"), sp_str_lit("world"), sp_str_lit(" - ")), "hello - world"); - SP_EXPECT_STR_EQ_CSTR(sp_str_join_a(sp_mem_get_scratch(), sp_str_lit("hello"), sp_str_lit("world"), sp_str_lit("")), "helloworld"); + SP_EXPECT_STR_EQ_CSTR(sp_str_join(sp_mem_get_scratch(), sp_str_lit("hello"), sp_str_lit("world"), sp_str_lit(" - ")), "hello - world"); + SP_EXPECT_STR_EQ_CSTR(sp_str_join(sp_mem_get_scratch(), sp_str_lit("hello"), sp_str_lit("world"), sp_str_lit("")), "helloworld"); const c8* strings[] = {"apple", "banana", "cherry"}; - SP_EXPECT_STR_EQ_CSTR(sp_str_join_cstr_n_a(sp_mem_get_scratch(), strings, 3, sp_str_lit(", ")), "apple, banana, cherry"); - SP_EXPECT_STR_EQ_CSTR(sp_str_join_cstr_n_a(sp_mem_get_scratch(), strings, 1, sp_str_lit(", ")), "apple"); - ASSERT_EQ(sp_str_join_cstr_n_a(sp_mem_get_scratch(), strings, 0, sp_str_lit(", ")).len, 0); + SP_EXPECT_STR_EQ_CSTR(sp_str_join_cstr_n(sp_mem_get_scratch(), strings, 3, sp_str_lit(", ")), "apple, banana, cherry"); + SP_EXPECT_STR_EQ_CSTR(sp_str_join_cstr_n(sp_mem_get_scratch(), strings, 1, sp_str_lit(", ")), "apple"); + ASSERT_EQ(sp_str_join_cstr_n(sp_mem_get_scratch(), strings, 0, sp_str_lit(", ")).len, 0); } UTEST(str_kernel, map_trim) { @@ -320,7 +320,7 @@ UTEST(str_kernel, map_trim) { sp_str_lit("no_trim"), }; - sp_da(sp_str_t) results = sp_str_map_a(sp_mem_get_scratch(), strings, 4, NULL, sp_str_map_kernel_trim); + sp_da(sp_str_t) results = sp_str_map(sp_mem_get_scratch(), strings, 4, NULL, sp_str_map_kernel_trim); ASSERT_EQ(sp_da_size(results), 4); SP_EXPECT_STR_EQ_CSTR(results[0], "hello"); @@ -337,14 +337,14 @@ UTEST(str_kernel, map_case_transform) { sp_str_lit("MiXeD cAsE"), }; - sp_da(sp_str_t) results = sp_str_map_a(sp_mem_get_scratch(), strings, 4, NULL, sp_str_map_kernel_to_upper); + sp_da(sp_str_t) results = sp_str_map(sp_mem_get_scratch(), strings, 4, NULL, sp_str_map_kernel_to_upper); ASSERT_EQ(sp_da_size(results), 4); SP_EXPECT_STR_EQ_CSTR(results[0], "HELLO WORLD"); SP_EXPECT_STR_EQ_CSTR(results[1], "ALREADY UPPER"); SP_EXPECT_STR_EQ_CSTR(results[2], "ALREADY LOWER"); SP_EXPECT_STR_EQ_CSTR(results[3], "MIXED CASE"); - results = sp_str_map_a(sp_mem_get_scratch(), strings, 4, NULL, sp_str_map_kernel_to_lower); + results = sp_str_map(sp_mem_get_scratch(), strings, 4, NULL, sp_str_map_kernel_to_lower); ASSERT_EQ(sp_da_size(results), 4); SP_EXPECT_STR_EQ_CSTR(results[0], "hello world"); SP_EXPECT_STR_EQ_CSTR(results[1], "already upper"); @@ -358,7 +358,7 @@ UTEST(str_kernel, map_case_transform) { sp_str_lit("123 numbers first"), }; - results = sp_str_map_a(sp_mem_get_scratch(), strings2, 4, NULL, sp_str_map_kernel_pascal_case); + results = sp_str_map(sp_mem_get_scratch(), strings2, 4, NULL, sp_str_map_kernel_pascal_case); ASSERT_EQ(sp_da_size(results), 4); SP_EXPECT_STR_EQ_CSTR(results[0], "Hello World"); SP_EXPECT_STR_EQ_CSTR(results[1], "The Quick Brown Fox"); @@ -436,7 +436,7 @@ UTEST(str, strip) { UTEST(str, split_c8) { { - sp_da(sp_str_t) parts = sp_str_split_c8_a(sp_mem_get_scratch(), sp_str_lit("hello,world,test"), ','); + sp_da(sp_str_t) parts = sp_str_split_c8(sp_mem_get_scratch(), sp_str_lit("hello,world,test"), ','); ASSERT_EQ(sp_da_size(parts), 3); SP_EXPECT_STR_EQ_CSTR(parts[0], "hello"); SP_EXPECT_STR_EQ_CSTR(parts[1], "world"); @@ -444,7 +444,7 @@ UTEST(str, split_c8) { } { - sp_da(sp_str_t) parts = sp_str_split_c8_a(sp_mem_get_scratch(), sp_str_lit("/home/user/file.txt"), '/'); + sp_da(sp_str_t) parts = sp_str_split_c8(sp_mem_get_scratch(), sp_str_lit("/home/user/file.txt"), '/'); ASSERT_EQ(sp_da_size(parts), 4); SP_EXPECT_STR_EQ_CSTR(parts[0], ""); SP_EXPECT_STR_EQ_CSTR(parts[1], "home"); @@ -453,7 +453,7 @@ UTEST(str, split_c8) { } { - sp_da(sp_str_t) parts = sp_str_split_c8_a(sp_mem_get_scratch(), sp_str_lit("a,,b"), ','); + sp_da(sp_str_t) parts = sp_str_split_c8(sp_mem_get_scratch(), sp_str_lit("a,,b"), ','); ASSERT_EQ(sp_da_size(parts), 3); SP_EXPECT_STR_EQ_CSTR(parts[0], "a"); SP_EXPECT_STR_EQ_CSTR(parts[1], ""); @@ -461,7 +461,7 @@ UTEST(str, split_c8) { } { - sp_da(sp_str_t) parts = sp_str_split_c8_a(sp_mem_get_scratch(), sp_str_lit("a,,,b"), ','); + sp_da(sp_str_t) parts = sp_str_split_c8(sp_mem_get_scratch(), sp_str_lit("a,,,b"), ','); ASSERT_EQ(sp_da_size(parts), 4); SP_EXPECT_STR_EQ_CSTR(parts[0], "a"); SP_EXPECT_STR_EQ_CSTR(parts[1], ""); @@ -470,18 +470,18 @@ UTEST(str, split_c8) { } { - sp_da(sp_str_t) parts = sp_str_split_c8_a(sp_mem_get_scratch(), sp_str_lit("hello"), ','); + sp_da(sp_str_t) parts = sp_str_split_c8(sp_mem_get_scratch(), sp_str_lit("hello"), ','); ASSERT_EQ(sp_da_size(parts), 1); SP_EXPECT_STR_EQ_CSTR(parts[0], "hello"); } { - sp_da(sp_str_t) parts = sp_str_split_c8_a(sp_mem_get_scratch(), sp_str_lit(""), ','); + sp_da(sp_str_t) parts = sp_str_split_c8(sp_mem_get_scratch(), sp_str_lit(""), ','); EXPECT_EQ(parts, SP_NULLPTR); } { - sp_da(sp_str_t) parts = sp_str_split_c8_a(sp_mem_get_scratch(), sp_str_lit(",hello,world,"), ','); + sp_da(sp_str_t) parts = sp_str_split_c8(sp_mem_get_scratch(), sp_str_lit(",hello,world,"), ','); ASSERT_EQ(sp_da_size(parts), 4); SP_EXPECT_STR_EQ_CSTR(parts[0], ""); SP_EXPECT_STR_EQ_CSTR(parts[1], "hello"); @@ -490,14 +490,14 @@ UTEST(str, split_c8) { } { - sp_da(sp_str_t) parts = sp_str_split_c8_a(sp_mem_get_scratch(), sp_str_lit(","), ','); + sp_da(sp_str_t) parts = sp_str_split_c8(sp_mem_get_scratch(), sp_str_lit(","), ','); ASSERT_EQ(sp_da_size(parts), 2); SP_EXPECT_STR_EQ(parts[0], sp_str_lit("")); SP_EXPECT_STR_EQ(parts[1], sp_str_lit("")); } { - sp_da(sp_str_t) parts = sp_str_split_c8_a(sp_mem_get_scratch(), sp_str_lit("x"), ','); + sp_da(sp_str_t) parts = sp_str_split_c8(sp_mem_get_scratch(), sp_str_lit("x"), ','); ASSERT_EQ(sp_da_size(parts), 1); SP_EXPECT_STR_EQ_CSTR(parts[0], "x"); } @@ -560,16 +560,16 @@ UTEST(str, cleave_c8) { } UTEST(str, pad) { - SP_EXPECT_STR_EQ(sp_str_pad_a(sp_mem_get_scratch(), sp_str_lit("hello"), 10), sp_str_lit("hello ")); - SP_EXPECT_STR_EQ(sp_str_pad_a(sp_mem_get_scratch(), sp_str_lit("hi"), 5), sp_str_lit("hi ")); + SP_EXPECT_STR_EQ(sp_str_pad(sp_mem_get_scratch(), sp_str_lit("hello"), 10), sp_str_lit("hello ")); + SP_EXPECT_STR_EQ(sp_str_pad(sp_mem_get_scratch(), sp_str_lit("hi"), 5), sp_str_lit("hi ")); - SP_EXPECT_STR_EQ(sp_str_pad_a(sp_mem_get_scratch(), sp_str_lit("hello world"), 5), sp_str_lit("hello world")); + SP_EXPECT_STR_EQ(sp_str_pad(sp_mem_get_scratch(), sp_str_lit("hello world"), 5), sp_str_lit("hello world")); - SP_EXPECT_STR_EQ(sp_str_pad_a(sp_mem_get_scratch(), sp_str_lit("hello"), 5), sp_str_lit("hello")); + SP_EXPECT_STR_EQ(sp_str_pad(sp_mem_get_scratch(), sp_str_lit("hello"), 5), sp_str_lit("hello")); - SP_EXPECT_STR_EQ(sp_str_pad_a(sp_mem_get_scratch(), sp_str_lit(""), 5), sp_str_lit(" ")); + SP_EXPECT_STR_EQ(sp_str_pad(sp_mem_get_scratch(), sp_str_lit(""), 5), sp_str_lit(" ")); - SP_EXPECT_STR_EQ(sp_str_pad_a(sp_mem_get_scratch(), sp_str_lit("hello"), 0), sp_str_lit("hello")); + SP_EXPECT_STR_EQ(sp_str_pad(sp_mem_get_scratch(), sp_str_lit("hello"), 0), sp_str_lit("hello")); } UTEST(str, pad_to_longest) { @@ -579,7 +579,7 @@ UTEST(str, pad_to_longest) { sp_str_lit("hello"), sp_str_lit("world!") }; - sp_da(sp_str_t) padded = sp_str_pad_to_longest_a(sp_mem_get_scratch(), strings, 3); + sp_da(sp_str_t) padded = sp_str_pad_to_longest(sp_mem_get_scratch(), strings, 3); ASSERT_EQ(sp_da_size(padded), 3); SP_EXPECT_STR_EQ(padded[0], sp_str_lit("hi ")); SP_EXPECT_STR_EQ(padded[1], sp_str_lit("hello ")); @@ -592,7 +592,7 @@ UTEST(str, pad_to_longest) { sp_str_lit("bbb"), sp_str_lit("ccc") }; - sp_da(sp_str_t) padded = sp_str_pad_to_longest_a(sp_mem_get_scratch(), strings, 3); + sp_da(sp_str_t) padded = sp_str_pad_to_longest(sp_mem_get_scratch(), strings, 3); ASSERT_EQ(sp_da_size(padded), 3); SP_EXPECT_STR_EQ(padded[0], sp_str_lit("aaa")); SP_EXPECT_STR_EQ(padded[1], sp_str_lit("bbb")); @@ -603,7 +603,7 @@ UTEST(str, pad_to_longest) { sp_str_t strings[] = { sp_str_lit("hello") }; - sp_da(sp_str_t) padded = sp_str_pad_to_longest_a(sp_mem_get_scratch(), strings, 1); + sp_da(sp_str_t) padded = sp_str_pad_to_longest(sp_mem_get_scratch(), strings, 1); ASSERT_EQ(sp_da_size(padded), 1); SP_EXPECT_STR_EQ(padded[0], sp_str_lit("hello")); } @@ -614,7 +614,7 @@ UTEST(str, pad_to_longest) { sp_str_lit("hello"), sp_str_lit("") }; - sp_da(sp_str_t) padded = sp_str_pad_to_longest_a(sp_mem_get_scratch(), strings, 3); + sp_da(sp_str_t) padded = sp_str_pad_to_longest(sp_mem_get_scratch(), strings, 3); ASSERT_EQ(sp_da_size(padded), 3); SP_EXPECT_STR_EQ(padded[0], sp_str_lit(" ")); SP_EXPECT_STR_EQ(padded[1], sp_str_lit("hello")); @@ -752,33 +752,33 @@ UTEST(str, view_creation) { UTEST(sp_str_from_cstr, string_from_cstr) { { const c8* cstr = "hello world"; - sp_str_t str = sp_str_from_cstr_a(sp_mem_get_scratch(), cstr); + sp_str_t str = sp_str_from_cstr(sp_mem_get_scratch(), cstr); ASSERT_EQ(str.len, 11); SP_EXPECT_STR_EQ(str, sp_str_view(cstr)); ASSERT_NE(str.data, cstr); } { - sp_str_t str = sp_str_from_cstr_a(sp_mem_get_scratch(), ""); + sp_str_t str = sp_str_from_cstr(sp_mem_get_scratch(), ""); ASSERT_EQ(str.len, 0); SP_EXPECT_STR_EQ(str, sp_str_lit("")); } { - sp_str_t str = sp_str_from_cstr_a(sp_mem_get_scratch(), SP_NULLPTR); + sp_str_t str = sp_str_from_cstr(sp_mem_get_scratch(), SP_NULLPTR); ASSERT_EQ(str.len, 0); ASSERT_EQ(str.data, SP_NULLPTR); } { c8 buffer[] = "mutable"; - sp_str_t str = sp_str_from_cstr_a(sp_mem_get_scratch(), buffer); + sp_str_t str = sp_str_from_cstr(sp_mem_get_scratch(), buffer); buffer[0] = 'M'; ASSERT_EQ(str.data[0], 'm'); } { - sp_str_t str = sp_str_from_cstr_n_a(sp_mem_get_scratch(), "hello world", 5); + sp_str_t str = sp_str_from_cstr_n(sp_mem_get_scratch(), "hello world", 5); ASSERT_EQ(str.len, 5); SP_EXPECT_STR_EQ(str, sp_str_lit("hello")); } @@ -786,25 +786,25 @@ UTEST(sp_str_from_cstr, string_from_cstr) { UTEST(str, truncate_longer_than_limit) { sp_str_t str = sp_str_lit("hello world"); - sp_str_t result = sp_str_truncate_a(sp_mem_get_scratch(), str, 8, sp_str_lit("...")); + sp_str_t result = sp_str_truncate(sp_mem_get_scratch(), str, 8, sp_str_lit("...")); SP_EXPECT_STR_EQ_CSTR(result, "hello..."); } UTEST(str, truncate_shorter_than_limit) { sp_str_t str = sp_str_lit("hi"); - sp_str_t result = sp_str_truncate_a(sp_mem_get_scratch(), str, 10, sp_str_lit("...")); + sp_str_t result = sp_str_truncate(sp_mem_get_scratch(), str, 10, sp_str_lit("...")); SP_EXPECT_STR_EQ_CSTR(result, "hi"); } UTEST(str, truncate_exact_limit) { sp_str_t str = sp_str_lit("exactly"); - sp_str_t result = sp_str_truncate_a(sp_mem_get_scratch(), str, 7, sp_str_lit("...")); + sp_str_t result = sp_str_truncate(sp_mem_get_scratch(), str, 7, sp_str_lit("...")); SP_EXPECT_STR_EQ_CSTR(result, "exactly"); } UTEST(str, truncate_zero_limit) { sp_str_t str = sp_str_lit("test"); - sp_str_t result = sp_str_truncate_a(sp_mem_get_scratch(), str, 0, sp_str_lit("...")); + sp_str_t result = sp_str_truncate(sp_mem_get_scratch(), str, 0, sp_str_lit("...")); SP_EXPECT_STR_EQ_CSTR(result, "test"); } @@ -1088,7 +1088,7 @@ UTEST(utf8, num_codepoints) { UTEST(utf8, builder_append) { sp_io_dyn_mem_writer_t builder = sp_zero; - sp_io_dyn_mem_writer_init_a(sp_mem_get_scratch(), &builder); + sp_io_dyn_mem_writer_init(sp_mem_get_scratch(), &builder); u32 codepoints[] = { 'a', 0xA2, 0x20AC, 0x1F600, 'z' }; for (u32 i = 0; i < 5; i++) { diff --git a/test/time.c b/test/time.c index 57a69ed..f11e0cb 100644 --- a/test/time.c +++ b/test/time.c @@ -44,7 +44,7 @@ UTEST(tm, epoch_to_iso) { SKIP_ON_FREESTANDING(); sp_tm_epoch_t epoch = sp_tm_now_epoch(); sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_str_t iso = sp_tm_epoch_to_iso8601_a(s.mem, epoch); + sp_str_t iso = sp_tm_epoch_to_iso8601(s.mem, epoch); ASSERT_GE(iso.len, 20); EXPECT_EQ(iso.data[4], '-'); EXPECT_EQ(iso.data[7], '-'); @@ -251,7 +251,7 @@ UTEST(tm, iso8601_known_values) { sp_mem_arena_marker_t s = sp_mem_begin_scratch(); SP_CARR_FOR(cases, i) { - sp_str_t result = sp_tm_epoch_to_iso8601_a(s.mem, cases[i].epoch); + sp_str_t result = sp_tm_epoch_to_iso8601(s.mem, cases[i].epoch); SP_EXPECT_STR_EQ_CSTR(result, cases[i].expected); } sp_mem_end_scratch(s); @@ -267,7 +267,7 @@ UTEST(tm, iso8601_millisecond_padding) { sp_mem_arena_marker_t s = sp_mem_begin_scratch(); SP_CARR_FOR(cases, i) { - sp_str_t result = sp_tm_epoch_to_iso8601_a(s.mem, cases[i].epoch); + sp_str_t result = sp_tm_epoch_to_iso8601(s.mem, cases[i].epoch); SP_EXPECT_STR_EQ_CSTR(result, cases[i].expected); } sp_mem_end_scratch(s); diff --git a/test/tools/features/main.c b/test/tools/features/main.c index bd0cb51..3b6328a 100644 --- a/test/tools/features/main.c +++ b/test/tools/features/main.c @@ -16,7 +16,7 @@ s32 fn(void* user_data) { #define NUM_THREADS 4 int main(void) { - sp_print_a("running {} threads...", sp_fmt_uint(NUM_THREADS)); + sp_print("running {} threads...", sp_fmt_uint(NUM_THREADS)); test_t threads [NUM_THREADS] = sp_zero; @@ -31,7 +31,7 @@ int main(void) { sp_thread_join(&threads[it].thread); } - sp_log_a("{.green}", sp_fmt_cstr("ok")); + sp_log("{.green}", sp_fmt_cstr("ok")); return 0; } diff --git a/test/tools/linkage/format-bare-args.c b/test/tools/linkage/format-bare-args.c index f82a63c..724f460 100644 --- a/test/tools/linkage/format-bare-args.c +++ b/test/tools/linkage/format-bare-args.c @@ -3,8 +3,8 @@ s32 main() { sp_str_t world = sp_str_lit("world"); - sp_str_t message = sp_fmt_a(sp_mem_os_new(), "hello, {}!", world).value; - sp_log_str_a(message); + sp_str_t message = sp_fmt(sp_mem_os_new(), "hello, {}!", world).value; + sp_log_str(message); SP_EXIT_SUCCESS(); } diff --git a/test/tools/process/process.c b/test/tools/process/process.c index ed0b63b..17a03e3 100644 --- a/test/tools/process/process.c +++ b/test/tools/process/process.c @@ -85,7 +85,7 @@ s32 main(s32 num_args, const c8** args) { test_proc_function_t function = test_proc_function_from_str(sp_str_view(function_str)); if (function == TEST_PROC_FUNCTION_INVALID) { - sp_log_a("unknown function: {.fg brightred}", sp_fmt_cstr(function_str)); + sp_log("unknown function: {.fg brightred}", sp_fmt_cstr(function_str)); SP_EXIT_FAILURE(); } @@ -107,7 +107,7 @@ s32 main(s32 num_args, const c8** args) { while (true) { u32 len = read_line(line, sizeof(line)); if (len == 0) break; - sp_str_t prefixed = sp_fmt_a(mem, "echo: {}\n", sp_fmt_str(sp_str(line, len))).value; + sp_str_t prefixed = sp_fmt(mem, "echo: {}\n", sp_fmt_str(sp_str(line, len))).value; write_str(out_on, err_on, prefixed); } break; @@ -125,8 +125,8 @@ s32 main(s32 num_args, const c8** args) { sp_str_t key = sp_str(line, len); sp_str_t value = sp_os_env_get(key); sp_str_t out = !sp_str_empty(value) - ? sp_fmt_a(mem, "{} {}\n", sp_fmt_str(key), sp_fmt_str(value)).value - : sp_fmt_a(mem, "{}\n", sp_fmt_str(key)).value; + ? sp_fmt(mem, "{} {}\n", sp_fmt_str(key), sp_fmt_str(value)).value + : sp_fmt(mem, "{}\n", sp_fmt_str(key)).value; write_str(out_on, err_on, out); } break; @@ -157,16 +157,16 @@ s32 main(s32 num_args, const c8** args) { if (n <= 0) break; total_read += (u64)n; } - sp_str_t line = sp_fmt_a(mem, "{}\n", sp_fmt_uint(total_read)).value; + sp_str_t line = sp_fmt(mem, "{}\n", sp_fmt_uint(total_read)).value; write_str(out_on, err_on, line); break; } case TEST_PROC_FUNCTION_WAIT: { sp_str_t arg = sp_str_view(args[0]); f64 ms = sp_parse_f64(arg); - sp_log_a("process.c ({.fg brightyellow}) is sleeping for {.fg cyan}ms", sp_fmt_int(sp_getpid()), sp_fmt_float(ms)); + sp_log("process.c ({.fg brightyellow}) is sleeping for {.fg cyan}ms", sp_fmt_int(sp_getpid()), sp_fmt_float(ms)); sp_os_sleep_ms(ms); - sp_log_a("process.c ({.fg brightyellow}) is done", sp_fmt_int(sp_getpid())); + sp_log("process.c ({.fg brightyellow}) is done", sp_fmt_int(sp_getpid())); return sp_test_ps_wait_exit_code; } case TEST_PROC_FUNCTION_EXIT_CODE: { @@ -174,7 +174,7 @@ s32 main(s32 num_args, const c8** args) { } case TEST_PROC_FUNCTION_FLOOD: { const u32 flood_size = 512 * 1024; - u8* buffer = sp_alloc_n_a(mem, u8, flood_size); + u8* buffer = sp_alloc_n(mem, u8, flood_size); for (u32 i = 0; i < flood_size; i++) { buffer[i] = (u8)('A' + (i % 26)); } @@ -182,7 +182,7 @@ s32 main(s32 num_args, const c8** args) { break; } case TEST_PROC_FUNCTION_PATTERN: { - u8* buffer = sp_alloc_n_a(mem, u8, pattern_size); + u8* buffer = sp_alloc_n(mem, u8, pattern_size); for (s32 i = 0; i < pattern_size; i++) { buffer[i] = (u8)pattern_char[0]; } @@ -192,7 +192,7 @@ s32 main(s32 num_args, const c8** args) { break; } default: { - sp_log_err_a("Unknown function: {}", sp_fmt_cstr(function_str)); + sp_log_err("Unknown function: {}", sp_fmt_cstr(function_str)); return 1; } } diff --git a/test/tools/smoke.c b/test/tools/smoke.c index 4eee0cf..82da79c 100644 --- a/test/tools/smoke.c +++ b/test/tools/smoke.c @@ -4,5 +4,5 @@ #include "sp.h" s32 main(s32 num_args, const c8** args) { - sp_log_a("hello, {.fg brightcyan}!", sp_fmt_cstr("world")); + sp_log("hello, {.fg brightcyan}!", sp_fmt_cstr("world")); } diff --git a/test/tools/stress.c b/test/tools/stress.c index f6631ff..80d5bb4 100644 --- a/test/tools/stress.c +++ b/test/tools/stress.c @@ -31,8 +31,8 @@ UTEST(stress, dyn_array) { } UTEST(stress, hash_table) { - sp_ht_a(u64, u64) ht = SP_NULLPTR; - sp_ht_init_a(sp_mem_os_new(), ht); + sp_ht(u64, u64) ht = SP_NULLPTR; + sp_ht_init(sp_mem_os_new(), ht); const s32 count = 10000; @@ -266,12 +266,12 @@ UTEST(stress, sp_context) { case CONTEXT_STRESS_FRAGMENTATION: { sp_mem_arena_marker_t scratch = sp_mem_begin_scratch(); s32 num_allocs = 10 + (rand() % 200); - u8** ptrs = sp_alloc_n_a(scratch.mem, u8*, num_allocs); - u32* sizes = sp_alloc_n_a(scratch.mem, u32, num_allocs); + u8** ptrs = sp_alloc_n(scratch.mem, u8*, num_allocs); + u32* sizes = sp_alloc_n(scratch.mem, u32, num_allocs); sp_for(it, num_allocs) { sizes[it] = 1 + (rand() % 1024); - ptrs[it] = sp_alloc_a(scratch.mem, sizes[it]); + ptrs[it] = sp_alloc(scratch.mem, sizes[it]); u8 pattern = (u8)((it + op) & 0xFF); sp_mem_fill_u8(ptrs[it], sizes[it], pattern); } @@ -290,15 +290,15 @@ UTEST(stress, sp_context) { } case CONTEXT_STRESS_NESTING: { sp_mem_arena_marker_t s1 = sp_mem_begin_scratch(); - u64* outer = sp_alloc_a(s1.mem, sizeof(u64)); + u64* outer = sp_alloc(s1.mem, sizeof(u64)); *outer = 0xCAFEBABE; { sp_mem_arena_marker_t s2 = sp_mem_begin_scratch(); - u64* inner = sp_alloc_a(s2.mem, sizeof(u64)); + u64* inner = sp_alloc(s2.mem, sizeof(u64)); *inner = 0xDEADBEEF; { sp_mem_arena_marker_t s3 = sp_mem_begin_scratch(); - u8* big = sp_alloc_a(s3.mem, 5000); + u8* big = sp_alloc(s3.mem, 5000); sp_mem_fill_u8(big, 5000, 0xAA); ASSERT_EQ(*outer, 0xCAFEBABE); @@ -317,7 +317,7 @@ UTEST(stress, sp_context) { } case CONTEXT_STRESS_REALLOC: { sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - u8* ptr = sp_alloc_a(s.mem, 16); + u8* ptr = sp_alloc(s.mem, 16); sp_mem_fill_u8(ptr, 16, 0x11); ptr = sp_mem_allocator_realloc(s.mem, ptr, 32); @@ -337,10 +337,10 @@ UTEST(stress, sp_context) { sp_mem_arena_marker_t s = sp_mem_begin_scratch(); sp_for(it, 50) { - u8* b = sp_alloc_type_a(s.mem, u8); + u8* b = sp_alloc_type(s.mem, u8); *b = 0xFF; - u64* aligned = sp_alloc_type_a(s.mem, u64); + u64* aligned = sp_alloc_type(s.mem, u64); uintptr_t address = (uintptr_t)aligned; EXPECT_EQ(address % SP_MEM_ALIGNMENT, 0); *aligned = (u64)it; @@ -389,12 +389,12 @@ void fmon_stress_poll(sp_fmon_t* monitor) { } void fmon_stress_create_dir_tree(sp_str_t base, s32 depth, sp_da(sp_str_t)* dirs) { - sp_da_push(*dirs, sp_str_copy_a(sp_mem_get_scratch(), base)); + sp_da_push(*dirs, sp_str_copy(sp_mem_get_scratch(), base)); if (depth <= 0) return; for (s32 i = 0; i < FMON_STRESS_DIRS_PER_LEVEL; i++) { - sp_str_t name = sp_fmt_a(sp_mem_get_scratch(), "d{}", sp_fmt_int(i)).value; - sp_str_t child = sp_fs_join_path_a(sp_mem_get_scratch(), base, name); + sp_str_t name = sp_fmt(sp_mem_get_scratch(), "d{}", sp_fmt_int(i)).value; + sp_str_t child = sp_fs_join_path(sp_mem_get_scratch(), base, name); sp_fs_create_dir(child); fmon_stress_create_dir_tree(child, depth - 1, dirs); } @@ -406,7 +406,7 @@ UTEST(stress, fmon) { sp_test_file_manager_init(&file_manager); fmon_stress_counters_t counters = {0}; - sp_fmon_t* monitor = sp_alloc_type_a(sp_mem_os_new(), sp_fmon_t); + sp_fmon_t* monitor = sp_alloc_type(sp_mem_os_new(), sp_fmon_t); sp_fmon_init(monitor, fmon_stress_callback, SP_FILE_CHANGE_EVENT_ADDED | SP_FILE_CHANGE_EVENT_MODIFIED | SP_FILE_CHANGE_EVENT_REMOVED, &counters); @@ -440,8 +440,8 @@ UTEST(stress, fmon) { while (files_created < batch_end) { sp_str_t dir = dirs[dir_idx % num_dirs]; - sp_str_t name = sp_fmt_a(sp_mem_get_scratch(), "f{}.txt", sp_fmt_uint(files_created)).value; - sp_str_t path = sp_fs_join_path_a(sp_mem_get_scratch(), dir, name); + sp_str_t name = sp_fmt(sp_mem_get_scratch(), "f{}.txt", sp_fmt_uint(files_created)).value; + sp_str_t path = sp_fs_join_path(sp_mem_get_scratch(), dir, name); sp_io_writer_t writer = sp_zero; sp_io_file_writer_from_path(&writer, path, SP_IO_WRITE_MODE_OVERWRITE); @@ -508,7 +508,7 @@ UTEST(stress, fmon) { // Cleanup sp_fmon_deinit(monitor); - sp_free_a(sp_mem_os_new(), monitor); + sp_free(sp_mem_os_new(), monitor); sp_da_free(files); sp_da_free(dirs); sp_test_file_manager_cleanup(&file_manager); diff --git a/test/tools/test.h b/test/tools/test.h index eb28113..b44d5ad 100644 --- a/test/tools/test.h +++ b/test/tools/test.h @@ -41,7 +41,7 @@ #define SP_TEST_REPORT(fmt, ...) \ do { \ - sp_str_t formatted = sp_fmt_a(utest_state.mem, fmt, ##__VA_ARGS__).value; \ + sp_str_t formatted = sp_fmt(utest_state.mem, fmt, ##__VA_ARGS__).value; \ UTEST_PRINTF("{}", sp_fmt_str(formatted)); \ } while (0) @@ -55,7 +55,7 @@ if (!sp_str_equal((a), (b))) { \ const c8* __file = __FILE__; \ const u32 __line = __LINE__; \ - sp_str_t __msg = sp_fmt_a( \ + sp_str_t __msg = sp_fmt( \ utest_state.mem, \ "{}:{} Failure:\n {.quote} != {.quote}", \ sp_fmt_cstr(__file), sp_fmt_uint(__line), \ @@ -152,16 +152,16 @@ bool sp_mem_tracking_ok(sp_mem_tracking_t* mem); static sp_str_t sp_test_file_manager_top_level = sp_zero; static sp_str_t sp_test_file_manager_get_repo_root(sp_mem_t a) { - sp_str_t path = sp_fs_get_exe_path_a(a); - if (sp_fs_exists_a(path) && sp_fs_is_file_a(path)) { + sp_str_t path = sp_fs_get_exe_path(a); + if (sp_fs_exists(path) && sp_fs_is_file(path)) { path = sp_fs_parent_path(path); } while (!sp_str_empty(path)) { if (sp_str_equal(sp_fs_get_name(path), sp_str_lit("sp"))) { - sp_str_t marker = sp_fs_join_path_a(a, path, sp_str_lit("sp.h")); - if (sp_fs_exists_a(marker)) { - return sp_fs_canonicalize_path_a(a, path); + sp_str_t marker = sp_fs_join_path(a, path, sp_str_lit("sp.h")); + if (sp_fs_exists(marker)) { + return sp_fs_canonicalize_path(a, path); } } @@ -178,61 +178,61 @@ static sp_str_t sp_test_file_manager_get_repo_root(sp_mem_t a) { } SP_ASSERT(false); - return sp_fs_canonicalize_path_a(a, sp_fs_get_cwd_a(a)); + return sp_fs_canonicalize_path(a, sp_fs_get_cwd(a)); } static sp_str_t sp_test_file_manager_get_top_level(sp_mem_t a, sp_str_t repo_root) { if (!sp_str_empty(sp_test_file_manager_top_level)) { - if (!sp_fs_exists_a(sp_test_file_manager_top_level)) { - sp_fs_create_dir_a(sp_test_file_manager_top_level); + if (!sp_fs_exists(sp_test_file_manager_top_level)) { + sp_fs_create_dir(sp_test_file_manager_top_level); } return sp_test_file_manager_top_level; } - sp_str_t tmp = sp_fs_join_path_a(a, repo_root, sp_str_lit(".tmp")); - if (!sp_fs_exists_a(tmp)) { - sp_fs_create_dir_a(tmp); + sp_str_t tmp = sp_fs_join_path(a, repo_root, sp_str_lit(".tmp")); + if (!sp_fs_exists(tmp)) { + sp_fs_create_dir(tmp); } sp_tm_epoch_t now = sp_tm_now_epoch(); - sp_str_t iso = sp_tm_epoch_to_iso8601_a(a, now); - sp_str_t sanitized = sp_str_replace_c8_a(a, iso, ':', '-'); - sp_str_t root = sp_fs_join_path_a(a, tmp, sanitized); + sp_str_t iso = sp_tm_epoch_to_iso8601(a, now); + sp_str_t sanitized = sp_str_replace_c8(a, iso, ':', '-'); + sp_str_t root = sp_fs_join_path(a, tmp, sanitized); - if (!sp_fs_exists_a(root)) { - sp_fs_create_dir_a(root); + if (!sp_fs_exists(root)) { + sp_fs_create_dir(root); } // Cache in a long-lived allocator (os) so the result outlives `a`, which is // typically a per-test arena that gets destroyed in cleanup. - sp_test_file_manager_top_level = sp_fs_canonicalize_path_a(sp_mem_os_new(), root); + sp_test_file_manager_top_level = sp_fs_canonicalize_path(sp_mem_os_new(), root); return sp_test_file_manager_top_level; } void sp_test_file_manager_init(sp_test_file_manager_t* fs) { fs->arena = sp_mem_arena_new(sp_mem_os_new()); fs->mem = sp_mem_arena_as_allocator(fs->arena); - fs->paths.bin = sp_fs_get_exe_path_a(fs->mem); + fs->paths.bin = sp_fs_get_exe_path(fs->mem); fs->paths.root = sp_test_file_manager_get_repo_root(fs->mem); fs->paths.build = fs->paths.root; fs->paths.test = sp_test_file_manager_get_top_level(fs->mem, fs->paths.root); - if (!sp_fs_exists_a(fs->paths.test)) { - sp_fs_create_dir_a(fs->paths.test); + if (!sp_fs_exists(fs->paths.test)) { + sp_fs_create_dir(fs->paths.test); } } sp_str_t sp_test_file_path(sp_test_file_manager_t* manager, sp_str_t name) { - return sp_fs_join_path_a(manager->mem, manager->paths.test, name); + return sp_fs_join_path(manager->mem, manager->paths.test, name); } void sp_test_file_create_ex(sp_test_file_config_t config) { sp_str_t parent = sp_fs_parent_path(config.path); - if (!sp_str_empty(parent) && !sp_str_equal(parent, config.path) && !sp_fs_exists_a(parent)) { - sp_fs_create_dir_a(parent); + if (!sp_str_empty(parent) && !sp_str_equal(parent, config.path) && !sp_fs_exists(parent)) { + sp_fs_create_dir(parent); } - sp_fs_remove_file_a(config.path); + sp_fs_remove_file(config.path); sp_io_file_writer_t stream = sp_zero; sp_io_file_writer_from_path(&stream, config.path, SP_IO_WRITE_MODE_OVERWRITE); @@ -267,8 +267,8 @@ void sp_test_file_manager_cleanup(sp_test_file_manager_t* manager) { return; } - if (sp_fs_exists_a(manager->paths.test)) { - sp_fs_remove_dir_a(manager->paths.test); + if (sp_fs_exists(manager->paths.test)) { + sp_fs_remove_dir(manager->paths.test); } if (manager->arena) { @@ -313,7 +313,7 @@ static void sp_mem_tracking_unlink(sp_mem_tracking_node_t** list, sp_mem_trackin static void* sp_mem_tracking_do_alloc(sp_mem_tracking_t* t, u64 size) { if (!size) return SP_NULLPTR; - void* raw = sp_alloc_a(t->backing, size + sizeof(sp_mem_tracking_node_t)); + void* raw = sp_alloc(t->backing, size + sizeof(sp_mem_tracking_node_t)); if (!raw) return SP_NULLPTR; sp_mem_tracking_node_t* node = (sp_mem_tracking_node_t*)raw; @@ -403,13 +403,13 @@ sp_mem_t sp_mem_tracking_as_allocator(sp_mem_tracking_t* t) { } void sp_mem_tracking_dump(sp_mem_tracking_t* t) { - sp_log_a("tracking: live={} bytes={} double_frees={} wild_frees={}", + sp_log("tracking: live={} bytes={} double_frees={} wild_frees={}", sp_fmt_uint(t->live_count), sp_fmt_uint(t->live_bytes), sp_fmt_uint(t->double_frees), sp_fmt_uint(t->wild_frees)); for (sp_mem_tracking_node_t* n = t->live; n; n = n->next) { - sp_log_a(" #{} size={}", sp_fmt_uint(n->id), sp_fmt_uint(n->size)); + sp_log(" #{} size={}", sp_fmt_uint(n->id), sp_fmt_uint(n->size)); } } @@ -417,13 +417,13 @@ void sp_mem_tracking_deinit(sp_mem_tracking_t* t) { sp_mem_tracking_node_t* n = t->live; while (n) { sp_mem_tracking_node_t* next = n->next; - sp_free_a(t->backing, n); + sp_free(t->backing, n); n = next; } n = t->freed; while (n) { sp_mem_tracking_node_t* next = n->next; - sp_free_a(t->backing, n); + sp_free(t->backing, n); n = next; } sp_mem_zero(t, sizeof(*t)); diff --git a/test/tools/unaligned.c b/test/tools/unaligned.c index 45206eb..a956bb2 100644 --- a/test/tools/unaligned.c +++ b/test/tools/unaligned.c @@ -7,12 +7,12 @@ s32 main(s32 num_args, const c8** args) { // u8 buf [16] = {0}; // u32* p = (u32*)(buf + 1); // volatile u32 v = *p; - // sp_log_a("loaded {} from misaligned address {}", sp_fmt_uint(v), sp_fmt_ptr(p)); + // sp_log("loaded {} from misaligned address {}", sp_fmt_uint(v), sp_fmt_ptr(p)); sp_da(u64) arr = sp_da_new(sp_mem_os_new(), u64); sp_da_push(arr, 69); sp_da_for(arr, it) { - sp_log_a("arr[{.gray}] -> {}", sp_fmt_uint(it), sp_fmt_uint(arr[it])); + sp_log("arr[{.gray}] -> {}", sp_fmt_uint(it), sp_fmt_uint(arr[it])); } return 0; } diff --git a/tools/sp.c b/tools/sp.c index f017d83..cdefd8f 100644 --- a/tools/sp.c +++ b/tools/sp.c @@ -7,24 +7,24 @@ s32 main(s32 num_args, const c8** args) { sp_mem_t mem = sp_mem_os_new(); - sp_str_t exe = sp_fs_get_name(sp_fs_get_exe_path_a(mem)); - sp_str_t usage = sp_fmt_a(mem, "usage: {} {.cyan} {.yellow}", sp_fmt_str(exe), sp_fmt_cstr("$triple"), sp_fmt_cstr("glob")).value; + sp_str_t exe = sp_fs_get_name(sp_fs_get_exe_path(mem)); + sp_str_t usage = sp_fmt(mem, "usage: {} {.cyan} {.yellow}", sp_fmt_str(exe), sp_fmt_cstr("$triple"), sp_fmt_cstr("glob")).value; switch (num_args) { case 3: break; - default: sp_log_str_a(usage); return 1; + default: sp_log_str(usage); return 1; } sp_str_t triple = sp_str_view(args[1]); sp_str_t pattern = sp_str_view(args[2]); - sp_str_t cwd = sp_fs_get_cwd_a(mem); - sp_str_t build = sp_fs_join_path_a(mem, cwd, str("build")); - build = sp_fs_join_path_a(mem, build, triple); - build = sp_fs_join_path_a(mem, build, str("test")); - if (!sp_fs_exists_a(build)) { + sp_str_t cwd = sp_fs_get_cwd(mem); + sp_str_t build = sp_fs_join_path(mem, cwd, str("build")); + build = sp_fs_join_path(mem, build, triple); + build = sp_fs_join_path(mem, build, str("test")); + if (!sp_fs_exists(build)) { sp_fatal("error: {.cyan} doesn't exist", sp_fmt_str(build)); } - sp_da(sp_str_t) parts = sp_str_split_c8_a(mem, triple, '-'); + sp_da(sp_str_t) parts = sp_str_split_c8(mem, triple, '-'); sp_os_kind_t os = sp_zero; if (sp_str_equal(parts[1], str("linux"))) os = SP_OS_LINUX; else if (sp_str_equal(parts[1], str("windows"))) os = SP_OS_WIN32; @@ -37,7 +37,7 @@ s32 main(s32 num_args, const c8** args) { sp_glob_set_t* glob = sp_glob_set_new(mem); sp_glob_set_add_str(glob, pattern); if (sp_str_at(pattern, -1) != '*') { - sp_glob_set_add_str(glob, sp_fmt_a(mem, "{}*", sp_fmt_str(pattern)).value); + sp_glob_set_add_str(glob, sp_fmt(mem, "{}*", sp_fmt_str(pattern)).value); } sp_glob_set_build(glob); @@ -96,8 +96,8 @@ s32 main(s32 num_args, const c8** args) { if (!match) continue; test_t test = { - .name = sp_str_copy_a(mem, it.entry.name), - .path = sp_str_copy_a(mem, it.entry.path) + .name = sp_str_copy(mem, it.entry.name), + .path = sp_str_copy(mem, it.entry.path) }; sp_da_push(tests, test); } @@ -123,9 +123,9 @@ s32 main(s32 num_args, const c8** args) { break; } case SP_OS_WIN32: { - sp_str_t inner = sp_fmt_a(mem, "cd C:/Users/spader/source/sp; ./build/{}/test/{}", sp_fmt_str(triple), sp_fmt_str(test.name)).value; - sp_str_t powershell = sp_fmt_a(mem, "powershell -Command {.quote}", sp_fmt_str(inner)).value; - cmd.display = sp_fmt_a(mem, "ssh -q spader@piotr {}", sp_fmt_str(powershell)).value; + sp_str_t inner = sp_fmt(mem, "cd C:/Users/spader/source/sp; ./build/{}/test/{}", sp_fmt_str(triple), sp_fmt_str(test.name)).value; + sp_str_t powershell = sp_fmt(mem, "powershell -Command {.quote}", sp_fmt_str(inner)).value; + cmd.display = sp_fmt(mem, "ssh -q spader@piotr {}", sp_fmt_str(powershell)).value; cmd.config = (sp_ps_config_t) { .command = str("ssh"), .args = { str("-q"), str("spader@piotr"), powershell }, @@ -134,8 +134,8 @@ s32 main(s32 num_args, const c8** args) { break; } case SP_OS_MACOS: { - sp_str_t inner = sp_fmt_a(mem, "cd ~/source/sp && ./build/{}/test/{}", sp_fmt_str(triple), sp_fmt_str(test.name)).value; - cmd.display = sp_fmt_a(mem, "ssh -q spader@miles {.quote}", sp_fmt_str(inner)).value; + sp_str_t inner = sp_fmt(mem, "cd ~/source/sp && ./build/{}/test/{}", sp_fmt_str(triple), sp_fmt_str(test.name)).value; + cmd.display = sp_fmt(mem, "ssh -q spader@miles {.quote}", sp_fmt_str(inner)).value; cmd.config = (sp_ps_config_t) { .command = str("ssh"), .args = { str("-q"), str("spader@miles"), inner }, @@ -152,34 +152,34 @@ s32 main(s32 num_args, const c8** args) { if (tests[it].name.len > width) width = tests[it].name.len; } - sp_log_a("{:<$ .gray} {.gray}", sp_fmt_uint(width), sp_fmt_cstr("name"), sp_fmt_cstr("command")); + sp_log("{:<$ .gray} {.gray}", sp_fmt_uint(width), sp_fmt_cstr("name"), sp_fmt_cstr("command")); sp_da_for(commands, it) { - sp_log_a("{:>$ .cyan} {}", sp_fmt_uint(width), sp_fmt_str(tests[it].name), sp_fmt_str(commands[it].display)); + sp_log("{:>$ .cyan} {}", sp_fmt_uint(width), sp_fmt_str(tests[it].name), sp_fmt_str(commands[it].display)); } - sp_log_a(""); + sp_log(""); sp_da(sp_ps_output_t) results = sp_da_new(mem, sp_ps_output_t); s32 status = 0; sp_da_for(commands, it) { - sp_print_a("{:>$}...", sp_fmt_uint(width), sp_fmt_str(tests[it].name)); - sp_ps_output_t result = sp_ps_run_a(mem, commands[it].config); + sp_print("{:>$}...", sp_fmt_uint(width), sp_fmt_str(tests[it].name)); + sp_ps_output_t result = sp_ps_run(mem, commands[it].config); sp_da_push(results, result); if (result.status.exit_code) { status = 1; - sp_log_a("{.fg red}", sp_fmt_cstr("fail")); + sp_log("{.fg red}", sp_fmt_cstr("fail")); } else { - sp_log_a("{.fg green}", sp_fmt_cstr("ok")); + sp_log("{.fg green}", sp_fmt_cstr("ok")); } } sp_da_for(results, it) { if (!results[it].status.exit_code) continue; - sp_log_a(""); - sp_log_a("{.fg red}: {}", sp_fmt_cstr("FAIL"), sp_fmt_str(tests[it].name)); - sp_log_str_a(commands[it].display); - sp_log_str_a(results[it].out); - sp_log_str_a(results[it].err); + sp_log(""); + sp_log("{.fg red}: {}", sp_fmt_cstr("FAIL"), sp_fmt_str(tests[it].name)); + sp_log_str(commands[it].display); + sp_log_str(results[it].out); + sp_log_str(results[it].err); } return status; diff --git a/tools/utest.h b/tools/utest.h index ff66722..c52a552 100644 --- a/tools/utest.h +++ b/tools/utest.h @@ -130,7 +130,7 @@ UTEST_EXTERN struct utest_state_s utest_state; #endif #define UTEST_PRINTF(...) \ do { \ - sp_str_t _utest_fmtd = sp_fmt_a(utest_state.mem, __VA_ARGS__).value; \ + sp_str_t _utest_fmtd = sp_fmt(utest_state.mem, __VA_ARGS__).value; \ sp_os_print(_utest_fmtd); \ if (utest_state.has_output) { \ sp_io_write_str(&utest_state.output.base, _utest_fmtd, SP_NULLPTR); \ @@ -716,7 +716,7 @@ static UTEST_INLINE int utest_strncmp(const c8 *a, const c8 *b, u32 n) { for (i = 0; i < (INDEX); i++) { \ const u64 index = utest_state.tests_length++; \ const c8 name_part[] = #FIXTURE "." #NAME; \ - sp_str_t fmtd = sp_fmt_a(sp_mem_get_scratch(), "{}/{}", sp_fmt_cstr(name_part), \ + sp_str_t fmtd = sp_fmt(sp_mem_get_scratch(), "{}/{}", sp_fmt_cstr(name_part), \ sp_fmt_uint(i)).value; \ u64 name_size = fmtd.len + 1; \ c8 *name = UTEST_PTR_CAST(c8 *, sp_mem_os_alloc(name_size)); \ @@ -925,7 +925,7 @@ s32 utest_main(s32 argc, const c8 **argv) { } else if (0 == UTEST_STRNCMP(argv[index], list_str, sizeof(list_str) - 1)) { for (index = 0; index < utest_state.tests_length; index++) { - sp_log_a("{}", sp_fmt_cstr(utest_state.tests[index].name)); + sp_log("{}", sp_fmt_cstr(utest_state.tests[index].name)); } /* when printing the test list, don't actually run the tests */ return 0; @@ -1002,7 +1002,7 @@ s32 utest_main(s32 argc, const c8 **argv) { abi = sp_str_lit("musl"); #endif - sp_log_a( + sp_log( "> running {.fg brightblack} test cases on {}-{}-{}", sp_fmt_uint(ran_tests), sp_fmt_str(arch), sp_fmt_str(os), sp_fmt_str(abi) @@ -1016,7 +1016,7 @@ s32 utest_main(s32 argc, const c8 **argv) { continue; } - sp_print_a("{}.{}...", sp_fmt_cstr(utest_state.tests[index].set), sp_fmt_cstr(utest_state.tests[index].test)); + sp_print("{}.{}...", sp_fmt_cstr(utest_state.tests[index].set), sp_fmt_cstr(utest_state.tests[index].test)); ns = utest_ns(); utest_state.tests[index].func(&result, utest_state.tests[index].index); @@ -1057,40 +1057,40 @@ s32 utest_main(s32 argc, const c8 **argv) { } if (UTEST_TEST_FAILURE == result) { - sp_print_a("{.fg red} ", sp_fmt_cstr("failed")); + sp_print("{.fg red} ", sp_fmt_cstr("failed")); } else if (UTEST_TEST_SKIPPED == result) { - sp_print_a("{.fg yellow} ", sp_fmt_cstr("skipped")); + sp_print("{.fg yellow} ", sp_fmt_cstr("skipped")); } else { - sp_print_a("{.fg green} ", sp_fmt_cstr("ok")); + sp_print("{.fg green} ", sp_fmt_cstr("ok")); } - sp_log_a("{.fg brightblack}{.fg brightblack}", sp_fmt_int(time), sp_fmt_cstr(units[unit_index])); + sp_log("{.fg brightblack}{.fg brightblack}", sp_fmt_int(time), sp_fmt_cstr(units[unit_index])); } } - sp_log_a("{.fg green} {} test cases ran.", + sp_log("{.fg green} {} test cases ran.", sp_fmt_cstr("[==========]"), sp_fmt_uint(ran_tests)); - sp_log_a("{}[ PASSED ]{} {} tests.", + sp_log("{}[ PASSED ]{} {} tests.", sp_fmt_cstr(SP_ANSI_FG_GREEN), sp_fmt_cstr(SP_ANSI_RESET), sp_fmt_uint(ran_tests - failed - skipped)); if (0 != skipped) { - sp_log_a("{}[ SKIPPED ]{} {} tests, listed below:", + sp_log("{}[ SKIPPED ]{} {} tests, listed below:", sp_fmt_cstr(SP_ANSI_FG_YELLOW), sp_fmt_cstr(SP_ANSI_RESET), sp_fmt_uint(skipped)); for (index = 0; index < skipped_testcases_length; index++) { - sp_log_a("{}[ SKIPPED ]{} {}", + sp_log("{}[ SKIPPED ]{} {}", sp_fmt_cstr(SP_ANSI_FG_YELLOW), sp_fmt_cstr(SP_ANSI_RESET), sp_fmt_cstr(utest_state.tests[skipped_testcases[index]].name)); } } if (0 != failed) { - sp_log_a("{}[ FAILED ]{} {} tests, listed below:", + sp_log("{}[ FAILED ]{} {} tests, listed below:", sp_fmt_cstr(SP_ANSI_FG_RED), sp_fmt_cstr(SP_ANSI_RESET), sp_fmt_uint(failed)); for (index = 0; index < failed_testcases_length; index++) { - sp_log_a("{}[ FAILED ]{} {}", + sp_log("{}[ FAILED ]{} {}", sp_fmt_cstr(SP_ANSI_FG_RED), sp_fmt_cstr(SP_ANSI_RESET), sp_fmt_cstr(utest_state.tests[failed_testcases[index]].name)); } From 83d56695f0ebfb2a20136145f13ca36568bb771b Mon Sep 17 00:00:00 2001 From: Thomas Spader Date: Wed, 13 May 2026 14:16:06 -0400 Subject: [PATCH 17/21] sys: exit --- sp.h | 1 - 1 file changed, 1 deletion(-) diff --git a/sp.h b/sp.h index 905ae4f..db65d4c 100644 --- a/sp.h +++ b/sp.h @@ -3637,7 +3637,6 @@ SP_IMP void sp_win32_env_it_set_current(sp_os_env_it_t* it); #if defined(SP_FREESTANDING) SP_IMP void* sp_sys_get_tp(void); SP_IMP s32 sp_sys_set_tp(void* tp); -SP_IMP void sp_sys_exit(s32 code); SP_IMP void sp_linux_env_it_set_current(sp_os_env_it_t* it); #endif #if defined(SP_FREESTANDING) || defined(SP_WASM_FREESTANDING) From a16e8efa6acbdf9b6e861cadfe75d90cc4a0a8c2 Mon Sep 17 00:00:00 2001 From: Thomas Spader Date: Wed, 13 May 2026 17:49:54 -0400 Subject: [PATCH 18/21] ps: parallel C string config struct --- AGENTS.md | 1 + example/format.c | 2 +- sp.h | 176 ++++++++++++++++++++++++++++++++----------- test/format.c | 13 ++-- test/ps.c | 189 ++++++++++++++++++++++++----------------------- 5 files changed, 238 insertions(+), 143 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 9f8bc43..13287a5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -62,6 +62,7 @@ make TRIPLE=aarch64-macos - Always use `sp_mem_begin_scratch_for(mem)` to avoid clobbering an argument-passed scratch allocator - For `sp_str_t` → cstr conversion before a syscall, use a stack `c8 buf[SP_PATH_MAX]` + `sp_cstr_copy_to_n`, not scratch - Never use `NULL`; use `SP_NULL` or `SP_NULLPTR` +- Never hand-align format strings; prefer to use the `:*^N` specifier and pass the content as an argument - Always use the following guide when casing macros: - Lowercase: - Function-likes (e.g. `sp_syscall`, `sp_sys_alloc_type`, `sp_max`) diff --git a/example/format.c b/example/format.c index 109f73c..b1be0b5 100644 --- a/example/format.c +++ b/example/format.c @@ -14,7 +14,7 @@ typedef struct { f32 x; f32 y; } point_t; void format_point(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { sp_unused(mem); - point_t* point = (point_t*)arg->custom.ptr; + point_t* point = (point_t*)arg->value.custom.ptr; u32 precision = sp_opt_is_null(arg->spec.precision) ? 2 : sp_opt_get(arg->spec.precision); sp_io_write_c8(io, '('); sp_fmt_write_f64(io, point->x, precision); diff --git a/sp.h b/sp.h index db65d4c..1179af6 100644 --- a/sp.h +++ b/sp.h @@ -2761,19 +2761,33 @@ typedef struct sp_fmt_arg sp_fmt_arg_t; SP_TYPEDEF_FN(void, sp_fmt_fn_t, sp_io_writer_t*, sp_mem_t, sp_fmt_arg_t*, sp_fmt_arg_t*); SP_TYPEDEF_FN(void, sp_fmt_transform_fn_t, sp_io_writer_t*, sp_mem_t, sp_str_t, sp_fmt_arg_t*, sp_fmt_arg_t*); -struct sp_fmt_arg { - sp_fmt_arg_kind_t id; - sp_fmt_spec_t spec; - union { +typedef union { u64 u; s64 i; f64 f; sp_str_t s; void* p; struct { sp_fmt_fn_t fn; void* ptr; } custom; - }; +} sp_fmt_value_t; + +typedef struct { + sp_fmt_arg_kind_t id; + sp_fmt_value_t value; +} sp_fmt_argv_t; + +struct sp_fmt_arg { + sp_fmt_arg_kind_t id; + sp_fmt_spec_t spec; + sp_fmt_value_t value; }; +static inline sp_fmt_arg_t sp_fmt_arg_from_argv(sp_fmt_argv_t v) { + sp_fmt_arg_t arg = sp_zero; + arg.id = v.id; + arg.value = v.value; + return arg; +} + typedef enum { sp_fmt_directive_renderer, sp_fmt_directive_transformer, @@ -2794,13 +2808,13 @@ typedef struct { }; } sp_fmt_directive_t; -#define sp_fmt_uint(_value) (sp_fmt_arg_t) { .id = sp_fmt_id_u64, .u = (_value) } -#define sp_fmt_int(_value) (sp_fmt_arg_t) { .id = sp_fmt_id_s64, .i = (_value) } -#define sp_fmt_float(_value) (sp_fmt_arg_t) { .id = sp_fmt_id_f64, .f = (_value) } -#define sp_fmt_char(_value) (sp_fmt_arg_t) { .id = sp_fmt_id_str, .s = { .data = &(_value), .len = 1 } } -#define sp_fmt_str(_value) (sp_fmt_arg_t) { .id = sp_fmt_id_str, .s = (_value) } -#define sp_fmt_cstr(_value) (sp_fmt_arg_t) { .id = sp_fmt_id_str, .s = sp_str_view(_value) } -#define sp_fmt_ptr(_value) (sp_fmt_arg_t) { .id = sp_fmt_id_ptr, .p = (_value) } +#define sp_fmt_uint(_value) (sp_fmt_argv_t) { .id = sp_fmt_id_u64, .value.u = (_value) } +#define sp_fmt_int(_value) (sp_fmt_argv_t) { .id = sp_fmt_id_s64, .value.i = (_value) } +#define sp_fmt_float(_value) (sp_fmt_argv_t) { .id = sp_fmt_id_f64, .value.f = (_value) } +#define sp_fmt_char(_value) (sp_fmt_argv_t) { .id = sp_fmt_id_str, .value.s = { .data = &(_value), .len = 1 } } +#define sp_fmt_str(_value) (sp_fmt_argv_t) { .id = sp_fmt_id_str, .value.s = (_value) } +#define sp_fmt_cstr(_value) (sp_fmt_argv_t) { .id = sp_fmt_id_str, .value.s = sp_str_view(_value) } +#define sp_fmt_ptr(_value) (sp_fmt_argv_t) { .id = sp_fmt_id_ptr, .value.p = (_value) } // Use a tiny, portable trick to get some damn good type safety for custom format string arguments: // @@ -2831,18 +2845,18 @@ typedef struct { // #define sp_fmt_custom(T, _fn, _value) ( \ (void)sizeof(*(T*)0 = (_value)), \ - (sp_fmt_arg_t) { \ + (sp_fmt_argv_t) { \ .id = sp_fmt_id_custom, \ - .custom = { \ + .value.custom = { \ .fn = (_fn), \ .ptr = (void*)&(_value) \ } \ }) #define sp_fmt_custom_v(T, _fn, ...) ( \ (void)sizeof(*(T*)0 = (__VA_ARGS__)), \ - (sp_fmt_arg_t) { \ + (sp_fmt_argv_t) { \ .id = sp_fmt_id_custom, \ - .custom = { \ + .value.custom = { \ .fn = (_fn), \ .ptr = (void*)&(__VA_ARGS__) \ } \ @@ -2878,6 +2892,7 @@ void sp_fmt_directive_register(const c8* name, sp_fmt_directive_t directive); sp_err_t sp_fmt_io_a(sp_io_writer_t* io, sp_mem_t mem, const c8* fmt, ...); SP_API sp_str_r sp_fmt(sp_mem_t mem, const c8* fmt, ...); +SP_API const c8* sp_fmt_to_cstr(sp_mem_t mem, const c8* fmt, ...); SP_API sp_err_t sp_fmt_v(sp_io_writer_t* io, sp_mem_t mem, sp_str_t fmt, va_list args); SP_API void sp_fmt_render_default(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param); @@ -3287,6 +3302,25 @@ typedef struct { sp_ps_io_config_t io; } sp_ps_config_t; +typedef struct { + const c8* key; + const c8* value; +} sp_ps_env_var_cstr_t; + +typedef struct { + sp_env_t env; + sp_ps_env_var_cstr_t extra [SP_PS_MAX_ENV]; + sp_ps_env_mode_t mode; +} sp_ps_env_config_cstr_t; + +typedef struct { + const c8* command; + const c8* args [SP_PS_MAX_ARGS]; + const c8* cwd; + sp_ps_env_config_cstr_t env; + sp_ps_io_config_t io; +} sp_ps_config_cstr_t; + typedef struct { sp_str_t data; u64 size; @@ -3312,19 +3346,21 @@ typedef struct { sp_mem_t mem; } sp_ps_t; -SP_API sp_ps_config_t sp_ps_config_copy(sp_mem_t mem, const sp_ps_config_t* src); -SP_API void sp_ps_config_add_arg(sp_mem_t mem, sp_ps_config_t* config, sp_str_t arg); SP_API sp_ps_t sp_ps_create(sp_mem_t mem, sp_ps_config_t config); SP_API sp_ps_output_t sp_ps_run(sp_mem_t mem, sp_ps_config_t config); -SP_API sp_io_file_writer_t* sp_ps_io_in(sp_ps_t* ps); -SP_API sp_io_reader_t* sp_ps_io_out(sp_ps_t* ps); -SP_API sp_io_reader_t* sp_ps_io_err(sp_ps_t* ps); +SP_API sp_ps_t sp_ps_create_c(sp_mem_t mem, sp_ps_config_cstr_t config); +SP_API sp_ps_output_t sp_ps_run_c(sp_mem_t mem, sp_ps_config_cstr_t config); +SP_API sp_ps_config_t sp_ps_config_copy(sp_mem_t mem, const sp_ps_config_t* src); +SP_API void sp_ps_config_add_arg(sp_mem_t mem, sp_ps_config_t* config, sp_str_t arg); SP_API sp_ps_status_t sp_ps_wait(sp_ps_t* ps); SP_API sp_ps_status_t sp_ps_poll(sp_ps_t* ps, u32 timeout_ms); SP_API sp_ps_output_t sp_ps_output(sp_ps_t* ps); SP_API bool sp_ps_kill(sp_ps_t* ps); SP_API void sp_ps_free(sp_ps_t* ps); SP_API void sp_ps_output_free(sp_mem_t mem, sp_ps_output_t* output); +SP_API sp_io_file_writer_t* sp_ps_io_in(sp_ps_t* ps); +SP_API sp_io_reader_t* sp_ps_io_out(sp_ps_t* ps); +SP_API sp_io_reader_t* sp_ps_io_err(sp_ps_t* ps); // █████████ ███████████ ███████████ @@ -6924,11 +6960,11 @@ sp_err_t sp_fmt_parse_specifier(sp_fmt_parser_t* p, sp_fmt_spec_t* spec) { // Pulls one int-kinded dynamic arg out of `a` and returns it as a signed // value. Used for dynamic fill/width/precision, all of which require an int. -static sp_err_t sp_fmt_pull_int_arg(sp_fmt_arg_t a, s64* out) { +static sp_err_t sp_fmt_pull_int_arg(sp_fmt_argv_t a, s64* out) { if (a.id != sp_fmt_id_u64 && a.id != sp_fmt_id_s64) { return SP_ERR_FMT_DIRECTIVE_ARG_WRONG_KIND; } - *out = (a.id == sp_fmt_id_s64) ? a.i : (s64)a.u; + *out = (a.id == sp_fmt_id_s64) ? a.value.i : (s64)a.value.u; return SP_OK; } @@ -7038,7 +7074,7 @@ static void sp_fmt_directive_ansi_reset(sp_io_writer_t* io, sp_mem_t mem, sp_fmt static void sp_fmt_directive_hyperlink(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(params); sp_io_write_cstr(io, "\033]8;;", SP_NULLPTR); - if (arg->id == sp_fmt_id_str) sp_io_write_str(io, arg->s, SP_NULLPTR); + if (arg->id == sp_fmt_id_str) sp_io_write_str(io, arg->value.s, SP_NULLPTR); sp_io_write_cstr(io, "\033\\", SP_NULLPTR); } @@ -7066,7 +7102,7 @@ static void sp_fmt_directive_redact_transform(sp_io_writer_t* io, sp_mem_t mem, } static void sp_fmt_directive_bytes_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { - u64 bytes = arg->u; sp_unused(params); + u64 bytes = arg->value.u; sp_unused(params); static const c8* units[] = { "B", "KB", "MB", "GB", "TB", "PB" }; u32 unit_idx = 0; u64 whole = bytes; @@ -7090,13 +7126,13 @@ static void sp_fmt_directive_bytes_render(sp_io_writer_t* io, sp_mem_t mem, sp_f static void sp_fmt_directive_iso_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(params); - sp_tm_epoch_t epoch = SP_RVAL(sp_tm_epoch_t) { .s = arg->u, .ns = 0 }; + sp_tm_epoch_t epoch = SP_RVAL(sp_tm_epoch_t) { .s = arg->value.u, .ns = 0 }; sp_io_write_str(io, sp_tm_epoch_to_iso8601(mem, epoch), SP_NULLPTR); } static void sp_fmt_directive_hex_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(params); - u64 value = (arg->id == sp_fmt_id_s64) ? (u64)arg->i : arg->u; + u64 value = (arg->id == sp_fmt_id_s64) ? (u64)arg->value.i : arg->value.u; c8 buf[16]; c8* end = buf + sizeof(buf); c8* start = sp_fmt_uint_to_buf_hex_ex(value, end, "0123456789ABCDEF"); @@ -7105,7 +7141,7 @@ static void sp_fmt_directive_hex_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt } static void sp_fmt_directive_ordinal_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { - s64 value = (arg->id == sp_fmt_id_s64) ? arg->i : (s64)arg->u; sp_unused(params); + s64 value = (arg->id == sp_fmt_id_s64) ? arg->value.i : (s64)arg->value.u; sp_unused(params); sp_fmt_write_s64(io, value); s64 abs = value < 0 ? -value : value; u32 mod100 = (u32)(abs % 100); @@ -7121,7 +7157,7 @@ static void sp_fmt_directive_ordinal_render(sp_io_writer_t* io, sp_mem_t mem, sp static void sp_fmt_directive_duration_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(params); - u64 ns = arg->u; + u64 ns = arg->value.u; if (ns < 1000) { sp_fmt_write_u64(io, ns); sp_io_write_cstr(io, " ns", SP_NULLPTR); @@ -7167,7 +7203,7 @@ sp_str_t sp_fmt_color_to_ansi_fg(sp_str_t id) { static void sp_fmt_directive_fg(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { sp_unused(arg); - sp_str_t ansi = sp_fmt_color_to_ansi_fg(param->s); + sp_str_t ansi = sp_fmt_color_to_ansi_fg(param->value.s); sp_io_write_str(io, ansi, SP_NULLPTR); } @@ -12194,6 +12230,40 @@ void sp_ps_output_free(sp_mem_t mem, sp_ps_output_t* output) { } #endif +// Non-owning view conversion: every field aliases storage owned by `src`, +// so the result is only valid for the duration of the immediate consumer call. +static sp_ps_config_t sp_ps_config_view_from_cstr(const sp_ps_config_cstr_t* src) { + sp_ps_config_t dst = sp_zero; + + dst.command = sp_str_view(src->command); + dst.cwd = sp_str_view(src->cwd); + + sp_for(i, SP_PS_MAX_ARGS) { + if (!src->args[i] || !*src->args[i]) break; + dst.args[i] = sp_str_view(src->args[i]); + } + + dst.env.env = src->env.env; + dst.env.mode = src->env.mode; + sp_for(i, SP_PS_MAX_ENV) { + if (!src->env.extra[i].key) break; + dst.env.extra[i].key = sp_str_view(src->env.extra[i].key); + dst.env.extra[i].value = sp_str_view(src->env.extra[i].value); + } + + dst.io = src->io; + + return dst; +} + +sp_ps_t sp_ps_create_c(sp_mem_t mem, sp_ps_config_cstr_t config) { + return sp_ps_create(mem, sp_ps_config_view_from_cstr(&config)); +} + +sp_ps_output_t sp_ps_run_c(sp_mem_t mem, sp_ps_config_cstr_t config) { + return sp_ps_run(mem, sp_ps_config_view_from_cstr(&config)); +} + // ███████████ █████ █████ ██████████ ██████ ██████ ███████ ██████ █████ █████ ███████████ ███████ ███████████ // ░░███░░░░░░█░░███ ░░███ ░░███░░░░░█ ░░██████ ██████ ███░░░░░███ ░░██████ ░░███ ░░███ ░█░░░███░░░█ ███░░░░░███ ░░███░░░░░███ @@ -14029,6 +14099,22 @@ sp_str_r sp_fmt(sp_mem_t mem, const c8* fmt, ...) { return result; } +const c8* sp_fmt_to_cstr(sp_mem_t mem, const c8* fmt, ...) { + va_list args; + va_start(args, fmt); + sp_io_dyn_mem_writer_t io = sp_zero; + sp_io_dyn_mem_writer_init(mem, &io); + + sp_mem_arena_marker_t s = sp_mem_begin_scratch_for(mem); + sp_err_t err = sp_fmt_v(&io.base, s.mem, sp_str_view(fmt), args); + sp_mem_end_scratch(s); + va_end(args); + + if (err) return SP_NULLPTR; + sp_io_write_c8(&io.base, 0); + return sp_mem_buffer_as_cstr(&io.storage); +} + sp_err_t sp_fmt_v(sp_io_writer_t* io, sp_mem_t mem, sp_str_t fmt, va_list args) { sp_fmt_parser_t p = { .str = fmt }; @@ -14049,19 +14135,19 @@ sp_err_t sp_fmt_v(sp_io_writer_t* io, sp_mem_t mem, sp_str_t fmt, va_list args) if (spec.fill_dynamic) { s64 v; - sp_try(sp_fmt_pull_int_arg(va_arg(args, sp_fmt_arg_t), &v)); + sp_try(sp_fmt_pull_int_arg(va_arg(args, sp_fmt_argv_t), &v)); spec.fill = (u8)v; } if (spec.width_dynamic) { s64 v; - sp_try(sp_fmt_pull_int_arg(va_arg(args, sp_fmt_arg_t), &v)); + sp_try(sp_fmt_pull_int_arg(va_arg(args, sp_fmt_argv_t), &v)); if (v < 0) v = 0; if (v > SP_FMT_WIDTH_MAX) v = SP_FMT_WIDTH_MAX; spec.width = (u32)v; } if (spec.precision_dynamic) { s64 v; - sp_try(sp_fmt_pull_int_arg(va_arg(args, sp_fmt_arg_t), &v)); + sp_try(sp_fmt_pull_int_arg(va_arg(args, sp_fmt_argv_t), &v)); if (v < 0) v = 0; if (v > 255) v = 255; sp_opt_set(spec.precision, (u8)v); @@ -14070,11 +14156,11 @@ sp_err_t sp_fmt_v(sp_io_writer_t* io, sp_mem_t mem, sp_str_t fmt, va_list args) sp_fmt_arg_t params[SP_FMT_MAX_DIRECTIVES] = sp_zero; for (u8 di = 0; di < spec.directive_count; di++) { if (spec.directive_arg_dynamic & (1u << di)) { - params[di] = va_arg(args, sp_fmt_arg_t); + params[di] = sp_fmt_arg_from_argv(va_arg(args, sp_fmt_argv_t)); } } - sp_fmt_arg_t arg = va_arg(args, sp_fmt_arg_t); + sp_fmt_arg_t arg = sp_fmt_arg_from_argv(va_arg(args, sp_fmt_argv_t)); arg.spec = spec; sp_try(sp_fmt_render(io, mem, &arg, params)); continue; @@ -14227,7 +14313,7 @@ sp_err_t sp_fmt_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_f else if (is_literal) { directive_params[it] = (sp_fmt_arg_t){ .id = sp_fmt_id_str, - .s = arg->spec.directive_args[it], + .value.s = arg->spec.directive_args[it], }; params[it] = true; } @@ -14265,10 +14351,10 @@ sp_err_t sp_fmt_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_f sp_fmt_arg_t* param; } render = sp_zero; if (arg->id == sp_fmt_id_custom) { - if (!arg->custom.fn) { + if (!arg->value.custom.fn) { return SP_ERR_FMT_CUSTOM_WITHOUT_FN; } - render.fn = arg->custom.fn; + render.fn = arg->value.custom.fn; } else { sp_for(it, num_dirs) { @@ -14480,18 +14566,18 @@ void sp_fmt_render_default(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_unused(param); switch (arg->id) { case sp_fmt_id_u64: - sp_fmt_write_u64(io, arg->u); + sp_fmt_write_u64(io, arg->value.u); break; case sp_fmt_id_s64: - sp_fmt_write_s64(io, arg->i); + sp_fmt_write_s64(io, arg->value.i); break; case sp_fmt_id_f64: { u32 p = sp_opt_is_null(arg->spec.precision) ? 6 : sp_opt_get(arg->spec.precision); - sp_fmt_write_f64(io, arg->f, p); + sp_fmt_write_f64(io, arg->value.f, p); break; } case sp_fmt_id_str: { - sp_str_t s = arg->s; + sp_str_t s = arg->value.s; if (!sp_opt_is_null(arg->spec.precision)) { u32 max = sp_opt_get(arg->spec.precision); if (max < s.len) s.len = max; @@ -14500,12 +14586,12 @@ void sp_fmt_render_default(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, break; } case sp_fmt_id_ptr: { - sp_fmt_write_ptr(io, arg->p); + sp_fmt_write_ptr(io, arg->value.p); break; } case sp_fmt_id_custom: { - if (arg->custom.fn) { - arg->custom.fn(io, mem, arg, SP_NULLPTR); + if (arg->value.custom.fn) { + arg->value.custom.fn(io, mem, arg, SP_NULLPTR); } break; } diff --git a/test/format.c b/test/format.c index 71e2fc7..eecc7f6 100644 --- a/test/format.c +++ b/test/format.c @@ -314,7 +314,8 @@ UTEST(sp_fmt_parse_directive, no_width_just_directive) { EXPECT_EQ(spec.directive_count, 1); } -static sp_str_t render_value_to_str(sp_fmt_arg_t arg) { +static sp_str_t render_value_to_str(sp_fmt_argv_t argv) { + sp_fmt_arg_t arg = sp_fmt_arg_from_argv(argv); sp_io_dyn_mem_writer_t io = sp_zero; sp_io_dyn_mem_writer_init(sp_mem_get_scratch(), &io); sp_fmt_render_default(&io.base, sp_mem_get_scratch(), &arg, SP_NULLPTR); @@ -425,7 +426,7 @@ UTEST(sp_fmt_render, f64_neg_inf) { } UTEST(sp_fmt_render, f64_custom_precision_via_spec) { - sp_fmt_arg_t arg = sp_fmt_float(3.14159); + sp_fmt_arg_t arg = sp_fmt_arg_from_argv(sp_fmt_float(3.14159)); sp_opt_set(arg.spec.precision, 2); sp_io_dyn_mem_writer_t io = sp_zero; sp_io_dyn_mem_writer_init(sp_mem_get_scratch(), &io); @@ -732,7 +733,7 @@ UTEST(sp_fmt_directive, custom_fn_fallback) { sp_fmt_directive_reset(); sp_fmt_register_decorator("wrap", _test_before_lt, _test_after_gt); u32 value = 0; - sp_fmt_arg_t arg = sp_fmt_custom(u32, _test_render_x, value); + sp_fmt_argv_t arg = sp_fmt_custom(u32, _test_render_x, value); sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.wrap}", arg).value; EXPECT_TRUE(sp_str_equal_cstr(got, "")); sp_fmt_directive_reset(); @@ -904,7 +905,7 @@ UTEST(sp_fmt_v, width_clamped_dynamic_negative) { static void _test_render_u64_only(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { (void)param; (void)mem; - sp_fmt_write_u64(io, arg->u); + sp_fmt_write_u64(io, arg->value.u); } UTEST(sp_fmt_directive, kinds_single_accepts_match) { @@ -1055,9 +1056,9 @@ static void _test_fg_before(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, (void)arg; (void)mem; _last_fg_had_param = (param != SP_NULLPTR); if (param && param->id == sp_fmt_id_str) { - _last_fg_param = param->s; + _last_fg_param = param->value.s; sp_io_write_cstr(io, "s, SP_NULLPTR); + sp_io_write_str(io, param->value.s, SP_NULLPTR); sp_io_write_cstr(io, ">", SP_NULLPTR); } else { diff --git a/test/ps.c b/test/ps.c index 83efbd3..95ba27b 100644 --- a/test/ps.c +++ b/test/ps.c @@ -59,6 +59,13 @@ sp_str_t get_process_path(sp_mem_t mem) { return process; } +const c8* get_process_path_c(sp_mem_t mem) { + sp_str_t exe = sp_fs_parent_path(sp_fs_get_exe_path(mem)); + sp_str_t process = sp_fs_join_path(mem, exe, sp_str_lit("process")); + process = sp_fs_replace_ext(mem, process, sp_os_get_executable_ext()); + return sp_str_to_cstr(mem, process); +} + typedef struct ps { sp_mem_arena_t* arena; sp_mem_t mem; @@ -618,11 +625,11 @@ UTEST_F(ps, empty_env_var) { // SP_PS_WAIT // ////////////////// UTEST_F(ps, wait_after_process_complete) { - sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { - .command = get_process_path(ut.mem), + sp_ps_t ps = sp_ps_create_c(ut.mem, (sp_ps_config_cstr_t) { + .command = get_process_path_c(ut.mem), .args = { - sp_str_lit("--fn"), sp_str_lit("exit_code"), - sp_str_lit("--exit-code"), sp_str_lit("42") + "--fn", "exit_code", + "--exit-code", "42" } }); @@ -634,11 +641,11 @@ UTEST_F(ps, wait_after_process_complete) { } UTEST_F(ps, wait_while_process_running) { - sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { - .command = get_process_path(ut.mem), + sp_ps_t ps = sp_ps_create_c(ut.mem, (sp_ps_config_cstr_t) { + .command = get_process_path_c(ut.mem), .args = { - sp_str_lit("--fn"), sp_str_lit("wait"), - sp_str_lit("100") + "--fn", "wait", + "100" } }); @@ -676,11 +683,11 @@ UTEST_F(ps, poll_while_process_running) { } UTEST_F(ps, process_complete_during_poll) { - sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { - .command = get_process_path(ut.mem), + sp_ps_t ps = sp_ps_create_c(ut.mem, (sp_ps_config_cstr_t) { + .command = get_process_path_c(ut.mem), .args = { - sp_str_lit("--fn"), sp_str_lit("wait"), - sp_str_lit("100") + "--fn", "wait", + "100" } }); @@ -690,11 +697,11 @@ UTEST_F(ps, process_complete_during_poll) { } UTEST_F(ps, poll_after_process_complete) { - sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { - .command = get_process_path(ut.mem), + sp_ps_t ps = sp_ps_create_c(ut.mem, (sp_ps_config_cstr_t) { + .command = get_process_path_c(ut.mem), .args = { - sp_str_lit("--fn"), sp_str_lit("exit_code"), - sp_str_lit("--exit-code"), sp_str_lit("72") + "--fn", "exit_code", + "--exit-code", "72" } }); @@ -706,11 +713,11 @@ UTEST_F(ps, poll_after_process_complete) { } UTEST_F(ps, poll_with_timeout_after_process_complete) { - sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { - .command = get_process_path(ut.mem), + sp_ps_t ps = sp_ps_create_c(ut.mem, (sp_ps_config_cstr_t) { + .command = get_process_path_c(ut.mem), .args = { - sp_str_lit("--fn"), sp_str_lit("exit_code"), - sp_str_lit("--exit-code"), sp_str_lit("72") + "--fn", "exit_code", + "--exit-code", "72" } }); @@ -722,11 +729,11 @@ UTEST_F(ps, poll_with_timeout_after_process_complete) { } UTEST_F(ps, wait_twice_while_process_running) { - sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { - .command = get_process_path(ut.mem), + sp_ps_t ps = sp_ps_create_c(ut.mem, (sp_ps_config_cstr_t) { + .command = get_process_path_c(ut.mem), .args = { - sp_str_lit("--fn"), sp_str_lit("exit_code"), - sp_str_lit("--exit-code"), sp_str_lit("72") + "--fn", "exit_code", + "--exit-code", "72" } }); @@ -740,11 +747,11 @@ UTEST_F(ps, wait_twice_while_process_running) { } UTEST_F(ps, poll_then_wait) { - sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { - .command = get_process_path(ut.mem), + sp_ps_t ps = sp_ps_create_c(ut.mem, (sp_ps_config_cstr_t) { + .command = get_process_path_c(ut.mem), .args = { - sp_str_lit("--fn"), sp_str_lit("wait"), - sp_str_lit("100") + "--fn", "wait", + "100" } }); @@ -757,11 +764,11 @@ UTEST_F(ps, poll_then_wait) { } UTEST_F(ps, poll_multiple) { - sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { - .command = get_process_path(ut.mem), + sp_ps_t ps = sp_ps_create_c(ut.mem, (sp_ps_config_cstr_t) { + .command = get_process_path_c(ut.mem), .args = { - sp_str_lit("--fn"), sp_str_lit("wait"), - sp_str_lit("300") + "--fn", "wait", + "300" } }); @@ -779,11 +786,11 @@ UTEST_F(ps, poll_multiple) { } UTEST_F(ps, wait_with_output) { - sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { - .command = get_process_path(ut.mem), + sp_ps_t ps = sp_ps_create_c(ut.mem, (sp_ps_config_cstr_t) { + .command = get_process_path_c(ut.mem), .args = { - sp_str_lit("--fn"), sp_str_lit("print"), - sp_str_lit("--stdout") + "--fn", "print", + "--stdout" }, .io = { .in = { .mode = SP_PS_IO_MODE_NULL }, @@ -803,11 +810,11 @@ UTEST_F(ps, wait_with_output) { } UTEST_F(ps, poll_with_io) { - sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { - .command = get_process_path(ut.mem), + sp_ps_t ps = sp_ps_create_c(ut.mem, (sp_ps_config_cstr_t) { + .command = get_process_path_c(ut.mem), .args = { - sp_str_lit("--fn"), sp_str_lit("wait"), - sp_str_lit("100") + "--fn", "wait", + "100" }, .io = { .in = { .mode = SP_PS_IO_MODE_CREATE }, @@ -827,11 +834,11 @@ UTEST_F(ps, poll_with_io) { } UTEST_F(ps, interleaved_read_write) { - sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { - .command = get_process_path(ut.mem), + sp_ps_t ps = sp_ps_create_c(ut.mem, (sp_ps_config_cstr_t) { + .command = get_process_path_c(ut.mem), .args = { - sp_str_lit("--fn"), sp_str_lit("echo_line"), - sp_str_lit("--stdout") + "--fn", "echo_line", + "--stdout" }, .io = { .in = { .mode = SP_PS_IO_MODE_CREATE }, @@ -869,11 +876,11 @@ UTEST_F(ps, interleaved_read_write) { } UTEST_F(ps, incremental_nonblocking_read) { - sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { - .command = get_process_path(ut.mem), + sp_ps_t ps = sp_ps_create_c(ut.mem, (sp_ps_config_cstr_t) { + .command = get_process_path_c(ut.mem), .args = { - sp_str_lit("--fn"), sp_str_lit("slow_write"), - sp_str_lit("--stdout") + "--fn", "slow_write", + "--stdout" }, .io = { .in = { .mode = SP_PS_IO_MODE_NULL }, @@ -914,11 +921,11 @@ UTEST_F(ps, incremental_nonblocking_read) { } UTEST_F(ps, output) { - sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { - .command = get_process_path(ut.mem), + sp_ps_t ps = sp_ps_create_c(ut.mem, (sp_ps_config_cstr_t) { + .command = get_process_path_c(ut.mem), .args = { - sp_str_lit("--fn"), sp_str_lit("print"), - sp_str_lit("--stdout") + "--fn", "print", + "--stdout" }, .io = { .in = { .mode = SP_PS_IO_MODE_NULL }, @@ -936,12 +943,12 @@ UTEST_F(ps, output) { } UTEST_F(ps, redirect_stderr_to_stdout) { - sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { - .command = get_process_path(ut.mem), + sp_ps_t ps = sp_ps_create_c(ut.mem, (sp_ps_config_cstr_t) { + .command = get_process_path_c(ut.mem), .args = { - sp_str_lit("--fn"), sp_str_lit("print"), - sp_str_lit("--stdout"), - sp_str_lit("--stderr") + "--fn", "print", + "--stdout", + "--stderr" }, .io = { .in = { .mode = SP_PS_IO_MODE_NULL }, @@ -964,12 +971,12 @@ UTEST_F(ps, redirect_stderr_to_stdout) { } UTEST_F(ps, redirect_stdout_to_stderr) { - sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { - .command = get_process_path(ut.mem), + sp_ps_t ps = sp_ps_create_c(ut.mem, (sp_ps_config_cstr_t) { + .command = get_process_path_c(ut.mem), .args = { - sp_str_lit("--fn"), sp_str_lit("print"), - sp_str_lit("--stdout"), - sp_str_lit("--stderr") + "--fn", "print", + "--stdout", + "--stderr" }, .io = { .in = { .mode = SP_PS_IO_MODE_NULL }, @@ -992,12 +999,12 @@ UTEST_F(ps, redirect_stdout_to_stderr) { } UTEST_F(ps, output_large_stdout_stderr_deadlock) { - sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { - .command = get_process_path(ut.mem), + sp_ps_t ps = sp_ps_create_c(ut.mem, (sp_ps_config_cstr_t) { + .command = get_process_path_c(ut.mem), .args = { - sp_str_lit("--fn"), sp_str_lit("flood"), - sp_str_lit("--stdout"), - sp_str_lit("--stderr") + "--fn", "flood", + "--stdout", + "--stderr" }, .io = { .in = { .mode = SP_PS_IO_MODE_NULL }, @@ -1017,11 +1024,11 @@ UTEST_F(ps, output_large_stdout_stderr_deadlock) { } UTEST_F(ps, output_large_stdout_deadlock) { - sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { - .command = get_process_path(ut.mem), + sp_ps_t ps = sp_ps_create_c(ut.mem, (sp_ps_config_cstr_t) { + .command = get_process_path_c(ut.mem), .args = { - sp_str_lit("--fn"), sp_str_lit("flood"), - sp_str_lit("--stdout"), + "--fn", "flood", + "--stdout", }, .io = { .in = { .mode = SP_PS_IO_MODE_NULL }, @@ -1091,14 +1098,14 @@ UTEST_F(ps, concurrent_existing_fd_small_writes) { const s32 write_size = 100; const s32 write_count = 50; - sp_ps_t ps_a = sp_ps_create(ut.mem, (sp_ps_config_t) { - .command = get_process_path(ut.mem), + sp_ps_t ps_a = sp_ps_create_c(ut.mem, (sp_ps_config_cstr_t) { + .command = get_process_path_c(ut.mem), .args = { - sp_str_lit("--fn"), sp_str_lit("pattern"), - sp_str_lit("--stdout"), - sp_str_lit("-c"), sp_str_lit("A"), - sp_str_lit("-s"), sp_str_lit("100"), - sp_str_lit("-n"), sp_str_lit("50"), + "--fn", "pattern", + "--stdout", + "-c", "A", + "-s", "100", + "-n", "50", }, .io = { .in = { .mode = SP_PS_IO_MODE_NULL }, @@ -1107,14 +1114,14 @@ UTEST_F(ps, concurrent_existing_fd_small_writes) { } }); - sp_ps_t ps_b = sp_ps_create(ut.mem, (sp_ps_config_t) { - .command = get_process_path(ut.mem), + sp_ps_t ps_b = sp_ps_create_c(ut.mem, (sp_ps_config_cstr_t) { + .command = get_process_path_c(ut.mem), .args = { - sp_str_lit("--fn"), sp_str_lit("pattern"), - sp_str_lit("--stdout"), - sp_str_lit("-c"), sp_str_lit("B"), - sp_str_lit("-s"), sp_str_lit("100"), - sp_str_lit("-n"), sp_str_lit("50"), + "--fn", "pattern", + "--stdout", + "-c", "B", + "-s", "100", + "-n", "50", }, .io = { .in = { .mode = SP_PS_IO_MODE_NULL }, @@ -1252,15 +1259,15 @@ UTEST_F(ps, concurrent_existing_fd_large_writes) { #endif // !_WIN32 UTEST_F(ps, create_nonexistent_binary) { - sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { - .command = sp_str_lit("/usr/bin/this_binary_does_not_exist_at_all") + sp_ps_t ps = sp_ps_create_c(ut.mem, (sp_ps_config_cstr_t) { + .command = "/usr/bin/this_binary_does_not_exist_at_all" }); EXPECT_EQ(ps.os, SP_NULLPTR); } UTEST_F(ps, wait_nonexistent_binary) { - sp_ps_t ps = sp_ps_create(ut.mem, (sp_ps_config_t) { - .command = sp_str_lit("/usr/bin/this_binary_does_not_exist_at_all") + sp_ps_t ps = sp_ps_create_c(ut.mem, (sp_ps_config_cstr_t) { + .command = "/usr/bin/this_binary_does_not_exist_at_all" }); EXPECT_EQ(ps.os, SP_NULLPTR); @@ -1270,8 +1277,8 @@ UTEST_F(ps, wait_nonexistent_binary) { } UTEST_F(ps, run_nonexistent_binary) { - sp_ps_output_t result = sp_ps_run(ut.mem, (sp_ps_config_t) { - .command = sp_str_lit("/usr/bin/this_binary_does_not_exist_at_all") + sp_ps_output_t result = sp_ps_run_c(ut.mem, (sp_ps_config_cstr_t) { + .command = "/usr/bin/this_binary_does_not_exist_at_all" }); EXPECT_EQ(result.status.exit_code, -1); } From e5808d6e4d099621e619ee53c367f316b5e217e1 Mon Sep 17 00:00:00 2001 From: Thomas Spader Date: Thu, 14 May 2026 18:19:46 -0400 Subject: [PATCH 19/21] fmt: fix sp_fmt_io name --- example/embed.c | 8 ++++---- example/io.c | 4 ++-- example/prompt_fancy.c | 10 +++++----- sp.h | 42 +++++++++++++++++++++--------------------- test/bench/ht.c | 4 ++-- test/format.c | 3 +-- 6 files changed, 35 insertions(+), 36 deletions(-) diff --git a/example/embed.c b/example/embed.c index ca24b99..28c19bf 100644 --- a/example/embed.c +++ b/example/embed.c @@ -163,23 +163,23 @@ s32 embed_main(s32 argc, const c8** argv) { sp_io_file_writer_from_path(&hdr, out_hdr, SP_IO_WRITE_MODE_OVERWRITE); sp_da_for(entries, it) { embed_entry_t entry = entries[it]; - sp_fmt_io_a(&hdr.base, mem, + sp_fmt_io(&hdr.base, mem, "extern const u8 {} [{}];\n", sp_fmt_str(entry.symbol), sp_fmt_uint(entry.size) ); - sp_fmt_io_a(&hdr.base, mem, + sp_fmt_io(&hdr.base, mem, "extern const u64 {}_size;\n\n", sp_fmt_str(entry.symbol) ); } sp_io_write_str(&hdr.base, sp_str_lit("typedef struct {\n const char* path;\n const void* data;\n unsigned long long size;\n} spn_embed_entry_t;\n"), SP_NULLPTR); - sp_fmt_io_a(&hdr.base, mem, "static const unsigned int spn_embed_count = {};\n", sp_fmt_uint(sp_da_size(entries))); + sp_fmt_io(&hdr.base, mem, "static const unsigned int spn_embed_count = {};\n", sp_fmt_uint(sp_da_size(entries))); sp_io_write_str(&hdr.base, sp_str_lit("static const spn_embed_entry_t spn_embed_manifest[] = {\n"), SP_NULLPTR); sp_da_for(entries, it) { embed_entry_t entry = entries[it]; sp_io_write_cstr(&hdr.base, " { ", SP_NULLPTR); - sp_fmt_io_a(&hdr.base, mem, "\"{}\", {}, {}", sp_fmt_str(entry.path), sp_fmt_str(entry.symbol), sp_fmt_uint(entry.size)); + sp_fmt_io(&hdr.base, mem, "\"{}\", {}, {}", sp_fmt_str(entry.path), sp_fmt_str(entry.symbol), sp_fmt_uint(entry.size)); sp_io_write_cstr(&hdr.base, " },\n", SP_NULLPTR); } sp_io_write_str(&hdr.base, sp_str_lit("};\n"), SP_NULLPTR); diff --git a/example/io.c b/example/io.c index 291748e..0f85b7e 100644 --- a/example/io.c +++ b/example/io.c @@ -38,7 +38,7 @@ s32 run(s32 num_args, const c8** args) { sp_io_mem_writer_from_buffer(&mw, str, 256); { sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_fmt_io_a(&mw.base, s.mem, "hello, {}", sp_fmt_cstr("world")); + sp_fmt_io(&mw.base, s.mem, "hello, {}", sp_fmt_cstr("world")); sp_mem_end_scratch(s); } sp_log("sp_io_writer_from_mem: {}", sp_fmt_cstr(str)); @@ -50,7 +50,7 @@ s32 run(s32 num_args, const c8** args) { sp_io_file_writer_from_fd(&fw, sp_sys_stdout, SP_IO_CLOSE_MODE_NONE); { sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_fmt_io_a(&fw.base, s.mem, "hello, {.cyan}", sp_fmt_cstr("stdout")); + sp_fmt_io(&fw.base, s.mem, "hello, {.cyan}", sp_fmt_cstr("stdout")); sp_mem_end_scratch(s); } sp_io_write(&fw.base, "\n", 1, SP_NULLPTR); diff --git a/example/prompt_fancy.c b/example/prompt_fancy.c index 8895559..6961512 100644 --- a/example/prompt_fancy.c +++ b/example/prompt_fancy.c @@ -519,11 +519,11 @@ static sp_str_t fancy_selected_changelog(sp_mem_t mem, fancy_changelog_item_t* i if (written) { sp_io_write_c8(&builder.base, '\n'); } - sp_fmt_io_a(&builder.base, s.mem, "[{}]", sp_fmt_cstr(section)); + sp_fmt_io(&builder.base, s.mem, "[{}]", sp_fmt_cstr(section)); sp_io_write_c8(&builder.base, '\n'); } - sp_fmt_io_a(&builder.base, s.mem, "- {}", sp_fmt_cstr(items[it].text)); + sp_fmt_io(&builder.base, s.mem, "- {}", sp_fmt_cstr(items[it].text)); sp_io_write_c8(&builder.base, '\n'); } sp_mem_end_scratch(s); @@ -534,15 +534,15 @@ static sp_str_t fancy_plan_note(sp_mem_t mem, const c8* kind, const c8* name, co sp_io_dyn_mem_writer_t builder = sp_zero; sp_io_dyn_mem_writer_init(mem, &builder); sp_mem_arena_marker_t s = sp_mem_begin_scratch_for(mem); - sp_fmt_io_a(&builder.base, s.mem, "version 0.13.3 ({})", sp_fmt_cstr(kind)); + sp_fmt_io(&builder.base, s.mem, "version 0.13.3 ({})", sp_fmt_cstr(kind)); sp_io_write_c8(&builder.base, '\n'); - sp_fmt_io_a(&builder.base, s.mem, "name {}", sp_fmt_cstr(name)); + sp_fmt_io(&builder.base, s.mem, "name {}", sp_fmt_cstr(name)); sp_io_write_c8(&builder.base, '\n'); sp_io_write_cstr(&builder.base, "tag v0.13.3", SP_NULLPTR); sp_io_write_c8(&builder.base, '\n'); sp_io_write_cstr(&builder.base, "artifacts linux, musl, freestanding, macos, windows", SP_NULLPTR); sp_io_write_c8(&builder.base, '\n'); - sp_fmt_io_a(&builder.base, s.mem, "changelog {}", sp_fmt_cstr(sections)); + sp_fmt_io(&builder.base, s.mem, "changelog {}", sp_fmt_cstr(sections)); sp_mem_end_scratch(s); return sp_io_dyn_mem_writer_as_str(&builder); } diff --git a/sp.h b/sp.h index 1179af6..1dfc3ca 100644 --- a/sp.h +++ b/sp.h @@ -1199,8 +1199,6 @@ typedef struct { u32 raw_attrs; } sp_sys_stat_t; -#define SP_FS_NAME_MAX 1024 - typedef struct { s64 handle; sp_mem_buffer_t buf; @@ -1208,7 +1206,7 @@ typedef struct { } sp_sys_fs_it_t; typedef struct { - c8 name [SP_FS_NAME_MAX]; + const c8* name; u32 len; sp_fs_kind_t kind; } sp_sys_fs_entry_t; @@ -2890,7 +2888,7 @@ typedef struct { void sp_fmt_directive_register(const c8* name, sp_fmt_directive_t directive); -sp_err_t sp_fmt_io_a(sp_io_writer_t* io, sp_mem_t mem, const c8* fmt, ...); +SP_API sp_err_t sp_fmt_io(sp_io_writer_t* io, sp_mem_t mem, const c8* fmt, ...); SP_API sp_str_r sp_fmt(sp_mem_t mem, const c8* fmt, ...); SP_API const c8* sp_fmt_to_cstr(sp_mem_t mem, const c8* fmt, ...); SP_API sp_err_t sp_fmt_v(sp_io_writer_t* io, sp_mem_t mem, sp_str_t fmt, va_list args); @@ -6247,7 +6245,7 @@ s64 sp_sys_canonicalize_path(const c8* path, u32 len, c8* buf, u64 size) { sp_io_mem_writer_t io = sp_zero; sp_io_mem_writer_from_buffer(&io, proc, 64); sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_fmt_io_a(&io.base, s.mem, "/proc/self/fd/{}", sp_fmt_int(fd)); + sp_fmt_io(&io.base, s.mem, "/proc/self/fd/{}", sp_fmt_int(fd)); sp_mem_end_scratch(s); s64 n = sp_syscall(SP_SYSCALL_NUM_READLINKAT, SP_AT_FDCWD, proc, buf, size); @@ -6968,7 +6966,7 @@ static sp_err_t sp_fmt_pull_int_arg(sp_fmt_argv_t a, s64* out) { return SP_OK; } -sp_err_t sp_fmt_io_a(sp_io_writer_t* io, sp_mem_t mem, const c8* fmt, ...) { +sp_err_t sp_fmt_io(sp_io_writer_t* io, sp_mem_t mem, const c8* fmt, ...) { va_list args; va_start(args, fmt); sp_err_t result = sp_fmt_v(io, mem, sp_str_view(fmt), args); @@ -9092,7 +9090,7 @@ SP_PRIVATE u32 sp_sys_diriter_win32_name_len(const u16* name) { s32 sp_sys_fs_it_open(sp_sys_fs_it_t* it, const c8* path, u32 path_len, void* buf, u64 cap) { *it = sp_zero_s(sp_sys_fs_it_t); - if (cap < sizeof(sp_win32_find_data_t)) return -1; + if (cap < sizeof(sp_win32_find_data_t) + (MAX_PATH * 3 + 1)) return -1; SP_ALIGNED u8 pattern_storage [SP_PATH_MAX]; sp_mem_fixed_t pattern_fixed = sp_mem_fixed(pattern_storage, sizeof(pattern_storage)); @@ -9146,12 +9144,16 @@ s32 sp_sys_fs_it_next(sp_sys_fs_it_t* it, sp_sys_fs_entry_t* out) { continue; } - sp_mem_fixed_t allocator = sp_mem_fixed(out->name, SP_FS_NAME_MAX); + u8* scratch_data = it->buf.data + sizeof(sp_win32_find_data_t); + u64 scratch_cap = it->buf.capacity - sizeof(sp_win32_find_data_t); + sp_mem_fixed_t allocator = sp_mem_fixed(scratch_data, scratch_cap); sp_mem_t mem = sp_mem_fixed_as_allocator(&allocator); + u32 wlen = sp_sys_diriter_win32_name_len(name); + sp_str_t utf8 = sp_wtf16_to_wtf8(mem, sp_wide_str(name, wlen)); out->kind = sp_sys_diriter_win32_attrs(fd->dwFileAttributes); - out->len = sp_sys_diriter_win32_name_len(name); - sp_wtf16_to_wtf8(mem, sp_wide_str(name, out->len)); + out->name = utf8.data; + out->len = utf8.len; return SP_OK; } } @@ -9198,8 +9200,8 @@ s32 sp_sys_fs_it_next(sp_sys_fs_it_t* it, sp_sys_fs_entry_t* out) { // If it's an absolute path, that's the one if (!sp_sys_diriter_is_dot(d->d_name)) { out->kind = sp_sys_diriter_dtype_to_kind(d->d_type); - out->len = sp_min(sp_cstr_len(d->d_name), SP_FS_NAME_MAX - 1); - sp_cstr_copy_to_n(d->d_name, out->len, out->name, SP_FS_NAME_MAX); + out->name = d->d_name; + out->len = sp_cstr_len(d->d_name); return SP_OK; } } @@ -9238,10 +9240,8 @@ s32 sp_sys_fs_it_next(sp_sys_fs_it_t* it, sp_sys_fs_entry_t* out) { struct dirent* d = readdir((DIR*)(intptr_t)it->handle); if (!d) return -1; if (sp_sys_diriter_is_dot(d->d_name)) continue; - u32 nlen = sp_cstr_len(d->d_name); - if (nlen >= SP_FS_NAME_MAX) nlen = SP_FS_NAME_MAX - 1; - sp_cstr_copy_to_n(d->d_name, nlen, out->name, SP_FS_NAME_MAX); - out->len = nlen; + out->name = d->d_name; + out->len = sp_cstr_len(d->d_name); out->kind = sp_sys_diriter_dtype_to_kind(d->d_type); return 0; } @@ -9285,7 +9285,7 @@ void sp_assert_f(sp_str_t file, sp_str_t line, sp_str_t func, sp_str_t expr, boo sp_io_file_writer_t io = sp_zero; sp_io_file_writer_from_fd(&io, sp_sys_stderr, SP_IO_CLOSE_MODE_NONE); sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_fmt_io_a( + sp_fmt_io( &io.base, s.mem, "{.red} {}:{.gray}:{.yellow}{.yellow} {}", @@ -11828,7 +11828,7 @@ sp_ps_t sp_ps_create(sp_mem_t mem, sp_ps_config_t config) { sp_str_t key = *sp_str_ht_it_getkp(env->vars, it); sp_str_t val = *sp_str_ht_it_getp(env->vars, it); - sp_fmt_io_a(&b.base, scratch.mem, "{}={}", sp_fmt_str(key), sp_fmt_str(val)); + sp_fmt_io(&b.base, scratch.mem, "{}={}", sp_fmt_str(key), sp_fmt_str(val)); sp_io_write_c8(&b.base, '\0'); } @@ -11843,7 +11843,7 @@ sp_ps_t sp_ps_create(sp_mem_t mem, sp_ps_config_t config) { sp_env_var_t e = config.env.extra[it]; if (sp_str_empty(e.key)) continue; - sp_fmt_io_a(&b.base, scratch.mem, "{}={}", sp_fmt_str(e.key), sp_fmt_str(e.value)); + sp_fmt_io(&b.base, scratch.mem, "{}={}", sp_fmt_str(e.key), sp_fmt_str(e.value)); sp_io_write_c8(&b.base, '\0'); } @@ -14923,9 +14923,9 @@ void sp_fs_it_next(sp_fs_it_t* it) { sp_sys_fs_entry_t d; if (sp_sys_fs_it_next(&top->sys, &d) == 0) { - it->entry.name = sp_str_from_cstr_n(it->mem, d.name, d.len); + it->entry.path = sp_fs_join_path(it->mem, top->path, sp_str(d.name, d.len)); + it->entry.name = sp_str_sub(it->entry.path, it->entry.path.len - d.len, d.len); it->entry.kind = d.kind; - it->entry.path = sp_fs_join_path(it->mem, top->path, it->entry.name); if (it->recursive && it->entry.kind == SP_FS_KIND_DIR) { sp_fs_it_push(it, it->entry.path); diff --git a/test/bench/ht.c b/test/bench/ht.c index 9f01484..62ae2e6 100644 --- a/test/bench/ht.c +++ b/test/bench/ht.c @@ -145,7 +145,7 @@ static void run_benchmarks(bench_t* benches, u32 num_benches) { sp_io_writer_t sb = sp_zero; sp_io_dyn_mem_writer_init(sp_mem_get_scratch(), &sb); sp_mem_arena_marker_t fmt_s = sp_mem_begin_scratch_for(sp_mem_get_scratch()); - sp_fmt_io_a(&sb, fmt_s.mem, "{}{} {} {} {} {}{}\n", + sp_fmt_io(&sb, fmt_s.mem, "{}{} {} {} {} {}{}\n", sp_fmt_cstr(SP_ANSI_FG_BRIGHT_BLACK), sp_fmt_str(sp_str_pad(sp_mem_get_scratch(), sp_str_lit("test"), max_name)), sp_fmt_str(sp_str_pad(sp_mem_get_scratch(), sp_str_lit("n"), max_n_width)), @@ -167,7 +167,7 @@ static void run_benchmarks(bench_t* benches, u32 num_benches) { sp_str_t ratio_color = color_for_ratio(ratio); sp_str_t ratio_str = sp_str_pad(sp_mem_get_scratch(), sp_fmt(sp_mem_get_scratch(), "{}x", sp_fmt_float(ratio)).value, ratio_width); - sp_fmt_io_a(&sb, fmt_s.mem, "{} {} {} {} {}{}{}\n", + sp_fmt_io(&sb, fmt_s.mem, "{} {} {} {} {}{}{}\n", sp_fmt_str(sp_str_pad(sp_mem_get_scratch(), r->name, max_name)), sp_fmt_str(sp_str_pad(sp_mem_get_scratch(), n_str, max_n_width)), sp_fmt_str(sp_time_str), diff --git a/test/format.c b/test/format.c index eecc7f6..6b192e3 100644 --- a/test/format.c +++ b/test/format.c @@ -1213,7 +1213,7 @@ UTEST(sp_fmt_transform, into_writer_backed_builder) { sp_io_mem_writer_from_buffer(&w, buf, sizeof(buf)); sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_fmt_io_a(&w.base, s.mem, "{.upper}", sp_fmt_cstr("hello")); + sp_fmt_io(&w.base, s.mem, "{.upper}", sp_fmt_cstr("hello")); sp_mem_end_scratch(s); sp_str_t got = { .data = buf, .len = 5 }; EXPECT_TRUE(sp_str_equal_cstr(got, "HELLO")); @@ -1552,4 +1552,3 @@ UTEST(fmt, hex_mixed_digits) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.hex}", sp_fmt_uint(0x1234abcd)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "0x1234ABCD")); } - From 7e760e20dc8ff5004ad273388dbe2cbdf23099e2 Mon Sep 17 00:00:00 2001 From: Thomas Spader Date: Thu, 14 May 2026 22:37:25 -0400 Subject: [PATCH 20/21] fs: join_path root fix --- sp.h | 3 +++ test/fs.c | 1 + test/fs/iter_root.c | 47 +++++++++++++++++++++++++++++++++++++++++++++ test/fs/join_path.c | 5 ++++- 4 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 test/fs/iter_root.c diff --git a/sp.h b/sp.h index 1dfc3ca..afcf93b 100644 --- a/sp.h +++ b/sp.h @@ -14696,6 +14696,9 @@ sp_str_t sp_fs_join_path(sp_mem_t mem, sp_str_t a, sp_str_t b) { b = sp_fs_trim_path(b); if (sp_str_empty(a)) return sp_str_copy(mem, b); if (sp_str_empty(b)) return sp_str_copy(mem, a); + if (sp_str_back(a) == '/' || sp_str_back(a) == '\\') { + return sp_str_concat(mem, a, b); + } return sp_str_join(mem, a, b, sp_str_lit("/")); } diff --git a/test/fs.c b/test/fs.c index 4d2cbe3..28fb88d 100644 --- a/test/fs.c +++ b/test/fs.c @@ -22,6 +22,7 @@ SP_TEST_MAIN() #include "fs/copy_glob.c" #include "fs/remove.c" #include "fs/collect.c" +#include "fs/iter_root.c" #include "fs/mod_time.c" #include "fs/system_paths.c" #include "fs/windows/wtf8.c" diff --git a/test/fs/iter_root.c b/test/fs/iter_root.c new file mode 100644 index 0000000..1a12d71 --- /dev/null +++ b/test/fs/iter_root.c @@ -0,0 +1,47 @@ +#include "fs.h" + +UTEST_F(fs, iterate_trailing_slash_no_double_separator) { + SKIP_ON_WASM() + sp_str_t sandbox = ut.file_manager.paths.test; + + fs_setup_t setup [] = { + { "iter_slash/a.txt", FS_SETUP_FILE }, + { "iter_slash/sub", FS_SETUP_DIR }, + { "iter_slash/sub/b.txt", FS_SETUP_FILE }, + sp_zero, + }; + fs_apply_setup(&ur, &ut.file_manager, sandbox, setup); + + sp_str_t base = sp_fs_join_path(ut.file_manager.mem, sandbox, sp_str_lit("iter_slash")); + sp_str_t base_slash = sp_str_concat(ut.file_manager.mem, base, sp_str_lit("/")); + + sp_da(sp_fs_entry_t) entries = sp_fs_collect_recursive(ut.file_manager.mem, base_slash); + + EXPECT_EQ(sp_da_size(entries), 3u); + sp_da_for(entries, i) { + EXPECT_FALSE(sp_str_contains(entries[i].path, sp_str_lit("//"))); + } +} + +UTEST_F(fs, iterate_name_matches_path_tail) { + SKIP_ON_WASM() + sp_str_t sandbox = ut.file_manager.paths.test; + + fs_setup_t setup [] = { + { "iter_tail/alpha.txt", FS_SETUP_FILE }, + { "iter_tail/beta", FS_SETUP_DIR }, + sp_zero, + }; + fs_apply_setup(&ur, &ut.file_manager, sandbox, setup); + + sp_str_t base = sp_fs_join_path(ut.file_manager.mem, sandbox, sp_str_lit("iter_tail")); + sp_da(sp_fs_entry_t) entries = sp_fs_collect(ut.file_manager.mem, base); + + EXPECT_EQ(sp_da_size(entries), 2u); + sp_da_for(entries, i) { + sp_fs_entry_t e = entries[i]; + sp_str_t tail = sp_str_sub(e.path, e.path.len - e.name.len, e.name.len); + EXPECT_TRUE(sp_str_equal(tail, e.name)); + EXPECT_TRUE(sp_str_equal(e.path, sp_fs_join_path(ut.file_manager.mem, base, e.name))); + } +} diff --git a/test/fs/join_path.c b/test/fs/join_path.c index e59c2eb..abc29be 100644 --- a/test/fs/join_path.c +++ b/test/fs/join_path.c @@ -14,7 +14,10 @@ UTEST(fs_join_path, cases) { { "foo/", "bar", "foo/bar" }, { "foo/", "bar/", "foo/bar" }, { "foo", "bar/baz", "foo/bar/baz" }, - { "/", "bar", "//bar" }, + { "/", "bar", "/bar" }, + { "/", "bar/baz", "/bar/baz" }, + { "C:/", "bar", "C:/bar" }, + { "C:", "bar", "C:/bar" }, { "", "bar", "bar" }, { "foo", "", "foo" }, { "", "", "" }, From ee86316a69b045d9fe1073012978f176f2355c1b Mon Sep 17 00:00:00 2001 From: Thomas Spader Date: Fri, 15 May 2026 01:58:27 -0400 Subject: [PATCH 21/21] bulk: prerelease --- .clangd | 1 + .github/DISCUSSION_TEMPLATE/issues.yml | 81 + .github/DISCUSSION_TEMPLATE/vouch-request.yml | 34 + .github/ISSUE_TEMPLATE/config.yml | 5 + .github/VOUCHED.td | 11 + .github/asset/vouch/unvouched-issue | 7 + .github/terraform/.gitignore | 5 + .github/terraform/.terraform.lock.hcl | 24 + .github/terraform/main.tf | 69 + .github/terraform/terraform.tfvars.example | 10 + .github/terraform/variables.tf | 10 + .github/workflows/ci.yml | 128 + .github/workflows/vouch-check-issue.yml | 27 + .github/workflows/vouch-check-pr.yml | 22 + .../workflows/vouch-manage-by-discussion.yml | 35 + .github/workflows/vouch-manage-by-issue.yml | 36 + .gitignore | 53 +- AGENTS.md | 19 +- CONTRIBUTING.md | 35 + Makefile | 128 +- doc/skill/SKILL.md | 167 -- example/format.c | 13 +- example/io.c | 36 +- example/io_copy_perf.c | 107 +- example/prompt_fancy.c | 14 +- sp.h | 2573 +++++++++-------- sp/sp_macho.h | 2 +- sp/sp_math.h | 12 - sp/sp_msvc.h | 4 +- sp/sp_prompt.h | 74 +- spn.lock | 21 - spn.toml | 146 - test/amalg.c | 3 +- test/bench/ht.c | 6 +- test/core.c | 44 - test/elf.c | 4 +- test/env.c | 52 +- test/etc.c | 77 + test/fmon.c | 6 +- test/format.c | 883 +++--- test/fs/canonicalize_path.c | 4 - test/fs/links.c | 2 +- test/fs/mod_time.c | 2 +- test/fs/system_paths.c | 15 - test/io/copy.c | 88 +- test/io/dyn.c | 14 +- test/io/file.c | 96 +- test/io/mem.c | 18 +- test/io/read.c | 28 +- test/io/seeking_reader.c | 4 +- test/io/write.c | 22 +- test/linkage.c | 406 --- test/{tools/process => }/process.c | 3 +- test/{tools/process => }/process.h | 0 test/prompt.c | 1 - test/ps.c | 24 +- test/tools/stress.c | 4 +- test/tools/test.h | 51 +- {tools => test/tools}/utest.h | 8 +- tools/aarch64/alpine.sh | 1 + tools/aarch64/debian.sh | 1 + tools/windows/sp/{elf.vcxproj => etc.vcxproj} | 12 +- .../examples/{elf.vcxproj => embed.vcxproj} | 124 +- .../io_copy_perf.vcxproj} | 28 +- tools/windows/sp/sp.sln | 39 +- {example => tools/wip}/elf.c | 0 {example => tools/wip}/embed.c | 10 +- {sp => tools/wip}/sp_elf.h | 2 +- 68 files changed, 3030 insertions(+), 2961 deletions(-) create mode 100644 .github/DISCUSSION_TEMPLATE/issues.yml create mode 100644 .github/DISCUSSION_TEMPLATE/vouch-request.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/VOUCHED.td create mode 100644 .github/asset/vouch/unvouched-issue create mode 100644 .github/terraform/.gitignore create mode 100644 .github/terraform/.terraform.lock.hcl create mode 100644 .github/terraform/main.tf create mode 100644 .github/terraform/terraform.tfvars.example create mode 100644 .github/terraform/variables.tf create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/vouch-check-issue.yml create mode 100644 .github/workflows/vouch-check-pr.yml create mode 100644 .github/workflows/vouch-manage-by-discussion.yml create mode 100644 .github/workflows/vouch-manage-by-issue.yml create mode 100644 CONTRIBUTING.md delete mode 100644 doc/skill/SKILL.md delete mode 100644 spn.lock delete mode 100644 spn.toml delete mode 100644 test/core.c create mode 100644 test/etc.c rename test/{tools/process => }/process.c (99%) rename test/{tools/process => }/process.h (100%) rename {tools => test/tools}/utest.h (99%) create mode 100755 tools/aarch64/alpine.sh create mode 100755 tools/aarch64/debian.sh rename tools/windows/sp/{elf.vcxproj => etc.vcxproj} (92%) rename tools/windows/sp/examples/{elf.vcxproj => embed.vcxproj} (89%) rename tools/windows/sp/{linkage.vcxproj => examples/io_copy_perf.vcxproj} (76%) rename {example => tools/wip}/elf.c (100%) rename {example => tools/wip}/embed.c (93%) rename {sp => tools/wip}/sp_elf.h (99%) diff --git a/.clangd b/.clangd index 2b5aa6c..8e66999 100644 --- a/.clangd +++ b/.clangd @@ -2,3 +2,4 @@ CompileFlags: Add: - -DSP_IMPLEMENTATION - -DSP_GLOB_IMPLEMENTATION + - -Wno-macro-redefined diff --git a/.github/DISCUSSION_TEMPLATE/issues.yml b/.github/DISCUSSION_TEMPLATE/issues.yml new file mode 100644 index 0000000..c4002c6 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/issues.yml @@ -0,0 +1,81 @@ +labels: ["needs-confirmation"] +body: + - type: textarea + attributes: + label: Description + description: What's the problem? + validations: + required: true + + - type: textarea + attributes: + label: Minimal Reproduction + description: The smallest C program which shows the issue. This must be runnable. A link to a gist is OK too. + render: c + value: | + #define SP_IMPLEMENTATION + #include "sp.h" + + s32 main(s32 num_args, const c8** args) { + // ...reproduction + return 0; + } + validations: + required: true + + - type: input + attributes: + label: sp.h commit + description: Commit SHA or release tag you're building against. + placeholder: e.g. 87aa835 or v0.3.1 + validations: + required: true + + - type: input + attributes: + label: OS + placeholder: e.g. Arch Linux, macOS 15.2, Windows 11 + validations: + required: true + + - type: dropdown + attributes: + label: Architecture + options: + - x86_64 + - aarch64 + - wasm32 + - other + validations: + required: true + + - type: dropdown + attributes: + label: Compiler + options: + - gcc + - clang + - MSVC + - mingw + - zig cc + - tcc + - cosmocc + - other + validations: + required: true + + - type: dropdown + attributes: + label: libc + options: + - glibc + - musl + - MSVCRT + - UCRT + - apple + - cosmopolitan + - WASI + - freestanding + - other + validations: + required: true diff --git a/.github/DISCUSSION_TEMPLATE/vouch-request.yml b/.github/DISCUSSION_TEMPLATE/vouch-request.yml new file mode 100644 index 0000000..d361e90 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/vouch-request.yml @@ -0,0 +1,34 @@ +body: + - type: markdown + attributes: + value: | + > [!IMPORTANT] + > This form is for **first-time contributors** who need to be vouched before submitting pull requests. Please read the [Contributing Guide](https://github.com/tspader/sp/blob/main/CONTRIBUTING.md) before submitting. + > + > Keep your request **concise** and write it **in your own voice** — do not have an AI write this for you. A maintainer will comment `!vouch` if your request is approved, after which you can submit PRs. + - type: textarea + attributes: + label: What do you want to change? + description: | + Describe the change you'd like to make to `sp.h`. If there is an existing issue or discussion, link to it. + validations: + required: true + - type: textarea + attributes: + label: Why do you want to make this change? + description: | + Explain your motivation. Why is this change important or useful? + validations: + required: true + - type: checkboxes + attributes: + label: "I acknowledge that:" + options: + - label: >- + I have read the [Contributing Guide](https://github.com/tspader/sp/blob/main/CONTRIBUTING.md) + and understand the contribution process. + required: true + - label: >- + I wrote this vouch request myself, in my + own voice, without AI generating it. + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..44bc402 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Features, Bug Reports, Questions + url: https://github.com/tspader/sp/discussions/new/choose + about: Our preferred starting point if you have any questions or suggestions about configuration, features or behavior. diff --git a/.github/VOUCHED.td b/.github/VOUCHED.td new file mode 100644 index 0000000..1818054 --- /dev/null +++ b/.github/VOUCHED.td @@ -0,0 +1,11 @@ +# Vouched contributors for this project. +# +# See https://github.com/mitchellh/vouch for details. +# +# Syntax: +# - One handle per line (without @), sorted alphabetically. +# - Optional platform prefix: platform:username (e.g., github:user). +# - Denounce with minus prefix: -username or -platform:username. +# - Optional details after a space following the handle. + +github:tspader diff --git a/.github/asset/vouch/unvouched-issue b/.github/asset/vouch/unvouched-issue new file mode 100644 index 0000000..a52d3e3 --- /dev/null +++ b/.github/asset/vouch/unvouched-issue @@ -0,0 +1,7 @@ +Thanks for opening an issue. I really appreciate anyone trying to make the project better by taking the time to open an issue. + +We use Mitchell Hashimoto's contribution model to guard against "plausible-lookingbut actually low-quality contributions" generated by AI. Instead of filing issues, I ask that you simply: +- Read [CONTRIBUTING.md](https://github.com/tspader/sp/blob/main/CONTRIBUTING.md) +- Open a Discussion instead, introducing yourself and describing the issue there instead. + +This issue will be closed automatically. diff --git a/.github/terraform/.gitignore b/.github/terraform/.gitignore new file mode 100644 index 0000000..af3bd20 --- /dev/null +++ b/.github/terraform/.gitignore @@ -0,0 +1,5 @@ +.terraform/ +terraform.tfstate +terraform.tfstate.backup +terraform.tfvars +*.pem diff --git a/.github/terraform/.terraform.lock.hcl b/.github/terraform/.terraform.lock.hcl new file mode 100644 index 0000000..f566b45 --- /dev/null +++ b/.github/terraform/.terraform.lock.hcl @@ -0,0 +1,24 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/integrations/github" { + version = "6.12.1" + constraints = "~> 6.0" + hashes = [ + "h1:bGz4LIep/7PVrqy6P8cTYbAJpdxXGrupUJjkCczlzIs=", + "zh:3e1a4081ecb9518fdf0074db83c16ad00dc81ffe8249a6e3cf1894e947e28df6", + "zh:4cb8224b7f530795b674ac044675f6b22a7c9154f55eb9f76c5af6c7534056a4", + "zh:560bc08637926191f6871a89e986022ca67c70afda5bebca34b5216e6fac69c9", + "zh:5a70b5d2ac650c5c9819a1875411ebda229d0fcc6c9f57f9d751852ca3cd77ac", + "zh:8668d93bd4dc2ffa2545e1473af600a925d479b16033a71a4498a16f3b683c0c", + "zh:86eacc6059fd057948e178b665ba5cce74bd5488a9e1035734e60ff5ef1b6f8f", + "zh:a329fac98881d8dfc211a9bdc0ec6f2948f0b0c2704d1b6cbe5307403c7ad1b2", + "zh:dadd44abab3c52b9d572955afaef1658790e17ea355ee22b58996d81d28e02d8", + "zh:de9f455ef342cc38fb76bce844bfcd376fb81a4b9f9bc2fae023ff99efdf1338", + "zh:f8c6d2e8351b334491790358574e0a30a7c6d7f5b80f7daf32a7c0f3e9b1ab19", + "zh:fab41971a3edee04ab6eceaeab4eeb9a2b2f38a2af3b06eda93e2117b64994be", + "zh:fb1279b566dd9c8c117b2e4e0cc8344413b8fc8f2a3e24be22a9b2610551777b", + "zh:fbd1fee2c9df3aa19cf8851ce134dea6e45ea01cb85695c1726670c285797e25", + "zh:fe79d2a861fb9af420fa5bd7f02c031b2a0a3edf5dbc46022c8ecc7a33cf2b6d", + ] +} diff --git a/.github/terraform/main.tf b/.github/terraform/main.tf new file mode 100644 index 0000000..1c4103c --- /dev/null +++ b/.github/terraform/main.tf @@ -0,0 +1,69 @@ +terraform { + required_version = ">= 1.5" + required_providers { + github = { + source = "integrations/github" + version = "~> 6.0" + } + } +} + +provider "github" { + owner = "tspader" +} + +resource "github_repository" "sp" { + name = "sp" + description = "A modern C standard library" + has_issues = true + has_projects = true + has_wiki = true + has_discussions = true + + lifecycle { + prevent_destroy = true + } +} + +resource "github_actions_secret" "vouch_app_id" { + repository = github_repository.sp.name + secret_name = "VOUCH_APP_ID" + value =var.vouch_app_id +} + +resource "github_actions_secret" "vouch_app_private_key" { + repository = github_repository.sp.name + secret_name = "VOUCH_APP_PRIVATE_KEY" + value =var.vouch_app_private_key +} + +resource "github_repository_ruleset" "main" { + name = "main" + repository = github_repository.sp.name + target = "branch" + enforcement = "active" + + conditions { + ref_name { + include = ["~DEFAULT_BRANCH"] + exclude = [] + } + } + + bypass_actors { + actor_id = 5 # RepositoryRole: admin + actor_type = "RepositoryRole" + bypass_mode = "always" + } + + bypass_actors { + actor_id = var.vouch_app_id + actor_type = "Integration" + bypass_mode = "always" + } + + rules { + deletion = true + non_fast_forward = true + } +} diff --git a/.github/terraform/terraform.tfvars.example b/.github/terraform/terraform.tfvars.example new file mode 100644 index 0000000..e3fa8dd --- /dev/null +++ b/.github/terraform/terraform.tfvars.example @@ -0,0 +1,10 @@ +vouch_app_id = 1234567 +vouch_app_installation_id = 89012345 + +# Paste the full .pem contents between the EOT markers, including the +# BEGIN/END lines. Indentation is stripped by <<-. +vouch_app_private_key = <<-EOT +-----BEGIN RSA PRIVATE KEY----- +... +-----END RSA PRIVATE KEY----- +EOT diff --git a/.github/terraform/variables.tf b/.github/terraform/variables.tf new file mode 100644 index 0000000..27da60a --- /dev/null +++ b/.github/terraform/variables.tf @@ -0,0 +1,10 @@ +variable "vouch_app_id" { + type = number + description = "App ID from the Vouch GitHub App's General page." +} + +variable "vouch_app_private_key" { + type = string + description = "Private key for the GitHub App installed to the repository" + sensitive = true +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1cb703c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,128 @@ +name: ci +on: + workflow_dispatch: + pull_request: + push: + tags: ['v*'] + +jobs: + matrix: + runs-on: ubuntu-latest + outputs: + include: ${{ steps.set.outputs.include }} + steps: + - id: set + shell: bash + run: | + { + echo 'include<> $GITHUB_OUTPUT + + build-zig: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: mlugg/setup-zig@v2 + with: + version: master + - run: make big + - uses: actions/upload-artifact@v4 + with: + name: build-all + path: build/ + + build-msvc: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - uses: microsoft/setup-msbuild@v2 + - run: msbuild tools\windows\sp\sp.sln /p:Configuration=Release /p:Platform=x64 /m + - uses: actions/upload-artifact@v4 + with: + name: build-msvc + path: tools/windows/sp/build/** + + test: + needs: [matrix, build-zig] + strategy: + fail-fast: false + matrix: + include: ${{ fromJSON(needs.matrix.outputs.include) }} + runs-on: ${{ matrix.runs-on }} + steps: + - uses: actions/download-artifact@v4 + with: + name: build-all + path: build + - shell: bash + run: | + for dir in build/${{ matrix.triple }}/test build/cpp/${{ matrix.triple }}/test; do + chmod +x "$dir"/*${{ matrix.ext }} + for t in "$dir"/*${{ matrix.ext }}; do + echo "=== $t ===" + "$t" || exit 1 + done + done + + test-wasm: + needs: build-zig + runs-on: ubuntu-latest + steps: + - uses: actions/download-artifact@v4 + with: + name: build-all + path: build + - uses: bytecodealliance/actions/wasmtime/setup@v1 + - shell: bash + run: | + for dir in build/wasm32-wasi/test build/cpp/wasm32-wasi/test; do + for t in "$dir"/*.wasm; do + echo "=== $t ===" + wasmtime run "$t" || exit 1 + done + done + + test-msvc: + needs: build-msvc + runs-on: windows-latest + steps: + - uses: actions/download-artifact@v4 + with: + name: build-msvc + path: bin + - shell: bash + run: for t in bin/**/test/*.exe; do echo "=== $t ==="; "$t" || exit 1; done + + release: + if: startsWith(github.ref, 'refs/tags/v') + needs: [test, test-wasm, test-msvc] + runs-on: ubuntu-latest + permissions: { contents: write } + steps: + - uses: actions/download-artifact@v4 + with: { path: dist, pattern: build-* } + - run: | + mkdir release + for d in dist/build-all/*/; do + t=$(basename "$d") + [ -d "$d/test" ] || continue + tar -czf release/sp-$t.tar.gz -C dist/build-all "$t" + done + [ -d dist/build-msvc ] && tar -czf release/sp-msvc.tar.gz -C dist build-msvc || true + - uses: softprops/action-gh-release@v2 + with: + files: release/*.tar.gz + generate_release_notes: true diff --git a/.github/workflows/vouch-check-issue.yml b/.github/workflows/vouch-check-issue.yml new file mode 100644 index 0000000..f3d3c40 --- /dev/null +++ b/.github/workflows/vouch-check-issue.yml @@ -0,0 +1,27 @@ +on: + issues: + types: [opened, reopened] + +name: "Vouch - Check Issue" + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 + id: app-token + with: + app-id: ${{ secrets.VOUCH_APP_ID }} + private-key: ${{ secrets.VOUCH_APP_PRIVATE_KEY }} + + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + sparse-checkout: .github/asset/vouch/unvouched-issue + + - uses: mitchellh/vouch/action/check-issue@c6d80ead49839655b61b422700b7a3bc9d0804a9 # v1.4.2 + with: + issue-number: ${{ github.event.issue.number }} + auto-close: true + template-file: .github/asset/vouch/unvouched-issue + env: + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} diff --git a/.github/workflows/vouch-check-pr.yml b/.github/workflows/vouch-check-pr.yml new file mode 100644 index 0000000..ecea450 --- /dev/null +++ b/.github/workflows/vouch-check-pr.yml @@ -0,0 +1,22 @@ +on: + pull_request_target: + types: [opened, reopened] + +name: "Vouch - Check PR" + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 + id: app-token + with: + app-id: ${{ secrets.VOUCH_APP_ID }} + private-key: ${{ secrets.VOUCH_APP_PRIVATE_KEY }} + + - uses: mitchellh/vouch/action/check-pr@c6d80ead49839655b61b422700b7a3bc9d0804a9 # v1.4.2 + with: + pr-number: ${{ github.event.pull_request.number }} + auto-close: true + env: + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} diff --git a/.github/workflows/vouch-manage-by-discussion.yml b/.github/workflows/vouch-manage-by-discussion.yml new file mode 100644 index 0000000..75c2948 --- /dev/null +++ b/.github/workflows/vouch-manage-by-discussion.yml @@ -0,0 +1,35 @@ +on: + discussion_comment: + types: [created] + +name: "Vouch - Manage by Discussion" + +concurrency: + group: vouch-manage + cancel-in-progress: false + +jobs: + manage: + runs-on: ubuntu-latest + steps: + - uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 + id: app-token + with: + app-id: ${{ secrets.VOUCH_APP_ID }} + private-key: ${{ secrets.VOUCH_APP_PRIVATE_KEY }} + + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + token: ${{ steps.app-token.outputs.token }} + + - uses: mitchellh/vouch/action/manage-by-discussion@c6d80ead49839655b61b422700b7a3bc9d0804a9 # v1.4.2 + with: + discussion-number: ${{ github.event.discussion.number }} + comment-node-id: ${{ github.event.comment.node_id }} + vouch-keyword: "!vouch" + denounce-keyword: "!denounce" + unvouch-keyword: "!unvouch" + pull-request: "true" + merge-immediately: "true" + env: + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} diff --git a/.github/workflows/vouch-manage-by-issue.yml b/.github/workflows/vouch-manage-by-issue.yml new file mode 100644 index 0000000..dbf7484 --- /dev/null +++ b/.github/workflows/vouch-manage-by-issue.yml @@ -0,0 +1,36 @@ +on: + issue_comment: + types: [created] + +name: "Vouch - Manage by Issue" + +concurrency: + group: vouch-manage + cancel-in-progress: false + +jobs: + manage: + runs-on: ubuntu-latest + steps: + - uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 + id: app-token + with: + app-id: ${{ secrets.VOUCH_APP_ID }} + private-key: ${{ secrets.VOUCH_APP_PRIVATE_KEY }} + + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + token: ${{ steps.app-token.outputs.token }} + + - uses: mitchellh/vouch/action/manage-by-issue@c6d80ead49839655b61b422700b7a3bc9d0804a9 # v1.4.2 + with: + repo: ${{ github.repository }} + issue-id: ${{ github.event.issue.number }} + comment-id: ${{ github.event.comment.id }} + vouch-keyword: "!vouch" + denounce-keyword: "!denounce" + unvouch-keyword: "!unvouch" + pull-request: "true" + merge-immediately: "true" + env: + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} diff --git a/.gitignore b/.gitignore index ed77e3f..a13e0bc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,32 +1,25 @@ -build .cache -.cache/* -compile_commands.json - -.spackle/* -!.spackle/spackle.py - -external/* -!external/llm - -.claude -.mcp.json -CLAUDE.md -doc/* -!doc/skill -**/.DS_Store -**/*.dSYM -/.llm/reference -/.llm -**/.vs -**/*.vcxproj.user -/mutagen.yml.lock +.DS_Store +.env +.env.* +.llm .tmp -**/*.exe -**/*.ilk -**/*.obj -**/*.pdb -**/*.idx -/mutagen.yml -/tools/msys64 -/tools/w64devkit +.vs +*.a +*.dll +*.dSYM +*.dylib +*.exe +*.key +*.idx +*.ilk +*.lib +*.o +*.obj +*.pdb +*.pem +*.so +*.vcxproj.user +build +compile_commands.json +doc diff --git a/AGENTS.md b/AGENTS.md index 13287a5..472e30c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,22 +10,6 @@ sp.h is a single-header C standard library replacement which focuses on building - `test/tools/*`: code for modules which test external processes - `tools/`: random, unstructured bullshit which is not part of the official build -## The Big One -Compile everything; freestanding, GNU, MUSL, Windows, macOS. Do this before marking a task done. Do not do this between small tweaks or changes. -```sh -make -make TRIPLE=x86_64-linux-gnu -make TRIPLE=x86_64-linux-musl -make TRIPLE=x86_64-linux-none -make TRIPLE=x86_64-windows-gnu -make TRIPLE=aarch64-macos -./build/sp x86_64-linux-none"*" -./build/sp x86_64-linux-musl "*" -./build/sp x86_64-linux-gnu "*" -./build/sp aarch64-macos "*" -./build/sp x86_64-windows-gnu"*" -``` - # quick reference - `sp_da`: stb-style dynamic array - `sp_ht`: hash table, arbitrary keys and values @@ -38,9 +22,8 @@ make TRIPLE=aarch64-macos - `sp_os`: platform polyfills # rules -- Always run The Big One before marking a task as done. - Never submit code with new comments. Code with new comments will be rejected outright. -- Never delete pre-existing comments. +- Never delete pre-existing comments unless they have become wrong - Never use libc unless *explicitly* implementing `sp_sys` on a libc platform - `malloc` + `free` -> `sp_alloc` + `sp_free` (prefer `sp_alloc_type` and `sp_alloc_n` to avoid casts) - `strcmp`, `strlen`, etc. -> `sp_cstr_*` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..25c2a8a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,35 @@ +# Contributing to sp.h +`sp.h` is still a newly public project. There's not a lot of precedent for contributing, and most of what's here comes from Mitchell Hashimoto's excellent [Ghostty](https://github.com/ghostty-org/ghostty) or [Vouch](https://github.com/mitchellh/vouch). + +In general, any contributions are accepted: +- Bug reports, obviously, and patches for bugs +- Improvements. I am not a genius of library design, and I make bad decisions all the time. If you have an idea to make any part of the code better, I would love to hear it. +- New features. I try to keep `sp.h` lean, but I will read a proposal for new code in the main header. +- New baseball libraries. Anything that is useful and high quality can be shipped as a double header library in `sp/sp_*.h`. + +If you want to talk about any of these things, or anything related to software at any time, pop in at [#sp.h](https://web.libera.chat/?channel=#sp.h) on [irc.libera.chat](https://libera.chat). It would make my day. + +## First-Time Contributors +If you've never contributed, you must open a discussion. Do not open an issue or PR through GitHub, or it will be closed. If you'd like to report an issue, submit a PR, ask a question, or talk shop, I would be delighted to talk to you: + +1. Open a [discussion in the "Vouch Request"](https://github.com/tspader/sp/discussions/new?category=vouch-request) category describing what you want to change and why. Follow the template. +2. Keep it concise +3. Write in your own voice, don't have an AI write this +4. We'll hash out the details together, and I'll give you permissions to create a PR, an issue, etc. + +Don't take this to mean I'm unfriendly to PRs, or that you shouldn't bother with a patch because it'll get rejected. Exactly the opposite. I would love to have anything you'd contribute, and I'll put in effort to get you what you need with as little friction as possible. Keeping issues and PRs as thoughtful artifacts is just a matter of hygiene. + +## AI +LLMs are transformative. Use them however you like. However, I have zero tolerance for people who use AI as a tool to disrespect open source maintainers and projects. This means: +- Never paste an AI generated response to a discussion, issue, PR, or otherwise. Ever. +- Never submit code that you are not personally familiar with every line of. +- Always disclose AI usage when asked. + +I will permaban anyone who breaks any of these rules without a second thought. That being said, please don't be afraid of using AI in fear of a ban. These rules are meant to prevent bad actors. I'm not going to permaban you if you make a small mistake. To quote Ghostty's AI policy: + +"Ghostty is written with plenty of AI assistance, and many maintainers embrace AI tools as a productive tool in their workflow. As a project, we welcome AI as a tool! + +**Our reason for the strict AI policy is not due to an anti-AI stance**, but instead due to the number of highly unqualified people using AI. It's the people, not the tools, that are the problem. + +I include this section to be transparent about the project's usage about AI for people who may disagree with it, and to address the misconception that this policy is anti-AI in nature."" + diff --git a/Makefile b/Makefile index 60ddc2d..addbb44 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,8 @@ -# Default to parallel when invoked directly. Skipped under a parent jobserver -# (e.g. the `big` target's sub-makes) to avoid oversubscription. ifeq (,$(findstring jobserver,$(MAKEFLAGS))) -MAKEFLAGS += -j$(shell nproc) + MAKEFLAGS += -j$(shell nproc) endif -LANG ?= c -ifeq ($(LANG),cpp) +ifeq ($(MODE), cpp) CFLAGS_LANG = -std=c++11 -x c++ BUILD_ROOT = build/cpp DEFAULT_CC = c++ @@ -14,121 +11,80 @@ else CFLAGS_LANG = -std=c99 BUILD_ROOT = build DEFAULT_CC = cc - ZIG_CC = zcc + ZIG_CC = zig cc endif ifdef TRIPLE -CC := $(ZIG_CC) --target=$(TRIPLE) -BUILD_DIR = $(BUILD_ROOT)/$(TRIPLE) + CC := $(ZIG_CC) --target=$(TRIPLE) + BUILD_DIR = $(BUILD_ROOT)/$(TRIPLE) else ifeq ($(origin CC),command line) -BUILD_DIR = $(BUILD_ROOT)/$(CC) + BUILD_DIR = $(BUILD_ROOT)/$(CC) else -CC := $(DEFAULT_CC) -BUILD_DIR = $(BUILD_ROOT) + CC := $(DEFAULT_CC) + BUILD_DIR = $(BUILD_ROOT) endif -WASM := $(if $(findstring wasm,$(TRIPLE)),1,) -WASM_FREESTANDING := $(if $(WASM),$(if $(findstring freestanding,$(TRIPLE)),1,),) -LINUX_FREESTANDING := $(if $(WASM),,$(if $(findstring linux-none,$(TRIPLE)),1,)) - -ifdef LINUX_FREESTANDING -CFLAGS_PLATFORM = -nostdlib -static -fno-stack-protector -fno-sanitize=undefined -DSP_FREESTANDING -else ifdef WASM_FREESTANDING -CFLAGS_PLATFORM = -nostdlib -fno-sanitize=undefined -else ifdef WASM CFLAGS_PLATFORM = +ifneq (,$(findstring linux-none,$(TRIPLE))) + CFLAGS_PLATFORM = -nostdlib -static -fno-stack-protector -fno-sanitize=undefined -DSP_FREESTANDING +endif +ifneq (,$(findstring wasm32-freestanding,$(TRIPLE))) + CFLAGS_PLATFORM = -nostdlib -fno-sanitize=undefined +endif + +ifneq (,$(findstring windows,$(TRIPLE))) + EXE := .exe +else ifneq (,$(findstring wasm,$(TRIPLE))) + EXE := .wasm else -CFLAGS_PLATFORM = + EXE := endif CFLAGS = $(CFLAGS_LANG) -g -Werror=return-type -fsanitize=undefined,alignment -fno-sanitize-recover=all $(CFLAGS_PLATFORM) +CFLAGS_TEST = -DSP_IMPLEMENTATION -DSP_TEST_IMPLEMENTATION -I. -Itest/tools -Itest -EXE := $(if $(findstring windows,$(TRIPLE)),.exe,) -EXE := $(if $(WASM),.wasm,$(EXE)) -TESTS = amalg app array asset cv elf env format fmon fs glob ht io linkage math ps rb str thread time mem prompt leak -EXAMPLES = app array elf embed format hash_table io io_copy_perf ls palette prompt prompt_fancy signal wc -# TESTS = app amalg str format -# EXAMPLES = app format hash_table +TESTS = amalg app array asset etc cv env format fmon fs glob ht io math process ps rb str thread time mem prompt leak +EXAMPLES = app array format hash_table io io_copy_perf ls palette prompt prompt_fancy signal wc TRIPLES = \ x86_64-linux-none x86_64-linux-gnu x86_64-linux-musl \ aarch64-linux-none aarch64-linux-gnu aarch64-linux-musl \ aarch64-macos \ x86_64-windows-gnu \ wasm32-freestanding wasm32-wasi -SMOKE = \ - x86_64-linux-none x86_64-linux-musl \ - aarch64-linux-musl \ - aarch64-macos \ - x86_64-windows-gnu \ - wasm32-freestanding wasm32-wasi - -.PHONY: all clean tests examples $(TRIPLES) big gcc tcc -all: examples tests tools - TEST_DIR = $(BUILD_DIR)/test -TEST_BINARIES = $(addsuffix $(EXE), $(addprefix $(TEST_DIR)/, $(TESTS))) -TEST_BINS = $(TEST_DIR)/process$(EXE) - EXAMPLE_DIR = $(BUILD_DIR)/example -EXAMPLE_BINARIES = $(addsuffix $(EXE), $(addprefix $(EXAMPLE_DIR)/, $(EXAMPLES))) +TEST_BINARIES = $(addsuffix $(EXE),$(addprefix $(TEST_DIR)/,$(TESTS))) +EXAMPLE_BINARIES = $(addsuffix $(EXE),$(addprefix $(EXAMPLE_DIR)/,$(EXAMPLES))) + +SP_HEADERS = sp.h $(wildcard sp/*.h) +TEST_SOURCES = $(wildcard test/*/*.c) $(wildcard test/*/*.h) $(wildcard test/*/*/*.c) $(wildcard test/*/*/*.h) +.PHONY: all clean tests examples smoke big c cpp gcc tcc $(TRIPLES) +all: examples tests tests: $(TEST_BINARIES) examples: $(EXAMPLE_BINARIES) -tools: build/sp -############ -# EXAMPLES # -############ -$(EXAMPLE_DIR)/prompt$(EXE): example/prompt.c sp.h sp/sp_prompt.h | $(EXAMPLE_DIR) +$(EXAMPLE_DIR)/%$(EXE): example/%.c $(SP_HEADERS) | $(EXAMPLE_DIR) $(CC) $(CFLAGS) -I. -o $@ $< -$(EXAMPLE_DIR)/%$(EXE): example/%.c sp.h | $(EXAMPLE_DIR) - $(CC) $(CFLAGS) -I. -o $@ $< - -######### -# TESTS # -######### -CFLAGS_TEST = -DSP_IMPLEMENTATION -DSP_TEST_IMPLEMENTATION -I. -Itools -Itest/tools -Itest/tools/process -Itest - -TEST_SOURCES = $(wildcard test/*/*.c) $(wildcard test/*/*.h) $(wildcard test/*/*/*.c) $(wildcard test/*/*/*.h) - -build/sp: tools/sp.c sp.h | $(BUILD_DIR) - cc -g -I. -o $@ $< - -$(TEST_DIR)/%$(EXE): test/%.c sp.h $(TEST_SOURCES) | $(TEST_DIR) $(TEST_DIR)/process$(EXE) +$(TEST_DIR)/%$(EXE): test/%.c $(SP_HEADERS) $(TEST_SOURCES) | $(TEST_DIR) $(CC) $(CFLAGS) $(CFLAGS_TEST) -o $@ $< -$(TEST_DIR)/process$(EXE): test/tools/process/process.c sp.h | $(TEST_DIR) - $(CC) $(CFLAGS) $(CFLAGS_TEST) -o $@ $< - -######### -# CROSS # -######### $(TRIPLES): +$(MAKE) TRIPLE=$@ examples tests -smoke: $(SMOKE) -big: $(TRIPLES) gcc tcc examples tests - -gcc: - +$(MAKE) CC=gcc examples tests +big: c cpp +c:; +$(MAKE) $(TRIPLES) examples tests gcc +cpp:; +$(MAKE) MODE=cpp $(TRIPLES) examples tests +gcc:; +$(MAKE) CC=gcc examples tests +tcc:; +$(MAKE) CC=tcc examples tests +wasm: + +$(MAKE) wasm32-wasi wasm32-freestanding + +$(MAKE) MODE=cpp wasm32-wasi wasm32-freestanding -tcc: - +$(MAKE) CC=tcc examples tests +$(BUILD_DIR) $(EXAMPLE_DIR) $(TEST_DIR): + mkdir -p $@ clean: rm -rf $(BUILD_DIR) - -######### -# UTILS # -######### -$(BUILD_DIR): - mkdir -p $(BUILD_DIR) - -$(EXAMPLE_DIR): - mkdir -p $(EXAMPLE_DIR) - -$(TEST_DIR): - mkdir -p $(TEST_DIR) - diff --git a/doc/skill/SKILL.md b/doc/skill/SKILL.md deleted file mode 100644 index e341421..0000000 --- a/doc/skill/SKILL.md +++ /dev/null @@ -1,167 +0,0 @@ ---- -name: sp -description: Guide for sp.h, a single-header C standard library replacement. You must use this guide when using or discussing sp.h in any capacity. -license: MIT ---- - -# sp.h Overview -- sp.h is a single-header C standard library replacement -- You MUST annotate references to functions from sp.h with verbatim function headers -- When providing references to code from `sp.h`, you MUST provide a matching declaration from `references/index.md`. Function names without the full declaration are COMPLETELY useless, and WILL NOT be tolerated. - -user: How do I use the asset registry from sp.h? -assistant: [Reads the index, uses the Task tool to search through sources bundled with skill (sp.h, spn.c), includes "sp_str_t sp_str_sub(sp_str_t str, s32 index, s32 len)" in answer] - - -user: Write a function that reads a file and logs its contends -assistant: [Searches through bundled source code with Task tool to find relevant APIs and writes function] - -- NEVER, EVER MODIFY THE REFERENCE CODE - -## Usage -- Search `references/index.md` before trying to search through the codebase. Do not guess; refer to `references/index.md` to find a precise search term. - -user: How do I read a file in sp.h? -assistant: [Reads index.md, searches through sp.h and spn.c with Task tool, provides concise, annotated answer] - -- Search through `references/sp.h` judiciously as needed; do not guess symbol names, function signatures, or implementation details. Read the source code. -- Function signatures are prefixed with `SP_API` -- Types are prefixed with `sp_` and suffixed with `_t` - -## Rules -- Never use `malloc`, `calloc`, or `realloc`; use `sp_alloc` (which zero initializes) -- Unless explicitly interfacing with an existing C API, never use `const char*`; use `sp_str_t` (pointer + length) -- Never use `strcmp`, `strlen`, or any `string.h` functions with `sp_str_t`; use `sp_str_*` -- Never use `strcmp`, `strlen`, or any `string.h` functions with `const char*`; use `sp_cstr_*` -- Always use `sp_zero`. When you need a type, use `sp_zero_s(T)` -- Always use `sp_da(T)` and `sp_ht(T)` for dynamic arrays and hash maps (`sp_da_*` and `sp_ht_*`) -- Always use `sp_da_for(arr, it)` and `sp_ht_for(ht, it)` to iterate sp_da and sp_ht -- Never check `str.len > 0`; always use `!sp_str_empty(str)` -- Always use C99 designated initializers for struct literals when possible -- Always use short literal types (`s32`, `u8`, `c8`, `const c8*`) -- Never use `printf` family; always use `sp_log()` -- Always use `sp_carr_for()` when iterating a C array -- Always explicitly handle all enum cases in a switch statement. Fallthroughs are OK, `default` is not. - -## Namespaces -Use these when searching through `references/index.md`, `references/sp.h`, or `references/spn.c` -- Memory: `sp_alloc`, `sp_context`, `sp_allocator`, `sp_os` -- Strings: `sp_str`, `sp_str_builder`, `sp_cstr` -- Containers: `sp_da` / `sp_da`, `sp_ht`, `sp_rb` -- IO: `sp_io` -- Process: `sp_ps` -- Filesystem: `sp_os` -- Platform: `sp_os` -- Time: `sp_tm` -- Concurrency: `sp_thread`, `sp_mutex`, `sp_semaphore`, `sp_atomic`, `sp_spin_lock` -- Logging: `sp_fmt`, `sp_log`, `sp_fmt_*` - -## Common Patterns -### Initialization -```c -// Always zero-initialize structs -sp_str_builder_t builder = sp_zero; -sp_dynamic_array_t arr = sp_zero; -``` - -### String Handling -```c -// Create strings -sp_str_t literal = sp_str_lit("hello"); // Compile-time string literal -sp_str_t view = sp_str_view(some_char_ptr); // Runtime C string (calculates length) -sp_str_t copy = sp_str_from_cstr("hello"); // Allocates and copies -const char* cstr = sp_str_to_cstr(str); -``` - -### Dynamic Arrays (stb-style) -```c -sp_da(int) numbers = SP_NULLPTR; -sp_da_push(numbers, 42); -sp_da_push(numbers, 100); - -sp_da_for(numbers, i) { - sp_log("numbers[{}] = {}", sp_fmt_uint(i), sp_fmt_int(numbers[i])); -} - -u32 count = sp_da_size(numbers); -u32 capacity = sp_da_capacity(numbers); - -// Cleanup happens automatically via allocator -``` - -### Hash Tables (stb-style) -```c -sp_ht(s32, s32) hta = SP_NULLPTR; -sp_ht(sp_str_t, s32) htb = SP_NULLPTR; -sp_ht_set_fns(hta, sp_ht_on_hash_str_key, sp_ht_on_compare_str_key); - -sp_ht_insert(htb, sp_str_lit("answer"), 42); - -s32* value_ptr = sp_ht_getp(htb, sp_str_lit("answer")); - -sp_ht_key_exists(htb, sp_str_lit("answer")); - -sp_ht_for(htb, it) { - sp_str_t* key = sp_ht_it_getkp(map, it); - s32* val = sp_ht_it_getp(map, it); -} - -// Cleanup happens automatically via allocator -``` - -### Formatting and Logging -```c -// Type-safe formatting with color support -sp_log( - "Processing {.fg cyan} with {} {}", - sp_fmt_str(name), - sp_fmt_uint(count), - sp_fmt_cstr("items") -); - -sp_str_t msg = sp_fmt("Result: {}", sp_fmt_int(42)); - -// Colors: .fg, .bg -// Colors: black, red, green, yellow, blue, magenta, cyan, white -// Add 'bright' prefix for bright variants -``` - -### Switch Statements -```c -// Always use braces, always handle all cases -switch (state) { - case STATE_IDLE: { - break; - } - case STATE_RUNNING: { - break; - } - default: { - SP_UNREACHABLE_CASE(); - } -} -``` - -### Error Handling -```c -// Return an enum for recoverable errors (consumer app may have their own error type) -sp_err_t load_config(sp_str_t path, config_t* config) { - if (!sp_os_does_path_exist(path)) { - sp_log("Config not found: {}", sp_fmt_str(path)); - return SP_ERR_WHATEVER; - } - - return SP_ERR_OK; -} - -// Prefer to SP_ASSERT when possible -void process_array(int* arr, u32 size) { - SP_ASSERT(arr); - SP_ASSERT(size > 0); -} - -// SP_FATAL is sp_log + SP_ASSERT(false) -if (critical_failure) { - SP_FATAL("Cannot continue: {.fg red}", sp_fmt_str(reason)); -} -``` diff --git a/example/format.c b/example/format.c index b1be0b5..5631294 100644 --- a/example/format.c +++ b/example/format.c @@ -12,8 +12,7 @@ // callers will use as wrappers over sp_fmt_custom() typedef struct { f32 x; f32 y; } point_t; -void format_point(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { - sp_unused(mem); +void format_point(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { point_t* point = (point_t*)arg->value.custom.ptr; u32 precision = sp_opt_is_null(arg->spec.precision) ? 2 : sp_opt_get(arg->spec.precision); sp_io_write_c8(io, '('); @@ -61,17 +60,12 @@ s32 run(s32 num_args, const c8** args) { sp_log("{.green} has Zig/Rust style format specifiers (fill, align, width), plus named directives which may:", sp_fmt_cstr("sp.h")); sp_log("- {} text from a format argument", sp_fmt_cstr("render")); sp_log("- {.bold .cyan} the rendered text", sp_fmt_cstr("decorate")); - sp_log("- {.upper} the rendered text", sp_fmt_cstr("transform")); section("decorators"); sp_log("{:<14 .italic} -> hello, {.bold}", sp_fmt_cstr("hello, {.bold}"), sp_fmt_cstr("world")); sp_log("{:<14 .italic} -> {.hyperlink}", sp_fmt_cstr("{.hyperlink}"), sp_fmt_cstr("https://spader.zone")); sp_log("{:<14 .italic} -> {.quote}", sp_fmt_cstr("{.quote}"), sp_fmt_cstr("supposedly")); - section("transformers"); - sp_log(".upper -> {.upper}", sp_fmt_cstr("hello world")); - sp_log(".redact -> {.redact}", sp_fmt_cstr("hunter2")); - section(".bytes"); u64 byte_samples[] = { 0ULL, 512ULL, 1536ULL, 10485760ULL, 5368709120ULL }; sp_carr_for(byte_samples, i) { @@ -107,12 +101,9 @@ s32 run(s32 num_args, const c8** args) { section("composition"); struct { const c8* name; sp_str_t str; } examples [] = { - { ".bold + .upper", sp_fmt(mem, "i never {.bold .upper} the kenosha kid", sp_fmt_cstr("did")).value }, { ".italic + .bold", sp_fmt(mem, "i never {.italic .bold} the kenosha kid", sp_fmt_cstr("did")).value }, - { ".quote + .upper", sp_fmt(mem, "i never {.quote .upper} the kenosha kid", sp_fmt_cstr("did")).value }, - { ".redact + .bold", sp_fmt(mem, "i never {.redact .bold} the kenosha kid", sp_fmt_cstr("did")).value }, { ".bold + .cyan", sp_fmt(mem, "i never {.bold .cyan} the kenosha kid", sp_fmt_cstr("did")).value }, - { "kitchen sink", sp_fmt(mem, "i never {.quote .bold .italic .upper .green} the kenosha kid", sp_fmt_cstr("did")).value }, + { "kitchen sink", sp_fmt(mem, "i never {.quote .bold .italic .green} the kenosha kid", sp_fmt_cstr("did")).value }, { ".bytes + :specifier", sp_fmt(mem, "{} bytes is [{:^$ .bytes}]", sp_fmt_uint(1536), sp_fmt_uint(12), sp_fmt_uint(1536)).value diff --git a/example/io.c b/example/io.c index 0f85b7e..91a9d2b 100644 --- a/example/io.c +++ b/example/io.c @@ -20,41 +20,21 @@ s32 run(s32 num_args, const c8** args) { sp_io_file_reader_close(&r); sp_mem_zero(buffer.data, buffer.capacity); - // Or, if you already have a file descriptor, wrap it. sp_io_file_t is just an alias - // for the platform native file handle integer. - // - // sp_io will close file descriptors for you automatically if you pass SP_IO_CLOSE_MODE_AUTO - sp_sys_fd_t fd = sp_sys_open_s(exe, SP_O_RDONLY | SP_O_BINARY, 0); - sp_io_file_reader_from_file(&r, (sp_io_file_t)fd, SP_IO_CLOSE_MODE_AUTO); + // Or, if you already have a file descriptor, wrap it. sp_io will close file descriptors + // for you automatically if you pass SP_IO_CLOSE_MODE_AUTO + sp_sys_fd_t fd = sp_sys_open_s(sp_fs_open_cwd(), exe, SP_O_RDONLY | SP_O_BINARY, 0); + sp_io_file_reader_from_file(&r, (sp_sys_fd_t)fd, SP_IO_CLOSE_MODE_AUTO); sp_io_read(&r.base, buffer.data, buffer.capacity, &buffer.len); sp_log("sp_io_file_reader_from_file: {}", sp_fmt_str(sp_mem_buffer_as_str(&buffer))); sp_io_file_reader_close(&r); - // sp_io_writer_t is the basis for the string builder, and therefore format strings. This - // means that any byte buffer can be trivially formatted to - c8 str [256] = sp_zero; - sp_io_mem_writer_t mw = sp_zero; - sp_io_mem_writer_from_buffer(&mw, str, 256); - { - sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_fmt_io(&mw.base, s.mem, "hello, {}", sp_fmt_cstr("world")); - sp_mem_end_scratch(s); - } - sp_log("sp_io_writer_from_mem: {}", sp_fmt_cstr(str)); - - sp_mem_zero(str, 256); - // You can also format directly to stdout - sp_io_file_writer_t fw = sp_zero; - sp_io_file_writer_from_fd(&fw, sp_sys_stdout, SP_IO_CLOSE_MODE_NONE); - { - sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_fmt_io(&fw.base, s.mem, "hello, {.cyan}", sp_fmt_cstr("stdout")); - sp_mem_end_scratch(s); - } + sp_io_stream_writer_t fw = sp_zero; + sp_io_stream_writer_from_fd(&fw, sp_sys_stdout, SP_IO_CLOSE_MODE_NONE); + sp_fmt_io(&fw.base, "hello, {.cyan}", sp_fmt_cstr("stdout")); sp_io_write(&fw.base, "\n", 1, SP_NULLPTR); - sp_io_file_writer_close(&fw); + sp_io_stream_writer_close(&fw); // When a reader is drained, sp_io_read returns SP_ERR_IO_EOF with // bytes_read == 0. Any successful call, even a short one, returns SP_OK; diff --git a/example/io_copy_perf.c b/example/io_copy_perf.c index 4f5ac00..b417999 100644 --- a/example/io_copy_perf.c +++ b/example/io_copy_perf.c @@ -27,7 +27,7 @@ static sp_err_t make_source(sp_str_t path, u64 size_bytes, sp_mem_t mem) { fill_random(chunk, 1u << 20, 0xdeadbeefcafebabeull); sp_io_file_writer_t w = sp_zero; - sp_try(sp_io_file_writer_from_path(&w, path, SP_IO_WRITE_MODE_OVERWRITE)); + sp_try(sp_io_file_writer_from_path(&w, path)); u64 remaining = size_bytes; while (remaining) { @@ -47,7 +47,7 @@ static run_t copy_naive(sp_str_t src, sp_str_t dst, sp_mem_t mem) { sp_io_file_reader_t r = sp_zero; sp_io_file_writer_t w = sp_zero; sp_io_file_reader_from_path(&r, src); - sp_io_file_writer_from_path(&w, dst, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&w, dst); u8* buf = sp_alloc_n(mem, u8, PERF_NAIVE_BUFFER); u64 copied = 0; @@ -68,7 +68,7 @@ static run_t copy_fast(sp_str_t src, sp_str_t dst) { sp_io_file_reader_t r = sp_zero; sp_io_file_writer_t w = sp_zero; sp_io_file_reader_from_path(&r, src); - sp_io_file_writer_from_path(&w, dst, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&w, dst); u64 copied = 0; sp_tm_timer_t t = sp_tm_start_timer(); @@ -83,6 +83,88 @@ static run_t copy_fast(sp_str_t src, sp_str_t dst) { return (run_t){ .bytes = copied, .ns = ns }; } +#if defined(SP_LINUX) +// Drain a pipe's read end into the void as fast as possible. The sendfile +// benchmark needs a consumer; without one, the pipe's 64 KiB buffer fills +// and the producing call blocks (or, with a non-blocking pipe, returns +// EAGAIN). We discard everything we read. +static s32 drain_pipe(void* userdata) { + sp_sys_fd_t fd = (sp_sys_fd_t)(uintptr_t)userdata; + u8 buf [1u << 16]; + while (true) { + s64 rc = sp_sys_read(fd, buf, sizeof(buf)); + if (rc <= 0) break; + } + sp_sys_close(fd); + return 0; +} + +// Open a blocking pipe. sp_sys_pipe sets O_NONBLOCK, which is wrong for this +// benchmark: with the consumer on another thread, we want producing calls to +// block when the pipe buffer is full, not bail with EAGAIN. +static s32 open_blocking_pipe(sp_sys_fd_t* out_r, sp_sys_fd_t* out_w) { + sp_sys_fd_t fds [2]; + s64 rc = sp_syscall(SP_SYSCALL_NUM_PIPE2, fds, SP_O_CLOEXEC, 0, 0, 0); + if (rc < 0) return -1; + *out_r = fds[0]; + *out_w = fds[1]; + return 0; +} + +static run_t copy_naive_pipe(sp_str_t src, sp_mem_t mem) { + sp_sys_fd_t pipe_r = SP_SYS_INVALID_FD; + sp_sys_fd_t pipe_w = SP_SYS_INVALID_FD; + open_blocking_pipe(&pipe_r, &pipe_w); + + sp_thread_t drainer = sp_zero; + sp_thread_init(&drainer, drain_pipe, (void*)(uintptr_t)pipe_r); + + sp_io_file_reader_t r = sp_zero; + sp_io_stream_writer_t w = sp_zero; + sp_io_file_reader_from_path(&r, src); + sp_io_stream_writer_from_fd(&w, pipe_w, SP_IO_CLOSE_MODE_NONE); + + u8* buf = sp_alloc_n(mem, u8, PERF_NAIVE_BUFFER); + u64 copied = 0; + + sp_tm_timer_t t = sp_tm_start_timer(); + sp_io_copy_b(&w.base, &r.base, buf, PERF_NAIVE_BUFFER, &copied); + u64 ns = sp_tm_read_timer(&t); + + sp_io_file_reader_close(&r); + sp_sys_close(pipe_w); + sp_thread_join(&drainer); + return (run_t){ .bytes = copied, .ns = ns }; +} + +static run_t copy_fast_pipe(sp_str_t src) { + sp_sys_fd_t pipe_r = SP_SYS_INVALID_FD; + sp_sys_fd_t pipe_w = SP_SYS_INVALID_FD; + open_blocking_pipe(&pipe_r, &pipe_w); + + sp_thread_t drainer = sp_zero; + sp_thread_init(&drainer, drain_pipe, (void*)(uintptr_t)pipe_r); + + sp_io_file_reader_t r = sp_zero; + sp_io_stream_writer_t w = sp_zero; + sp_io_file_reader_from_path(&r, src); + sp_io_stream_writer_from_fd(&w, pipe_w, SP_IO_CLOSE_MODE_NONE); + + u64 copied = 0; + sp_tm_timer_t t = sp_tm_start_timer(); + // sp_io_copy negotiates the fast path. Source is a regular file (positional, + // exposes &pos via as_fd), destination is a pipe-backed stream writer that + // wires .read_from -> sendfile on Linux. + sp_io_copy(&w.base, &r.base, &copied); + u64 ns = sp_tm_read_timer(&t); + + sp_io_file_reader_close(&r); + sp_sys_close(pipe_w); + sp_thread_join(&drainer); + return (run_t){ .bytes = copied, .ns = ns }; +} +#endif + static void report(const c8* label, run_t run) { // MB/s = bytes / 1e6 / (ns / 1e9) = bytes * 1000 / ns. u64 mb_per_s = run.ns ? (run.bytes * 1000u) / run.ns : 0; @@ -115,7 +197,7 @@ s32 run(s32 num_args, const c8** args) { // naive run typically pays for cold reads of the source; the second runs // against a warm page cache. The fast-path run benefits from a warm cache // too, but the dominant cost it skips is the userspace copy itself. - sp_log("running benchmark (two trials each)"); + sp_log("file -> file (copy_file_range when available; two trials each)"); run_t n1 = copy_naive(src, dst, mem); report("naive #1", n1); run_t f1 = copy_fast (src, dst); report("fast #1", f1); run_t n2 = copy_naive(src, dst, mem); report("naive #2", n2); @@ -130,6 +212,23 @@ s32 run(s32 num_args, const c8** args) { ); } +#if defined(SP_LINUX) + sp_log("file -> pipe (sendfile when available; two trials each)"); + run_t pn1 = copy_naive_pipe(src, mem); report("naive #1", pn1); + run_t pf1 = copy_fast_pipe (src); report("fast #1", pf1); + run_t pn2 = copy_naive_pipe(src, mem); report("naive #2", pn2); + run_t pf2 = copy_fast_pipe (src); report("fast #2", pf2); + + if (pf2.ns) { + u64 speedup_x100 = (pn2.ns * 100u) / pf2.ns; + sp_log( + "warm-cache speedup: {}.{}x (naive #2 / fast #2)", + sp_fmt_uint(speedup_x100 / 100u), + sp_fmt_uint(speedup_x100 % 100u) + ); + } +#endif + sp_fs_remove_file(src); sp_fs_remove_file(dst); return 0; diff --git a/example/prompt_fancy.c b/example/prompt_fancy.c index 6961512..1c44360 100644 --- a/example/prompt_fancy.c +++ b/example/prompt_fancy.c @@ -505,7 +505,6 @@ static bool fancy_has_arg(s32 argc, const c8** argv, const c8* arg) { static sp_str_t fancy_selected_changelog(sp_mem_t mem, fancy_changelog_item_t* items, u32 num_items) { sp_io_dyn_mem_writer_t builder = sp_zero; sp_io_dyn_mem_writer_init(mem, &builder); - sp_mem_arena_marker_t s = sp_mem_begin_scratch_for(mem); const c8* section = ""; u64 written = 0; sp_for(it, num_items) { @@ -519,31 +518,28 @@ static sp_str_t fancy_selected_changelog(sp_mem_t mem, fancy_changelog_item_t* i if (written) { sp_io_write_c8(&builder.base, '\n'); } - sp_fmt_io(&builder.base, s.mem, "[{}]", sp_fmt_cstr(section)); + sp_fmt_io(&builder.base, "[{}]", sp_fmt_cstr(section)); sp_io_write_c8(&builder.base, '\n'); } - sp_fmt_io(&builder.base, s.mem, "- {}", sp_fmt_cstr(items[it].text)); + sp_fmt_io(&builder.base, "- {}", sp_fmt_cstr(items[it].text)); sp_io_write_c8(&builder.base, '\n'); } - sp_mem_end_scratch(s); return sp_io_dyn_mem_writer_as_str(&builder); } static sp_str_t fancy_plan_note(sp_mem_t mem, const c8* kind, const c8* name, const c8* sections) { sp_io_dyn_mem_writer_t builder = sp_zero; sp_io_dyn_mem_writer_init(mem, &builder); - sp_mem_arena_marker_t s = sp_mem_begin_scratch_for(mem); - sp_fmt_io(&builder.base, s.mem, "version 0.13.3 ({})", sp_fmt_cstr(kind)); + sp_fmt_io(&builder.base, "version 0.13.3 ({})", sp_fmt_cstr(kind)); sp_io_write_c8(&builder.base, '\n'); - sp_fmt_io(&builder.base, s.mem, "name {}", sp_fmt_cstr(name)); + sp_fmt_io(&builder.base, "name {}", sp_fmt_cstr(name)); sp_io_write_c8(&builder.base, '\n'); sp_io_write_cstr(&builder.base, "tag v0.13.3", SP_NULLPTR); sp_io_write_c8(&builder.base, '\n'); sp_io_write_cstr(&builder.base, "artifacts linux, musl, freestanding, macos, windows", SP_NULLPTR); sp_io_write_c8(&builder.base, '\n'); - sp_fmt_io(&builder.base, s.mem, "changelog {}", sp_fmt_cstr(sections)); - sp_mem_end_scratch(s); + sp_fmt_io(&builder.base, "changelog {}", sp_fmt_cstr(sections)); return sp_io_dyn_mem_writer_as_str(&builder); } diff --git a/sp.h b/sp.h index afcf93b..235cbd6 100644 --- a/sp.h +++ b/sp.h @@ -53,7 +53,7 @@ + sp_fs path manipulation, filesystem, common system paths (e.g. appdata) sp_hash pseudorandom hashing, terrible and stolen + sp_hash_table stb-style hash table (macros) - + sp_io read and write to files and buffers + + sp_io push bytes around sp_mem fundamental memory APIs, allocators, scratch storage @ sp_mutex os native mutex wrappers - sp_os grab bag of platform bullshit @@ -245,10 +245,12 @@ ////////////////////// // SP_HAS_ATTRIBUTE // ////////////////////// -#if !defined(SP_MSVC) - #define SP_HAS_ATTRIBUTE(attr) __has_attribute(attr) -#else +#if defined(SP_MSVC) + #define SP_HAS_ATTRIBUTE(attr) 0 +#elif defined(SP_TCC) #define SP_HAS_ATTRIBUTE(attr) 0 +#else + #define SP_HAS_ATTRIBUTE(attr) __has_attribute(attr) #endif ////////////// @@ -344,9 +346,11 @@ // SP_*_EXTERN_C // ///////////////////// #ifdef SP_CPP + #define SP_EXTERN_C extern "C" #define SP_BEGIN_EXTERN_C() extern "C" { #define SP_END_EXTERN_C() } #else + #define SP_EXTERN_C #define SP_BEGIN_EXTERN_C() #define SP_END_EXTERN_C() #endif @@ -380,10 +384,12 @@ #define sp_cast(type, x) static_cast(x) #define sp_ptr_cast(type, x) reinterpret_cast(x) #define sp_void_cast(lhs, expr) static_cast(expr) + #define sp_const_cast(type, x) const_cast(x) #else #define sp_cast(type, x) ((type)(x)) #define sp_ptr_cast(type, x) ((type)(x)) #define sp_void_cast(lhs, expr) (expr) + #define sp_const_cast(type, x) ((type)(x)) #endif #define SP_EXIT_SUCCESS() exit(0) @@ -392,7 +398,7 @@ // sp_assert() is platform dependent, so it's defined with the other platform stuff. The macros // that just wrap it with some goodies are agnostic, though, so they're here #define SP_ASSERT(condition) sp_assert((condition)) -#define sp_assert(x) sp_assert_f(sp_str_lit(__FILE__), sp_str_lit(SP_MACRO_STR(__LINE__)), sp_cstr_as_str(__func__), sp_str_lit(#x), (bool)(x)) +#define sp_assert(x) sp_assert_f(sp_str_lit(__FILE__), sp_str_lit(SP_MACRO_STR(__LINE__)), sp_cstr_as_str(__func__), sp_str_lit(#x), ((x) ? 1 : 0)) #define sp_fatal(FMT, ...) do { sp_log((FMT), ##__VA_ARGS__); sp_assert(false); } while (0) @@ -432,8 +438,9 @@ #define SP_UNIQUE_ID() SP_MACRO_CAT(__sp_unique_name__, __LINE__) #define sp_unique_id() SP_UNIQUE_ID() -#define sp_max(a, b) (a) > (b) ? (a) : (b) -#define sp_min(a, b) (a) > (b) ? (b) : (a) +#define sp_max(a, b) (((a) > (b)) ? (a) : (b)) +#define sp_min(a, b) (((a) > (b)) ? (b) : (a)) +#define sp_clamp(v, lo, hi) (((v) < (lo)) ? (lo) : ((v) > (hi)) ? (hi) : (v)) #define sp_swap(t, a, b) { t SP_UNIQUE_ID() = (a); (a) = (b); (b) = SP_UNIQUE_ID(); } #define SP_QSORT_A_FIRST -1 @@ -743,6 +750,7 @@ typedef enum { SP_ERR_FMT_DIRECTIVE_ARG_WRONG_KIND = 1109, SP_ERR_FMT_UNTERMINATED_PLACEHOLDER = 1111, SP_ERR_FMT_CUSTOM_WITHOUT_FN = 1112, + SP_ERR_FMT_WIDTH_ON_OPAQUE_RENDERER = 1113, SP_ERR_LAZY, SP_ERR_OS, } sp_err_t; @@ -778,6 +786,7 @@ typedef struct sp_io_writer sp_io_writer_t; typedef struct sp_io_file_writer sp_io_file_writer_t; typedef struct sp_io_mem_writer sp_io_mem_writer_t; typedef struct sp_io_dyn_mem_writer sp_io_dyn_mem_writer_t; +typedef struct sp_io_stream_writer sp_io_stream_writer_t; #if defined(SP_WIN32) typedef HANDLE sp_win32_handle_t; @@ -1212,58 +1221,65 @@ typedef struct { } sp_sys_fs_entry_t; SP_TYPEDEF_FN(int, sp_qsort_fn_t, const void *, const void *); - -s64 sp_sys_read(sp_sys_fd_t fd, void* buf, u64 count); -s64 sp_sys_write(sp_sys_fd_t fd, const void* buf, u64 count); -sp_sys_fd_t sp_sys_open(const c8* path, u32 len, s32 flags, s32 mode); -s32 sp_sys_close(sp_sys_fd_t fd); -s32 sp_sys_pipe(sp_sys_fd_t* read_end, sp_sys_fd_t* write_end); -s64 sp_sys_lseek(sp_sys_fd_t fd, s64 offset, s32 whence); -s32 sp_sys_stat(const c8* path, u32 len, sp_sys_stat_t* st); -s32 sp_sys_lstat(const c8* path, u32 len, sp_sys_stat_t* st); -s32 sp_sys_fstat(sp_sys_fd_t fd, sp_sys_stat_t* st); -s32 sp_sys_mkdir(const c8* path, u32 len, s32 mode); -s32 sp_sys_rmdir(const c8* path, u32 len); -s32 sp_sys_unlink(const c8* path, u32 len); -s32 sp_sys_rename(const c8* from, u32 from_len, const c8* to, u32 to_len); -s32 sp_sys_chdir(const c8* path, u32 len); -s64 sp_sys_getcwd(char* buf, u64 size); -s32 sp_sys_link(const c8* existing, u32 existing_len, const c8* alias, u32 alias_len); -s32 sp_sys_symlink(const c8* existing, u32 existing_len, const c8* alias, u32 alias_len); -s32 sp_sys_chmod(const c8* path, u32 len, const sp_sys_stat_t* st); -s32 sp_sys_clock_gettime(s32 clockid, sp_sys_timespec_t* ts); -s32 sp_sys_nanosleep(const sp_sys_timespec_t* req, sp_sys_timespec_t* rem); -s64 sp_sys_canonicalize_path(const c8* path, u32 len, c8* buf, u64 size); -s64 sp_sys_get_exe_path(c8* buf, u64 size); -s64 sp_sys_get_storage_path(c8* buf, u64 size); -s64 sp_sys_get_config_path(c8* buf, u64 size); -s32 sp_sys_fd_ready(sp_sys_fd_t fd, u8* ready); -s32 sp_sys_fd_wait(sp_sys_fd_t fd); -s32 sp_sys_fds_wait(const sp_sys_fd_t* fds, u8* ready, u64 nfds); -void* sp_sys_alloc(u64 size); -void sp_sys_free(void* ptr, u64 size); -void* sp_sys_memcpy(void* dest, const void* src, u64 n); -void* sp_sys_memmove(void* dest, const void* src, u64 n); -void* sp_sys_memset(void* dest, u8 fill, u64 n); -s32 sp_sys_memcmp(const void* a, const void* b, u64 n); -void sp_sys_assert(bool cond); -void sp_sys_exit(s32 code); -sp_sys_fd_t sp_sys_open_s(sp_str_t path, s32 flags, s32 mode); -s32 sp_sys_stat_s(sp_str_t path, sp_sys_stat_t* st); -s32 sp_sys_lstat_s(sp_str_t path, sp_sys_stat_t* st); -s32 sp_sys_mkdir_s(sp_str_t path, s32 mode); -s32 sp_sys_rmdir_s(sp_str_t path); -s32 sp_sys_unlink_s(sp_str_t path); -s32 sp_sys_rename_s(sp_str_t from, sp_str_t to); -s32 sp_sys_chdir_s(sp_str_t path); -s32 sp_sys_link_s(sp_str_t existing, sp_str_t alias); -s32 sp_sys_symlink_s(sp_str_t existing, sp_str_t alias); -s32 sp_sys_chmod_s(sp_str_t path, const sp_sys_stat_t* st); -s64 sp_sys_canonicalize_path_s(sp_str_t path, c8* buf, u64 size); -s32 sp_sys_fs_it_open(sp_sys_fs_it_t* it, const c8* path, u32 path_len, void* buf, u64 cap); -s32 sp_sys_fs_it_open_s(sp_sys_fs_it_t* it, sp_str_t path, sp_mem_slice_t buf); -s32 sp_sys_fs_it_next(sp_sys_fs_it_t* it, sp_sys_fs_entry_t* out); -void sp_sys_fs_it_close(sp_sys_fs_it_t* it); +SP_API void sp_sys_init(); +SP_API s64 sp_sys_read(sp_sys_fd_t fd, void* buf, u64 count); +SP_API s64 sp_sys_write(sp_sys_fd_t fd, const void* buf, u64 count); +SP_API s64 sp_sys_pread(sp_sys_fd_t fd, void* buf, u64 count, u64 offset); +SP_API s64 sp_sys_pwrite(sp_sys_fd_t fd, const void* buf, u64 count, u64 offset); +SP_API sp_sys_fd_t sp_sys_root(s32 it, c8* buffer, u32 len); +SP_API sp_sys_fd_t sp_sys_open(sp_sys_fd_t fd, const c8* path, u32 len, s32 flags, s32 mode); +SP_API s32 sp_sys_close(sp_sys_fd_t fd); +SP_API s32 sp_sys_pipe(sp_sys_fd_t* read_end, sp_sys_fd_t* write_end); +SP_API s64 sp_sys_lseek(sp_sys_fd_t fd, s64 offset, s32 whence); +SP_API s32 sp_sys_stat(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_stat_t* st); +SP_API s32 sp_sys_lstat(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_stat_t* st); +SP_API s32 sp_sys_fstat(sp_sys_fd_t fd, sp_sys_stat_t* st); +SP_API s32 sp_sys_mkdir(sp_sys_fd_t fd, const c8* path, u32 len, s32 mode); +SP_API s32 sp_sys_rmdir(sp_sys_fd_t fd, const c8* path, u32 len); +SP_API s32 sp_sys_unlink(sp_sys_fd_t fd, const c8* path, u32 len); +SP_API s32 sp_sys_rename(sp_sys_fd_t from_fd, const c8* from, u32 from_len, sp_sys_fd_t to_fd, const c8* to, u32 to_len); +SP_API s32 sp_sys_chdir(const c8* path, u32 len); +SP_API s64 sp_sys_getcwd(char* buf, u64 size); +SP_API s32 sp_sys_link(sp_sys_fd_t from_fd, const c8* existing, u32 existing_len, sp_sys_fd_t to_fd, const c8* alias, u32 alias_len); +SP_API s32 sp_sys_symlink(const c8* existing, u32 existing_len, sp_sys_fd_t to_fd, const c8* alias, u32 alias_len); +SP_API s32 sp_sys_chmod(sp_sys_fd_t fd, const c8* path, u32 len, const sp_sys_stat_t* st); +SP_API s32 sp_sys_clock_gettime(s32 clockid, sp_sys_timespec_t* ts); +SP_API s32 sp_sys_nanosleep(const sp_sys_timespec_t* req, sp_sys_timespec_t* rem); +SP_API s64 sp_sys_canonicalize_path(const c8* path, u32 len, c8* buf, u64 size); +SP_API s64 sp_sys_get_exe_path(c8* buf, u64 size); +SP_API s64 sp_sys_get_storage_path(c8* buf, u64 size); +SP_API s64 sp_sys_get_config_path(c8* buf, u64 size); +SP_API s32 sp_sys_fd_ready(sp_sys_fd_t fd, u8* ready); +SP_API s32 sp_sys_fd_wait(sp_sys_fd_t fd); +SP_API s32 sp_sys_fds_wait(const sp_sys_fd_t* fds, u8* ready, u64 nfds); +SP_API void* sp_sys_alloc(u64 size); +SP_API void sp_sys_free(void* ptr, u64 size); +SP_API void* sp_sys_memcpy(void* dest, const void* src, u64 n); +SP_API void* sp_sys_memmove(void* dest, const void* src, u64 n); +SP_API void* sp_sys_memset(void* dest, u8 fill, u64 n); +SP_API s32 sp_sys_memcmp(const void* a, const void* b, u64 n); +SP_API void sp_sys_assert(bool cond); +SP_API void sp_sys_exit(s32 code); + +SP_API void sp_sys_env(const c8** env, u32* len); + + +SP_API sp_sys_fd_t sp_sys_open_s(sp_sys_fd_t fd, sp_str_t path, s32 flags, s32 mode); +SP_API s32 sp_sys_stat_s(sp_sys_fd_t fd, sp_str_t path, sp_sys_stat_t* st); +SP_API s32 sp_sys_lstat_s(sp_sys_fd_t fd, sp_str_t path, sp_sys_stat_t* st); +SP_API s32 sp_sys_mkdir_s(sp_sys_fd_t fd, sp_str_t path, s32 mode); +SP_API s32 sp_sys_rmdir_s(sp_sys_fd_t fd, sp_str_t path); +SP_API s32 sp_sys_unlink_s(sp_sys_fd_t fd, sp_str_t path); +SP_API s32 sp_sys_rename_s(sp_sys_fd_t from_fd, sp_str_t from, sp_sys_fd_t to_fd, sp_str_t to); +SP_API s32 sp_sys_chdir_s(sp_str_t path); +SP_API s32 sp_sys_link_s(sp_sys_fd_t from_fd, sp_str_t existing, sp_sys_fd_t to_fd, sp_str_t alias); +SP_API s32 sp_sys_symlink_s(sp_str_t existing, sp_sys_fd_t to_fd, sp_str_t alias); +SP_API s32 sp_sys_chmod_s(sp_sys_fd_t fd, sp_str_t path, const sp_sys_stat_t* st); +SP_API s64 sp_sys_canonicalize_path_s(sp_str_t path, c8* buf, u64 size); +SP_API s32 sp_sys_fs_it_open(sp_sys_fd_t fd, sp_sys_fs_it_t* it, const c8* path, u32 path_len, void* buf, u64 cap); +SP_API s32 sp_sys_fs_it_open_s(sp_sys_fd_t fd, sp_sys_fs_it_t* it, sp_str_t path, sp_mem_slice_t buf); +SP_API s32 sp_sys_fs_it_next(sp_sys_fs_it_t* it, sp_sys_fs_entry_t* out); +SP_API void sp_sys_fs_it_close(sp_sys_fs_it_t* it); // ██████ ██████ █████████ █████ ██████ █████ // ░░██████ ██████ ███░░░░░███ ░░███ ░░██████ ░░███ @@ -1276,8 +1292,15 @@ void sp_sys_fs_it_close(sp_sys_fs_it_t* it); // @main typedef s32 (*sp_entry_fn_t)(s32, const c8**); +// C++ name mangling means we can't encode the real main function as a symbol name +// in the assembly, so we have to store the function pointer somewhere +#define SP_MAIN_STASH(fn) \ + SP_EXTERN_C sp_entry_fn_t sp_main_fn; \ + sp_entry_fn_t sp_main_fn = &fn; + #define SP_MAIN_FREESTANDING_AMD64(fn) \ - __attribute__((naked)) \ + SP_MAIN_STASH(fn) \ + SP_EXTERN_C __attribute__((naked)) \ void _start() { \ __asm__ volatile ( \ /* The SysV ABI requires that we zero the frame pointer to mark the outermost stack frame (e.g. for a debugger) */ \ @@ -1301,12 +1324,12 @@ typedef s32 (*sp_entry_fn_t)(s32, const c8**); * force a relocation if compiled as PIE, but since this is freestanding there's nothing to apply the * relocation. * - * Instead, we use `lea fn(rip)` to encode it as an offset from the instruction pointer, which avoids - * a relocation entirely. + * Instead, encode it as an offset from the instruction pointer, which avoids the relocatin entirely */ \ - "mov (%%rsp), %%rdi" "\n" \ - "lea 8(%%rsp), %%rsi" "\n" \ - "lea " #fn "(%%rip), %%rdx" "\n" \ + "xor %%rbp, %%rbp" "\n" \ + "mov (%%rsp), %%rdi" "\n" \ + "lea 8(%%rsp), %%rsi" "\n" \ + "mov sp_main_fn(%%rip), %%rdx" "\n" \ /* * Now that the arguments are set up, call into our entry point, sp_main(). Freestanding builds require a few * things to be initialized; we could do that initialization here and jump directly to the user's function, @@ -1318,22 +1341,23 @@ typedef s32 (*sp_entry_fn_t)(s32, const c8**); } #define SP_MAIN_FREESTANDING_ARM64(fn) \ - __attribute__((naked)) \ - void _start() { \ + SP_MAIN_STASH(fn) \ + SP_EXTERN_C __attribute__((naked)) \ + void _start() { \ __asm__ volatile ( \ /* Same as x86_64, pretty much. Load the arguments. */ \ "ldr x0, [sp]" "\n" \ "add x1, sp, #8" "\n" \ - /* Calculate the function pointer as relative to the instruction pointer */ \ - "adrp x2, " #fn "\n" \ - "add x2, x2, :lo12:" #fn "\n" \ + /* Load the user fn pointer from the C-linkage global */ \ + "adrp x2, sp_main_fn" "\n" \ + "ldr x2, [x2, :lo12:sp_main_fn]" "\n" \ "bl sp_main" "\n" \ ::: "memory" \ ); \ } #define SP_MAIN_WASM_FREESTANDING(fn) \ - __attribute__((export_name("_start"))) \ + SP_EXTERN_C __attribute__((export_name("_start"))) \ void _start() { \ struct { uint8_t** data; __wasi_size_t len; } args; \ struct { uint8_t** data; __wasi_size_t len; } env; \ @@ -1343,13 +1367,13 @@ typedef s32 (*sp_entry_fn_t)(s32, const c8**); } buffers; \ \ (void)__wasi_args_sizes_get(&args.len, &buffers.args.len); \ - args.data = sp_sys_alloc((args.len + 1) * sizeof(uint8_t*)); \ - buffers.args.data = sp_sys_alloc(buffers.args.len); \ + args.data = sp_sys_alloc_n(uint8_t*, args.len + 1); \ + buffers.args.data = sp_sys_alloc_n(uint8_t, buffers.args.len); \ (void)__wasi_args_get(args.data, buffers.args.data); \ \ (void)__wasi_environ_sizes_get(&env.len, &buffers.env.len); \ - env.data = sp_sys_alloc((env.len + 1) * sizeof(uint8_t*)); \ - buffers.env.data = sp_sys_alloc(buffers.env.len); \ + env.data = sp_sys_alloc_n(uint8_t*, env.len + 1); \ + buffers.env.data = sp_sys_alloc_n(uint8_t, buffers.env.len); \ (void)__wasi_environ_get(env.data, buffers.env.data); \ \ environ = (c8**)env.data; \ @@ -2443,6 +2467,7 @@ SP_API f64 sp_tm_ns_to_s_f(f64 ns); SP_API f64 sp_tm_ns_to_ms_f(f64 ns); SP_API f64 sp_tm_ns_to_us_f(f64 ns); SP_API sp_str_t sp_tm_epoch_to_iso8601(sp_mem_t mem, sp_tm_epoch_t time); +SP_API void sp_tm_epoch_to_iso8601_w(sp_io_writer_t* io, sp_tm_epoch_t time); // ███████████ █████ █████ ██████████ █████████ █████ █████ █████████ ███████████ ██████████ ██████ ██████ @@ -2505,6 +2530,8 @@ SP_API sp_str_t sp_fs_replace_ext(sp_mem_t mem, sp_str_t path, sp_st SP_API sp_str_t sp_fs_canonicalize_path(sp_mem_t mem, sp_str_t path); SP_API sp_str_t sp_fs_get_exe_path(sp_mem_t mem); SP_API sp_str_t sp_fs_get_cwd(sp_mem_t mem); +SP_API sp_sys_fd_t sp_fs_open_cwd(void); +SP_API sp_str_t sp_fs_resolve(sp_mem_t mem, sp_sys_fd_t fd); SP_API sp_str_t sp_fs_get_storage_path(sp_mem_t mem); SP_API sp_str_t sp_fs_get_config_path(sp_mem_t mem); SP_API sp_da(sp_fs_entry_t) sp_fs_collect(sp_mem_t mem, sp_str_t path); @@ -2532,13 +2559,13 @@ SP_API sp_err_t sp_fs_copy(sp_str_t from, sp_str_t to); SP_API void sp_fs_copy_file(sp_str_t from, sp_str_t to); SP_API void sp_fs_copy_dir(sp_str_t from, sp_str_t to); SP_API void sp_fs_copy_glob(sp_str_t from, sp_str_t glob, sp_str_t to); -SP_IMP sp_fs_it_t sp_fs_it_new(sp_mem_t mem, sp_str_t path); -SP_IMP sp_fs_it_t sp_fs_it_new_recursive(sp_mem_t mem, sp_str_t path); -SP_IMP void sp_fs_it_begin(sp_fs_it_t* it, sp_str_t path); -SP_IMP void sp_fs_it_next(sp_fs_it_t* it); -SP_IMP void sp_fs_it_push(sp_fs_it_t* it, sp_str_t path); -SP_IMP bool sp_fs_it_valid(sp_fs_it_t* it); -SP_IMP void sp_fs_it_deinit(sp_fs_it_t* it); +SP_API sp_fs_it_t sp_fs_it_new(sp_mem_t mem, sp_str_t path); +SP_API sp_fs_it_t sp_fs_it_new_recursive(sp_mem_t mem, sp_str_t path); +SP_API void sp_fs_it_begin(sp_fs_it_t* it, sp_str_t path); +SP_API void sp_fs_it_next(sp_fs_it_t* it); +SP_API void sp_fs_it_push(sp_fs_it_t* it, sp_str_t path); +SP_API bool sp_fs_it_valid(sp_fs_it_t* it); +SP_API void sp_fs_it_deinit(sp_fs_it_t* it); // ███████████ █████ █████ ███████████ ██████████ █████████ ██████████ █████ ██████ █████ █████████ @@ -2698,11 +2725,6 @@ SP_API sp_str_t sp_os_env_get(sp_str_t key); SP_API sp_os_env_it_t sp_os_env_it_begin(); SP_API bool sp_os_env_it_valid(sp_os_env_it_t* it); SP_API void sp_os_env_it_next(sp_os_env_it_t* it); -SP_API sp_err_t sp_os_create_file(sp_str_t path); -SP_API sp_err_t sp_os_create_dir(sp_str_t path); -SP_API sp_err_t sp_os_create_hard_link(sp_str_t target, sp_str_t link_path); -SP_API sp_err_t sp_os_create_sym_link(sp_str_t target, sp_str_t link_path); -SP_API sp_str_t sp_os_get_cwd(sp_mem_t mem); SP_API void sp_os_register_signal_handler(sp_os_signal_t, sp_os_signal_handler_t, void* userdata); SP_API bool sp_os_is_tty(sp_sys_fd_t fd); SP_API void sp_os_tty_size(sp_sys_fd_t fd, u32* cols, u32* rows); @@ -2721,6 +2743,14 @@ SP_API void sp_assert_f(sp_str_t file, sp_str_t line, sp_str_t func, s // █████ ░░░███████░ █████ █████ █████ █████ █████ █████ █████ // â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘ // @format +SP_API sp_str_r sp_fmt(sp_mem_t mem, const c8* fmt, ...); +SP_API const c8* sp_fmt_mem_cstr(sp_mem_t mem, const c8* fmt, ...); +SP_API sp_str_r sp_fmt_mem_v(sp_mem_t mem, sp_str_t fmt, va_list args); +SP_API sp_str_r sp_fmt_buf(c8* buffer, u64 len, const c8* fmt, ...); +SP_API sp_str_r sp_fmt_buf_v(c8* buffer, u64 len, sp_str_t fmt, va_list args); +SP_API sp_err_t sp_fmt_io(sp_io_writer_t* io, const c8* fmt, ...); +SP_API sp_err_t sp_fmt_io_v(sp_io_writer_t* io, sp_str_t fmt, va_list args); + typedef enum { SP_FMT_ALIGN_NONE, SP_FMT_ALIGN_LEFT, @@ -2746,26 +2776,29 @@ typedef struct { sp_fmt_align_t align; u8 fill; sp_opt(u8) precision; - u8 fill_dynamic; - u8 width_dynamic; - u8 precision_dynamic; - u8 directive_count; - u8 directive_arg_dynamic; - sp_str_t directive_names[SP_FMT_MAX_DIRECTIVES]; - sp_str_t directive_args [SP_FMT_MAX_DIRECTIVES]; + struct { + u8 fill; + u8 width; + u8 precision; + u8 directive; + } dynamic; + struct { + u8 num; + sp_str_t names [SP_FMT_MAX_DIRECTIVES]; + sp_str_t args [SP_FMT_MAX_DIRECTIVES]; + } directive; } sp_fmt_spec_t; typedef struct sp_fmt_arg sp_fmt_arg_t; -SP_TYPEDEF_FN(void, sp_fmt_fn_t, sp_io_writer_t*, sp_mem_t, sp_fmt_arg_t*, sp_fmt_arg_t*); -SP_TYPEDEF_FN(void, sp_fmt_transform_fn_t, sp_io_writer_t*, sp_mem_t, sp_str_t, sp_fmt_arg_t*, sp_fmt_arg_t*); +SP_TYPEDEF_FN(void, sp_fmt_fn_t, sp_io_writer_t*, sp_fmt_arg_t*, sp_fmt_arg_t*); typedef union { - u64 u; - s64 i; - f64 f; - sp_str_t s; - void* p; - struct { sp_fmt_fn_t fn; void* ptr; } custom; + u64 u; + s64 i; + f64 f; + sp_str_t s; + void* p; + struct { sp_fmt_fn_t fn; void* ptr; } custom; } sp_fmt_value_t; typedef struct { @@ -2779,40 +2812,34 @@ struct sp_fmt_arg { sp_fmt_value_t value; }; -static inline sp_fmt_arg_t sp_fmt_arg_from_argv(sp_fmt_argv_t v) { - sp_fmt_arg_t arg = sp_zero; - arg.id = v.id; - arg.value = v.value; - return arg; -} - typedef enum { sp_fmt_directive_renderer, - sp_fmt_directive_transformer, sp_fmt_directive_decorator, } sp_fmt_directive_kind_t; typedef struct { sp_fmt_directive_kind_t kind; - sp_fmt_arg_kind_t arg_kinds; - sp_fmt_arg_kind_t param_kinds; + sp_fmt_arg_kind_t args; + sp_fmt_arg_kind_t params; union { - sp_fmt_fn_t renderer; - sp_fmt_transform_fn_t transformer; struct { sp_fmt_fn_t before; sp_fmt_fn_t after; } decorator; + sp_fmt_fn_t renderer; }; } sp_fmt_directive_t; -#define sp_fmt_uint(_value) (sp_fmt_argv_t) { .id = sp_fmt_id_u64, .value.u = (_value) } -#define sp_fmt_int(_value) (sp_fmt_argv_t) { .id = sp_fmt_id_s64, .value.i = (_value) } -#define sp_fmt_float(_value) (sp_fmt_argv_t) { .id = sp_fmt_id_f64, .value.f = (_value) } -#define sp_fmt_char(_value) (sp_fmt_argv_t) { .id = sp_fmt_id_str, .value.s = { .data = &(_value), .len = 1 } } -#define sp_fmt_str(_value) (sp_fmt_argv_t) { .id = sp_fmt_id_str, .value.s = (_value) } -#define sp_fmt_cstr(_value) (sp_fmt_argv_t) { .id = sp_fmt_id_str, .value.s = sp_str_view(_value) } -#define sp_fmt_ptr(_value) (sp_fmt_argv_t) { .id = sp_fmt_id_ptr, .value.p = (_value) } +SP_API void sp_fmt_render_default(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* param); +SP_API void sp_fmt_directive_register(const c8* name, sp_fmt_directive_t directive); + +#define sp_fmt_uint(_value) (sp_fmt_argv_t) { .id = sp_fmt_id_u64, .value = { .u = (_value) } } +#define sp_fmt_int(_value) (sp_fmt_argv_t) { .id = sp_fmt_id_s64, .value = { .i = (_value) } } +#define sp_fmt_float(_value) (sp_fmt_argv_t) { .id = sp_fmt_id_f64, .value = { .f = (_value) } } +#define sp_fmt_char(_value) (sp_fmt_argv_t) { .id = sp_fmt_id_str, .value = { .s = { .data = &(_value), .len = 1 } } } +#define sp_fmt_str(_value) (sp_fmt_argv_t) { .id = sp_fmt_id_str, .value = { .s = (_value) } } +#define sp_fmt_cstr(_value) (sp_fmt_argv_t) { .id = sp_fmt_id_str, .value = { .s = sp_cstr_as_str(_value) } } +#define sp_fmt_ptr(_value) (sp_fmt_argv_t) { .id = sp_fmt_id_ptr, .value = { .p = (_value) } } // Use a tiny, portable trick to get some damn good type safety for custom format string arguments: // @@ -2845,9 +2872,11 @@ typedef struct { (void)sizeof(*(T*)0 = (_value)), \ (sp_fmt_argv_t) { \ .id = sp_fmt_id_custom, \ - .value.custom = { \ - .fn = (_fn), \ - .ptr = (void*)&(_value) \ + .value = { \ + .custom = { \ + .fn = (_fn), \ + .ptr = (void*)&(_value) \ + } \ } \ }) #define sp_fmt_custom_v(T, _fn, ...) ( \ @@ -2860,17 +2889,11 @@ typedef struct { } \ }) -#define sp_fmt_register_renderer(_name, _fn, _arg_kinds) \ +#define sp_fmt_register_renderer(_name, _fn, _args) \ sp_fmt_directive_register(_name, (sp_fmt_directive_t) { \ .kind = sp_fmt_directive_renderer, \ - .arg_kinds = _arg_kinds, \ - .renderer = (_fn), \ - }) - -#define sp_fmt_register_transformer(_name, _fn) \ - sp_fmt_directive_register(_name, (sp_fmt_directive_t) { \ - .kind = sp_fmt_directive_transformer, \ - .transformer = (_fn), \ + .args = _args, \ + .renderer = _fn, \ }) #define sp_fmt_register_decorator(_name, _before, _after) \ @@ -2879,20 +2902,13 @@ typedef struct { .decorator = { .before = (_before), .after = (_after) }, \ }) -#define sp_fmt_register_decorator_p(_name, _before, _after, _param_kinds) \ +#define sp_fmt_register_decorator_p(_name, _before, _after, _params) \ sp_fmt_directive_register(_name, (sp_fmt_directive_t) { \ .kind = sp_fmt_directive_decorator, \ - .param_kinds = sp_cast(sp_fmt_arg_kind_t, _param_kinds), \ + .params = sp_cast(sp_fmt_arg_kind_t, _params), \ .decorator = { .before = (_before), .after = (_after) }, \ }) -void sp_fmt_directive_register(const c8* name, sp_fmt_directive_t directive); - -SP_API sp_err_t sp_fmt_io(sp_io_writer_t* io, sp_mem_t mem, const c8* fmt, ...); -SP_API sp_str_r sp_fmt(sp_mem_t mem, const c8* fmt, ...); -SP_API const c8* sp_fmt_to_cstr(sp_mem_t mem, const c8* fmt, ...); -SP_API sp_err_t sp_fmt_v(sp_io_writer_t* io, sp_mem_t mem, sp_str_t fmt, va_list args); -SP_API void sp_fmt_render_default(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param); SP_API u8 sp_parse_u8(sp_str_t str); SP_API u16 sp_parse_u16(sp_str_t str); @@ -2966,11 +2982,6 @@ extern sp_tls_block_t sp_tls_block; SP_TYPEDEF_FN(void, sp_tls_once_fn_t); SP_TYPEDEF_FN(void, sp_tls_deinit_fn_t, void*); -SP_PRIVATE void sp_tls_new(sp_tls_key_t* key, sp_tls_deinit_fn_t fn); -SP_PRIVATE void* sp_tls_get(sp_tls_key_t key); -SP_PRIVATE void sp_tls_set(sp_tls_key_t key, void* data); -SP_PRIVATE void sp_tls_once(sp_tls_once_t* once, sp_tls_once_fn_t); - typedef struct { sp_mem_t mem; sp_mem_arena_t* scratch [2]; @@ -2979,8 +2990,8 @@ typedef struct { sp_ht(sp_str_t, sp_fmt_directive_t) directives; } format; struct { - sp_io_file_writer_t* out; - sp_io_file_writer_t* err; + sp_io_stream_writer_t* out; + sp_io_stream_writer_t* err; } std; } sp_tls_rt_t; @@ -3060,15 +3071,46 @@ void sp_sys_tls_init(sp_tls_rt_t* tls); SP_API sp_rt_t sp_rt; -// █████ ███████ -// ░░███ ███░░░░░███ -// ░███ ███ ░░███ -// ░███ ░███ ░███ -// ░███ ░███ ░███ -// ░███ ░░███ ███ -// █████ ░░░███████░ -// â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘â–‘â–‘ -// @io +/* + █████ ███████ + ░░███ ███░░░░░███ + ░███ ███ ░░███ + ░███ ░███ ░███ + ░███ ░███ ░███ + ░███ ░░███ ███ + █████ ░░░███████░ + â–‘â–‘â–‘â–‘â–‘ â–‘â–‘â–‘â–‘â–‘â–‘â–‘ + @io + + sp.h's IO library is based on a few primitives: + sp_io_reader_t and sp_io_writer_t are the fundamental types for moving bytes and have exactly two things: + 1. A table of function pointers (i.e. a vtable) + 2. A userspace buffer + + The vtable is how concrete types can define what it means to e.g. read bytes from the underlying thing. + + A + file reader, for example, uses pread() whereas a + Buffering is baked into the types themselves (unlike e.g. Go which builds buffered IO strictly on top of unbuffered), + but is optional and must be opted into by providing the buffer. + + - Concrete implementations + + The entire point of all this interface design is to exactly one end: Avoiding unnecessary copies. This is the matrix of ways you can copy memory: + + | from | to | primitive | zig | example | + | --------- | --------- | --------- | ------------------ | ---------------------------------- | + | kernel | kernel | send_file | File.Reader.stream | Copying data from file A to file B | + | kernel | userspace | read | File.Reader.read* | Reading a file | + | userspace | kernel | write | File.Writer.write | Writing a file | + | userspace | userspace | memcpy | Reader.readSlice* | A regular copy | + + Kernel means that you don't actually have a pointer to read from or write to; you have to use some syscall. Userspace means that you do. Our design boils down to: how do we make sure to always ask the side that represents a kernel resource to call its byte-moving implementation? The userspace to userspace case is trivially handled by using the buffers on Reader and Writer to store the source and destination, and setting `stream()` to `memcpy()` (obviously, not literally). The interesting case is when the kernel is involved. + + What made it all stick for me: A byte moving function takes a source and a destination. Both can be either a slice, or an IO type. If either one is a slice, **that side cannot represent memory in the kernel**. Memory in the kernel, like the memory backing a file, is represented by a file descriptor. A handle. You cannot get a pointer to kernel memory. Of course, in general that's not true; you can `mmap()` a file, for example. But remember, we're just speaking in the context of fast paths. If you have a pointer to kernel memory, the fast path is to treat it like any other pointer and just `memcpy()`. We only care about cases where that's not true, where the fast path is some special syscall. + + This trivializes the asymmetric cases (kernel -> userspace and userspace -> kernel). The kernel side, in terms of function signatures, must be represented by an IO type. The userspace side must be represented by a pointer. (You can, of course, wrap the pointer in an IO type if you like the interface, but again we're only talking about fast path dispatching) We therefore have a case where one side *cannot* have a fast path function, by definition, and so we know to use the other one. + */ typedef enum { SP_IO_SEEK_SET, SP_IO_SEEK_CUR, @@ -3086,27 +3128,11 @@ typedef enum { SP_IO_CLOSE_MODE_AUTO, } sp_io_close_mode_t; -typedef sp_sys_fd_t sp_io_file_t; -typedef sp_sys_fd_t sp_io_pipe_t; - -typedef enum { - SP_IO_WRITE_MODE_OVERWRITE, - SP_IO_WRITE_MODE_APPEND, -} sp_io_write_mode_t; - SP_TYPEDEF_FN(sp_err_t, sp_io_reader_read_cb, sp_io_reader_t* r, void* ptr, u64 size, u64* bytes_read); SP_TYPEDEF_FN(sp_err_t, sp_io_seek_cb, sp_io_reader_t* r, s64 offset, sp_io_whence_t whence, s64* position); -// Optional. Returns the underlying OS handle if this reader has one. Used as -// the universal currency for kernel-to-kernel fast paths (sendfile, splice, -// copy_file_range). A reader that has no fd leaves this NULL and the fast -// path is unavailable. -SP_TYPEDEF_FN(sp_err_t, sp_io_reader_as_fd_cb, sp_io_reader_t* r, sp_io_file_t* fd); +SP_TYPEDEF_FN(sp_err_t, sp_io_reader_as_fd_cb, sp_io_reader_t* r, sp_sys_fd_t* fd, u64** pos); SP_TYPEDEF_FN(sp_err_t, sp_io_writer_write_cb, sp_io_writer_t* w, const void* ptr, u64 size, u64* bytes_written); -// Optional. The writer-side fast path. Implementations consume bytes from `r` -// however they like; in practice this means asking `r->as_fd` for an OS handle -// and routing through a kernel-side syscall. Return SP_ERR_IO_UNIMPLEMENTED to -// tell sp_io_copy that it should fall back to the generic loop. SP_TYPEDEF_FN(sp_err_t, sp_io_writer_read_from_cb, sp_io_writer_t* w, sp_io_reader_t* r, u64* bytes_moved); struct sp_io_reader { @@ -3116,22 +3142,25 @@ struct sp_io_reader { u64 cursor; }; -typedef struct { - sp_io_reader_t* reader; - sp_io_seek_cb seek; -} sp_io_seeking_reader_t; - typedef struct { sp_io_reader_t base; - sp_io_file_t file; + sp_sys_fd_t file; + u64 pos; + u64 size; sp_io_close_mode_t close_mode; } sp_io_file_reader_t; typedef struct { sp_io_reader_t base; - sp_io_pipe_t pipe; + sp_sys_fd_t fd; sp_io_close_mode_t close_mode; -} sp_io_pipe_reader_t; +} sp_io_stream_reader_t; + +typedef struct { + sp_io_reader_t* reader; + sp_io_seek_cb seek; +} sp_io_seeking_reader_t; + struct sp_io_writer { sp_io_writer_write_cb write; @@ -3141,7 +3170,15 @@ struct sp_io_writer { struct sp_io_file_writer { sp_io_writer_t base; - sp_io_file_t fd; + sp_sys_fd_t fd; + u64 pos; + u64 size; + sp_io_close_mode_t close_mode; +}; + +struct sp_io_stream_writer { + sp_io_writer_t base; + sp_sys_fd_t fd; sp_io_close_mode_t close_mode; }; @@ -3182,18 +3219,24 @@ SP_API void sp_io_seeking_reader_from_mem(sp_io_seeking_reader_t* sr, SP_API void sp_io_seeking_reader_from_file_reader(sp_io_seeking_reader_t* sr, sp_io_file_reader_t* fr); SP_API sp_err_t sp_io_file_reader_from_path(sp_io_file_reader_t* r, sp_str_t path); -SP_API void sp_io_file_reader_from_file(sp_io_file_reader_t* r, sp_io_file_t file, sp_io_close_mode_t mode); +SP_API sp_err_t sp_io_file_reader_from_file(sp_io_file_reader_t* r, sp_sys_fd_t file, sp_io_close_mode_t mode); SP_API sp_err_t sp_io_file_reader_seek(sp_io_file_reader_t* r, s64 offset, sp_io_whence_t whence, s64* position); SP_API sp_err_t sp_io_file_reader_size(sp_io_file_reader_t* r, u64* size); +SP_API sp_err_t sp_io_file_reader_size_force(sp_io_file_reader_t* r, u64* size); SP_API sp_err_t sp_io_file_reader_close(sp_io_file_reader_t* r); -SP_API sp_err_t sp_io_file_writer_from_path(sp_io_file_writer_t* w, sp_str_t path, sp_io_write_mode_t mode); -SP_API void sp_io_file_writer_from_fd(sp_io_file_writer_t* w, sp_sys_fd_t fd, sp_io_close_mode_t close_mode); + +SP_API sp_err_t sp_io_file_writer_from_path(sp_io_file_writer_t* w, sp_str_t path); +SP_API sp_err_t sp_io_file_writer_from_fd(sp_io_file_writer_t* w, sp_sys_fd_t fd, sp_io_close_mode_t close_mode); SP_API sp_err_t sp_io_file_writer_seek(sp_io_file_writer_t* w, s64 offset, sp_io_whence_t whence, s64* position); SP_API sp_err_t sp_io_file_writer_size(sp_io_file_writer_t* w, u64* size); +SP_API sp_err_t sp_io_file_writer_size_force(sp_io_file_writer_t* w, u64* size); SP_API sp_err_t sp_io_file_writer_close(sp_io_file_writer_t* w); -SP_API void sp_io_pipe_reader_from_pipe(sp_io_pipe_reader_t* r, sp_io_pipe_t pipe, sp_io_close_mode_t mode); -SP_API sp_err_t sp_io_pipe_reader_close(sp_io_pipe_reader_t* r); +SP_API void sp_io_stream_reader_from_fd(sp_io_stream_reader_t* r, sp_sys_fd_t fd, sp_io_close_mode_t mode); +SP_API sp_err_t sp_io_stream_reader_close(sp_io_stream_reader_t* r); + +SP_API void sp_io_stream_writer_from_fd(sp_io_stream_writer_t* w, sp_sys_fd_t fd, sp_io_close_mode_t mode); +SP_API sp_err_t sp_io_stream_writer_close(sp_io_stream_writer_t* w); SP_API sp_err_t sp_io_mem_seek(sp_io_reader_t* r, s64 offset, sp_io_whence_t whence, s64* position); SP_API void sp_io_mem_writer_from_buffer(sp_io_mem_writer_t* w, void* ptr, u64 size); @@ -3206,9 +3249,10 @@ SP_API sp_err_t sp_io_dyn_mem_writer_seek(sp_io_dyn_mem_writer_t* w, s64 o SP_API sp_err_t sp_io_dyn_mem_writer_size(sp_io_dyn_mem_writer_t* w, u64* size); SP_API sp_err_t sp_io_dyn_mem_writer_close(sp_io_dyn_mem_writer_t* w); SP_API sp_str_t sp_io_dyn_mem_writer_as_str(sp_io_dyn_mem_writer_t* w); +SP_API const c8* sp_io_dyn_mem_writer_as_cstr(sp_io_dyn_mem_writer_t* w); -SP_API void sp_io_get_std_out(sp_io_file_writer_t* io); -SP_API void sp_io_get_std_err(sp_io_file_writer_t* io); +SP_API void sp_io_get_std_out(sp_io_stream_writer_t* io); +SP_API void sp_io_get_std_err(sp_io_stream_writer_t* io); // ███████████ ███████████ ███████ █████████ ██████████ █████████ █████████ @@ -3356,7 +3400,7 @@ SP_API sp_ps_output_t sp_ps_output(sp_ps_t* ps); SP_API bool sp_ps_kill(sp_ps_t* ps); SP_API void sp_ps_free(sp_ps_t* ps); SP_API void sp_ps_output_free(sp_mem_t mem, sp_ps_output_t* output); -SP_API sp_io_file_writer_t* sp_ps_io_in(sp_ps_t* ps); +SP_API sp_io_stream_writer_t* sp_ps_io_in(sp_ps_t* ps); SP_API sp_io_reader_t* sp_ps_io_out(sp_ps_t* ps); SP_API sp_io_reader_t* sp_ps_io_err(sp_ps_t* ps); @@ -3510,10 +3554,11 @@ SP_IMP void sp_fmt_write_u64(sp_io_writer_t* io, u64 value); SP_IMP void sp_fmt_write_s64(sp_io_writer_t* io, s64 value); SP_IMP void sp_fmt_write_f64(sp_io_writer_t* io, f64 value, u32 precision); SP_IMP void sp_fmt_write_ptr(sp_io_writer_t* io, void* value); -SP_IMP sp_err_t sp_fmt_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params); -SP_IMP void sp_fmt_apply_spec(sp_io_writer_t* io, sp_str_t pre, sp_str_t str, sp_str_t post, sp_fmt_spec_t spec); +SP_IMP sp_err_t sp_fmt_render(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params); SP_IMP sp_err_t sp_fmt_parse_specifier(sp_fmt_parser_t* p, sp_fmt_spec_t* spec); SP_IMP sp_str_t sp_fmt_color_to_ansi_fg(sp_str_t id); +SP_IMP sp_fmt_arg_t sp_fmt_arg_from_argv(sp_fmt_argv_t v); + // @hash SP_IMP sp_hash_t sp_hash_str(sp_str_t str); @@ -3531,11 +3576,6 @@ SP_IMP bool sp_utf8_is_surrogate(u32 codepoint); SP_IMP bool sp_utf8_is_bounds_ok(u32 codepoint, u8 len); SP_IMP u32 sp_utf8_mask(u8 byte, u8 mask, u8 shift); -// @env -SP_IMP bool sp_os_env_key_equal(sp_str_t a, sp_str_t b); -SP_IMP void sp_os_env_it_set(sp_os_env_it_t* it); -SP_IMP c8** sp_env_to_posix_envp(sp_mem_t mem, sp_env_t* env); - // @context SP_IMP sp_mem_arena_t* sp_tls_rt_get_scratch_arena(sp_tls_rt_t* tls); SP_IMP sp_mem_arena_t* sp_tls_rt_get_scratch_arena_for(sp_tls_rt_t* tls, sp_mem_t mem); @@ -3549,12 +3589,20 @@ SP_IMP BOOL CALLBACK sp_tls_once_trampoline(PINIT_ONCE once, PVOID param, PVOI SP_IMP sp_fs_kind_t sp_fs_lstat_kind(sp_str_t path); SP_IMP sp_fs_kind_t sp_fs_stat_kind(sp_str_t path); +// @tls +SP_PRIVATE void sp_tls_new(sp_tls_key_t* key, sp_tls_deinit_fn_t fn); +SP_PRIVATE void* sp_tls_get(sp_tls_key_t key); +SP_PRIVATE void sp_tls_set(sp_tls_key_t key, void* data); +SP_PRIVATE void sp_tls_once(sp_tls_once_t* once, sp_tls_once_fn_t); + // @io SP_IMP sp_err_t sp_io_file_reader_read(sp_io_reader_t* reader, void* ptr, u64 size, u64* bytes_read); SP_IMP sp_err_t sp_io_file_reader_seek_cb(sp_io_reader_t* reader, s64 offset, sp_io_whence_t whence, s64* position); -SP_IMP sp_err_t sp_io_pipe_reader_read(sp_io_reader_t* reader, void* ptr, u64 size, u64* bytes_read); +SP_IMP sp_err_t sp_io_stream_reader_read(sp_io_reader_t* reader, void* ptr, u64 size, u64* bytes_read); +SP_IMP sp_err_t sp_io_stream_reader_as_fd(sp_io_reader_t* r, sp_sys_fd_t* fd, u64** pos); SP_IMP sp_err_t sp_io_eof_read(sp_io_reader_t* r, void* ptr, u64 size, u64* bytes_read); SP_IMP sp_err_t sp_io_file_writer_write(sp_io_writer_t* writer, const void* ptr, u64 size, u64* bytes_written); +SP_IMP sp_err_t sp_io_stream_writer_write(sp_io_writer_t* writer, const void* ptr, u64 size, u64* bytes_written); SP_IMP sp_err_t sp_io_mem_writer_write(sp_io_writer_t* writer, const void* ptr, u64 size, u64* bytes_written); SP_IMP sp_err_t sp_io_dyn_mem_writer_write(sp_io_writer_t* writer, const void* ptr, u64 size, u64* bytes_written); @@ -3575,7 +3623,7 @@ SP_IMP sp_env_var_t sp_os_env_parse_var(sp_str_t entry); #if defined(SP_LINUX) SP_IMP bool sp_linux_fmon_file_matches(sp_fmon_os_t* os, sp_str_t full_path); #endif -#if defined(SP_MACOS) +#if defined(SP_MACOS) && defined(SP_FMON_MACOS_USE_FSEVENTS) SP_IMP void sp_fmon_os_destroy_stream(sp_fmon_os_t* os); SP_IMP void sp_fmon_fsevents_recreate_stream(sp_fmon_t* monitor); SP_IMP void sp_fmon_os_push_dir(sp_fmon_os_t* os, sp_str_t dir); @@ -3647,11 +3695,10 @@ s64 sp_syscall6(s64 n, s64 a1, s64 a2, s64 a3, s64 a4, s64 a5, s64 a6); #define sp_syscall(...) __sp_syscall_ret((u64)__sp_syscall(__VA_ARGS__)) // Typed wrappers for individual syscalls -SP_IMP s32 sp_lx_inotify_init1(s32 flags); -SP_IMP s32 sp_lx_inotify_add_watch(s32 fd, const c8* pathname, u32 mask); -SP_IMP s32 sp_lx_wait4(s32 pid, s32* status, s32 options, void* rusage); +SP_IMP s32 sp_syscall_notify_init1(s32 flags); +SP_IMP s32 sp_syscall_inotify_add_watch(s32 fd, const c8* pathname, u32 mask); +SP_IMP s32 sp_syscall_wait4(s32 pid, s32* status, s32 options, void* rusage); SP_IMP s64 sp_lx_getdents64(s32 fd, void* buf, u64 count); -SP_IMP s64 sp_lx_copy_file_range(s32 in_fd, s32 out_fd, u64 count); #endif @@ -3661,7 +3708,7 @@ SP_IMP u8* sp_nt_peb_base(void); SP_IMP void* sp_nt_process_heap(void); SP_IMP u8* sp_nt_process_params(void); SP_IMP u32 sp_sys_nt_disposition_from_flags(s32 flags); -SP_IMP void* sp_sys_nt_open(sp_str_t utf8, u32 access, u32 share, u32 disposition, u32 options, u32 file_attr); +SP_IMP void* sp_sys_nt_open(sp_sys_fd_t root, sp_str_t utf8, u32 access, u32 share, u32 disposition, u32 options, u32 file_attr); SP_IMP u32 sp_sys_nt_access_from_flags(s32 flags); SP_IMP sp_str_t sp_win32_utf16_to_utf8(const u16* utf16, s32 len); SP_IMP u32 sp_win32_utf16_len(const u16* str); @@ -3673,9 +3720,6 @@ SP_IMP void* sp_sys_get_tp(void); SP_IMP s32 sp_sys_set_tp(void* tp); SP_IMP void sp_linux_env_it_set_current(sp_os_env_it_t* it); #endif -#if defined(SP_FREESTANDING) || defined(SP_WASM_FREESTANDING) -SP_IMP void sp_sys_init(); -#endif SP_END_EXTERN_C() #endif // SP_PRIVATE_HEADER or SP_IMPLEMENTATION @@ -3746,9 +3790,6 @@ s32 errno; #if defined(SP_WASM_WASI) #include #elif defined(SP_WASM_FREESTANDING) - // No wasi-libc headers: vendor the same types and import declarations. - // Layout/field names mirror wasi-libc's exactly so call sites - // are identical across both axes. typedef __SIZE_TYPE__ __wasi_size_t; typedef uint16_t __wasi_errno_t; @@ -3829,6 +3870,12 @@ s32 errno; sp_wasi_import("fd_write") extern __wasi_errno_t __wasi_fd_write(__wasi_fd_t fd, const __wasi_ciovec_t* iovs, __wasi_size_t niov, __wasi_size_t* nwritten); + sp_wasi_import("fd_pread") + extern __wasi_errno_t __wasi_fd_pread(__wasi_fd_t fd, const __wasi_iovec_t* iovs, __wasi_size_t niov, __wasi_filesize_t offset, __wasi_size_t* nread); + + sp_wasi_import("fd_pwrite") + extern __wasi_errno_t __wasi_fd_pwrite(__wasi_fd_t fd, const __wasi_ciovec_t* iovs, __wasi_size_t niov, __wasi_filesize_t offset, __wasi_size_t* nwritten); + sp_wasi_import("fd_close") extern __wasi_errno_t __wasi_fd_close(__wasi_fd_t fd); @@ -3859,7 +3906,6 @@ s32 errno; #if defined(SP_LINUX) - // We vendor any constant we use on Linux to make freestanding compilation as simple as // possible. We also vendor types, for the same reason. Since our Linux backend never calls // libc, there is no possibility of a mismatch between a vendored struct and a libc struct. @@ -3889,6 +3935,8 @@ s32 errno; #if defined(SP_AMD64) #define SP_SYSCALL_NUM_READ 0 #define SP_SYSCALL_NUM_WRITE 1 + #define SP_SYSCALL_NUM_PREAD64 17 + #define SP_SYSCALL_NUM_PWRITE64 18 #define SP_SYSCALL_NUM_OPEN 2 #define SP_SYSCALL_NUM_CLOSE 3 #define SP_SYSCALL_NUM_STAT 4 @@ -3938,6 +3986,8 @@ s32 errno; #define SP_SYSCALL_NUM_NEWFSTATAT 262 #define SP_SYSCALL_NUM_UNLINKAT 263 #define SP_SYSCALL_NUM_RENAMEAT 264 + #define SP_SYSCALL_NUM_LINKAT 265 + #define SP_SYSCALL_NUM_SYMLINKAT 266 #define SP_SYSCALL_NUM_READLINKAT 267 #define SP_SYSCALL_NUM_FCHMODAT 268 #define SP_SYSCALL_NUM_PPOLL 271 @@ -3968,6 +4018,8 @@ s32 errno; #define SP_SYSCALL_NUM_LSEEK 62 #define SP_SYSCALL_NUM_READ 63 #define SP_SYSCALL_NUM_WRITE 64 + #define SP_SYSCALL_NUM_PREAD64 67 + #define SP_SYSCALL_NUM_PWRITE64 68 #define SP_SYSCALL_NUM_PPOLL 73 #define SP_SYSCALL_NUM_READLINKAT 78 #define SP_SYSCALL_NUM_NEWFSTATAT 79 @@ -4200,34 +4252,18 @@ s64 sp_syscall6(s64 n, s64 a1, s64 a2, s64 a3, s64 a4, s64 a5, s64 a6) { return ret; } -////////////////////// -// SYSCALL WRAPPERS // -////////////////////// -s32 sp_lx_inotify_init1(s32 flags) { +s32 sp_syscall_notify_init1(s32 flags) { return (s32)sp_syscall(SP_SYSCALL_NUM_INOTIFY_INIT1, flags); } -s32 sp_lx_inotify_add_watch(s32 fd, const c8* pathname, u32 mask) { +s32 sp_syscall_inotify_add_watch(s32 fd, const c8* pathname, u32 mask) { return (s32)sp_syscall(SP_SYSCALL_NUM_INOTIFY_ADD_WATCH, fd, pathname, mask); } -s32 sp_lx_wait4(s32 pid, s32* status, s32 options, void* rusage) { +s32 sp_syscall_wait4(s32 pid, s32* status, s32 options, void* rusage) { return (s32)sp_syscall(SP_SYSCALL_NUM_WAIT4, pid, status, options, rusage); } -// Kernel-to-kernel copy. NULL offset pointers means "use and advance the fd's -// own seek position", which is what we want for stream-style copies. -s64 sp_lx_copy_file_range(s32 in_fd, s32 out_fd, u64 count) { - s64 rc; - do { - rc = sp_syscall(SP_SYSCALL_NUM_COPY_FILE_RANGE, in_fd, 0, out_fd, 0, count, 0); - } while (rc == -1 && errno == SP_EINTR); - return rc; -} - -////////////// -// PLATFORM // -////////////// static void sp_sys_stat_from_linux(const sp_sys_linux_stat_t* raw, sp_sys_stat_t* out) { if (SP_S_ISLNK(raw->st_mode)) out->kind = SP_FS_KIND_SYMLINK; else if (SP_S_ISDIR(raw->st_mode)) out->kind = SP_FS_KIND_DIR; @@ -4247,17 +4283,9 @@ static void sp_sys_stat_from_linux(const sp_sys_linux_stat_t* raw, sp_sys_stat_t out->raw_attrs = raw->st_mode; } -s32 sp_sys_fstat(sp_sys_fd_t fd, sp_sys_stat_t* st) { - sp_sys_linux_stat_t raw = sp_zero; - s32 rc = (s32)sp_syscall(SP_SYSCALL_NUM_FSTAT, fd, &raw); - if (rc == 0) sp_sys_stat_from_linux(&raw, st); - return rc; -} - s64 sp_lx_getdents64(s32 fd, void* buf, u64 count) { return sp_syscall(SP_SYSCALL_NUM_GETDENTS64, fd, buf, count); } - #endif #if defined(SP_MACOS) || defined(SP_COSMO) @@ -4287,20 +4315,24 @@ static void sp_sys_stat_from_libc(const struct stat* src, sp_sys_stat_t* out) { out->btime = out->mtime; #endif } +#endif -s32 sp_sys_fstat(sp_sys_fd_t fd, sp_sys_stat_t* st) { - struct stat native; - s32 rc = fstat(fd, &native); - if (rc == 0) sp_sys_stat_from_libc(&native, st); - return rc; -} - -#elif defined(SP_WIN32) +#if defined(SP_WIN32) SP_PRIVATE void sp_rt_init(void); #define SP_NT(fn) ((SP_UNLIKELY(!sp_rt.nt.fn) ? sp_tls_once(&sp_rt.tls.once, sp_rt_init) : (void)0), sp_rt.nt.fn) +typedef struct { + u8 ReplaceIfExists; + u8 _pad[7]; + void* RootDirectory; + u32 FileNameLength; + u16 FileName[1]; +} sp_nt_file_rename_information_t; + +typedef sp_nt_file_rename_information_t sp_nt_file_link_information_t; + SP_PRIVATE void sp_nt_load(void) { HMODULE h = GetModuleHandleW(L"ntdll.dll"); if (!h) return; @@ -4340,6 +4372,31 @@ SP_PRIVATE u8* sp_nt_process_params(void) { return *(u8**)(sp_nt_peb_base() + 0x20); } +SP_PRIVATE bool sp_sys_is_absolute_wtf16(const u16* p, u32 len) { + if (len < 1) return false; + if (p[0] == '\\' || p[0] == '/') return true; + if (len < 2 || p[1] != ':') return false; + if (len < 3) return false; + return p[2] == '\\' || p[2] == '/'; +} + +SP_PRIVATE u32 sp_sys_normalize_relative_wtf16(u16* p, u32 len) { + u32 w = 0; + u32 r = 0; + bool need_sep = false; + while (r < len) { + if (p[r] == '/' || p[r] == '\\') { r++; continue; } + u32 start = r; + while (r < len && p[r] != '/' && p[r] != '\\') r++; + u32 seg_len = r - start; + if (seg_len == 1 && p[start] == '.') continue; + if (need_sep) p[w++] = '\\'; + sp_for(i, seg_len) p[w++] = p[start + i]; + need_sep = true; + } + return w; +} + sp_nt_status_t sp_sys_nt_path(sp_str_t utf8, sp_sys_nt_path_t* out) { *out = sp_zero_s(sp_sys_nt_path_t); if (sp_str_empty(utf8)) return SP_NT_STATUS_OBJECT_NAME_INVALID; @@ -4395,13 +4452,30 @@ SP_PRIVATE u32 sp_sys_nt_disposition_from_flags(s32 flags) { return SP_NT_FILE_OPEN; } -SP_PRIVATE void* sp_sys_nt_open(sp_str_t utf8, u32 access, u32 share, u32 disposition, u32 options, u32 file_attr) { - sp_sys_nt_path_t nt = sp_zero; - if (!SP_NT_SUCCESS(sp_sys_nt_path(utf8, &nt))) return SP_NULLPTR; +SP_PRIVATE void* sp_sys_nt_open(sp_sys_fd_t root, sp_str_t utf8, u32 access, u32 share, u32 disposition, u32 options, u32 file_attr) { + if (sp_str_empty(utf8)) return SP_NULLPTR; + sp_wide_str_t wpath = sp_wtf8_to_wtf16(sp_mem_get_scratch(), utf8); + if (!wpath.data) return SP_NULLPTR; + + sp_sys_nt_path_t nt_owned = sp_zero; + sp_nt_unicode_string_t name = sp_zero; + HANDLE root_dir = SP_NULLPTR; + + if (sp_sys_is_absolute_wtf16(wpath.data, wpath.len)) { + if (!SP_NT_SUCCESS(sp_sys_nt_path(utf8, &nt_owned))) return SP_NULLPTR; + name = nt_owned.name; + } else { + u32 nlen = sp_sys_normalize_relative_wtf16((u16*)wpath.data, wpath.len); + name.Length = (u16)(nlen * sizeof(u16)); + name.MaximumLength = name.Length; + name.Buffer = (u16*)wpath.data; + root_dir = (HANDLE)root; + } sp_nt_object_attributes_t attr = { .Length = sizeof(sp_nt_object_attributes_t), - .ObjectName = &nt.name, + .RootDirectory = root_dir, + .ObjectName = &name, .Attributes = SP_NT_OBJ_CASE_INSENSITIVE, }; @@ -4412,7 +4486,7 @@ SP_PRIVATE void* sp_sys_nt_open(sp_str_t utf8, u32 access, u32 share, u32 dispos file_attr, share, disposition, options, SP_NULLPTR, 0 ); - sp_sys_nt_path_free(&nt); + sp_sys_nt_path_free(&nt_owned); if (!SP_NT_SUCCESS(status)) return SP_NULLPTR; return handle; @@ -4476,10 +4550,11 @@ static s32 sp_sys_stat_from_nt_handle(HANDLE handle, sp_sys_stat_t* out) { return 0; } -static s32 sp_sys_stat_from_nt_path(sp_str_t path, sp_sys_stat_t* out, bool follow_symlinks) { +static s32 sp_sys_stat_from_nt_path(sp_sys_fd_t root, sp_str_t path, sp_sys_stat_t* out, bool follow_symlinks) { u32 options = SP_NT_FILE_SYNCHRONOUS_IO_NONALERT | SP_NT_FILE_OPEN_FOR_BACKUP_INTENT; if (!follow_symlinks) options |= SP_NT_FILE_OPEN_REPARSE_POINT; void* h = sp_sys_nt_open( + root, path, FILE_READ_ATTRIBUTES | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, @@ -4490,27 +4565,46 @@ static s32 sp_sys_stat_from_nt_path(sp_str_t path, sp_sys_stat_t* out, bool foll SP_NT(NtClose)(h); return rc; } +#endif + +////////////////// +// SP_SYS_FSTAT // +////////////////// s32 sp_sys_fstat(sp_sys_fd_t fd, sp_sys_stat_t* st) { +#if defined(SP_WIN32) if (fd == SP_SYS_INVALID_FD) return -1; return sp_sys_stat_from_nt_handle((HANDLE)fd, st); -} + +#elif defined(SP_LINUX) + sp_sys_linux_stat_t raw = sp_zero; + s32 rc = (s32)sp_syscall(SP_SYSCALL_NUM_FSTAT, fd, &raw); + if (rc == 0) sp_sys_stat_from_linux(&raw, st); + return rc; + +#elif defined(SP_MACOS) || defined(SP_COSMO) + struct stat native; + s32 rc = fstat(fd, &native); + if (rc == 0) sp_sys_stat_from_libc(&native, st); + return rc; + +#elif defined(SP_WASM) + (void)fd; (void)st; + return -1; + +#else + #error "sp_sys_fstat" #endif +} + +/////////////////// // SP_SYS_RENAME // +/////////////////// +s32 sp_sys_rename(sp_sys_fd_t from_fd, const c8* from, u32 from_len, sp_sys_fd_t to_fd, const c8* to, u32 to_len) { #if defined(SP_WIN32) -typedef struct { - u8 ReplaceIfExists; - u8 _pad[7]; - void* RootDirectory; - u32 FileNameLength; - u16 FileName[1]; -} sp_nt_file_rename_information_t; - -typedef sp_nt_file_rename_information_t sp_nt_file_link_information_t; - -s32 sp_sys_rename(const c8* from, u32 from_len, const c8* to, u32 to_len) { void* handle = sp_sys_nt_open( + from_fd, sp_str(from, from_len), DELETE | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, @@ -4520,20 +4614,40 @@ s32 sp_sys_rename(const c8* from, u32 from_len, const c8* to, u32 to_len) { ); if (!handle) return -1; - sp_sys_nt_path_t nt = sp_zero; - if (!SP_NT_SUCCESS(sp_sys_nt_path(sp_str(to, to_len), &nt))) { + sp_str_t to_utf8 = sp_str(to, to_len); + sp_wide_str_t to_w = sp_wtf8_to_wtf16(sp_mem_get_scratch(), to_utf8); + if (!to_w.data) { SP_NT(NtClose)(handle); return -1; } - u32 name_bytes = nt.name.Length; + sp_sys_nt_path_t nt = sp_zero; + const u16* name_buf; + u32 name_bytes; + HANDLE root_dir; + if (sp_sys_is_absolute_wtf16(to_w.data, to_w.len)) { + if (!SP_NT_SUCCESS(sp_sys_nt_path(to_utf8, &nt))) { + SP_NT(NtClose)(handle); + return -1; + } + name_buf = nt.name.Buffer; + name_bytes = nt.name.Length; + root_dir = SP_NULLPTR; + } else { + u32 nlen = sp_sys_normalize_relative_wtf16((u16*)to_w.data, to_w.len); + name_buf = to_w.data; + name_bytes = nlen * (u32)sizeof(u16); + root_dir = (HANDLE)to_fd; + } + u32 info_bytes = sizeof(sp_nt_file_rename_information_t) + name_bytes - sizeof(u16); sp_mem_arena_marker_t s = sp_mem_begin_scratch(); sp_nt_file_rename_information_t* info = (sp_nt_file_rename_information_t*)sp_alloc(s.mem, info_bytes); *info = sp_zero_s(sp_nt_file_rename_information_t); info->ReplaceIfExists = 1; + info->RootDirectory = root_dir; info->FileNameLength = name_bytes; - sp_mem_copy(info->FileName, nt.name.Buffer, name_bytes); + sp_mem_copy(info->FileName, name_buf, name_bytes); sp_nt_io_status_block_t iosb = sp_zero; sp_nt_status_t status = SP_NT(NtSetInformationFile)(handle, &iosb, info, info_bytes, SP_NT_FILE_RENAME_INFORMATION); @@ -4543,80 +4657,65 @@ s32 sp_sys_rename(const c8* from, u32 from_len, const c8* to, u32 to_len) { sp_mem_end_scratch(s); return SP_NT_SUCCESS(status) ? 0 : -1; -} #elif defined(SP_LINUX) -s32 sp_sys_rename(const c8* from, u32 from_len, const c8* to, u32 to_len) { struct { c8 from [SP_PATH_MAX]; c8 to [SP_PATH_MAX]; } buffers = sp_zero; sp_cstr_copy_to_n(from, from_len, buffers.from, SP_PATH_MAX); sp_cstr_copy_to_n(to, to_len, buffers.to, SP_PATH_MAX); -#if defined(SP_AMD64) - return (s32)sp_syscall(SP_SYSCALL_NUM_RENAME, buffers.from, buffers.to); -#else - return (s32)sp_syscall(SP_SYSCALL_NUM_RENAMEAT, SP_AT_FDCWD, buffers.from, SP_AT_FDCWD, buffers.to); -#endif -} + return (s32)sp_syscall(SP_SYSCALL_NUM_RENAMEAT, from_fd, buffers.from, to_fd, buffers.to); #elif defined(SP_MACOS) || defined(SP_COSMO) -s32 sp_sys_rename(const c8* from, u32 from_len, const c8* to, u32 to_len) { struct { c8 from [SP_PATH_MAX]; c8 to [SP_PATH_MAX]; } buffers = sp_zero; sp_cstr_copy_to_n(from, from_len, buffers.from, SP_PATH_MAX); sp_cstr_copy_to_n(to, to_len, buffers.to, SP_PATH_MAX); - return rename(buffers.from, buffers.to); -} + return renameat((int)from_fd, buffers.from, (int)to_fd, buffers.to); #elif defined(SP_WASM) -s32 sp_sys_rename(const c8* from, u32 from_len, const c8* to, u32 to_len) { - (void)from; (void)from_len; (void)to; (void)to_len; + (void)from_fd; (void)from; (void)from_len; (void)to_fd; (void)to; (void)to_len; return -1; -} + #else -#error "" + #error "sp_sys_rename" #endif +} -s32 sp_sys_rename_s(sp_str_t from, sp_str_t to) { - return sp_sys_rename(from.data, from.len, to.data, to.len); +s32 sp_sys_rename_s(sp_sys_fd_t from_fd, sp_str_t from, sp_sys_fd_t to_fd, sp_str_t to) { + return sp_sys_rename(from_fd, from.data, from.len, to_fd, to.data, to.len); } ////////////////// // SP_SYS_CLOSE // ////////////////// -#if defined(SP_WIN32) s32 sp_sys_close(sp_sys_fd_t fd) { +#if defined(SP_WIN32) if (fd == SP_SYS_INVALID_FD) return -1; return CloseHandle((HANDLE)fd) ? 0 : -1; -} #elif defined(SP_MACOS) || defined(SP_COSMO) -s32 sp_sys_close(sp_sys_fd_t fd) { return close(fd); -} #elif defined(SP_LINUX) -s32 sp_sys_close(sp_sys_fd_t fd) { return (s32)sp_syscall(SP_SYSCALL_NUM_CLOSE, fd); -} #elif defined(SP_WASM) -s32 sp_sys_close(s32 fd) { return __wasi_fd_close(fd) ? -1 : 0; -} #else -#error "sp_sys_close" + #error "sp_sys_close" #endif +} ///////////////// // SP_SYS_PIPE // ///////////////// -#if defined(SP_WIN32) s32 sp_sys_pipe(sp_sys_fd_t* read_end, sp_sys_fd_t* write_end) { +#if defined(SP_WIN32) HANDLE r = SP_NULLPTR; HANDLE w = SP_NULLPTR; SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), SP_NULLPTR, FALSE }; @@ -4630,20 +4729,16 @@ s32 sp_sys_pipe(sp_sys_fd_t* read_end, sp_sys_fd_t* write_end) { *read_end = (sp_sys_fd_t)r; *write_end = (sp_sys_fd_t)w; return 0; -} #elif defined(SP_LINUX) -s32 sp_sys_pipe(sp_sys_fd_t* read_end, sp_sys_fd_t* write_end) { s32 fds[2]; s32 r = (s32)sp_syscall(SP_SYSCALL_NUM_PIPE2, fds, SP_O_NONBLOCK | SP_O_CLOEXEC, 0, 0, 0); if (r < 0) return -1; *read_end = fds[0]; *write_end = fds[1]; return 0; -} #elif defined(SP_MACOS) || defined(SP_COSMO) -s32 sp_sys_pipe(sp_sys_fd_t* read_end, sp_sys_fd_t* write_end) { s32 fds[2]; if (pipe(fds) < 0) return -1; fcntl(fds[0], F_SETFL, fcntl(fds[0], F_GETFL) | O_NONBLOCK); @@ -4652,134 +4747,194 @@ s32 sp_sys_pipe(sp_sys_fd_t* read_end, sp_sys_fd_t* write_end) { *read_end = fds[0]; *write_end = fds[1]; return 0; -} #elif defined(SP_WASM) -s32 sp_sys_pipe(sp_sys_fd_t* read_end, sp_sys_fd_t* write_end) { *read_end = SP_SYS_INVALID_FD; *write_end = SP_SYS_INVALID_FD; return -1; -} #else -#error "sp_sys_pipe" + #error "sp_sys_pipe" #endif +} ////////////////// // SP_SYS_READ // ////////////////// -#if defined(SP_WIN32) s64 sp_sys_read(sp_sys_fd_t fd, void* buf, u64 count) { +#if defined(SP_WIN32) DWORD n = 0; if (!ReadFile((HANDLE)fd, buf, (DWORD)count, &n, SP_NULLPTR)) { if (GetLastError() == ERROR_BROKEN_PIPE) return 0; return -1; } return (s64)n; -} #elif defined(SP_LINUX) -s64 sp_sys_read(sp_sys_fd_t fd, void* buf, u64 count) { s64 rc; do { rc = sp_syscall(SP_SYSCALL_NUM_READ, fd, buf, count); } while (rc == -1 && errno == SP_EINTR); return rc; -} #elif defined(SP_MACOS) || defined(SP_COSMO) -s64 sp_sys_read(sp_sys_fd_t fd, void* buf, u64 count) { s64 rc; do { rc = read(fd, buf, count); } while (rc == -1 && errno == SP_EINTR); return rc; -} #elif defined(SP_WASM) -s64 sp_sys_read(s32 fd, void* buf, u64 count) { __wasi_iovec_t iov = { (uint8_t*)buf, (__wasi_size_t)count }; __wasi_size_t n = 0; __wasi_errno_t err = __wasi_fd_read((__wasi_fd_t)fd, &iov, 1, &n); return err ? -1 : (s64)n; -} #else -#error "sp_sys_read" + #error "sp_sys_read" #endif +} /////////////////// // SP_SYS_WRITE // /////////////////// -#if defined(SP_WIN32) s64 sp_sys_write(sp_sys_fd_t fd, const void* buf, u64 count) { +#if defined(SP_WIN32) DWORD n = 0; if (!WriteFile((HANDLE)fd, buf, (DWORD)count, &n, SP_NULLPTR)) return -1; return (s64)n; -} #elif defined(SP_LINUX) -s64 sp_sys_write(sp_sys_fd_t fd, const void* buf, u64 count) { s64 rc; do { rc = sp_syscall(SP_SYSCALL_NUM_WRITE, fd, buf, count); } while (rc == -1 && errno == SP_EINTR); return rc; -} #elif defined(SP_MACOS) || defined(SP_COSMO) -s64 sp_sys_write(sp_sys_fd_t fd, const void* buf, u64 count) { s64 rc; do { rc = write(fd, buf, count); } while (rc == -1 && errno == SP_EINTR); return rc; -} #elif defined(SP_WASM) -s64 sp_sys_write(s32 fd, const void* buf, u64 count) { __wasi_ciovec_t iov = { (const uint8_t*)buf, (__wasi_size_t)count }; __wasi_size_t n = 0; __wasi_errno_t err = __wasi_fd_write((__wasi_fd_t)fd, &iov, 1, &n); return err ? -1 : (s64)n; -} #else -#error "sp_sys_write" + #error "sp_sys_write" #endif +} -////////////////// -// SP_SYS_LSEEK // -////////////////// +/////////////////// +// SP_SYS_PREAD // +/////////////////// +s64 sp_sys_pread(sp_sys_fd_t fd, void* buf, u64 count, u64 offset) { #if defined(SP_WIN32) -s64 sp_sys_lseek(sp_sys_fd_t fd, s64 offset, s32 whence) { - DWORD method; - switch (whence) { - case SP_IO_SEEK_SET: method = FILE_BEGIN; break; - case SP_IO_SEEK_CUR: method = FILE_CURRENT; break; - case SP_IO_SEEK_END: method = FILE_END; break; - default: return -1; + OVERLAPPED ov = sp_zero; + ov.Offset = (DWORD)(offset & 0xFFFFFFFFu); + ov.OffsetHigh = (DWORD)(offset >> 32); + DWORD n = 0; + if (!ReadFile((HANDLE)fd, buf, (DWORD)count, &n, &ov)) { + DWORD e = GetLastError(); + if (e == ERROR_BROKEN_PIPE || e == ERROR_HANDLE_EOF) return 0; + return -1; } - LARGE_INTEGER dist = { .QuadPart = offset }; - LARGE_INTEGER new_pos; - if (!SetFilePointerEx((HANDLE)fd, dist, &new_pos, method)) return -1; - return (s64)new_pos.QuadPart; -} + return (s64)n; #elif defined(SP_LINUX) -s64 sp_sys_lseek(sp_sys_fd_t fd, s64 offset, s32 whence) { - s32 native; - switch (whence) { - case SP_IO_SEEK_SET: native = 0; break; - case SP_IO_SEEK_CUR: native = 1; break; + s64 rc; + do { + rc = sp_syscall(SP_SYSCALL_NUM_PREAD64, fd, buf, count, offset); + } while (rc == -1 && errno == SP_EINTR); + return rc; + +#elif defined(SP_MACOS) || defined(SP_COSMO) + s64 rc; + do { + rc = pread(fd, buf, count, (off_t)offset); + } while (rc == -1 && errno == SP_EINTR); + return rc; + +#elif defined(SP_WASM) + __wasi_iovec_t iov = { (uint8_t*)buf, (__wasi_size_t)count }; + __wasi_size_t n = 0; + __wasi_errno_t err = __wasi_fd_pread((__wasi_fd_t)fd, &iov, 1, (__wasi_filesize_t)offset, &n); + return err ? -1 : (s64)n; + +#else + #error "sp_sys_pread" +#endif +} + +//////////////////// +// SP_SYS_PWRITE // +//////////////////// +s64 sp_sys_pwrite(sp_sys_fd_t fd, const void* buf, u64 count, u64 offset) { +#if defined(SP_WIN32) + OVERLAPPED ov = sp_zero; + ov.Offset = (DWORD)(offset & 0xFFFFFFFFu); + ov.OffsetHigh = (DWORD)(offset >> 32); + DWORD n = 0; + if (!WriteFile((HANDLE)fd, buf, (DWORD)count, &n, &ov)) return -1; + return (s64)n; + +#elif defined(SP_LINUX) + s64 rc; + do { + rc = sp_syscall(SP_SYSCALL_NUM_PWRITE64, fd, buf, count, offset); + } while (rc == -1 && errno == SP_EINTR); + return rc; + +#elif defined(SP_MACOS) || defined(SP_COSMO) + s64 rc; + do { + rc = pwrite(fd, buf, count, (off_t)offset); + } while (rc == -1 && errno == SP_EINTR); + return rc; + +#elif defined(SP_WASM) + __wasi_ciovec_t iov = { (const uint8_t*)buf, (__wasi_size_t)count }; + __wasi_size_t n = 0; + __wasi_errno_t err = __wasi_fd_pwrite((__wasi_fd_t)fd, &iov, 1, (__wasi_filesize_t)offset, &n); + return err ? -1 : (s64)n; + +#else + #error "sp_sys_pwrite" +#endif +} + +////////////////// +// SP_SYS_LSEEK // +////////////////// +s64 sp_sys_lseek(sp_sys_fd_t fd, s64 offset, s32 whence) { +#if defined(SP_WIN32) + DWORD method; + switch (whence) { + case SP_IO_SEEK_SET: method = FILE_BEGIN; break; + case SP_IO_SEEK_CUR: method = FILE_CURRENT; break; + case SP_IO_SEEK_END: method = FILE_END; break; + default: return -1; + } + LARGE_INTEGER dist = { .QuadPart = offset }; + LARGE_INTEGER new_pos; + if (!SetFilePointerEx((HANDLE)fd, dist, &new_pos, method)) return -1; + return (s64)new_pos.QuadPart; + +#elif defined(SP_LINUX) + s32 native; + switch (whence) { + case SP_IO_SEEK_SET: native = 0; break; + case SP_IO_SEEK_CUR: native = 1; break; case SP_IO_SEEK_END: native = 2; break; default: return -1; } return sp_syscall(SP_SYSCALL_NUM_LSEEK, fd, offset, native); -} #elif defined(SP_MACOS) || defined(SP_COSMO) -s64 sp_sys_lseek(sp_sys_fd_t fd, s64 offset, s32 whence) { s32 native; switch (whence) { case SP_IO_SEEK_SET: native = SEEK_SET; break; @@ -4788,10 +4943,8 @@ s64 sp_sys_lseek(sp_sys_fd_t fd, s64 offset, s32 whence) { default: return -1; } return lseek(fd, offset, native); -} #elif defined(SP_WASM) -s64 sp_sys_lseek(sp_sys_fd_t fd, s64 offset, s32 whence) { __wasi_whence_t native; switch (whence) { case SP_IO_SEEK_SET: native = __WASI_WHENCE_SET; break; @@ -4802,18 +4955,24 @@ s64 sp_sys_lseek(sp_sys_fd_t fd, s64 offset, s32 whence) { __wasi_filesize_t newoff = 0; __wasi_errno_t err = __wasi_fd_seek((__wasi_fd_t)fd, (__wasi_filedelta_t)offset, native, &newoff); return err ? -1 : (s64)newoff; -} + #else -#error "sp_sys_lseek" + #error "sp_sys_lseek" #endif +} /////////////////////////// // SP_SYS_CLOCK_GETTIME // ////////////////////////// -#if defined(SP_WIN32) +#if defined(SP_WASM) +#define SP_CLOCK_REALTIME 0 +#define SP_CLOCK_MONOTONIC 1 +#endif + s32 sp_sys_clock_gettime(s32 clockid, sp_sys_timespec_t* ts) { +#if defined(SP_WIN32) if (clockid == SP_CLOCK_MONOTONIC) { LARGE_INTEGER freq, counter; QueryPerformanceFrequency(&freq); @@ -4834,15 +4993,11 @@ s32 sp_sys_clock_gettime(s32 clockid, sp_sys_timespec_t* ts) { return 0; } return -1; -} #elif defined(SP_LINUX) -s32 sp_sys_clock_gettime(s32 clockid, sp_sys_timespec_t* ts) { return (s32)sp_syscall(SP_SYSCALL_NUM_CLOCK_GETTIME, clockid, ts); -} #elif defined(SP_MACOS) || defined(SP_COSMO) -s32 sp_sys_clock_gettime(s32 clockid, sp_sys_timespec_t* ts) { struct timespec native; s32 rc = clock_gettime((clockid_t)clockid, &native); if (rc == 0) { @@ -4850,22 +5005,19 @@ s32 sp_sys_clock_gettime(s32 clockid, sp_sys_timespec_t* ts) { ts->tv_nsec = (s64)native.tv_nsec; } return rc; -} #elif defined(SP_WASM) -#define SP_CLOCK_REALTIME 0 -#define SP_CLOCK_MONOTONIC 1 -s32 sp_sys_clock_gettime(s32 clockid, sp_sys_timespec_t* ts) { __wasi_timestamp_t ns = 0; __wasi_errno_t err = __wasi_clock_time_get((__wasi_clockid_t)clockid, 1000, &ns); if (err) return -1; ts->tv_sec = (s64)(ns / 1000000000ULL); ts->tv_nsec = (s64)(ns % 1000000000ULL); return 0; -} + #else -#error "sp_sys_clock_gettime" + #error "sp_sys_clock_gettime" #endif +} ///////////////// // SP_SYS_OPEN // @@ -4888,14 +5040,27 @@ SP_PRIVATE u32 sp_sys_nt_access_from_flags(s32 flags) { } -sp_sys_fd_t sp_sys_open(const c8* path, u32 len, s32 flags, s32 mode) { +#endif + +#if defined(SP_WASM) +#define SP_O_CREAT 0 +#define SP_O_WRONLY 0 +#define SP_O_TRUNC 0 +#define SP_O_BINARY 0 +#define SP_O_DIRECTORY 0 +#define SP_O_RDONLY 0 +#define SP_O_APPEND 0 +#endif + +sp_sys_fd_t sp_sys_open(sp_sys_fd_t fd, const c8* path, u32 len, s32 flags, s32 mode) { +#if defined(SP_WIN32) (void)mode; u32 access = sp_sys_nt_access_from_flags(flags); u32 share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; u32 disposition = sp_sys_nt_disposition_from_flags(flags); u32 options = SP_NT_FILE_SYNCHRONOUS_IO_NONALERT | SP_NT_FILE_NON_DIRECTORY_FILE | SP_NT_FILE_OPEN_FOR_BACKUP_INTENT; - void* handle = sp_sys_nt_open(sp_str(path, len), access, share, disposition, options, FILE_ATTRIBUTE_NORMAL); + void* handle = sp_sys_nt_open(fd, sp_str(path, len), access, share, disposition, options, FILE_ATTRIBUTE_NORMAL); if (!handle) return SP_SYS_INVALID_FD; if (flags & SP_O_APPEND) { @@ -4904,63 +5069,79 @@ sp_sys_fd_t sp_sys_open(const c8* path, u32 len, s32 flags, s32 mode) { } return (sp_sys_fd_t)handle; -} + #elif defined(SP_LINUX) -sp_sys_fd_t sp_sys_open(const c8* path, u32 len, s32 flags, s32 mode) { c8 buf [SP_PATH_MAX] = sp_zero; sp_cstr_copy_to_n(path, len, buf, SP_PATH_MAX); - return (sp_sys_fd_t)sp_syscall(SP_SYSCALL_NUM_OPENAT, SP_AT_FDCWD, buf, flags, mode); -} + return (sp_sys_fd_t)sp_syscall(SP_SYSCALL_NUM_OPENAT, fd, buf, flags, mode); #elif defined(SP_MACOS) || defined(SP_COSMO) -sp_sys_fd_t sp_sys_open(const c8* path, u32 len, s32 flags, s32 mode) { c8 buf [SP_PATH_MAX] = sp_zero; sp_cstr_copy_to_n(path, len, buf, SP_PATH_MAX); - return (sp_sys_fd_t)open(buf, flags, mode); -} + return (sp_sys_fd_t)openat((int)fd, buf, flags, mode); #elif defined(SP_WASM) -#define SP_O_CREAT 0 -#define SP_O_WRONLY 0 -#define SP_O_TRUNC 0 -#define SP_O_BINARY 0 -#define SP_O_DIRECTORY 0 -#define SP_O_RDONLY 0 -#define SP_O_APPEND 0 -sp_sys_fd_t sp_sys_open(const c8* path, u32 len, s32 flags, s32 mode) { - (void)path; (void)len; (void)flags; (void)mode; + (void)fd; (void)path; (void)len; (void)flags; (void)mode; return -1; -} + #else -#error "sp_sys_open" + #error "sp_sys_open" #endif +} + +sp_sys_fd_t sp_sys_open_s(sp_sys_fd_t fd, sp_str_t path, s32 flags, s32 mode) { + return sp_sys_open(fd, path.data, path.len, flags, mode); +} + +///////////////// +// SP_SYS_ROOT // +///////////////// +sp_sys_fd_t sp_sys_root(s32 it, c8* buffer, u32 len) { +#if defined(SP_WIN32) + if (it != 0) return SP_SYS_INVALID_FD; + if (buffer && len) { + buffer[0] = '.'; + if (len > 1) buffer[1] = 0; + } + sp_nt_unicode_string_t* cwd = (sp_nt_unicode_string_t*)(sp_nt_process_params() + 0x38); + void** cwd_handle = (void**)(((u8*)cwd) + sizeof(sp_nt_unicode_string_t)); + return (sp_sys_fd_t)*cwd_handle; + +#elif defined(SP_LINUX) || defined(SP_MACOS) || defined(SP_COSMO) + if (it != 0) return SP_SYS_INVALID_FD; + if (buffer && len) { + buffer[0] = '.'; + if (len > 1) buffer[1] = 0; + } + return (sp_sys_fd_t)SP_AT_FDCWD; + +#elif defined(SP_WASM) + (void)it; (void)buffer; (void)len; + return SP_SYS_INVALID_FD; -sp_sys_fd_t sp_sys_open_s(sp_str_t path, s32 flags, s32 mode) { - return sp_sys_open(path.data, path.len, flags, mode); +#else + #error "sp_sys_root" +#endif } ////////////////////// // SP_SYS_NANOSLEEP // ////////////////////// -#if defined(SP_WIN32) s32 sp_sys_nanosleep(const sp_sys_timespec_t* req, sp_sys_timespec_t* rem) { +#if defined(SP_WIN32) (void)rem; u64 ns = (u64)req->tv_sec * 1000000000ULL + (u64)req->tv_nsec; Sleep((DWORD)(ns / 1000000ULL)); return 0; -} #elif defined(SP_LINUX) -s32 sp_sys_nanosleep(const sp_sys_timespec_t* req, sp_sys_timespec_t* rem) { s32 rc = (s32)sp_syscall(SP_SYSCALL_NUM_NANOSLEEP, req, rem); while (rc == -1 && errno == SP_EINTR) { rc = (s32)sp_syscall(SP_SYSCALL_NUM_NANOSLEEP, rem, rem); } return rc; -} #elif defined(SP_MACOS) || defined(SP_COSMO) -s32 sp_sys_nanosleep(const sp_sys_timespec_t* req, sp_sys_timespec_t* rem) { struct timespec r_native = { .tv_sec = (time_t)req->tv_sec, .tv_nsec = (long)req->tv_nsec }; struct timespec rem_native = sp_zero; s32 rc = nanosleep(&r_native, &rem_native); @@ -4973,10 +5154,8 @@ s32 sp_sys_nanosleep(const sp_sys_timespec_t* req, sp_sys_timespec_t* rem) { rem->tv_nsec = (s64)rem_native.tv_nsec; } return rc; -} #elif defined(SP_WASM) -s32 sp_sys_nanosleep(const sp_sys_timespec_t* req, sp_sys_timespec_t* rem) { __wasi_timestamp_t ns = (__wasi_timestamp_t)(req->tv_sec * SP_TM_S_TO_NS + req->tv_nsec); __wasi_subscription_t sub = { .userdata = 0, @@ -4999,11 +5178,11 @@ s32 sp_sys_nanosleep(const sp_sys_timespec_t* req, sp_sys_timespec_t* rem) { rem->tv_nsec = 0; } return 0; -} #else -#error "sp_sys_nanosleep" + #error "sp_sys_nanosleep" #endif +} #if defined(SP_WIN32) static s64 sp_sys_copy_env_var(const c8* name, c8* buf, u64 size) { @@ -5478,22 +5657,22 @@ void* memset(void* dest, s32 c, u64 n) { return dest; } #elif defined(SP_WASM) -__attribute__((weak)) +__attribute__((weak, nothrow)) void* memcpy(void* dest, const void* src, size_t n) { return __builtin_memcpy(dest, src, n); } -__attribute__((weak)) +__attribute__((weak, nothrow)) void* memmove(void* dest, const void* src, size_t n) { return __builtin_memmove(dest, src, n); } -__attribute__((weak)) -void* memset(void* dest, s32 c, size_t n) { +__attribute__((weak, nothrow)) +void* memset(void* dest, int c, size_t n) { return __builtin_memset(dest, c, n); } -__attribute__((weak)) +__attribute__((weak, nothrow)) s32 memcmp(const void* va, const void* vb, size_t n) { const u8* a = (const u8*)va; const u8* b = (const u8*)vb; @@ -5692,12 +5871,6 @@ void sp_sys_exit(s32 code) { ///////////////////// #if defined(SP_FREESTANDING) || defined(SP_WASM_FREESTANDING) void sp_sys_tls_init(sp_tls_rt_t* tls) { - sp_env_init(tls->mem, &tls->env); - if (!environ) return; - for (c8** p = environ; *p; p++) { - sp_str_pair_t pair = sp_str_cleave_c8(sp_str_view(*p), '='); - sp_str_ht_insert(tls->env.vars, pair.first, pair.second); - } } #else void sp_sys_tls_init(sp_tls_rt_t* tls) { @@ -5708,112 +5881,93 @@ void sp_sys_tls_init(sp_tls_rt_t* tls) { ///////////////// // SP_SYS_INIT // ///////////////// -#if defined(SP_FREESTANDING) || defined(SP_WASM_FREESTANDING) -void sp_sys_init() { - sp_tls_block.self = &sp_tls_block; - sp_tls_block.data = SP_NULLPTR; - sp_sys_set_tp(&sp_tls_block); - sp_tls_rt_get(); -} -#else void sp_sys_init() { +#if defined(SP_WASM) -} +#else #endif +} ///////////////// // SP_SYS_STAT // ///////////////// +s32 sp_sys_stat(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_stat_t* st) { #if defined(SP_WIN32) -s32 sp_sys_stat(const c8* path, u32 len, sp_sys_stat_t* st) { - return sp_sys_stat_from_nt_path(sp_str(path, len), st, true); -} + return sp_sys_stat_from_nt_path(fd, sp_str(path, len), st, true); + #elif defined(SP_LINUX) -s32 sp_sys_stat(const c8* path, u32 len, sp_sys_stat_t* st) { c8 buf [SP_PATH_MAX] = sp_zero; sp_cstr_copy_to_n(path, len, buf, SP_PATH_MAX); sp_sys_linux_stat_t raw = sp_zero; - s32 rc; -#if defined(SP_AMD64) - rc = (s32)sp_syscall(SP_SYSCALL_NUM_STAT, buf, &raw); -#else - rc = (s32)sp_syscall(SP_SYSCALL_NUM_NEWFSTATAT, SP_AT_FDCWD, buf, &raw, 0); -#endif + s32 rc = (s32)sp_syscall(SP_SYSCALL_NUM_NEWFSTATAT, fd, buf, &raw, 0); if (rc == 0) sp_sys_stat_from_linux(&raw, st); return rc; -} + #elif defined(SP_MACOS) || defined(SP_COSMO) -s32 sp_sys_stat(const c8* path, u32 len, sp_sys_stat_t* st) { c8 buf [SP_PATH_MAX] = sp_zero; sp_cstr_copy_to_n(path, len, buf, SP_PATH_MAX); struct stat native; - s32 rc = stat(buf, &native); + s32 rc = fstatat((int)fd, buf, &native, 0); if (rc == 0) sp_sys_stat_from_libc(&native, st); return rc; -} + #elif defined(SP_WASM) -s32 sp_sys_stat(const c8* path, u32 len, sp_sys_stat_t* st) { - (void)path; (void)len; (void)st; + (void)fd; (void)path; (void)len; (void)st; return -1; -} + #else -#error "sp_sys_stat" + #error "sp_sys_stat" #endif +} -s32 sp_sys_stat_s(sp_str_t path, sp_sys_stat_t* st) { - return sp_sys_stat(path.data, path.len, st); +s32 sp_sys_stat_s(sp_sys_fd_t fd, sp_str_t path, sp_sys_stat_t* st) { + return sp_sys_stat(fd, path.data, path.len, st); } ////////////////// // SP_SYS_LSTAT // ////////////////// +s32 sp_sys_lstat(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_stat_t* st) { #if defined(SP_WIN32) -s32 sp_sys_lstat(const c8* path, u32 len, sp_sys_stat_t* st) { - return sp_sys_stat_from_nt_path(sp_str(path, len), st, false); -} + return sp_sys_stat_from_nt_path(fd, sp_str(path, len), st, false); + #elif defined(SP_LINUX) -s32 sp_sys_lstat(const c8* path, u32 len, sp_sys_stat_t* st) { c8 buf [SP_PATH_MAX] = sp_zero; sp_cstr_copy_to_n(path, len, buf, SP_PATH_MAX); sp_sys_linux_stat_t raw = sp_zero; - s32 rc; -#if defined(SP_AMD64) - rc = (s32)sp_syscall(SP_SYSCALL_NUM_LSTAT, buf, &raw); -#else - rc = (s32)sp_syscall(SP_SYSCALL_NUM_NEWFSTATAT, SP_AT_FDCWD, buf, &raw, SP_AT_SYMLINK_NOFOLLOW); -#endif + s32 rc = (s32)sp_syscall(SP_SYSCALL_NUM_NEWFSTATAT, fd, buf, &raw, SP_AT_SYMLINK_NOFOLLOW); if (rc == 0) sp_sys_stat_from_linux(&raw, st); return rc; -} + #elif defined(SP_MACOS) || defined(SP_COSMO) -s32 sp_sys_lstat(const c8* path, u32 len, sp_sys_stat_t* st) { c8 buf [SP_PATH_MAX] = sp_zero; sp_cstr_copy_to_n(path, len, buf, SP_PATH_MAX); struct stat native; - s32 rc = lstat(buf, &native); + s32 rc = fstatat((int)fd, buf, &native, AT_SYMLINK_NOFOLLOW); if (rc == 0) sp_sys_stat_from_libc(&native, st); return rc; -} + #elif defined(SP_WASM) -s32 sp_sys_lstat(const c8* path, u32 len, sp_sys_stat_t* st) { - (void)path; (void)len; (void)st; + (void)fd; (void)path; (void)len; (void)st; return -1; -} + #else -#error "sp_sys_lstat" + #error "sp_sys_lstat" #endif +} -s32 sp_sys_lstat_s(sp_str_t path, sp_sys_stat_t* st) { - return sp_sys_lstat(path.data, path.len, st); +s32 sp_sys_lstat_s(sp_sys_fd_t fd, sp_str_t path, sp_sys_stat_t* st) { + return sp_sys_lstat(fd, path.data, path.len, st); } ////////////////// // SP_SYS_MKDIR // ////////////////// +s32 sp_sys_mkdir(sp_sys_fd_t fd, const c8* path, u32 len, s32 mode) { #if defined(SP_WIN32) -s32 sp_sys_mkdir(const c8* path, u32 len, s32 mode) { (void)mode; void* handle = sp_sys_nt_open( + fd, sp_str(path, len), FILE_LIST_DIRECTORY | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, @@ -5824,42 +5978,37 @@ s32 sp_sys_mkdir(const c8* path, u32 len, s32 mode) { if (!handle) return -1; SP_NT(NtClose)(handle); return 0; -} + #elif defined(SP_LINUX) -s32 sp_sys_mkdir(const c8* path, u32 len, s32 mode) { c8 buf [SP_PATH_MAX] = sp_zero; sp_cstr_copy_to_n(path, len, buf, SP_PATH_MAX); -#if defined(SP_AMD64) - return (s32)sp_syscall(SP_SYSCALL_NUM_MKDIR, buf, mode); -#else - return (s32)sp_syscall(SP_SYSCALL_NUM_MKDIRAT, SP_AT_FDCWD, buf, mode); -#endif -} + return (s32)sp_syscall(SP_SYSCALL_NUM_MKDIRAT, fd, buf, mode); + #elif defined(SP_MACOS) || defined(SP_COSMO) -s32 sp_sys_mkdir(const c8* path, u32 len, s32 mode) { c8 buf [SP_PATH_MAX] = sp_zero; sp_cstr_copy_to_n(path, len, buf, SP_PATH_MAX); - return mkdir(buf, (mode_t)mode); -} + return mkdirat((int)fd, buf, (mode_t)mode); + #elif defined(SP_WASM) -s32 sp_sys_mkdir(const c8* path, u32 len, s32 mode) { - (void)path; (void)len; (void)mode; + (void)fd; (void)path; (void)len; (void)mode; return -1; -} + #else -#error "sp_sys_mkdir" + #error "sp_sys_mkdir" #endif +} -s32 sp_sys_mkdir_s(sp_str_t path, s32 mode) { - return sp_sys_mkdir(path.data, path.len, mode); +s32 sp_sys_mkdir_s(sp_sys_fd_t fd, sp_str_t path, s32 mode) { + return sp_sys_mkdir(fd, path.data, path.len, mode); } ////////////////// // SP_SYS_RMDIR // ////////////////// +s32 sp_sys_rmdir(sp_sys_fd_t fd, const c8* path, u32 len) { #if defined(SP_WIN32) -s32 sp_sys_rmdir(const c8* path, u32 len) { void* handle = sp_sys_nt_open( + fd, sp_str(path, len), DELETE | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, @@ -5870,42 +6019,37 @@ s32 sp_sys_rmdir(const c8* path, u32 len) { if (!handle) return -1; SP_NT(NtClose)(handle); return 0; -} + #elif defined(SP_LINUX) -s32 sp_sys_rmdir(const c8* path, u32 len) { c8 buf [SP_PATH_MAX] = sp_zero; sp_cstr_copy_to_n(path, len, buf, SP_PATH_MAX); -#if defined(SP_AMD64) - return (s32)sp_syscall(SP_SYSCALL_NUM_RMDIR, buf); -#else - return (s32)sp_syscall(SP_SYSCALL_NUM_UNLINKAT, SP_AT_FDCWD, buf, SP_AT_REMOVEDIR); -#endif -} + return (s32)sp_syscall(SP_SYSCALL_NUM_UNLINKAT, fd, buf, SP_AT_REMOVEDIR); + #elif defined(SP_MACOS) || defined(SP_COSMO) -s32 sp_sys_rmdir(const c8* path, u32 len) { c8 buf [SP_PATH_MAX] = sp_zero; sp_cstr_copy_to_n(path, len, buf, SP_PATH_MAX); - return rmdir(buf); -} + return unlinkat((int)fd, buf, AT_REMOVEDIR); + #elif defined(SP_WASM) -s32 sp_sys_rmdir(const c8* path, u32 len) { - (void)path; (void)len; + (void)fd; (void)path; (void)len; return -1; -} + #else -#error "sp_sys_rmdir" + #error "sp_sys_rmdir" #endif +} -s32 sp_sys_rmdir_s(sp_str_t path) { - return sp_sys_rmdir(path.data, path.len); +s32 sp_sys_rmdir_s(sp_sys_fd_t fd, sp_str_t path) { + return sp_sys_rmdir(fd, path.data, path.len); } /////////////////// // SP_SYS_UNLINK // /////////////////// +s32 sp_sys_unlink(sp_sys_fd_t fd, const c8* path, u32 len) { #if defined(SP_WIN32) -s32 sp_sys_unlink(const c8* path, u32 len) { void* handle = sp_sys_nt_open( + fd, sp_str(path, len), DELETE | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, @@ -5916,70 +6060,62 @@ s32 sp_sys_unlink(const c8* path, u32 len) { if (!handle) return -1; SP_NT(NtClose)(handle); return 0; -} + #elif defined(SP_LINUX) -s32 sp_sys_unlink(const c8* path, u32 len) { c8 buf [SP_PATH_MAX] = sp_zero; sp_cstr_copy_to_n(path, len, buf, SP_PATH_MAX); -#if defined(SP_AMD64) - return (s32)sp_syscall(SP_SYSCALL_NUM_UNLINK, buf); -#else - return (s32)sp_syscall(SP_SYSCALL_NUM_UNLINKAT, SP_AT_FDCWD, buf, 0); -#endif -} + return (s32)sp_syscall(SP_SYSCALL_NUM_UNLINKAT, fd, buf, 0); + #elif defined(SP_MACOS) || defined(SP_COSMO) -s32 sp_sys_unlink(const c8* path, u32 len) { c8 buf [SP_PATH_MAX] = sp_zero; sp_cstr_copy_to_n(path, len, buf, SP_PATH_MAX); - return unlink(buf); -} + return unlinkat((int)fd, buf, 0); + #elif defined(SP_WASM) -s32 sp_sys_unlink(const c8* path, u32 len) { - (void)path; (void)len; + (void)fd; (void)path; (void)len; return -1; -} + #else -#error "sp_sys_unlink" + #error "sp_sys_unlink" #endif +} -s32 sp_sys_unlink_s(sp_str_t path) { - return sp_sys_unlink(path.data, path.len); +s32 sp_sys_unlink_s(sp_sys_fd_t fd, sp_str_t path) { + return sp_sys_unlink(fd, path.data, path.len); } ////////////////// // SP_SYS_CHDIR // ////////////////// -#if defined(SP_WIN32) s32 sp_sys_chdir(const c8* path, u32 len) { +#if defined(SP_WIN32) sp_wide_str_t w = sp_wtf8_to_wtf16(sp_mem_get_scratch(), sp_str(path, len)); if (!w.data) return -1; sp_nt_unicode_string_t us = { .Length = sp_cast(u16, w.len * sizeof(u16)), .MaximumLength = sp_cast(u16, (w.len + 1) * sizeof(u16)), - .Buffer = sp_ptr_cast(u16*, w.data), + .Buffer = sp_ptr_cast(u16*, sp_const_cast(u16*, w.data)), }; return SP_NT_SUCCESS(SP_NT(RtlSetCurrentDirectory_U)(&us)) ? 0 : -1; -} + #elif defined(SP_LINUX) -s32 sp_sys_chdir(const c8* path, u32 len) { c8 buf [SP_PATH_MAX] = sp_zero; sp_cstr_copy_to_n(path, len, buf, SP_PATH_MAX); return (s32)sp_syscall(SP_SYSCALL_NUM_CHDIR, buf); -} + #elif defined(SP_MACOS) || defined(SP_COSMO) -s32 sp_sys_chdir(const c8* path, u32 len) { c8 buf [SP_PATH_MAX] = sp_zero; sp_cstr_copy_to_n(path, len, buf, SP_PATH_MAX); return chdir(buf); -} + #elif defined(SP_WASM) -s32 sp_sys_chdir(const c8* path, u32 len) { (void)path; (void)len; return -1; -} + #else -#error "sp_sys_chdir" + #error "sp_sys_chdir" #endif +} s32 sp_sys_chdir_s(sp_str_t path) { return sp_sys_chdir(path.data, path.len); @@ -5988,8 +6124,8 @@ s32 sp_sys_chdir_s(sp_str_t path) { /////////////////// // SP_SYS_GETCWD // /////////////////// -#if defined(SP_WIN32) s64 sp_sys_getcwd(char* buf, u64 size) { +#if defined(SP_WIN32) sp_nt_unicode_string_t* cwd = (sp_nt_unicode_string_t*)(sp_nt_process_params() + 0x38); u32 wlen = cwd->Length / (u32)sizeof(u16); if (wlen && cwd->Buffer[wlen - 1] == '\\') wlen--; @@ -5998,32 +6134,31 @@ s64 sp_sys_getcwd(char* buf, u64 size) { sp_mem_copy(buf, utf8.data, utf8.len); buf[utf8.len] = 0; return (s64)utf8.len; -} + #elif defined(SP_LINUX) -s64 sp_sys_getcwd(char* buf, u64 size) { return sp_syscall(SP_SYSCALL_NUM_GETCWD, buf, size); -} + #elif defined(SP_MACOS) || defined(SP_COSMO) -s64 sp_sys_getcwd(char* buf, u64 size) { return getcwd(buf, size) ? 0 : -1; -} + #elif defined(SP_WASM) -s64 sp_sys_getcwd(char* buf, u64 size) { if (!buf || size < 2) return -1; buf[0] = '/'; buf[1] = '\0'; return 1; -} + #else -#error "sp_sys_getcwd" + #error "sp_sys_getcwd" #endif +} ///////////////// // SP_SYS_LINK // ///////////////// +s32 sp_sys_link(sp_sys_fd_t from_fd, const c8* existing, u32 existing_len, sp_sys_fd_t to_fd, const c8* alias, u32 alias_len) { #if defined(SP_WIN32) -s32 sp_sys_link(const c8* existing, u32 existing_len, const c8* alias, u32 alias_len) { void* handle = sp_sys_nt_open( + from_fd, sp_str(existing, existing_len), FILE_READ_ATTRIBUTES | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, @@ -6033,20 +6168,40 @@ s32 sp_sys_link(const c8* existing, u32 existing_len, const c8* alias, u32 alias ); if (!handle) return -1; - sp_sys_nt_path_t nt = sp_zero; - if (!SP_NT_SUCCESS(sp_sys_nt_path(sp_str(alias, alias_len), &nt))) { + sp_str_t alias_utf8 = sp_str(alias, alias_len); + sp_wide_str_t alias_w = sp_wtf8_to_wtf16(sp_mem_get_scratch(), alias_utf8); + if (!alias_w.data) { SP_NT(NtClose)(handle); return -1; } - u32 name_bytes = nt.name.Length; + sp_sys_nt_path_t nt = sp_zero; + const u16* name_buf; + u32 name_bytes; + HANDLE root_dir; + if (sp_sys_is_absolute_wtf16(alias_w.data, alias_w.len)) { + if (!SP_NT_SUCCESS(sp_sys_nt_path(alias_utf8, &nt))) { + SP_NT(NtClose)(handle); + return -1; + } + name_buf = nt.name.Buffer; + name_bytes = nt.name.Length; + root_dir = SP_NULLPTR; + } else { + u32 nlen = sp_sys_normalize_relative_wtf16((u16*)alias_w.data, alias_w.len); + name_buf = alias_w.data; + name_bytes = nlen * (u32)sizeof(u16); + root_dir = (HANDLE)to_fd; + } + u32 info_bytes = sizeof(sp_nt_file_link_information_t) + name_bytes - sizeof(u16); sp_mem_arena_marker_t s = sp_mem_begin_scratch(); sp_nt_file_link_information_t* info = (sp_nt_file_link_information_t*)sp_alloc(s.mem, info_bytes); *info = sp_zero_s(sp_nt_file_link_information_t); info->ReplaceIfExists = 0; + info->RootDirectory = root_dir; info->FileNameLength = name_bytes; - sp_mem_copy(info->FileName, nt.name.Buffer, name_bytes); + sp_mem_copy(info->FileName, name_buf, name_bytes); sp_nt_io_status_block_t iosb = sp_zero; sp_nt_status_t status = SP_NT(NtSetInformationFile)(handle, &iosb, info, info_bytes, SP_NT_FILE_LINK_INFORMATION); @@ -6056,49 +6211,44 @@ s32 sp_sys_link(const c8* existing, u32 existing_len, const c8* alias, u32 alias sp_mem_end_scratch(s); return SP_NT_SUCCESS(status) ? 0 : -1; -} + #elif defined(SP_LINUX) -s32 sp_sys_link(const c8* existing, u32 existing_len, const c8* alias, u32 alias_len) { struct { c8 existing [SP_PATH_MAX]; c8 alias [SP_PATH_MAX]; - } buffers = sp_zero; - sp_cstr_copy_to_n(existing, existing_len, buffers.existing, SP_PATH_MAX); - sp_cstr_copy_to_n(alias, alias_len, buffers.alias, SP_PATH_MAX); -#if defined(SP_AMD64) - return (s32)sp_syscall(SP_SYSCALL_NUM_LINK, buffers.existing, buffers.alias); -#else - return (s32)sp_syscall(SP_SYSCALL_NUM_LINKAT, SP_AT_FDCWD, buffers.existing, SP_AT_FDCWD, buffers.alias, 0); -#endif -} + } buffers = sp_zero; + sp_cstr_copy_to_n(existing, existing_len, buffers.existing, SP_PATH_MAX); + sp_cstr_copy_to_n(alias, alias_len, buffers.alias, SP_PATH_MAX); + return (s32)sp_syscall(SP_SYSCALL_NUM_LINKAT, from_fd, buffers.existing, to_fd, buffers.alias, 0); + #elif defined(SP_MACOS) || defined(SP_COSMO) -s32 sp_sys_link(const c8* existing, u32 existing_len, const c8* alias, u32 alias_len) { struct { c8 existing [SP_PATH_MAX]; c8 alias [SP_PATH_MAX]; } buffers = sp_zero; sp_cstr_copy_to_n(existing, existing_len, buffers.existing, SP_PATH_MAX); sp_cstr_copy_to_n(alias, alias_len, buffers.alias, SP_PATH_MAX); - return link(buffers.existing, buffers.alias); -} + return linkat((int)from_fd, buffers.existing, (int)to_fd, buffers.alias, 0); + #elif defined(SP_WASM) -s32 sp_sys_link(const c8* existing, u32 existing_len, const c8* alias, u32 alias_len) { - (void)existing; (void)existing_len; (void)alias; (void)alias_len; + (void)from_fd; (void)existing; (void)existing_len; (void)to_fd; (void)alias; (void)alias_len; return -1; -} + #else -#error "sp_sys_link" + #error "sp_sys_link" #endif +} -s32 sp_sys_link_s(sp_str_t existing, sp_str_t alias) { - return sp_sys_link(existing.data, existing.len, alias.data, alias.len); +s32 sp_sys_link_s(sp_sys_fd_t from_fd, sp_str_t existing, sp_sys_fd_t to_fd, sp_str_t alias) { + return sp_sys_link(from_fd, existing.data, existing.len, to_fd, alias.data, alias.len); } //////////////////// // SP_SYS_SYMLINK // //////////////////// +s32 sp_sys_symlink(const c8* existing, u32 existing_len, sp_sys_fd_t to_fd, const c8* alias, u32 alias_len) { #if defined(SP_WIN32) -s32 sp_sys_symlink(const c8* existing, u32 existing_len, const c8* alias, u32 alias_len) { + (void)to_fd; sp_wide_str_t wtarget = sp_wtf8_to_wtf16(sp_mem_get_scratch(), sp_str(existing, existing_len)); sp_wide_str_t wlink = sp_wtf8_to_wtf16(sp_mem_get_scratch(), sp_str(alias, alias_len)); if (!wtarget.data || !wlink.data) return -1; @@ -6112,50 +6262,45 @@ s32 sp_sys_symlink(const c8* existing, u32 existing_len, const c8* alias, u32 al flags |= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; #endif return CreateSymbolicLinkW((LPCWSTR)wlink.data, (LPCWSTR)wtarget.data, flags) ? 0 : -1; -} + #elif defined(SP_LINUX) -s32 sp_sys_symlink(const c8* existing, u32 existing_len, const c8* alias, u32 alias_len) { struct { c8 existing [SP_PATH_MAX]; c8 alias [SP_PATH_MAX]; } buffers = sp_zero; sp_cstr_copy_to_n(existing, existing_len, buffers.existing, SP_PATH_MAX); sp_cstr_copy_to_n(alias, alias_len, buffers.alias, SP_PATH_MAX); -#if defined(SP_AMD64) - return (s32)sp_syscall(SP_SYSCALL_NUM_SYMLINK, buffers.existing, buffers.alias); -#else - return (s32)sp_syscall(SP_SYSCALL_NUM_SYMLINKAT, buffers.existing, SP_AT_FDCWD, buffers.alias); -#endif -} + return (s32)sp_syscall(SP_SYSCALL_NUM_SYMLINKAT, buffers.existing, to_fd, buffers.alias); + #elif defined(SP_MACOS) || defined(SP_COSMO) -s32 sp_sys_symlink(const c8* existing, u32 existing_len, const c8* alias, u32 alias_len) { struct { c8 existing [SP_PATH_MAX]; c8 alias [SP_PATH_MAX]; } buffers = sp_zero; sp_cstr_copy_to_n(existing, existing_len, buffers.existing, SP_PATH_MAX); sp_cstr_copy_to_n(alias, alias_len, buffers.alias, SP_PATH_MAX); - return symlink(buffers.existing, buffers.alias); -} + return symlinkat(buffers.existing, (int)to_fd, buffers.alias); + #elif defined(SP_WASM) -s32 sp_sys_symlink(const c8* existing, u32 existing_len, const c8* alias, u32 alias_len) { - (void)existing; (void)existing_len; (void)alias; (void)alias_len; + (void)existing; (void)existing_len; (void)to_fd; (void)alias; (void)alias_len; return -1; -} + #else -#error "sp_sys_symlink" + #error "sp_sys_symlink" #endif +} -s32 sp_sys_symlink_s(sp_str_t existing, sp_str_t alias) { - return sp_sys_symlink(existing.data, existing.len, alias.data, alias.len); +s32 sp_sys_symlink_s(sp_str_t existing, sp_sys_fd_t to_fd, sp_str_t alias) { + return sp_sys_symlink(existing.data, existing.len, to_fd, alias.data, alias.len); } ////////////////// // SP_SYS_CHMOD // ////////////////// +s32 sp_sys_chmod(sp_sys_fd_t fd, const c8* path, u32 len, const sp_sys_stat_t* st) { #if defined(SP_WIN32) -s32 sp_sys_chmod(const c8* path, u32 len, const sp_sys_stat_t* st) { void* handle = sp_sys_nt_open( + fd, sp_str(path, len), FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, @@ -6173,45 +6318,40 @@ s32 sp_sys_chmod(const c8* path, u32 len, const sp_sys_stat_t* st) { SP_NT(NtClose)(handle); return SP_NT_SUCCESS(status) ? 0 : -1; -} + #elif defined(SP_LINUX) -s32 sp_sys_chmod(const c8* path, u32 len, const sp_sys_stat_t* st) { c8 buf [SP_PATH_MAX] = sp_zero; sp_cstr_copy_to_n(path, len, buf, SP_PATH_MAX); s32 mode = (s32)(st->raw_attrs & 07777); -#if defined(SP_AMD64) - return (s32)sp_syscall(SP_SYSCALL_NUM_CHMOD, buf, mode); -#else - return (s32)sp_syscall(SP_SYSCALL_NUM_FCHMODAT, SP_AT_FDCWD, buf, mode, 0); -#endif -} + return (s32)sp_syscall(SP_SYSCALL_NUM_FCHMODAT, fd, buf, mode, 0); + #elif defined(SP_MACOS) || defined(SP_COSMO) -s32 sp_sys_chmod(const c8* path, u32 len, const sp_sys_stat_t* st) { c8 buf [SP_PATH_MAX] = sp_zero; sp_cstr_copy_to_n(path, len, buf, SP_PATH_MAX); - return chmod(buf, (mode_t)(st->raw_attrs & 07777)); -} + return fchmodat((int)fd, buf, (mode_t)(st->raw_attrs & 07777), 0); + #elif defined(SP_WASM) -s32 sp_sys_chmod(const c8* path, u32 len, const sp_sys_stat_t* st) { - (void)path; (void)len; (void)st; + (void)fd; (void)path; (void)len; (void)st; return -1; -} + #else -#error "sp_sys_chmod" + #error "sp_sys_chmod" #endif +} -s32 sp_sys_chmod_s(sp_str_t path, const sp_sys_stat_t* st) { - return sp_sys_chmod(path.data, path.len, st); +s32 sp_sys_chmod_s(sp_sys_fd_t fd, sp_str_t path, const sp_sys_stat_t* st) { + return sp_sys_chmod(fd, path.data, path.len, st); } /////////////////////////////// // SP_SYS_CANONICALIZE_PATH // /////////////////////////////// -#if defined(SP_WIN32) s64 sp_sys_canonicalize_path(const c8* path, u32 len, c8* buf, u64 size) { +#if defined(SP_WIN32) if (!buf) return -1; void* h = sp_sys_nt_open( + sp_fs_open_cwd(), sp_str(path, len), FILE_READ_ATTRIBUTES | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, @@ -6233,9 +6373,8 @@ s64 sp_sys_canonicalize_path(const c8* path, u32 len, c8* buf, u64 size) { sp_mem_copy(buf, utf8.data, utf8.len); buf[utf8.len] = 0; return (s64)utf8.len; -} + #elif defined(SP_LINUX) -s64 sp_sys_canonicalize_path(const c8* path, u32 len, c8* buf, u64 size) { c8 pbuf [SP_PATH_MAX] = sp_zero; sp_cstr_copy_to_n(path, len, pbuf, SP_PATH_MAX); s64 fd = sp_syscall(SP_SYSCALL_NUM_OPENAT, SP_AT_FDCWD, pbuf, SP_O_RDONLY | SP_O_CLOEXEC, 0); @@ -6244,16 +6383,13 @@ s64 sp_sys_canonicalize_path(const c8* path, u32 len, c8* buf, u64 size) { c8 proc [64] = sp_zero; sp_io_mem_writer_t io = sp_zero; sp_io_mem_writer_from_buffer(&io, proc, 64); - sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_fmt_io(&io.base, s.mem, "/proc/self/fd/{}", sp_fmt_int(fd)); - sp_mem_end_scratch(s); + sp_fmt_io(&io.base, "/proc/self/fd/{}", sp_fmt_int(fd)); s64 n = sp_syscall(SP_SYSCALL_NUM_READLINKAT, SP_AT_FDCWD, proc, buf, size); sp_sys_close(fd); return n; -} + #elif defined(SP_MACOS) || defined(SP_COSMO) -s64 sp_sys_canonicalize_path(const c8* path, u32 len, c8* buf, u64 size) { if (!path || !buf || size == 0) return -1; c8 pbuf [SP_PATH_MAX] = sp_zero; sp_cstr_copy_to_n(path, len, pbuf, SP_PATH_MAX); @@ -6263,18 +6399,18 @@ s64 sp_sys_canonicalize_path(const c8* path, u32 len, c8* buf, u64 size) { while (resolved[n] && n < size - 1) { buf[n] = resolved[n]; n++; } buf[n] = '\0'; return (s64)n; -} + #elif defined(SP_WASM) -s64 sp_sys_canonicalize_path(const c8* path, u32 len, c8* buf, u64 size) { if (!path || !buf || size == 0) return -1; u64 n = 0; while (n < len && n < size - 1) { buf[n] = path[n]; n++; } buf[n] = '\0'; return (s64)n; -} + #else -#error "sp_sys_canonicalize_path" + #error "sp_sys_canonicalize_path" #endif +} s64 sp_sys_canonicalize_path_s(sp_str_t path, c8* buf, u64 size) { return sp_sys_canonicalize_path(path.data, path.len, buf, size); @@ -6283,8 +6419,8 @@ s64 sp_sys_canonicalize_path_s(sp_str_t path, c8* buf, u64 size) { ///////////////////////// // SP_SYS_GET_EXE_PATH // ///////////////////////// -#if defined(SP_WIN32) s64 sp_sys_get_exe_path(c8* buf, u64 size) { +#if defined(SP_WIN32) if (!buf) return -1; sp_nt_unicode_string_t* image = (sp_nt_unicode_string_t*)(sp_nt_process_params() + 0x60); @@ -6294,67 +6430,59 @@ s64 sp_sys_get_exe_path(c8* buf, u64 size) { sp_mem_copy(buf, utf8.data, utf8.len); buf[utf8.len] = 0; return (s64)utf8.len; -} + #elif defined(SP_LINUX) -s64 sp_sys_get_exe_path(c8* buf, u64 size) { return sp_syscall(SP_SYSCALL_NUM_READLINKAT, SP_AT_FDCWD, "/proc/self/exe", buf, size); -} -#elif defined(SP_MACOS) || defined(SP_COSMO) -s64 sp_sys_get_exe_path(c8* buf, u64 size) { + +#elif defined(SP_MACOS) if (!buf || size == 0) return -1; -#if defined(SP_MACOS) c8 raw[4096]; u32 raw_size = sizeof(raw); if (_NSGetExecutablePath(raw, &raw_size)) return -1; return sp_sys_canonicalize_path(raw, sp_cstr_len(raw), buf, size); + #elif defined(SP_COSMO) + if (!buf || size == 0) return -1; extern char* program_invocation_name; return sp_sys_canonicalize_path(program_invocation_name, sp_cstr_len(program_invocation_name), buf, size); -#else - (void)buf; (void)size; - return -1; -#endif -} + #elif defined(SP_WASM) -s64 sp_sys_get_exe_path(c8* buf, u64 size) { (void)buf; (void)size; return -1; -} + #else -#error "sp_sys_get_exe_path" + #error "sp_sys_get_exe_path" #endif +} ///////////////////// // SP_OS_GET_NAME // ///////////////////// -#if defined(SP_WIN32) sp_str_t sp_os_get_name() { +#if defined(SP_WIN32) return sp_str_lit("windows"); -} + #elif defined(SP_LINUX) -sp_str_t sp_os_get_name() { return sp_str_lit("linux"); -} + #elif defined(SP_MACOS) -sp_str_t sp_os_get_name() { return sp_str_lit("macos"); -} + #elif defined(SP_COSMO) -sp_str_t sp_os_get_name() { switch (sp_os_get_kind()) { case SP_OS_LINUX: return sp_str_lit("linux"); case SP_OS_WIN32: return sp_str_lit("windows"); case SP_OS_MACOS: return sp_str_lit("macos"); } SP_UNREACHABLE_RETURN(sp_str_lit("")); -} + #elif defined(SP_WASM) -sp_str_t sp_os_get_name() { return sp_str_lit("wasm"); -} + #else -#error "sp_os_get_name" + #error "sp_os_get_name" #endif +} ///////////// // SP_MAIN // @@ -6362,7 +6490,10 @@ sp_str_t sp_os_get_name() { #if defined(SP_FREESTANDING) || defined(SP_WASM_FREESTANDING) void sp_main(s32 argc, const c8** argv, sp_entry_fn_t fn) { environ = (c8**)(argv + argc + 1); - sp_sys_init(); + sp_tls_block.self = &sp_tls_block; + sp_tls_block.data = SP_NULLPTR; + sp_sys_set_tp(&sp_tls_block); + sp_tls_rt_get(); sp_sys_exit(fn(argc, argv)); } #endif @@ -6862,7 +6993,7 @@ static sp_err_t sp_fmt_parse_number(sp_fmt_parser_t* p, u32* out) { static sp_err_t sp_fmt_parse_spec_body(sp_fmt_parser_t* p, sp_fmt_spec_t* spec) { sp_fmt_peek_t peek = sp_fmt_peek2(p); if (sp_fmt_is_align(peek.second)) { - if (peek.first == '$') spec->fill_dynamic = 1; + if (peek.first == '$') spec->dynamic.fill = 1; else spec->fill = peek.first; spec->align = sp_fmt_align_from_char(peek.second); sp_fmt_advance(p); @@ -6876,7 +7007,7 @@ static sp_err_t sp_fmt_parse_spec_body(sp_fmt_parser_t* p, sp_fmt_spec_t* spec) c8 c = sp_fmt_peek(p, 0); if (c == '$') { sp_fmt_advance(p); - spec->width_dynamic = 1; + spec->dynamic.width = 1; } else if (sp_fmt_is_digit(c)) { sp_fmt_parse_number(p, &spec->width); @@ -6886,7 +7017,7 @@ static sp_err_t sp_fmt_parse_spec_body(sp_fmt_parser_t* p, sp_fmt_spec_t* spec) sp_fmt_advance(p); if (sp_fmt_peek(p, 0) == '$') { sp_fmt_advance(p); - spec->precision_dynamic = 1; + spec->dynamic.precision = 1; } else { u32 prec = 0; @@ -6903,15 +7034,15 @@ static sp_err_t sp_fmt_parse_spec_body(sp_fmt_parser_t* p, sp_fmt_spec_t* spec) // EOF). The trailing whitespace — if any — is left for the top-level loop to // consume: it separates this directive from the next one. static sp_err_t sp_fmt_parse_directive(sp_fmt_parser_t* p, sp_fmt_spec_t* spec) { - if (spec->directive_count >= SP_FMT_MAX_DIRECTIVES) { + if (spec->directive.num >= SP_FMT_MAX_DIRECTIVES) { return SP_ERR_FMT_TOO_MANY_DIRECTIVES; } sp_str_t name = sp_fmt_directive_name(p); if (!name.len) return SP_ERR_FMT_BAD_DIRECTIVE; - u32 index = spec->directive_count++; - spec->directive_names[index] = name; + u32 index = spec->directive.num++; + spec->directive.names[index] = name; // An arg must be preceded by whitespace. Peek past any whitespace to // decide whether one follows — if not, leave the cursor on the whitespace @@ -6923,11 +7054,11 @@ static sp_err_t sp_fmt_parse_directive(sp_fmt_parser_t* p, sp_fmt_spec_t* spec) if (c == '$') { sp_fmt_eat_whitespace(p); sp_fmt_advance(p); - spec->directive_arg_dynamic |= (u8)(1u << index); + spec->dynamic.directive |= (u8)(1u << index); } else if (c && c != '}' && c != '.') { sp_fmt_eat_whitespace(p); - spec->directive_args[index] = sp_fmt_directive_arg(p); + spec->directive.args[index] = sp_fmt_directive_arg(p); } } @@ -6956,9 +7087,7 @@ sp_err_t sp_fmt_parse_specifier(sp_fmt_parser_t* p, sp_fmt_spec_t* spec) { } -// Pulls one int-kinded dynamic arg out of `a` and returns it as a signed -// value. Used for dynamic fill/width/precision, all of which require an int. -static sp_err_t sp_fmt_pull_int_arg(sp_fmt_argv_t a, s64* out) { +static sp_err_t sp_fmt_pull_specifier_arg(sp_fmt_argv_t a, s64* out) { if (a.id != sp_fmt_id_u64 && a.id != sp_fmt_id_s64) { return SP_ERR_FMT_DIRECTIVE_ARG_WRONG_KIND; } @@ -6966,140 +7095,119 @@ static sp_err_t sp_fmt_pull_int_arg(sp_fmt_argv_t a, s64* out) { return SP_OK; } -sp_err_t sp_fmt_io(sp_io_writer_t* io, sp_mem_t mem, const c8* fmt, ...) { - va_list args; - va_start(args, fmt); - sp_err_t result = sp_fmt_v(io, mem, sp_str_view(fmt), args); - va_end(args); - return result; -} - -static void sp_fmt_directive_bold(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_bold(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { (void)arg; sp_unused(params); sp_io_write_cstr(io, "\033[1m", SP_NULLPTR); } -static void sp_fmt_directive_italic(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_italic(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { (void)arg; sp_unused(params); sp_io_write_cstr(io, "\033[3m", SP_NULLPTR); } -static void sp_fmt_directive_red(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_red(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(arg); sp_unused(params); sp_io_write_cstr(io, SP_ANSI_FG_RED, SP_NULLPTR); } -static void sp_fmt_directive_green(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_green(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(arg); sp_unused(params); sp_io_write_cstr(io, SP_ANSI_FG_GREEN, SP_NULLPTR); } -static void sp_fmt_directive_yellow(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_yellow(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(arg); sp_unused(params); sp_io_write_cstr(io, SP_ANSI_FG_YELLOW, SP_NULLPTR); } -static void sp_fmt_directive_blue(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_blue(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(arg); sp_unused(params); sp_io_write_cstr(io, SP_ANSI_FG_BLUE, SP_NULLPTR); } -static void sp_fmt_directive_cyan(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_cyan(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(arg); sp_unused(params); sp_io_write_cstr(io, SP_ANSI_FG_CYAN, SP_NULLPTR); } -static void sp_fmt_directive_magenta(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_magenta(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(arg); sp_unused(params); sp_io_write_cstr(io, SP_ANSI_FG_MAGENTA, SP_NULLPTR); } -static void sp_fmt_directive_white(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_white(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(arg); sp_unused(params); sp_io_write_cstr(io, SP_ANSI_FG_WHITE, SP_NULLPTR); } -static void sp_fmt_directive_black(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_black(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(arg); sp_unused(params); sp_io_write_cstr(io, SP_ANSI_FG_BLACK, SP_NULLPTR); } -static void sp_fmt_directive_bright_red(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_bright_red(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(arg); sp_unused(params); sp_io_write_cstr(io, SP_ANSI_FG_BRIGHT_RED, SP_NULLPTR); } -static void sp_fmt_directive_bright_green(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_bright_green(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(arg); sp_unused(params); sp_io_write_cstr(io, SP_ANSI_FG_BRIGHT_GREEN, SP_NULLPTR); } -static void sp_fmt_directive_bright_yellow(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_bright_yellow(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(arg); sp_unused(params); sp_io_write_cstr(io, SP_ANSI_FG_BRIGHT_YELLOW, SP_NULLPTR); } -static void sp_fmt_directive_bright_blue(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_bright_blue(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(arg); sp_unused(params); sp_io_write_cstr(io, SP_ANSI_FG_BRIGHT_BLUE, SP_NULLPTR); } -static void sp_fmt_directive_bright_cyan(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_bright_cyan(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(arg); sp_unused(params); sp_io_write_cstr(io, SP_ANSI_FG_BRIGHT_CYAN, SP_NULLPTR); } -static void sp_fmt_directive_bright_magenta(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_bright_magenta(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(arg); sp_unused(params); sp_io_write_cstr(io, SP_ANSI_FG_BRIGHT_MAGENTA, SP_NULLPTR); } -static void sp_fmt_directive_bright_white(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_bright_white(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(arg); sp_unused(params); sp_io_write_cstr(io, SP_ANSI_FG_BRIGHT_WHITE, SP_NULLPTR); } -static void sp_fmt_directive_gray(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_gray(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(arg); sp_unused(params); sp_io_write_cstr(io, SP_ANSI_FG_BRIGHT_BLACK, SP_NULLPTR); } -static void sp_fmt_directive_ansi_reset(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { +static void sp_fmt_directive_ansi_reset(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { sp_unused(arg); sp_unused(param); sp_io_write_cstr(io, SP_ANSI_RESET, SP_NULLPTR); } -static void sp_fmt_directive_hyperlink(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_hyperlink(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(params); sp_io_write_cstr(io, "\033]8;;", SP_NULLPTR); if (arg->id == sp_fmt_id_str) sp_io_write_str(io, arg->value.s, SP_NULLPTR); sp_io_write_cstr(io, "\033\\", SP_NULLPTR); } -static void sp_fmt_directive_quote(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_quote(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { (void)arg; sp_unused(params); sp_io_write_c8(io, '"'); } -static void sp_fmt_directive_quote_after(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_quote_after(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { (void)arg; sp_unused(params); sp_io_write_c8(io, '"'); } -static void sp_fmt_directive_upper_transform(sp_io_writer_t* io, sp_mem_t mem, sp_str_t content, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { - (void)arg; sp_unused(params); - sp_for(i, content.len) { - c8 c = content.data[i]; - sp_io_write_c8(io, (c >= 'a' && c <= 'z') ? (c8)(c - 32) : c); - } -} - -static void sp_fmt_directive_redact_transform(sp_io_writer_t* io, sp_mem_t mem, sp_str_t content, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { - (void)arg; sp_unused(params); - sp_for(i, content.len) sp_io_write_c8(io, '*'); -} - -static void sp_fmt_directive_bytes_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_bytes_render(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { u64 bytes = arg->value.u; sp_unused(params); static const c8* units[] = { "B", "KB", "MB", "GB", "TB", "PB" }; u32 unit_idx = 0; @@ -7122,13 +7230,12 @@ static void sp_fmt_directive_bytes_render(sp_io_writer_t* io, sp_mem_t mem, sp_f sp_io_write_cstr(io, units[unit_idx], SP_NULLPTR); } -static void sp_fmt_directive_iso_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_iso_render(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(params); - sp_tm_epoch_t epoch = SP_RVAL(sp_tm_epoch_t) { .s = arg->value.u, .ns = 0 }; - sp_io_write_str(io, sp_tm_epoch_to_iso8601(mem, epoch), SP_NULLPTR); + sp_tm_epoch_to_iso8601_w(io, (sp_tm_epoch_t) { .s = arg->value.u }); } -static void sp_fmt_directive_hex_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_hex_render(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(params); u64 value = (arg->id == sp_fmt_id_s64) ? (u64)arg->value.i : arg->value.u; c8 buf[16]; @@ -7138,7 +7245,7 @@ static void sp_fmt_directive_hex_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt sp_io_write(io, start, (u64)(end - start), SP_NULLPTR); } -static void sp_fmt_directive_ordinal_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_ordinal_render(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { s64 value = (arg->id == sp_fmt_id_s64) ? arg->value.i : (s64)arg->value.u; sp_unused(params); sp_fmt_write_s64(io, value); s64 abs = value < 0 ? -value : value; @@ -7153,7 +7260,7 @@ static void sp_fmt_directive_ordinal_render(sp_io_writer_t* io, sp_mem_t mem, sp sp_io_write_cstr(io, suffix, SP_NULLPTR); } -static void sp_fmt_directive_duration_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { +static void sp_fmt_directive_duration_render(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* params) { sp_unused(params); u64 ns = arg->value.u; if (ns < 1000) { @@ -7199,7 +7306,7 @@ sp_str_t sp_fmt_color_to_ansi_fg(sp_str_t id) { return sp_str_lit(SP_ANSI_RESET); } -static void sp_fmt_directive_fg(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { +static void sp_fmt_directive_fg(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { sp_unused(arg); sp_str_t ansi = sp_fmt_color_to_ansi_fg(param->value.s); sp_io_write_str(io, ansi, SP_NULLPTR); @@ -7228,8 +7335,6 @@ void sp_fmt_register_builtins() { sp_fmt_register_decorator("italic", sp_fmt_directive_italic, sp_fmt_directive_ansi_reset); sp_fmt_register_decorator("hyperlink", sp_fmt_directive_hyperlink, sp_fmt_directive_ansi_reset); sp_fmt_register_decorator("quote", sp_fmt_directive_quote, sp_fmt_directive_quote_after); - sp_fmt_register_transformer("upper", sp_fmt_directive_upper_transform); - sp_fmt_register_transformer("redact", sp_fmt_directive_redact_transform); sp_fmt_register_renderer("bytes", sp_fmt_directive_bytes_render, sp_fmt_id_none); sp_fmt_register_renderer("hex", sp_fmt_directive_hex_render, sp_fmt_id_none); sp_fmt_register_renderer("iso", sp_fmt_directive_iso_render, sp_fmt_id_none); @@ -7634,8 +7739,8 @@ sp_mem_arena_t* sp_tls_rt_get_scratch_arena_for(sp_tls_rt_t* tls, sp_mem_t mem) void sp_rt_init() { - sp_mutex_init(&sp_rt.mutex, SP_MUTEX_PLAIN); sp_tls_new(&sp_rt.tls.key, sp_tls_rt_deinit); + sp_mutex_init(&sp_rt.mutex, SP_MUTEX_PLAIN); #if defined(SP_WIN32) sp_nt_load(); #endif @@ -7668,10 +7773,10 @@ sp_tls_rt_t* sp_tls_rt_get() { sp_carr_for(tls->scratch, it) { tls->scratch[it] = sp_mem_arena_new(tls->mem); } - tls->std.out = sp_alloc_type(tls->mem, sp_io_file_writer_t); - tls->std.err = sp_alloc_type(tls->mem, sp_io_file_writer_t); - sp_io_file_writer_from_fd(tls->std.out, sp_sys_stdout, SP_IO_CLOSE_MODE_NONE); - sp_io_file_writer_from_fd(tls->std.err, sp_sys_stderr, SP_IO_CLOSE_MODE_NONE); + tls->std.out = sp_alloc_type(tls->mem, sp_io_stream_writer_t); + tls->std.err = sp_alloc_type(tls->mem, sp_io_stream_writer_t); + sp_io_stream_writer_from_fd(tls->std.out, sp_sys_stdout, SP_IO_CLOSE_MODE_NONE); + sp_io_stream_writer_from_fd(tls->std.err, sp_sys_stderr, SP_IO_CLOSE_MODE_NONE); sp_str_ht_init(tls->mem, tls->format.directives); sp_fmt_register_builtins(); sp_sys_tls_init(tls); @@ -7714,7 +7819,7 @@ void sp_tls_set(sp_tls_key_t key, void* data) { } void sp_tls_once(sp_tls_once_t* once, sp_tls_once_fn_t fn) { - if (once) return; + if (*once) return; fn(); *once = 1; } @@ -9065,8 +9170,8 @@ SP_PRIVATE bool sp_sys_diriter_is_dot(const c8* name) { return name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0)); } -s32 sp_sys_fs_it_open_s(sp_sys_fs_it_t* it, sp_str_t path, sp_mem_slice_t buf) { - return sp_sys_fs_it_open(it, path.data, path.len, buf.data, buf.len); +s32 sp_sys_fs_it_open_s(sp_sys_fd_t fd, sp_sys_fs_it_t* it, sp_str_t path, sp_mem_slice_t buf) { + return sp_sys_fs_it_open(fd, it, path.data, path.len, buf.data, buf.len); } #if defined(SP_WIN32) @@ -9088,7 +9193,8 @@ SP_PRIVATE u32 sp_sys_diriter_win32_name_len(const u16* name) { return n; } -s32 sp_sys_fs_it_open(sp_sys_fs_it_t* it, const c8* path, u32 path_len, void* buf, u64 cap) { +s32 sp_sys_fs_it_open(sp_sys_fd_t fd, sp_sys_fs_it_t* it, const c8* path, u32 path_len, void* buf, u64 cap) { + (void)fd; *it = sp_zero_s(sp_sys_fs_it_t); if (cap < sizeof(sp_win32_find_data_t) + (MAX_PATH * 3 + 1)) return -1; @@ -9113,8 +9219,8 @@ s32 sp_sys_fs_it_open(sp_sys_fs_it_t* it, const c8* path, u32 path_len, void* bu wpat[prefixed_len] = 0; sp_sys_nt_path_free(&nt); - sp_win32_find_data_t* fd = (sp_win32_find_data_t*)buf; - HANDLE h = FindFirstFileW((LPCWSTR)wpat, fd); + sp_win32_find_data_t* find = (sp_win32_find_data_t*)buf; + HANDLE h = FindFirstFileW((LPCWSTR)wpat, find); if (h == INVALID_HANDLE_VALUE) return -1; it->handle = (s64)(intptr_t)h; @@ -9168,9 +9274,9 @@ SP_PRIVATE sp_fs_kind_t sp_sys_diriter_dtype_to_kind(u8 d_type) { return SP_FS_KIND_NONE; } -s32 sp_sys_fs_it_open(sp_sys_fs_it_t* it, const c8* path, u32 path_len, void* buf, u64 cap) { +s32 sp_sys_fs_it_open(sp_sys_fd_t root, sp_sys_fs_it_t* it, const c8* path, u32 path_len, void* buf, u64 cap) { *it = sp_zero_s(sp_sys_fs_it_t); - sp_sys_fd_t fd = sp_sys_open(path, path_len, SP_O_RDONLY | SP_O_DIRECTORY, 0); + sp_sys_fd_t fd = sp_sys_open(root, path, path_len, SP_O_RDONLY | SP_O_DIRECTORY, 0); if (fd < 0) return -1; it->handle = (s64)fd; it->buf.data = (u8*)buf; @@ -9217,7 +9323,8 @@ SP_PRIVATE sp_fs_kind_t sp_sys_diriter_dtype_to_kind(u8 d_type) { return SP_FS_KIND_NONE; } -s32 sp_sys_fs_it_open(sp_sys_fs_it_t* it, const c8* path, u32 path_len, void* buf, u64 cap) { +s32 sp_sys_fs_it_open(sp_sys_fd_t fd, sp_sys_fs_it_t* it, const c8* path, u32 path_len, void* buf, u64 cap) { + (void)fd; *it = sp_zero_s(sp_sys_fs_it_t); c8 cstr [SP_PATH_MAX] = sp_zero; sp_cstr_copy_to_n(path, path_len, cstr, SP_PATH_MAX); @@ -9248,7 +9355,7 @@ s32 sp_sys_fs_it_next(sp_sys_fs_it_t* it, sp_sys_fs_entry_t* out) { } #elif defined(SP_WASM) -s32 sp_sys_fs_it_open(sp_sys_fs_it_t* it, const c8* path, u32 path_len, void* buf, u64 cap) { +s32 sp_sys_fs_it_open(sp_sys_fd_t fd, sp_sys_fs_it_t* it, const c8* path, u32 path_len, void* buf, u64 cap) { sp_unreachable_return(-1); } @@ -9282,12 +9389,10 @@ s32 sp_sys_fs_it_next(sp_sys_fs_it_t* it, sp_sys_fs_entry_t* out) { void sp_assert_f(sp_str_t file, sp_str_t line, sp_str_t func, sp_str_t expr, bool cond) { if (cond) return; - sp_io_file_writer_t io = sp_zero; - sp_io_file_writer_from_fd(&io, sp_sys_stderr, SP_IO_CLOSE_MODE_NONE); - sp_mem_arena_marker_t s = sp_mem_begin_scratch(); + sp_io_stream_writer_t io = sp_zero; + sp_io_stream_writer_from_fd(&io, sp_sys_stderr, SP_IO_CLOSE_MODE_NONE); sp_fmt_io( &io.base, - s.mem, "{.red} {}:{.gray}:{.yellow}{.yellow} {}", sp_fmt_cstr("assert"), sp_fmt_str(file), @@ -9296,7 +9401,6 @@ void sp_assert_f(sp_str_t file, sp_str_t line, sp_str_t func, sp_str_t expr, boo sp_fmt_cstr("()"), sp_fmt_str(expr) ); - sp_mem_end_scratch(s); sp_io_write_cstr(&io.base, "\n", SP_NULLPTR); sp_sys_assert(cond); @@ -9620,43 +9724,6 @@ sp_str_t sp_os_lib_to_file_name(sp_mem_t mem, sp_str_t lib_name, sp_os_lib_kind_ } #endif - -sp_err_t sp_os_create_dir(sp_str_t path) { - return sp_sys_mkdir_s(path, 0755) == 0 ? SP_OK : SP_ERR_OS; -} - -sp_str_t sp_os_get_cwd(sp_mem_t mem) { - c8 path[SP_PATH_MAX] = sp_zero; - if (sp_sys_getcwd(path, SP_PATH_MAX - 1) < 0) { - return sp_zero_s(sp_str_t); - } - - return sp_str_from_cstr(mem, path); -} - -sp_err_t sp_os_create_file(sp_str_t path) { - sp_sys_fd_t fd = sp_sys_open_s(path, SP_O_CREAT | SP_O_WRONLY | SP_O_TRUNC, 0644); - if (fd != SP_SYS_INVALID_FD) { - sp_sys_close(fd); - } - - return fd != SP_SYS_INVALID_FD ? SP_OK : SP_ERR_OS; -} - -sp_err_t sp_os_create_hard_link(sp_str_t target, sp_str_t link_path) { - if (sp_sys_link_s(target, link_path)) { - return SP_ERR_OS; - } - return SP_OK; -} - -sp_err_t sp_os_create_sym_link(sp_str_t target, sp_str_t link_path) { - if (sp_sys_symlink_s(target, link_path) != 0) { - return SP_ERR_OS; - } - return SP_OK; -} - //////////// // SIGNAL // //////////// @@ -9853,7 +9920,7 @@ SP_PRIVATE sp_str_t sp_win32_utf16_to_utf8(const u16* utf16, s32 len) { utf8[num_bytes] = '\0'; return SP_RVAL(sp_str_t) { .data = utf8, - .len = num_bytes + .len = (u32)num_bytes }; } @@ -9947,7 +10014,7 @@ bool sp_os_env_it_valid(sp_os_env_it_t* it) { void sp_os_env_it_next(sp_os_env_it_t* it) { sp_win32_env_it_t* state = (sp_win32_env_it_t*)it->os; - state->cursor += wcslen(state->cursor) + 1; + state->cursor += wcslen(sp_ptr_cast(const wchar_t*, state->cursor)) + 1; if (state->cursor[0] != L'\0') { sp_win32_env_it_set_current(it); } else { @@ -9958,54 +10025,60 @@ void sp_os_env_it_next(sp_os_env_it_t* it) { #elif defined(SP_FREESTANDING) typedef struct { - sp_env_t* env; + sp_env_t env; sp_ht_it_t it; } sp_linux_env_it_t; SP_PRIVATE void sp_linux_env_it_set_current(sp_os_env_it_t* it) { sp_linux_env_it_t* state = (sp_linux_env_it_t*)it->os; - it->key = *sp_str_ht_it_getkp(state->env->vars, state->it); - it->value = *sp_str_ht_it_getp(state->env->vars, state->it); + it->key = *sp_str_ht_it_getkp(state->env.vars, state->it); + it->value = *sp_str_ht_it_getp(state->env.vars, state->it); } sp_str_t sp_os_env_get(sp_str_t key) { - sp_tls_rt_t* state = sp_tls_rt_get(); - sp_str_t* value = sp_str_ht_get(state->env.vars, key); - return value ? *value : sp_zero_s(sp_str_t); + for (c8** p = environ; *p; p++) { + sp_str_pair_t pair = sp_str_cleave_c8(sp_cstr_as_str(*p), '='); + if (sp_str_equal(key, pair.first)) { + return pair.second; + } + } + return sp_zero_s(sp_str_t); } sp_os_env_it_t sp_os_env_it_begin() { - sp_os_env_it_t it = sp_zero; - sp_tls_rt_t* runtime = sp_tls_rt_get(); - if (!runtime->env.vars) { - return it; + sp_linux_env_it_t* os = (sp_linux_env_it_t*)sp_mem_os_alloc(sizeof(sp_linux_env_it_t)); + *os = sp_zero_s(sp_linux_env_it_t); + + sp_mem_t mem = sp_mem_os_new(); + sp_env_init(mem, &os->env); + for (c8** p = environ; *p; p++) { + sp_str_pair_t pair = sp_str_cleave_c8(sp_str_view(*p), '='); + sp_str_ht_insert(os->env.vars, pair.first, pair.second); } - sp_linux_env_it_t* state = (sp_linux_env_it_t*)sp_mem_os_alloc(sizeof(sp_linux_env_it_t)); - *state = sp_zero_s(sp_linux_env_it_t); - state->env = &runtime->env; - state->it = sp_str_ht_it_init(runtime->env.vars); + os->it = sp_str_ht_it_init(os->env.vars); - if (!sp_str_ht_it_valid(runtime->env.vars, state->it)) { - sp_mem_os_free(state); - return it; + if (!sp_str_ht_it_valid(os->env.vars, os->it)) { + sp_mem_os_free(os); + return sp_zero_s(sp_os_env_it_t); } - it.os = state; + sp_os_env_it_t it = sp_zero; + it.os = os; sp_linux_env_it_set_current(&it); return it; } bool sp_os_env_it_valid(sp_os_env_it_t* it) { sp_linux_env_it_t* state = (sp_linux_env_it_t*)it->os; - return state && sp_str_ht_it_valid(state->env->vars, state->it); + return state && sp_str_ht_it_valid(state->env.vars, state->it); } void sp_os_env_it_next(sp_os_env_it_t* it) { sp_linux_env_it_t* state = (sp_linux_env_it_t*)it->os; - sp_str_ht_it_advance(state->env->vars, state->it); + sp_str_ht_it_advance(state->env.vars, state->it); - if (sp_str_ht_it_valid(state->env->vars, state->it)) { + if (sp_str_ht_it_valid(state->env.vars, state->it)) { sp_linux_env_it_set_current(it); } else { it->key = sp_zero_s(sp_str_t); @@ -10091,6 +10164,17 @@ void sp_os_env_it_next(sp_os_env_it_t* it) { #error "sp_os_env_it_next" #endif +void sp_sys_env(const c8** env, u32* len) { +#if defined(SP_WIN32) +#elif defined(SP_FREESTANDING) +#elif defined(SP_MACOS) || defined(SP_COSMO) || defined(SP_LINUX) || defined(SP_WASM) +#else + sp_unused(env); + sp_unused(len); +#endif +} + + void sp_env_init(sp_mem_t mem, sp_env_t* env) { *env = sp_zero_s(sp_env_t); env->mem = mem; @@ -10567,7 +10651,7 @@ void sp_semaphore_init(sp_semaphore_t* semaphore) { } void sp_semaphore_destroy(sp_semaphore_t* semaphore) { - //dispatch_release(*semaphore); + dispatch_release(*semaphore); } void sp_semaphore_wait(sp_semaphore_t* semaphore) { @@ -10684,19 +10768,20 @@ void sp_mutex_destroy(sp_mutex_t* mutex) { #else void sp_mutex_init(sp_mutex_t* mutex, sp_mutex_kind_t kind) { - SP_UNIMPLEMENTED(); + sp_unused(mutex); + sp_unused(kind); } void sp_mutex_lock(sp_mutex_t* mutex) { - SP_UNIMPLEMENTED(); + sp_unused(mutex); } void sp_mutex_unlock(sp_mutex_t* mutex) { - SP_UNIMPLEMENTED(); + sp_unused(mutex); } void sp_mutex_destroy(sp_mutex_t* mutex) { - SP_UNIMPLEMENTED(); + sp_unused(mutex); } #endif @@ -11276,12 +11361,12 @@ sp_io_close_mode_t sp_ps_io_close_mode(sp_ps_io_mode_t mode) { SP_UNREACHABLE_RETURN(SP_IO_CLOSE_MODE_NONE); } -sp_io_file_writer_t* sp_ps_io_in(sp_ps_t* ps) { +sp_io_stream_writer_t* sp_ps_io_in(sp_ps_t* ps) { if (!ps) return SP_NULLPTR; if (!sp_ps_is_fd_valid(ps->io.in.fd)) return SP_NULLPTR; - sp_io_file_writer_t* writer = sp_alloc_type(ps->mem, sp_io_file_writer_t); - sp_io_file_writer_from_fd(writer, ps->io.in.fd, sp_ps_io_close_mode(ps->io.in.mode)); + sp_io_stream_writer_t* writer = sp_alloc_type(ps->mem, sp_io_stream_writer_t); + sp_io_stream_writer_from_fd(writer, ps->io.in.fd, sp_ps_io_close_mode(ps->io.in.mode)); return writer; } @@ -11289,8 +11374,8 @@ sp_io_reader_t* sp_ps_io_out(sp_ps_t* ps) { if (!ps) return SP_NULLPTR; if (!sp_ps_is_fd_valid(ps->io.out.fd)) return SP_NULLPTR; - sp_io_pipe_reader_t* reader = sp_alloc_type(ps->mem, sp_io_pipe_reader_t); - sp_io_pipe_reader_from_pipe(reader, (sp_io_pipe_t)ps->io.out.fd, sp_ps_io_close_mode(ps->io.out.mode)); + sp_io_stream_reader_t* reader = sp_alloc_type(ps->mem, sp_io_stream_reader_t); + sp_io_stream_reader_from_fd(reader, ps->io.out.fd, sp_ps_io_close_mode(ps->io.out.mode)); return &reader->base; } @@ -11298,8 +11383,8 @@ sp_io_reader_t* sp_ps_io_err(sp_ps_t* ps) { if (!ps) return SP_NULLPTR; if (!sp_ps_is_fd_valid(ps->io.err.fd)) return SP_NULLPTR; - sp_io_pipe_reader_t* reader = sp_alloc_type(ps->mem, sp_io_pipe_reader_t); - sp_io_pipe_reader_from_pipe(reader, (sp_io_pipe_t)ps->io.err.fd, sp_ps_io_close_mode(ps->io.err.mode)); + sp_io_stream_reader_t* reader = sp_alloc_type(ps->mem, sp_io_stream_reader_t); + sp_io_stream_reader_from_fd(reader, ps->io.err.fd, sp_ps_io_close_mode(ps->io.err.mode)); return &reader->base; } @@ -11307,7 +11392,7 @@ sp_io_reader_t* sp_ps_io_err(sp_ps_t* ps) { #define SP_POSIX_WAITPID_BLOCK 0 #if defined(SP_LINUX) - #define sp_wait4(p, s, o, r) sp_lx_wait4(p, s, o, r) + #define sp_wait4(p, s, o, r) sp_syscall_wait4(p, s, o, r) #else #define sp_wait4(p, s, o, r) wait4(p, s, o, r) #endif @@ -11713,7 +11798,7 @@ sp_err_t sp_ps_win32_configure_io_out(sp_ps_io_out_config_t* io, sp_win32_dword_ } } - SP_UNREACHABLE_RETURN(false); + SP_UNREACHABLE_RETURN(SP_OK); } void sp_ps_win32_close_child_handles(sp_ps_win32_stdio_t* io) { @@ -11828,7 +11913,7 @@ sp_ps_t sp_ps_create(sp_mem_t mem, sp_ps_config_t config) { sp_str_t key = *sp_str_ht_it_getkp(env->vars, it); sp_str_t val = *sp_str_ht_it_getp(env->vars, it); - sp_fmt_io(&b.base, scratch.mem, "{}={}", sp_fmt_str(key), sp_fmt_str(val)); + sp_fmt_io(&b.base, "{}={}", sp_fmt_str(key), sp_fmt_str(val)); sp_io_write_c8(&b.base, '\0'); } @@ -11843,7 +11928,7 @@ sp_ps_t sp_ps_create(sp_mem_t mem, sp_ps_config_t config) { sp_env_var_t e = config.env.extra[it]; if (sp_str_empty(e.key)) continue; - sp_fmt_io(&b.base, scratch.mem, "{}={}", sp_fmt_str(e.key), sp_fmt_str(e.value)); + sp_fmt_io(&b.base, "{}={}", sp_fmt_str(e.key), sp_fmt_str(e.value)); sp_io_write_c8(&b.base, '\0'); } @@ -11860,6 +11945,10 @@ sp_ps_t sp_ps_create(sp_mem_t mem, sp_ps_config_t config) { .err = { .parent_fd = SP_SYS_INVALID_FD }, }; + STARTUPINFOA startup = sp_zero; + PROCESS_INFORMATION process_info = sp_zero; + BOOL created = false; + sp_err_t error = SP_OK; sp_try_goto(sp_ps_win32_configure_io_in(&proc.io.in, &io.in), error, fail); @@ -11874,16 +11963,13 @@ sp_ps_t sp_ps_create(sp_mem_t mem, sp_ps_config_t config) { sp_try_goto(sp_ps_win32_configure_io_out(&proc.io.err, STD_ERROR_HANDLE, GENERIC_WRITE, &io.err), error, fail); } - STARTUPINFOA startup = { - .cb = sizeof(STARTUPINFOA), - .dwFlags = STARTF_USESTDHANDLES, - .hStdInput = io.in.child, - .hStdOutput = io.out.child, - .hStdError = io.err.child, - }; + startup.cb = sizeof(STARTUPINFOA); + startup.dwFlags = STARTF_USESTDHANDLES; + startup.hStdInput = io.in.child; + startup.hStdOutput = io.out.child; + startup.hStdError = io.err.child; - PROCESS_INFORMATION process_info = sp_zero; - BOOL created = CreateProcessA(SP_NULLPTR, cmdline, SP_NULLPTR, SP_NULLPTR, true, 0, (void*)env.data, cwd, &startup, &process_info); + created = CreateProcessA(SP_NULLPTR, cmdline, SP_NULLPTR, SP_NULLPTR, true, 0, (void*)env.data, cwd, &startup, &process_info); sp_mem_end_scratch(scratch); if (!created) { @@ -11925,12 +12011,12 @@ sp_ps_output_t sp_ps_run(sp_mem_t mem, sp_ps_config_t config) { return (sp_ps_output_t) { .status = { .state = SP_PS_STATE_DONE, .exit_code = -1 } }; } -sp_io_file_writer_t* sp_ps_io_in(sp_ps_t* ps) { +sp_io_stream_writer_t* sp_ps_io_in(sp_ps_t* ps) { if (!ps) return SP_NULLPTR; if (!sp_ps_is_fd_valid(ps->io.in.fd)) return SP_NULLPTR; - sp_io_file_writer_t* writer = sp_alloc_type(ps->mem, sp_io_file_writer_t); - sp_io_file_writer_from_fd(writer, ps->io.in.fd, sp_ps_io_close_mode(ps->io.in.mode)); + sp_io_stream_writer_t* writer = sp_alloc_type(ps->mem, sp_io_stream_writer_t); + sp_io_stream_writer_from_fd(writer, ps->io.in.fd, sp_ps_io_close_mode(ps->io.in.mode)); return writer; } @@ -11938,8 +12024,8 @@ sp_io_reader_t* sp_ps_io_out(sp_ps_t* ps) { if (!ps) return SP_NULLPTR; if (!sp_ps_is_fd_valid(ps->io.out.fd)) return SP_NULLPTR; - sp_io_pipe_reader_t* reader = sp_alloc_type(ps->mem, sp_io_pipe_reader_t); - sp_io_pipe_reader_from_pipe(reader, (sp_io_pipe_t)ps->io.out.fd, sp_ps_io_close_mode(ps->io.out.mode)); + sp_io_stream_reader_t* reader = sp_alloc_type(ps->mem, sp_io_stream_reader_t); + sp_io_stream_reader_from_fd(reader, ps->io.out.fd, sp_ps_io_close_mode(ps->io.out.mode)); return &reader->base; } @@ -11947,8 +12033,8 @@ sp_io_reader_t* sp_ps_io_err(sp_ps_t* ps) { if (!ps) return SP_NULLPTR; if (!sp_ps_is_fd_valid(ps->io.err.fd)) return SP_NULLPTR; - sp_io_pipe_reader_t* reader = sp_alloc_type(ps->mem, sp_io_pipe_reader_t); - sp_io_pipe_reader_from_pipe(reader, (sp_io_pipe_t)ps->io.err.fd, sp_ps_io_close_mode(ps->io.err.mode)); + sp_io_stream_reader_t* reader = sp_alloc_type(ps->mem, sp_io_stream_reader_t); + sp_io_stream_reader_from_fd(reader, ps->io.err.fd, sp_ps_io_close_mode(ps->io.err.mode)); return &reader->base; } @@ -12168,7 +12254,7 @@ sp_ps_output_t sp_ps_run(sp_mem_t mem, sp_ps_config_t config) { return sp_ps_output(&ps); } -sp_io_file_writer_t* sp_ps_io_in(sp_ps_t* ps) { +sp_io_stream_writer_t* sp_ps_io_in(sp_ps_t* ps) { SP_UNIMPLEMENTED(); SP_UNUSED(ps); return SP_NULLPTR; @@ -12411,6 +12497,7 @@ void sp_fmon_os_add_dir(sp_fmon_t* monitor, sp_str_t path) { if (!event) return; sp_win32_handle_t handle = sp_sys_nt_open( + sp_fs_open_cwd(), path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, @@ -12501,7 +12588,10 @@ void sp_fmon_os_process_changes(sp_fmon_t* monitor) { } if (events != SP_FILE_CHANGE_EVENT_NONE) { - sp_str_t partial_path_str = sp_win32_utf16_to_utf8(¬ify->FileName[0], (s32)(notify->FileNameLength / sizeof(WCHAR))); + sp_str_t partial_path_str = sp_win32_utf16_to_utf8( + sp_ptr_cast(const u16*, ¬ify->FileName[0]), + (s32)(notify->FileNameLength / sizeof(WCHAR)) + ); sp_str_t full_path = sp_fs_join_path(monitor->mem, info->path, partial_path_str); // @spader @@ -12566,7 +12656,7 @@ void sp_win32_fmon_issue_read(sp_fmon_t* monitor, sp_fmon_dir_t* info) { void sp_fmon_os_init(sp_fmon_t* monitor) { sp_fmon_os_t* linux_monitor = sp_alloc_type(monitor->mem, sp_fmon_os_t); - linux_monitor->fd = sp_lx_inotify_init1(SP_IN_NONBLOCK | SP_IN_CLOEXEC); + linux_monitor->fd = sp_syscall_notify_init1(SP_IN_NONBLOCK | SP_IN_CLOEXEC); if (linux_monitor->fd == -1) { // Handle error but don't crash linux_monitor->fd = 0; @@ -12599,7 +12689,7 @@ void sp_fmon_os_add_dir(sp_fmon_t* monitor, sp_str_t path) { mask |= SP_IN_DELETE | SP_IN_MOVED_FROM; } - s32 wd = sp_lx_inotify_add_watch(os->fd, path_cstr, mask); + s32 wd = sp_syscall_inotify_add_watch(os->fd, path_cstr, mask); if (wd != -1) { sp_da_push(os->fds, wd); @@ -13104,7 +13194,7 @@ void sp_fmon_os_process_changes(sp_fmon_t* monitor) { // @io sp_err_t sp_io_file_reader_read(sp_io_reader_t* reader, void* ptr, u64 size, u64* bytes_read) { sp_io_file_reader_t* r = (sp_io_file_reader_t*)reader; - s64 rc = sp_sys_read(r->file, ptr, size); + s64 rc = sp_sys_pread(r->file, ptr, size, r->pos); u64 num_bytes = rc < 0 ? 0 : (u64)rc; sp_err_t result = SP_OK; @@ -13114,14 +13204,17 @@ sp_err_t sp_io_file_reader_read(sp_io_reader_t* reader, void* ptr, u64 size, u64 else if (size && !num_bytes) { result = SP_ERR_IO_EOF; } + else { + r->pos += num_bytes; + } if (bytes_read) *bytes_read = num_bytes; return result; } -sp_err_t sp_io_pipe_reader_read(sp_io_reader_t* reader, void* ptr, u64 size, u64* bytes_read) { - sp_io_pipe_reader_t* pr = (sp_io_pipe_reader_t*)reader; - s64 rc = sp_sys_read(pr->pipe, ptr, size); +sp_err_t sp_io_stream_reader_read(sp_io_reader_t* reader, void* ptr, u64 size, u64* bytes_read) { + sp_io_stream_reader_t* pr = (sp_io_stream_reader_t*)reader; + s64 rc = sp_sys_read(pr->fd, ptr, size); u64 num_bytes = rc < 0 ? 0 : (u64)rc; sp_err_t result = SP_OK; @@ -13142,14 +13235,15 @@ sp_err_t sp_io_eof_read(sp_io_reader_t* r, void* ptr, u64 size, u64* bytes_read) return SP_ERR_IO_EOF; } -sp_err_t sp_io_file_reader_as_fd(sp_io_reader_t* r, sp_io_file_t* fd) { +sp_err_t sp_io_file_reader_as_fd(sp_io_reader_t* r, sp_sys_fd_t* fd, u64** pos) { sp_io_file_reader_t* fr = (sp_io_file_reader_t*)r; if (fr->file == SP_SYS_INVALID_FD) return SP_ERR_IO; - *fd = fr->file; + if (fd) *fd = fr->file; + if (pos) *pos = &fr->pos; return SP_OK; } -void sp_io_file_reader_from_file(sp_io_file_reader_t* r, sp_io_file_t file, sp_io_close_mode_t mode) { +sp_err_t sp_io_file_reader_from_file(sp_io_file_reader_t* r, sp_sys_fd_t file, sp_io_close_mode_t mode) { *r = (sp_io_file_reader_t) { .base = { .read = sp_io_file_reader_read, @@ -13158,16 +13252,22 @@ void sp_io_file_reader_from_file(sp_io_file_reader_t* r, sp_io_file_t file, sp_i .file = file, .close_mode = mode, }; + + if (sp_io_file_reader_size_force(r, SP_NULLPTR) != SP_OK) { + if (mode == SP_IO_CLOSE_MODE_AUTO) sp_sys_close(file); + *r = sp_zero_s(sp_io_file_reader_t); + return SP_ERR_IO; + } + return SP_OK; } sp_err_t sp_io_file_reader_from_path(sp_io_file_reader_t* r, sp_str_t path) { - sp_sys_fd_t fd = sp_sys_open_s(path, SP_O_RDONLY | SP_O_BINARY, 0); + sp_sys_fd_t fd = sp_sys_open_s(sp_fs_open_cwd(), path, SP_O_RDONLY | SP_O_BINARY, 0); if (fd == SP_SYS_INVALID_FD) { *r = sp_zero_s(sp_io_file_reader_t); return SP_ERR_IO_OPEN_FAILED; } - sp_io_file_reader_from_file(r, fd, SP_IO_CLOSE_MODE_AUTO); - return SP_OK; + return sp_io_file_reader_from_file(r, fd, SP_IO_CLOSE_MODE_AUTO); } sp_err_t sp_io_file_reader_seek(sp_io_file_reader_t* r, s64 offset, sp_io_whence_t whence, s64* position) { @@ -13176,22 +13276,23 @@ sp_err_t sp_io_file_reader_seek(sp_io_file_reader_t* r, s64 offset, sp_io_whence sp_io_reader_t* base = &r->base; - // If anything is buffered, do the naive thing and simply throw it away. It's possible that - // what's in the buffer could still be OK after the seek (e.g. if we're seeking forward a - // little, we can just discard bytes). But we'd rather be simple and correct for now. - if (base->buffer.len > base->cursor) { - s64 buffered = (s64)(base->buffer.len - base->cursor); - sp_sys_lseek(r->file, -buffered, SP_IO_SEEK_CUR); - base->cursor = 0; - base->buffer.len = 0; + base->cursor = 0; + base->buffer.len = 0; + + switch (whence) { + case SP_IO_SEEK_SET: pos = offset; break; + case SP_IO_SEEK_CUR: pos = (s64)r->pos + offset; break; + case SP_IO_SEEK_END: pos = (s64)r->size + offset; break; } - pos = sp_sys_lseek(r->file, offset, (s32)whence); if (pos < 0) { - err = SP_ERR_IO_SEEK_FAILED; + err = SP_ERR_IO_SEEK_INVALID; + pos = -1; goto done; } + r->pos = (u64)pos; + done: if (position) *position = pos; return err; @@ -13202,29 +13303,19 @@ sp_err_t sp_io_file_reader_seek_cb(sp_io_reader_t* reader, s64 offset, sp_io_whe } sp_err_t sp_io_file_reader_size(sp_io_file_reader_t* r, u64* size) { - sp_err_t err = SP_OK; - u64 result = 0; - s64 current = 0; - s64 end = 0; - - current = sp_sys_lseek(r->file, 0, SP_IO_SEEK_CUR); - if (current < 0) { - err = SP_ERR_IO_SEEK_FAILED; - goto done; - } + if (size) *size = r->size; + return SP_OK; +} - end = sp_sys_lseek(r->file, 0, SP_IO_SEEK_END); - if (end < 0) { - err = SP_ERR_IO_SEEK_FAILED; - goto done; +sp_err_t sp_io_file_reader_size_force(sp_io_file_reader_t* r, u64* size) { + sp_sys_stat_t st = sp_zero; + if (sp_sys_fstat(r->file, &st) < 0) { + if (size) *size = 0; + return SP_ERR_IO; } - - sp_sys_lseek(r->file, current, SP_IO_SEEK_SET); - result = (u64)end; - -done: - if (size) *size = result; - return err; + r->size = (u64)st.size; + if (size) *size = r->size; + return SP_OK; } sp_err_t sp_io_file_reader_close(sp_io_file_reader_t* r) { @@ -13236,33 +13327,116 @@ sp_err_t sp_io_file_reader_close(sp_io_file_reader_t* r) { return SP_OK; } -sp_err_t sp_io_pipe_reader_as_fd(sp_io_reader_t* r, sp_io_file_t* fd) { - sp_io_pipe_reader_t* pr = (sp_io_pipe_reader_t*)r; - if (pr->pipe == SP_SYS_INVALID_FD) return SP_ERR_IO; - *fd = pr->pipe; +sp_err_t sp_io_stream_reader_as_fd(sp_io_reader_t* r, sp_sys_fd_t* fd, u64** pos) { + sp_io_stream_reader_t* pr = (sp_io_stream_reader_t*)r; + if (pr->fd == SP_SYS_INVALID_FD) return SP_ERR_IO; + if (fd) *fd = pr->fd; + if (pos) *pos = SP_NULLPTR; return SP_OK; } -void sp_io_pipe_reader_from_pipe(sp_io_pipe_reader_t* r, sp_io_pipe_t pipe, sp_io_close_mode_t mode) { - *r = (sp_io_pipe_reader_t) { +void sp_io_stream_reader_from_fd(sp_io_stream_reader_t* r, sp_sys_fd_t fd, sp_io_close_mode_t mode) { + *r = (sp_io_stream_reader_t) { .base = { - .read = sp_io_pipe_reader_read, - .as_fd = sp_io_pipe_reader_as_fd, + .read = sp_io_stream_reader_read, + .as_fd = sp_io_stream_reader_as_fd, }, - .pipe = pipe, + .fd = fd, .close_mode = mode, }; } -sp_err_t sp_io_pipe_reader_close(sp_io_pipe_reader_t* r) { +sp_err_t sp_io_stream_reader_close(sp_io_stream_reader_t* r) { if (r->close_mode == SP_IO_CLOSE_MODE_AUTO) { - if (sp_sys_close(r->pipe) < 0) { + if (sp_sys_close(r->fd) < 0) { return SP_ERR_IO_CLOSE_FAILED; } } return SP_OK; } +sp_err_t sp_io_stream_writer_write(sp_io_writer_t* writer, const void* ptr, u64 size, u64* bytes_written) { + sp_io_stream_writer_t* w = (sp_io_stream_writer_t*)writer; + s64 rc = sp_sys_write(w->fd, ptr, size); + u64 num_bytes = rc < 0 ? 0 : (u64)rc; + + sp_err_t result = SP_OK; + if (rc < 0) { + result = SP_ERR_IO_WRITE_FAILED; + } + else if (size && !num_bytes) { + result = SP_ERR_IO_EOF; + } + + if (bytes_written) *bytes_written = num_bytes; + return result; +} + +#if defined(SP_LINUX) +sp_err_t sp_io_stream_writer_read_from(sp_io_writer_t* writer, sp_io_reader_t* r, u64* bytes_moved) { + sp_io_stream_writer_t* w = (sp_io_stream_writer_t*)writer; + u64 total = 0; + + if (!r->as_fd) { if (bytes_moved) *bytes_moved = 0; return SP_ERR_IO_UNIMPLEMENTED; } + + sp_sys_fd_t in_fd = SP_SYS_INVALID_FD; + u64* in_pos = SP_NULLPTR; + if (r->as_fd(r, &in_fd, &in_pos) != SP_OK) { + if (bytes_moved) *bytes_moved = 0; + return SP_ERR_IO_UNIMPLEMENTED; + } + if (!in_pos) { + if (bytes_moved) *bytes_moved = 0; + return SP_ERR_IO_UNIMPLEMENTED; + } + + const u64 chunk = (u64)1 << 30; + + while (true) { + s64 off = (s64)*in_pos; + s64 rc; + do { + rc = sp_syscall(SP_SYSCALL_NUM_SENDFILE, w->fd, in_fd, &off, chunk); + } while (rc == -1 && errno == SP_EINTR); + + if (rc < 0) { + if (total == 0) { + if (bytes_moved) *bytes_moved = 0; + return SP_ERR_IO_UNIMPLEMENTED; + } + if (bytes_moved) *bytes_moved = total; + return SP_ERR_IO_WRITE_FAILED; + } + if (rc == 0) break; + *in_pos = (u64)off; + total += (u64)rc; + } + + if (bytes_moved) *bytes_moved = total; + return SP_OK; +} +#endif + +void sp_io_stream_writer_from_fd(sp_io_stream_writer_t* w, sp_sys_fd_t fd, sp_io_close_mode_t mode) { + *w = (sp_io_stream_writer_t) { + .base = { + .write = sp_io_stream_writer_write, +#if defined(SP_LINUX) + .read_from = sp_io_stream_writer_read_from, +#endif + }, + .fd = fd, + .close_mode = mode, + }; +} + +sp_err_t sp_io_stream_writer_close(sp_io_stream_writer_t* w) { + sp_io_flush(&w->base); + if (w->close_mode != SP_IO_CLOSE_MODE_AUTO) return SP_OK; + if (sp_sys_close(w->fd) < 0) return SP_ERR_IO_CLOSE_FAILED; + return SP_OK; +} + void sp_io_reader_from_mem(sp_io_reader_t* reader, const void* ptr, u64 size) { *reader = (sp_io_reader_t) { .read = sp_io_eof_read, @@ -13361,44 +13535,6 @@ sp_err_t sp_io_copy_b(sp_io_writer_t* w, sp_io_reader_t* r, u8* buffer, u64 n, u return err; } -sp_err_t sp_io_copy_b2(sp_io_writer_t* w, sp_io_reader_t* r, u8* buffer, u64 n, u64* bytes_copied) { - u64 total = 0; - struct { - u64 r; - u64 w; - } chunk = sp_zero; - struct { - u64 r; - u64 w; - u64 e; - } err = sp_zero; - - while (true) { - err.w = sp_io_read(r, buffer, n, &chunk.r); - if (chunk.r) { - err.r = sp_io_write(w, buffer, chunk.r, &chunk.w); - if (chunk.w != chunk.r) { - err.w = SP_ERR_IO_INVALID_WRITE; - } - total += chunk.w; - if (err.w) { - err.e = err.w; - break; - } - } - - if (err.r) { - if (err.r != SP_ERR_IO_EOF) { - err.e = err.r; - break; - } - } - } - - if (bytes_copied) *bytes_copied = total; - return err.e; -} - sp_err_t sp_io_read(sp_io_reader_t* reader, void* ptr, u64 size, u64* bytes_read) { sp_assert(reader); @@ -13485,8 +13621,10 @@ void sp_io_seeking_reader_from_file_reader(sp_io_seeking_reader_t* sr, sp_io_fil sp_err_t sp_io_file_writer_write(sp_io_writer_t* writer, const void* ptr, u64 size, u64* bytes_written) { sp_io_file_writer_t* w = (sp_io_file_writer_t*)writer; - s64 rc = sp_sys_write(w->fd, ptr, size); + s64 rc = sp_sys_pwrite(w->fd, ptr, size, w->pos); u64 num_bytes = rc < 0 ? 0 : (u64)rc; + w->pos += num_bytes; + if (w->pos > w->size) w->size = w->pos; sp_err_t result = SP_OK; if (rc < 0) { @@ -13513,28 +13651,35 @@ sp_err_t sp_io_file_writer_read_from(sp_io_writer_t* writer, sp_io_reader_t* r, if (!r->as_fd) { if (bytes_moved) *bytes_moved = 0; return SP_ERR_IO_UNIMPLEMENTED; } - sp_io_file_t in_fd = SP_SYS_INVALID_FD; - if (r->as_fd(r, &in_fd) != SP_OK) { + sp_sys_fd_t in_fd = SP_SYS_INVALID_FD; + u64* in_pos = SP_NULLPTR; + if (r->as_fd(r, &in_fd, &in_pos) != SP_OK) { + if (bytes_moved) *bytes_moved = 0; + return SP_ERR_IO_UNIMPLEMENTED; + } + if (!in_pos) { + // Source is streaming (cursor in kernel). copy_file_range requires + // regular files on both sides, so it would fail anyway. Bail cleanly. if (bytes_moved) *bytes_moved = 0; return SP_ERR_IO_UNIMPLEMENTED; } - // Cap each syscall at 1 GiB; Linux's copy_file_range historically caps at - // 0x7ffff000 internally anyway, and we want to bound the unit of progress - // we report. const u64 chunk = (u64)1 << 30; while (true) { - s64 rc = sp_lx_copy_file_range(in_fd, w->fd, chunk); + s64 rc; + do { + rc = sp_syscall(SP_SYSCALL_NUM_COPY_FILE_RANGE, in_fd, in_pos, w->fd, &w->pos, chunk, 0); + } while (rc == -1 && errno == SP_EINTR); + if (rc < 0) { if (total == 0) { - // First syscall failed. Source might not be a regular file - // (EINVAL), kernel might be too old (ENOSYS), or the pair might - // cross filesystems on a pre-5.3 kernel (EXDEV). Tell the caller - // to fall back to read+write. + // If the first call fails, the operation isn't supported, so we tell + // the caller to fall back to a userspace copy if (bytes_moved) *bytes_moved = 0; return SP_ERR_IO_UNIMPLEMENTED; } + // We already made progress; surface as a write failure. if (bytes_moved) *bytes_moved = total; return SP_ERR_IO_WRITE_FAILED; @@ -13543,6 +13688,7 @@ sp_err_t sp_io_file_writer_read_from(sp_io_writer_t* writer, sp_io_reader_t* r, total += (u64)rc; } + if (w->pos > w->size) w->size = w->pos; if (bytes_moved) *bytes_moved = total; return SP_OK; } @@ -13552,34 +13698,37 @@ sp_err_t sp_io_file_writer_seek(sp_io_file_writer_t* w, s64 offset, sp_io_whence sp_assert(w); sp_try(sp_io_flush(&w->base)); - s64 p = 0; - position = position ? position : &p; + s64 new_pos = 0; + switch (whence) { + case SP_IO_SEEK_SET: new_pos = offset; break; + case SP_IO_SEEK_CUR: new_pos = (s64)w->pos + offset; break; + case SP_IO_SEEK_END: new_pos = (s64)w->size + offset; break; + } - *position = sp_sys_lseek(w->fd, offset, (s32)whence); - if (*position < 0) { - return SP_ERR_IO_SEEK_FAILED; + if (new_pos < 0) { + if (position) *position = -1; + return SP_ERR_IO_SEEK_INVALID; } + + w->pos = (u64)new_pos; + if (position) *position = new_pos; return SP_OK; } sp_err_t sp_io_file_writer_size(sp_io_file_writer_t* w, u64* size) { sp_assert(w); - sp_try(sp_io_flush(&w->base)); - - u64 s = 0; - size = size ? size : &s; + if (size) *size = w->size; + return SP_OK; +} - s64 current = sp_sys_lseek(w->fd, 0, SP_IO_SEEK_CUR); - if (current < 0) { +sp_err_t sp_io_file_writer_size_force(sp_io_file_writer_t* w, u64* size) { + sp_sys_stat_t st = sp_zero; + if (sp_sys_fstat(w->fd, &st) < 0) { + if (size) *size = 0; return SP_ERR_IO; } - - *size = sp_sys_lseek(w->fd, 0, SP_IO_SEEK_END); - if (*size < 0) { - return SP_ERR_IO_SEEK_FAILED; - } - - sp_sys_lseek(w->fd, current, SP_IO_SEEK_SET); + w->size = (u64)st.size; + if (size) *size = w->size; return SP_OK; } @@ -13597,20 +13746,7 @@ sp_err_t sp_io_file_writer_close(sp_io_file_writer_t* w) { return SP_OK; } -sp_err_t sp_io_file_writer_from_path(sp_io_file_writer_t* w, sp_str_t path, sp_io_write_mode_t mode) { - s32 flags = SP_O_WRONLY | SP_O_CREAT | SP_O_BINARY; - switch (mode) { - case SP_IO_WRITE_MODE_OVERWRITE: flags |= SP_O_TRUNC; break; - case SP_IO_WRITE_MODE_APPEND: flags |= SP_O_APPEND; break; - } - - sp_sys_fd_t fd = sp_sys_open_s(path, flags, 0644); - - if (fd == SP_SYS_INVALID_FD) { - *w = sp_zero_s(sp_io_file_writer_t); - return SP_ERR_IO_OPEN_FAILED; - } - +sp_err_t sp_io_file_writer_from_fd(sp_io_file_writer_t* w, sp_sys_fd_t fd, sp_io_close_mode_t close_mode) { *w = (sp_io_file_writer_t) { .base = { .write = sp_io_file_writer_write, @@ -13619,22 +13755,27 @@ sp_err_t sp_io_file_writer_from_path(sp_io_file_writer_t* w, sp_str_t path, sp_i #endif }, .fd = fd, - .close_mode = SP_IO_CLOSE_MODE_AUTO, + .close_mode = close_mode, }; + + if (sp_io_file_writer_size_force(w, SP_NULLPTR) != SP_OK) { + if (close_mode == SP_IO_CLOSE_MODE_AUTO) sp_sys_close(fd); + *w = sp_zero_s(sp_io_file_writer_t); + return SP_ERR_IO; + } return SP_OK; } -void sp_io_file_writer_from_fd(sp_io_file_writer_t* w, sp_sys_fd_t fd, sp_io_close_mode_t close_mode) { - *w = (sp_io_file_writer_t) { - .base = { - .write = sp_io_file_writer_write, -#if defined(SP_LINUX) - .read_from = sp_io_file_writer_read_from, -#endif - }, - .fd = fd, - .close_mode = close_mode, - }; +sp_err_t sp_io_file_writer_from_path(sp_io_file_writer_t* w, sp_str_t path) { + s32 flags = SP_O_WRONLY | SP_O_CREAT | SP_O_TRUNC | SP_O_BINARY; + sp_sys_fd_t fd = sp_sys_open_s(sp_fs_open_cwd(), path, flags, 0644); + + if (fd == SP_SYS_INVALID_FD) { + *w = sp_zero_s(sp_io_file_writer_t); + return SP_ERR_IO_OPEN_FAILED; + } + + return sp_io_file_writer_from_fd(w, fd, SP_IO_CLOSE_MODE_AUTO); } sp_err_t sp_io_writer_set_buffer(sp_io_writer_t* writer, u8* ptr, u64 size) { @@ -13760,12 +13901,12 @@ sp_err_t sp_io_pad(sp_io_writer_t* writer, u64 size, u64* bytes_written) { return result; } -void sp_io_get_std_out(sp_io_file_writer_t* io) { - sp_io_file_writer_from_fd(io, sp_sys_stdout, SP_IO_CLOSE_MODE_NONE); +void sp_io_get_std_out(sp_io_stream_writer_t* io) { + sp_io_stream_writer_from_fd(io, sp_sys_stdout, SP_IO_CLOSE_MODE_NONE); } -void sp_io_get_std_err(sp_io_file_writer_t* io) { - sp_io_file_writer_from_fd(io, sp_sys_stderr, SP_IO_CLOSE_MODE_NONE); +void sp_io_get_std_err(sp_io_stream_writer_t* io) { + sp_io_stream_writer_from_fd(io, sp_sys_stderr, SP_IO_CLOSE_MODE_NONE); } ///////// @@ -14082,40 +14223,95 @@ sp_str_t sp_io_dyn_mem_writer_as_str(sp_io_dyn_mem_writer_t* w) { return sp_mem_buffer_as_str(&w->storage); } +const c8* sp_io_dyn_mem_writer_as_cstr(sp_io_dyn_mem_writer_t* w) { + return sp_mem_buffer_as_cstr(&w->storage); +} + +sp_err_t sp_fmt_check_type(sp_fmt_arg_kind_t expected, sp_fmt_arg_kind_t actual, sp_err_t err) { + return (expected && !(expected & actual)) ? err : SP_OK; +} + +sp_err_t sp_fmt_check_arg(sp_fmt_arg_kind_t expected, sp_fmt_arg_kind_t actual) { + return sp_fmt_check_type(expected, actual, SP_ERR_FMT_DIRECTIVE_ARG_WRONG_KIND); +} + +sp_err_t sp_fmt_check_param(sp_fmt_arg_kind_t expected, sp_fmt_arg_kind_t actual) { + return sp_fmt_check_type(expected, actual, SP_ERR_FMT_WRONG_PARAM_KIND); +} + +bool sp_fmt_is_directive_dynamic(u8 dynamic, u8 index) { + return dynamic & (1u << index); +} + +sp_fmt_arg_t sp_fmt_arg_from_argv(sp_fmt_argv_t v) { + sp_fmt_arg_t arg = sp_zero; + arg.id = v.id; + arg.value = v.value; + return arg; +} + sp_str_r sp_fmt(sp_mem_t mem, const c8* fmt, ...) { va_list args; va_start(args, fmt); - sp_io_dyn_mem_writer_t io = sp_zero; - sp_io_dyn_mem_writer_init(mem, &io); - - sp_mem_arena_marker_t s = sp_mem_begin_scratch_for(mem); - sp_err_t err = sp_fmt_v(&io.base, s.mem, sp_str_view(fmt), args); - sp_mem_end_scratch(s); + sp_str_r str = sp_fmt_mem_v(mem, sp_cstr_as_str(fmt), args); va_end(args); - sp_str_r result = sp_zero; - result.err = err; - if (!err) result.value = sp_io_dyn_mem_writer_as_str(&io); + return str; +} + +sp_err_t sp_fmt_io(sp_io_writer_t* io, const c8* fmt, ...) { + va_list args; + va_start(args, fmt); + sp_err_t result = sp_fmt_io_v(io, sp_cstr_as_str(fmt), args); + va_end(args); return result; } -const c8* sp_fmt_to_cstr(sp_mem_t mem, const c8* fmt, ...) { +sp_str_r sp_fmt_buf(c8* buffer, u64 len, const c8* fmt, ...) { va_list args; va_start(args, fmt); + sp_str_r str = sp_fmt_buf_v(buffer, len, sp_cstr_as_str(fmt), args); + va_end(args); + + return str; +} + +const c8* sp_fmt_mem_cstr(sp_mem_t mem, const c8* fmt, ...) { sp_io_dyn_mem_writer_t io = sp_zero; sp_io_dyn_mem_writer_init(mem, &io); - sp_mem_arena_marker_t s = sp_mem_begin_scratch_for(mem); - sp_err_t err = sp_fmt_v(&io.base, s.mem, sp_str_view(fmt), args); - sp_mem_end_scratch(s); + va_list args; + va_start(args, fmt); + sp_err_t err = sp_fmt_io_v(&io.base, sp_cstr_as_str(fmt), args); va_end(args); if (err) return SP_NULLPTR; - sp_io_write_c8(&io.base, 0); - return sp_mem_buffer_as_cstr(&io.storage); + + if (sp_io_write_c8(&io.base, '\0')) return SP_NULLPTR; + return sp_io_dyn_mem_writer_as_cstr(&io); +} + +sp_str_r sp_fmt_mem_v(sp_mem_t mem, sp_str_t fmt, va_list args) { + sp_str_r str = sp_zero; + + sp_io_dyn_mem_writer_t io = sp_zero; + sp_io_dyn_mem_writer_init(mem, &io); + str.err = sp_fmt_io_v(&io.base, fmt, args); + if (!str.err) str.value = sp_io_dyn_mem_writer_as_str(&io); + return str; +} + +sp_str_r sp_fmt_buf_v(c8* buffer, u64 len, sp_str_t fmt, va_list args) { + sp_str_r str = sp_zero; + + sp_io_mem_writer_t io = sp_zero; + sp_io_mem_writer_from_buffer(&io, buffer, len); + str.err = sp_fmt_io_v(&io.base, fmt, args); + if (!str.err) str.value = sp_io_mem_writer_as_str(&io); + return str; } -sp_err_t sp_fmt_v(sp_io_writer_t* io, sp_mem_t mem, sp_str_t fmt, va_list args) { +sp_err_t sp_fmt_io_v(sp_io_writer_t* io, sp_str_t fmt, va_list args) { sp_fmt_parser_t p = { .str = fmt }; while (true) { @@ -14133,36 +14329,32 @@ sp_err_t sp_fmt_v(sp_io_writer_t* io, sp_mem_t mem, sp_str_t fmt, va_list args) sp_fmt_spec_t spec = sp_zero; sp_try(sp_fmt_parse_specifier(&p, &spec)); - if (spec.fill_dynamic) { + if (spec.dynamic.fill) { s64 v; - sp_try(sp_fmt_pull_int_arg(va_arg(args, sp_fmt_argv_t), &v)); - spec.fill = (u8)v; + sp_try(sp_fmt_pull_specifier_arg(va_arg(args, sp_fmt_argv_t), &v)); + spec.fill = sp_cast(u8, v); } - if (spec.width_dynamic) { + if (spec.dynamic.width) { s64 v; - sp_try(sp_fmt_pull_int_arg(va_arg(args, sp_fmt_argv_t), &v)); - if (v < 0) v = 0; - if (v > SP_FMT_WIDTH_MAX) v = SP_FMT_WIDTH_MAX; - spec.width = (u32)v; + sp_try(sp_fmt_pull_specifier_arg(va_arg(args, sp_fmt_argv_t), &v)); + spec.width = sp_cast(u32, sp_clamp(v, 0, SP_FMT_WIDTH_MAX)); } - if (spec.precision_dynamic) { + if (spec.dynamic.precision) { s64 v; - sp_try(sp_fmt_pull_int_arg(va_arg(args, sp_fmt_argv_t), &v)); - if (v < 0) v = 0; - if (v > 255) v = 255; - sp_opt_set(spec.precision, (u8)v); + sp_try(sp_fmt_pull_specifier_arg(va_arg(args, sp_fmt_argv_t), &v)); + sp_opt_set(spec.precision, sp_cast(u8, sp_clamp(v, 0, 255))); } - sp_fmt_arg_t params[SP_FMT_MAX_DIRECTIVES] = sp_zero; - for (u8 di = 0; di < spec.directive_count; di++) { - if (spec.directive_arg_dynamic & (1u << di)) { - params[di] = sp_fmt_arg_from_argv(va_arg(args, sp_fmt_argv_t)); + sp_fmt_arg_t params [SP_FMT_MAX_DIRECTIVES] = sp_zero; + sp_for(it, spec.directive.num) { + if (sp_fmt_is_directive_dynamic(spec.dynamic.directive, it)) { + params[it] = sp_fmt_arg_from_argv(va_arg(args, sp_fmt_argv_t)); } } sp_fmt_arg_t arg = sp_fmt_arg_from_argv(va_arg(args, sp_fmt_argv_t)); arg.spec = spec; - sp_try(sp_fmt_render(io, mem, &arg, params)); + sp_try(sp_fmt_render(io, &arg, params)); continue; } @@ -14184,250 +14376,189 @@ sp_err_t sp_fmt_v(sp_io_writer_t* io, sp_mem_t mem, sp_str_t fmt, va_list args) } void sp_log(const c8* fmt, ...) { - u8 buffer [4096] = sp_zero; - sp_io_mem_writer_t io = sp_zero; - sp_io_mem_writer_from_buffer(&io, buffer, sizeof(buffer)); - - sp_mem_arena_marker_t s = sp_mem_begin_scratch(); + sp_tls_rt_t* tls = sp_tls_rt_get(); va_list args; va_start(args, fmt); - sp_fmt_v(&io.base, s.mem, sp_str_view(fmt), args); + sp_fmt_io_v(&tls->std.out->base, sp_str_view(fmt), args); va_end(args); - sp_mem_end_scratch(s); - - sp_str_t str = sp_io_mem_writer_as_str(&io); - sp_tls_rt_t* tls = sp_tls_rt_get(); - sp_io_write_str(&tls->std.out->base, str, SP_NULLPTR); sp_io_write_cstr(&tls->std.out->base, "\n", SP_NULLPTR); } void sp_log_str(sp_str_t fmt, ...) { - u8 buffer[4096] = sp_zero; - sp_io_mem_writer_t io = sp_zero; - sp_io_mem_writer_from_buffer(&io, buffer, sizeof(buffer)); - - sp_mem_arena_marker_t s = sp_mem_begin_scratch(); + sp_tls_rt_t* tls = sp_tls_rt_get(); va_list args; va_start(args, fmt); - sp_fmt_v(&io.base, s.mem, fmt, args); + sp_fmt_io_v(&tls->std.out->base, fmt, args); va_end(args); - sp_mem_end_scratch(s); - - sp_tls_rt_t* tls = sp_tls_rt_get(); - sp_io_write_str(&tls->std.out->base, sp_io_mem_writer_as_str(&io), SP_NULLPTR); sp_io_write_cstr(&tls->std.out->base, "\n", SP_NULLPTR); } void sp_log_err(const c8* fmt, ...) { - u8 buffer[4096] = sp_zero; - sp_io_mem_writer_t io = sp_zero; - sp_io_mem_writer_from_buffer(&io, buffer, sizeof(buffer)); - - sp_mem_arena_marker_t s = sp_mem_begin_scratch(); + sp_tls_rt_t* tls = sp_tls_rt_get(); va_list args; va_start(args, fmt); - sp_fmt_v(&io.base, s.mem, sp_str_view(fmt), args); + sp_fmt_io_v(&tls->std.out->base, sp_str_view(fmt), args); va_end(args); - sp_mem_end_scratch(s); - - sp_tls_rt_t* tls = sp_tls_rt_get(); - sp_io_write_str(&tls->std.out->base, sp_io_mem_writer_as_str(&io), SP_NULLPTR); sp_io_write_cstr(&tls->std.out->base, "\n", SP_NULLPTR); } void sp_print(const c8* fmt, ...) { - u8 buffer[4096] = sp_zero; - sp_io_mem_writer_t io = sp_zero; - sp_io_mem_writer_from_buffer(&io, buffer, sizeof(buffer)); - - sp_mem_arena_marker_t s = sp_mem_begin_scratch(); + sp_tls_rt_t* tls = sp_tls_rt_get(); va_list args; va_start(args, fmt); - sp_fmt_v(&io.base, s.mem, sp_str_view(fmt), args); + sp_fmt_io_v(&tls->std.out->base, sp_str_view(fmt), args); va_end(args); - sp_mem_end_scratch(s); - - sp_tls_rt_t* tls = sp_tls_rt_get(); - sp_io_write_str(&tls->std.out->base, sp_io_mem_writer_as_str(&io), SP_NULLPTR); } void sp_print_str(sp_str_t fmt, ...) { - u8 buffer[4096] = sp_zero; - sp_io_mem_writer_t io = sp_zero; - sp_io_mem_writer_from_buffer(&io, buffer, sizeof(buffer)); - - sp_mem_arena_marker_t s = sp_mem_begin_scratch(); + sp_tls_rt_t* tls = sp_tls_rt_get(); va_list args; va_start(args, fmt); - sp_fmt_v(&io.base, s.mem, fmt, args); + sp_fmt_io_v(&tls->std.out->base, fmt, args); va_end(args); - sp_mem_end_scratch(s); - - sp_tls_rt_t* tls = sp_tls_rt_get(); - sp_io_write_str(&tls->std.out->base, sp_io_mem_writer_as_str(&io), SP_NULLPTR); } void sp_print_err(const c8* fmt, ...) { - u8 buffer[4096] = sp_zero; - sp_io_mem_writer_t io = sp_zero; - sp_io_mem_writer_from_buffer(&io, buffer, sizeof(buffer)); - - sp_mem_arena_marker_t s = sp_mem_begin_scratch(); + sp_tls_rt_t* tls = sp_tls_rt_get(); va_list args; va_start(args, fmt); - sp_fmt_v(&io.base, s.mem, sp_str_view(fmt), args); + sp_fmt_io_v(&tls->std.err->base, sp_str_view(fmt), args); va_end(args); - sp_mem_end_scratch(s); +} - sp_tls_rt_t* tls = sp_tls_rt_get(); - sp_io_write_str(&tls->std.err->base, sp_io_mem_writer_as_str(&io), SP_NULLPTR); +bool sp_fmt_dir_has_params(sp_fmt_directive_t* dir) { + return dir->params != sp_fmt_id_none; } -sp_err_t sp_fmt_render(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* directive_params) { - sp_fmt_directive_t* directives[SP_FMT_MAX_DIRECTIVES]; +sp_err_t sp_fmt_render(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* directive_params) { + sp_fmt_directive_t* directives [SP_FMT_MAX_DIRECTIVES] = sp_zero; + sp_fmt_arg_t* args [SP_FMT_MAX_DIRECTIVES] = sp_zero; - u8 num_dirs = arg->spec.directive_count; - sp_for(i, num_dirs) { - sp_str_t name = arg->spec.directive_names[i]; - sp_fmt_directive_t* directive = sp_fmt_directive_lookup(name); - directives[i] = directive; - if (!directives[i]) { - return SP_ERR_FMT_UNKNOWN_DIRECTIVE; - } - } + u8 num_dirs = arg->spec.directive.num; + sp_fmt_fn_t render_fn = SP_NULLPTR; + sp_fmt_arg_t* render_param = SP_NULLPTR; - sp_for(it, num_dirs) { - sp_fmt_arg_kind_t accepted = directives[it]->arg_kinds; - if (accepted && !(accepted & arg->id)) { - return SP_ERR_FMT_WRONG_PARAM_KIND; - } - } + sp_for(i, arg->spec.directive.num) { + sp_fmt_directive_t* d = sp_fmt_directive_lookup(arg->spec.directive.names[i]); + if (!d) return SP_ERR_FMT_UNKNOWN_DIRECTIVE; - bool params [SP_FMT_MAX_DIRECTIVES] = sp_zero; - sp_for(it, num_dirs) { - bool is_dynamic = (arg->spec.directive_arg_dynamic & (1u << it)) != 0; - bool is_literal = !sp_str_empty(arg->spec.directive_args[it]); - if (is_dynamic) { - params[it] = true; - } - else if (is_literal) { - directive_params[it] = (sp_fmt_arg_t){ + sp_try(sp_fmt_check_param(d->args, arg->id)); + + //sp_fmt_arg_t arg = sp_zero; + bool is_dynamic = (arg->spec.dynamic.directive & (1u << i)) != 0; + bool is_literal = !sp_str_empty(arg->spec.directive.args[i]); + if (is_literal) { + directive_params[i] = (sp_fmt_arg_t){ .id = sp_fmt_id_str, - .value.s = arg->spec.directive_args[it], + .value = { + .s = arg->spec.directive.args[i], + } }; - params[it] = true; - } - else { - params[it] = false; } + if (is_dynamic || is_literal) args[i] = &directive_params[i]; - sp_fmt_arg_kind_t kind = directives[it]->param_kinds; - if (!kind) { - if (params[it]) return SP_ERR_FMT_DIRECTIVE_ARG_UNEXPECTED; + if (!sp_fmt_dir_has_params(d)) { + if (args[i]) return SP_ERR_FMT_DIRECTIVE_ARG_UNEXPECTED; } else { - if (!params[it]) return SP_ERR_FMT_DIRECTIVE_ARG_MISSING; - if (!(kind & directive_params[it].id)) return SP_ERR_FMT_DIRECTIVE_ARG_WRONG_KIND; + if (!args[i]) return SP_ERR_FMT_DIRECTIVE_ARG_MISSING; + if (!(d->params & args[i]->id)) return SP_ERR_FMT_DIRECTIVE_ARG_WRONG_KIND; } - } - sp_io_dyn_mem_writer_t before_io = sp_zero; - sp_io_dyn_mem_writer_t content_io = sp_zero; - sp_io_dyn_mem_writer_t after_io = sp_zero; - sp_io_dyn_mem_writer_init(mem, &before_io); - sp_io_dyn_mem_writer_init(mem, &content_io); - sp_io_dyn_mem_writer_init(mem, &after_io); + if (d->kind == sp_fmt_directive_renderer) { + if (render_fn) return SP_ERR_FMT_TOO_MANY_RENDERERS; + render_fn = d->renderer; + render_param = args[i]; + } - sp_for(it, num_dirs) { - if (directives[it]->kind != sp_fmt_directive_decorator) continue; - sp_fmt_fn_t before_fn = directives[it]->decorator.before; - if (!before_fn) continue; - sp_fmt_arg_t* p = params[it] ? &directive_params[it] : SP_NULLPTR; - before_fn(&before_io.base, mem, arg, p); + directives[i] = d; } - struct { - sp_fmt_fn_t fn; - sp_fmt_arg_t* param; - } render = sp_zero; + bool is_opaque = (render_fn != SP_NULLPTR); if (arg->id == sp_fmt_id_custom) { - if (!arg->value.custom.fn) { - return SP_ERR_FMT_CUSTOM_WITHOUT_FN; - } - render.fn = arg->value.custom.fn; + if (!arg->value.custom.fn) return SP_ERR_FMT_CUSTOM_WITHOUT_FN; + render_fn = arg->value.custom.fn; + render_param = SP_NULLPTR; + is_opaque = true; } - else { + + if (is_opaque && arg->spec.width) return SP_ERR_FMT_WIDTH_ON_OPAQUE_RENDERER; + + if (is_opaque) { sp_for(it, num_dirs) { - if (directives[it]->kind != sp_fmt_directive_renderer) continue; - if (render.fn) { - return SP_ERR_FMT_TOO_MANY_RENDERERS; - } - render.fn = directives[it]->renderer; - render.param = params[it] ? &directive_params[it] : SP_NULLPTR; + if (directives[it]->kind != sp_fmt_directive_decorator) continue; + sp_fmt_fn_t before = directives[it]->decorator.before; + if (before) before(io, arg, args[it]); } + render_fn(io, arg, render_param); + u8 j = num_dirs; + while (j--) { + if (directives[j]->kind != sp_fmt_directive_decorator) continue; + sp_fmt_fn_t after = directives[j]->decorator.after; + if (after) after(io, arg, args[j]); + } + return SP_OK; } - render.fn = render.fn ? render.fn : sp_fmt_render_default; - render.fn(&content_io.base, mem, arg, render.param); - sp_str_t content = sp_io_dyn_mem_writer_as_str(&content_io); - - u8 j = num_dirs; - while (j--) { - if (directives[j]->kind != sp_fmt_directive_transformer) continue; - sp_fmt_arg_t* p = params[j] ? &directive_params[j] : SP_NULLPTR; - sp_io_dyn_mem_writer_t next = sp_zero; - sp_io_dyn_mem_writer_init(mem, &next); - directives[j]->transformer(&next.base, mem, content, arg, p); - content = sp_io_dyn_mem_writer_as_str(&next); + sp_str_t content = sp_zero; + c8 buf[64]; + sp_io_mem_writer_t cw = sp_zero; + switch (arg->id) { + case sp_fmt_id_u64: + case sp_fmt_id_s64: + case sp_fmt_id_f64: + case sp_fmt_id_ptr: { + sp_io_mem_writer_from_buffer(&cw, buf, sizeof(buf)); + sp_fmt_render_default(&cw.base, arg, SP_NULLPTR); + content = sp_io_mem_writer_as_str(&cw); + break; + } + case sp_fmt_id_str: { + content = arg->value.s; + if (!sp_opt_is_null(arg->spec.precision)) { + u32 max = sp_opt_get(arg->spec.precision); + if (max < content.len) content.len = max; + } + break; + } + case sp_fmt_id_custom: + case sp_fmt_id_none: break; } - j = num_dirs; - while (j--) { - if (directives[j]->kind != sp_fmt_directive_decorator) continue; - sp_fmt_fn_t after_fn = directives[j]->decorator.after; - if (!after_fn) continue; - sp_fmt_arg_t* p = params[j] ? &directive_params[j] : SP_NULLPTR; - after_fn(&after_io.base, mem, arg, p); + u32 width = arg->spec.width > SP_FMT_WIDTH_MAX ? SP_FMT_WIDTH_MAX : arg->spec.width; + u32 pad = (width > content.len) ? (u32)(width - content.len) : 0; + u8 fill = arg->spec.fill ? arg->spec.fill : ' '; + sp_fmt_align_t align = arg->spec.align; + if (align == SP_FMT_ALIGN_NONE) align = SP_FMT_ALIGN_RIGHT; + + u32 left_pad = 0; + u32 right_pad = 0; + switch (align) { + case SP_FMT_ALIGN_LEFT: right_pad = pad; break; + case SP_FMT_ALIGN_CENTER: left_pad = pad / 2; right_pad = pad - left_pad; break; + case SP_FMT_ALIGN_RIGHT: left_pad = pad; break; + case SP_FMT_ALIGN_NONE: left_pad = pad; break; } - sp_str_t before = sp_io_dyn_mem_writer_as_str(&before_io); - sp_str_t after = sp_io_dyn_mem_writer_as_str(&after_io); + sp_for(it, left_pad) sp_io_write_c8(io, fill); - sp_fmt_apply_spec(io, before, content, after, arg->spec); - return SP_OK; -} + sp_for(it, num_dirs) { + if (directives[it]->kind != sp_fmt_directive_decorator) continue; + sp_fmt_fn_t before = directives[it]->decorator.before; + if (before) before(io, arg, args[it]); + } -void sp_fmt_apply_spec(sp_io_writer_t* io, sp_str_t pre, sp_str_t str, sp_str_t post, sp_fmt_spec_t spec) { - u32 content_len = (u32)str.len; - u32 width = spec.width > SP_FMT_WIDTH_MAX ? SP_FMT_WIDTH_MAX : spec.width; - u32 pad = (width > content_len) ? (width - content_len) : 0; - u8 fill = spec.fill ? spec.fill : ' '; - sp_fmt_align_t align = spec.align; - if (align == SP_FMT_ALIGN_NONE) align = SP_FMT_ALIGN_RIGHT; + sp_io_write_str(io, content, SP_NULLPTR); - u32 left = 0; - u32 right = 0; - switch (align) { - case SP_FMT_ALIGN_LEFT: - right = pad; - break; - case SP_FMT_ALIGN_CENTER: - left = pad / 2; - right = pad - left; - break; - case SP_FMT_ALIGN_RIGHT: - left = pad; - break; - case SP_FMT_ALIGN_NONE: - left = pad; - break; + u8 j = num_dirs; + while (j--) { + if (directives[j]->kind != sp_fmt_directive_decorator) continue; + sp_fmt_fn_t after = directives[j]->decorator.after; + if (after) after(io, arg, args[j]); } - sp_for(it, left) sp_io_write_c8(io, fill); - sp_io_write_str(io, pre, SP_NULLPTR); - sp_io_write_str(io, str, SP_NULLPTR); - sp_io_write_str(io, post, SP_NULLPTR); - sp_for(it, right) sp_io_write_c8(io, fill); + sp_for(it, right_pad) sp_io_write_c8(io, fill); + return SP_OK; } static const c8 sp_fmt_digit_pairs[201] = @@ -14562,7 +14693,7 @@ void sp_fmt_write_f64(sp_io_writer_t* io, f64 value, u32 precision) { } } -void sp_fmt_render_default(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { +void sp_fmt_render_default(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { sp_unused(param); switch (arg->id) { case sp_fmt_id_u64: @@ -14591,7 +14722,7 @@ void sp_fmt_render_default(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, } case sp_fmt_id_custom: { if (arg->value.custom.fn) { - arg->value.custom.fn(io, mem, arg, SP_NULLPTR); + arg->value.custom.fn(io, arg, SP_NULLPTR); } break; } @@ -14712,14 +14843,14 @@ sp_str_t sp_fs_replace_ext(sp_mem_t mem, sp_str_t path, sp_str_t ext) { sp_fs_kind_t sp_fs_lstat_kind(sp_str_t path) { if (sp_str_empty(path)) return SP_FS_KIND_NONE; sp_sys_stat_t st = sp_zero; - s32 rc = sp_sys_lstat_s(path, &st); + s32 rc = sp_sys_lstat_s(sp_fs_open_cwd(), path, &st); return rc == 0 ? st.kind : SP_FS_KIND_NONE; } sp_fs_kind_t sp_fs_stat_kind(sp_str_t path) { if (sp_str_empty(path)) return SP_FS_KIND_NONE; sp_sys_stat_t st = sp_zero; - s32 rc = sp_sys_stat_s(path, &st); + s32 rc = sp_sys_stat_s(sp_fs_open_cwd(), path, &st); return rc == 0 ? st.kind : SP_FS_KIND_NONE; } @@ -14758,7 +14889,7 @@ sp_fs_kind_t sp_fs_get_target_kind(sp_str_t path) { sp_tm_epoch_t sp_fs_get_mod_time(sp_str_t path) { sp_tm_epoch_t result = sp_zero_s(sp_tm_epoch_t); sp_sys_stat_t st; - if (sp_sys_stat_s(path, &st) == 0) { + if (sp_sys_stat_s(sp_fs_open_cwd(), path, &st) == 0) { result.s = (u64)st.mtime.tv_sec; result.ns = (u32)st.mtime.tv_nsec; } @@ -14784,10 +14915,62 @@ sp_str_t sp_fs_get_exe_path(sp_mem_t mem) { } sp_str_t sp_fs_get_cwd(sp_mem_t mem) { - sp_str_t cwd = sp_os_get_cwd(mem); + c8 path[SP_PATH_MAX] = sp_zero; + if (sp_sys_getcwd(path, SP_PATH_MAX - 1) < 0) { + return sp_zero_s(sp_str_t); + } + + sp_str_t cwd = sp_str_from_cstr(mem, path); return sp_fs_normalize_path(mem, cwd); } +sp_sys_fd_t sp_fs_open_cwd(void) { + return sp_sys_root(0, SP_NULLPTR, 0); +} + +sp_str_t sp_fs_resolve(sp_mem_t mem, sp_sys_fd_t fd) { +#if defined(SP_WIN32) + u16 wbuf[SP_PATH_MAX]; + DWORD wlen = GetFinalPathNameByHandleW((HANDLE)fd, (LPWSTR)wbuf, SP_PATH_MAX, 0); + if (wlen == 0 || wlen >= SP_PATH_MAX) return sp_zero_s(sp_str_t); + u32 skip = 0; + if (wlen >= 4 && wbuf[0] == '\\' && wbuf[1] == '\\' && wbuf[2] == '?' && wbuf[3] == '\\') skip = 4; + return sp_wtf16_to_wtf8(mem, (sp_wide_str_t) { .data = wbuf + skip, .len = wlen - skip }); + +#elif defined(SP_LINUX) + if (fd == (sp_sys_fd_t)SP_AT_FDCWD) return sp_fs_get_cwd(mem); + c8 link_cstr[64] = sp_zero; + c8* p = link_cstr; + const c8* prefix = "/proc/self/fd/"; + while (*prefix) *p++ = *prefix++; + c8 num[24] = sp_zero; + s32 ni = 0; + s64 v = (s64)fd; + if (v < 0) { *p++ = '-'; v = -v; } + if (v == 0) { num[ni++] = '0'; } + else { while (v > 0) { num[ni++] = (c8)('0' + (v % 10)); v /= 10; } } + while (ni-- > 0) *p++ = num[ni]; + *p = 0; + c8 buf[SP_PATH_MAX] = sp_zero; + s64 n = sp_syscall(SP_SYSCALL_NUM_READLINKAT, SP_AT_FDCWD, link_cstr, buf, (u64)SP_PATH_MAX - 1); + if (n <= 0) return sp_zero_s(sp_str_t); + return sp_str_copy(mem, (sp_str_t){ .data = buf, .len = (u32)n }); + +#elif defined(SP_MACOS) || defined(SP_COSMO) + if (fd == (sp_sys_fd_t)SP_AT_FDCWD) return sp_fs_get_cwd(mem); + c8 buf[SP_PATH_MAX] = sp_zero; + if (fcntl((int)fd, F_GETPATH, buf) < 0) return sp_zero_s(sp_str_t); + return sp_str_from_cstr(mem, buf); + +#elif defined(SP_WASM) + (void)mem; (void)fd; + return sp_zero_s(sp_str_t); + +#else + #error "sp_fs_resolve" +#endif +} + sp_str_t sp_fs_get_storage_path(sp_mem_t mem) { c8 buf[SP_PATH_MAX]; s64 len = sp_sys_get_storage_path(buf, SP_PATH_MAX); @@ -14850,7 +15033,7 @@ sp_err_t sp_fs_create_dir(sp_str_t path) { // Walk back down and create each one sp_da_rfor(missing, it) { - result = sp_os_create_dir(missing[it]); + result = sp_sys_mkdir_s(sp_fs_open_cwd(), missing[it], 0755) == 0 ? SP_OK : SP_ERR_OS; if (result && !sp_fs_exists(missing[it])) goto cleanup; } @@ -14860,22 +15043,27 @@ sp_err_t sp_fs_create_dir(sp_str_t path) { } sp_err_t sp_fs_create_file(sp_str_t path) { - return sp_os_create_file(path); + sp_sys_fd_t fd = sp_sys_open_s(sp_fs_open_cwd(), path, SP_O_CREAT | SP_O_WRONLY | SP_O_TRUNC, 0644); + if (fd != SP_SYS_INVALID_FD) { + sp_sys_close(fd); + } + + return fd != SP_SYS_INVALID_FD ? SP_OK : SP_ERR_OS; } sp_err_t sp_fs_create_file_slice(sp_str_t path, sp_mem_slice_t slice) { - sp_try(sp_os_create_file(path)); + sp_try(sp_fs_create_file(path)); sp_io_file_writer_t io = sp_zero; - sp_try(sp_io_file_writer_from_path(&io, path, SP_IO_WRITE_MODE_OVERWRITE)); + sp_try(sp_io_file_writer_from_path(&io, path)); sp_try(sp_io_write(&io.base, slice.data, slice.len, SP_NULLPTR)); sp_try(sp_io_file_writer_close(&io)); return SP_OK; } sp_err_t sp_fs_create_file_str(sp_str_t path, sp_str_t str) { - sp_try(sp_os_create_file(path)); + sp_try(sp_fs_create_file(path)); sp_io_file_writer_t io = sp_zero; - sp_try(sp_io_file_writer_from_path(&io, path, SP_IO_WRITE_MODE_OVERWRITE)); + sp_try(sp_io_file_writer_from_path(&io, path)); sp_try(sp_io_write_str(&io.base, str, SP_NULLPTR)); sp_try(sp_io_file_writer_close(&io)); return SP_OK; @@ -14886,11 +15074,17 @@ sp_err_t sp_fs_create_file_cstr(sp_str_t path, const c8* str) { } sp_err_t sp_fs_create_hard_link(sp_str_t target, sp_str_t link_path) { - return sp_os_create_hard_link(target, link_path); + if (sp_sys_link_s(sp_fs_open_cwd(), target, sp_fs_open_cwd(), link_path)) { + return SP_ERR_OS; + } + return SP_OK; } sp_err_t sp_fs_create_sym_link(sp_str_t target, sp_str_t link_path) { - return sp_os_create_sym_link(target, link_path); + if (sp_sys_symlink_s(target, sp_fs_open_cwd(), link_path) != 0) { + return SP_ERR_OS; + } + return SP_OK; } sp_err_t sp_fs_link(sp_str_t from, sp_str_t to, sp_fs_link_kind_t kind) { @@ -14903,12 +15097,12 @@ sp_err_t sp_fs_link(sp_str_t from, sp_str_t to, sp_fs_link_kind_t kind) { } sp_err_t sp_fs_remove_file(sp_str_t path) { - return sp_cast(sp_err_t, sp_sys_unlink_s(path)); + return sp_cast(sp_err_t, sp_sys_unlink_s(sp_fs_open_cwd(), path)); } void sp_fs_it_push(sp_fs_it_t* it, sp_str_t path) { sp_fs_it_frame_t frame = sp_zero; - if (sp_sys_fs_it_open_s(&frame.sys, path, sp_mem_slice(frame.buf, SP_FS_IT_BUF_SIZE)) < 0) return; + if (sp_sys_fs_it_open_s(sp_fs_open_cwd(), &frame.sys, path, sp_mem_slice(frame.buf, SP_FS_IT_BUF_SIZE)) < 0) return; frame.path = sp_str_copy(it->mem, path); sp_da_push(it->stack, frame); } @@ -14986,7 +15180,7 @@ sp_err_t sp_fs_remove_dir(sp_str_t path) { if (err) goto done; } - err = sp_cast(sp_err_t, sp_sys_rmdir_s(path)); + err = sp_cast(sp_err_t, sp_sys_rmdir_s(sp_fs_open_cwd(), path)); done: sp_mem_end_scratch(s); @@ -15003,9 +15197,9 @@ void sp_fs_copy_file(sp_str_t from, sp_str_t to) { to = sp_fs_join_path(s.mem, to, sp_fs_get_name(from)); } - if (sp_sys_stat_s(from, &st)) goto done; + if (sp_sys_stat_s(sp_fs_open_cwd(), from, &st)) goto done; if (sp_io_file_reader_from_path(&reader, from)) goto done; - if (sp_io_file_writer_from_path(&writer, to, SP_IO_WRITE_MODE_OVERWRITE)) { + if (sp_io_file_writer_from_path(&writer, to)) { sp_io_file_reader_close(&reader); goto done; } @@ -15019,7 +15213,7 @@ void sp_fs_copy_file(sp_str_t from, sp_str_t to) { } sp_io_file_reader_close(&reader); sp_io_file_writer_close(&writer); - sp_sys_chmod_s(to, &st); + sp_sys_chmod_s(sp_fs_open_cwd(), to, &st); done: sp_mem_end_scratch(s); @@ -15090,9 +15284,9 @@ sp_fs_it_t sp_fs_it_new_recursive(sp_mem_t mem, sp_str_t path) { // // time // -sp_str_t sp_tm_epoch_to_iso8601(sp_mem_t mem, sp_tm_epoch_t time) { +void sp_tm_epoch_to_iso8601_w(sp_io_writer_t* io, sp_tm_epoch_t time) { sp_tm_date_time_t dt = sp_tm_epoch_to_date_time(time); - c8* buf = (c8*)sp_mem_allocator_alloc(mem, 24); + c8 buf[24]; // YYYY-MM-DDTHH:MM:SS.mmmZ buf[0] = (c8)('0' + ((u32)dt.year / 1000) % 10); buf[1] = (c8)('0' + ((u32)dt.year / 100) % 10); @@ -15118,9 +15312,16 @@ sp_str_t sp_tm_epoch_to_iso8601(sp_mem_t mem, sp_tm_epoch_t time) { buf[21] = (c8)('0' + ((u32)dt.millisecond / 10) % 10); buf[22] = (c8)('0' + (u32)dt.millisecond % 10); buf[23] = 'Z'; + sp_io_write(io, buf, 24, SP_NULLPTR); +} + +sp_str_t sp_tm_epoch_to_iso8601(sp_mem_t mem, sp_tm_epoch_t time) { + c8* buf = (c8*)sp_mem_allocator_alloc(mem, 24); + sp_io_mem_writer_t io = sp_zero; + sp_io_mem_writer_from_buffer(&io, buf, 24); + sp_tm_epoch_to_iso8601_w(&io.base, time); return sp_str(buf, 24); } -// @refactor sp_str_t sp_str_reduce(sp_mem_t mem, sp_str_t* strings, u32 num_strings, void* user_data, sp_str_reduce_fn_t fn) { sp_io_dyn_mem_writer_t io = sp_zero; diff --git a/sp/sp_macho.h b/sp/sp_macho.h index 1034bea..4f5f3f8 100644 --- a/sp/sp_macho.h +++ b/sp/sp_macho.h @@ -357,7 +357,7 @@ sp_err_t sp_macho_write(sp_macho_t* m, sp_io_writer_t* out) { sp_err_t sp_macho_write_to_file(sp_macho_t* m, sp_str_t path) { sp_io_file_writer_t f = sp_zero; - sp_try(sp_io_file_writer_from_path(&f, path, SP_IO_WRITE_MODE_OVERWRITE)); + sp_try(sp_io_file_writer_from_path(&f, path)); sp_err_t err = sp_macho_write(m, &f.base); sp_io_file_writer_close(&f); return err; diff --git a/sp/sp_math.h b/sp/sp_math.h index 91349a2..fe599e5 100644 --- a/sp/sp_math.h +++ b/sp/sp_math.h @@ -217,7 +217,6 @@ SP_API sp_color_t sp_color_rgb_to_hsv(sp_color_t color); SP_API sp_color_t sp_color_hsv_to_rgb(sp_color_t color); SP_API f32 sp_inv_sqrtf(f32 value); SP_API f32 sp_lerp(f32 a, f32 t, f32 b); -SP_API f32 sp_clamp(f32 low, f32 value, f32 high); SP_API sp_vec2_t sp_vec2(f32 x, f32 y); SP_API sp_vec3_t sp_vec3(f32 x, f32 y, f32 z); SP_API sp_vec4_t sp_vec4(f32 x, f32 y, f32 z, f32 w); @@ -405,17 +404,6 @@ f32 sp_lerp(f32 a, f32 t, f32 b) { return (1.0f - t) * a + t * b; } -f32 sp_clamp(f32 low, f32 value, f32 high) { - f32 result = value; - if (result < low) { - result = low; - } - if (result > high) { - result = high; - } - return result; -} - sp_vec2_t sp_vec2(f32 x, f32 y) { return (sp_vec2_t){ .x = x, .y = y }; } diff --git a/sp/sp_msvc.h b/sp/sp_msvc.h index 6a6e3bf..4df5833 100644 --- a/sp/sp_msvc.h +++ b/sp/sp_msvc.h @@ -183,7 +183,7 @@ sp_msvc_err_t sp_msvc_find_sdks(sp_msvc_t* msvc, sp_msvc_arch_t arch) { sp_str_t lib_dir = sp_fs_join_path(mem, sdk_root, sp_str_lit("Lib")); sp_str_t arch_str = sp_msvc_arch_str(arch); - for (sp_fs_it_t it = sp_fs_it_new(lib_dir); sp_fs_it_valid(&it); sp_fs_it_next(&it)) { + for (sp_fs_it_t it = sp_fs_it_new(mem, lib_dir); sp_fs_it_valid(&it); sp_fs_it_next(&it)) { if (it.entry.kind != SP_FS_KIND_DIR) continue; sp_str_t name = it.entry.name; if (!sp_str_starts_with(name, sp_str_lit("10."))) continue; @@ -239,7 +239,7 @@ sp_msvc_err_t sp_msvc_find_installations(sp_msvc_t* msvc, sp_msvc_arch_t arch) { sp_str_t arch_str = sp_msvc_arch_str(arch); - for (sp_fs_it_t it = sp_fs_it_new(instances_dir); sp_fs_it_valid(&it); sp_fs_it_next(&it)) { + for (sp_fs_it_t it = sp_fs_it_new(mem, instances_dir); sp_fs_it_valid(&it); sp_fs_it_next(&it)) { if (it.entry.kind != SP_FS_KIND_DIR) continue; sp_str_t state_path = sp_fs_join_path(mem, it.entry.path, sp_str_lit("state.json")); diff --git a/sp/sp_prompt.h b/sp/sp_prompt.h index 03b6c8b..975978c 100644 --- a/sp/sp_prompt.h +++ b/sp/sp_prompt.h @@ -751,6 +751,7 @@ void sp_prompt_set_state(sp_prompt_ctx_t* ctx, sp_prompt_state_t sta // @custom void sp_prompt_line(sp_prompt_ctx_t* ctx, sp_str_t text); +void sp_prompt_line_fmt(sp_prompt_ctx_t* ctx, const c8* fmt, ...); void sp_prompt_render_line(sp_prompt_ctx_t* ctx, sp_str_t text, sp_prompt_style_t style); u32 sp_prompt_text_width(sp_str_t text); sp_str_t sp_prompt_repeat(sp_prompt_ctx_t* ctx, u32 codepoint, u32 count); @@ -1029,7 +1030,7 @@ void sp_prompt_ctx_init(sp_prompt_ctx_t* ctx, sp_mem_t mem, u32 cols, u32 rows) // times every single frame. // // Empirically, you get pretty bad tearing on Windows without buffering. - sp_io_file_writer_t* fw = sp_mem_arena_alloc_type(ctx->arena, sp_io_file_writer_t); + sp_io_stream_writer_t* fw = sp_mem_arena_alloc_type(ctx->arena, sp_io_stream_writer_t); sp_io_get_std_out(fw); ctx->writer = &fw->base; @@ -1069,8 +1070,8 @@ bool sp_prompt_get_bool(sp_prompt_ctx_t* ctx) { } const c8* sp_prompt_join_selection(sp_prompt_ctx_t* ctx, sp_prompt_select_option_t* options, u32 num_options) { - sp_io_dyn_mem_writer_t builder = sp_zero; - sp_io_dyn_mem_writer_init(ctx->mem, &builder); + sp_io_dyn_mem_writer_t w = sp_zero; + sp_io_dyn_mem_writer_init(ctx->mem, &w); bool first = true; sp_for(it, num_options) { if (!options[it].selected) { @@ -1078,13 +1079,14 @@ const c8* sp_prompt_join_selection(sp_prompt_ctx_t* ctx, sp_prompt_select_option } if (!first) { - sp_io_write_str(&builder.base, sp_str_lit(", "), SP_NULLPTR); + sp_io_write_str(&w.base, sp_str_lit(", "), SP_NULLPTR); } first = false; - sp_io_write_str(&builder.base, sp_str_view(options[it].label), SP_NULLPTR); + sp_io_write_str(&w.base, sp_str_view(options[it].label), SP_NULLPTR); } - return sp_str_to_cstr(ctx->mem, sp_io_dyn_mem_writer_as_str(&builder)); + sp_io_write_c8(&w.base, '\0'); + return sp_io_dyn_mem_writer_as_cstr(&w); } // Instead of just writing to the file descriptor that the main loop waits on for events, @@ -1219,6 +1221,20 @@ void sp_prompt_line(sp_prompt_ctx_t* ctx, sp_str_t text) { ctx->cursor_row++; } +void sp_prompt_line_fmt(sp_prompt_ctx_t* ctx, const c8* fmt, ...) { + sp_mem_arena_marker_t s = sp_mem_begin_scratch(); + + va_list args; + va_start(args, fmt); + sp_io_dyn_mem_writer_t io = sp_zero; + sp_io_dyn_mem_writer_init(s.mem, &io); + sp_err_t err = sp_fmt_io_v(&io.base, sp_cstr_as_str(fmt), args); + va_end(args); + + sp_prompt_line(ctx, sp_io_dyn_mem_writer_as_str(&io)); + sp_mem_end_scratch(s); +} + void sp_prompt_dispatch_event(sp_prompt_ctx_t* ctx, sp_prompt_widget_t widget, sp_prompt_event_t event) { widget.on_event(ctx, event); } @@ -1588,16 +1604,12 @@ static void sp_prompt_static_update(sp_prompt_ctx_t* ctx, sp_prompt_event_t even static void sp_prompt_intro_render(sp_prompt_ctx_t* ctx) { sp_prompt_intro_t* prompt = (sp_prompt_intro_t*)ctx->user_data; - sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_prompt_line(ctx, sp_fmt(s.mem, "┌ {}", sp_fmt_str(prompt->text)).value); - sp_mem_end_scratch(s); + sp_prompt_line_fmt(ctx, "┌ {}", sp_fmt_str(prompt->text)); } static void sp_prompt_outro_render(sp_prompt_ctx_t* ctx) { sp_prompt_outro_t* prompt = (sp_prompt_outro_t*)ctx->user_data; - sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_prompt_line(ctx, sp_fmt(s.mem, "â”” {}", sp_fmt_str(prompt->text)).value); - sp_mem_end_scratch(s); + sp_prompt_line_fmt(ctx, "â”” {}", sp_fmt_str(prompt->text)); } static void sp_prompt_note_render(sp_prompt_ctx_t* ctx) { @@ -1630,18 +1642,18 @@ static void sp_prompt_note_render(sp_prompt_ctx_t* ctx) { sp_str_t spacer = sp_prompt_repeat(ctx, ' ', width); sp_str_t bottom = sp_prompt_repeat(ctx, 0x2500, width + 2); - sp_prompt_line(ctx, sp_fmt(s.mem, "â—‡ {} {}â•®", sp_fmt_str(prompt->title), sp_fmt_str(top_tail)).value); - sp_prompt_line(ctx, sp_fmt(s.mem, "│ {}│", sp_fmt_str(spacer)).value); + sp_prompt_line_fmt(ctx, "â—‡ {} {}â•®", sp_fmt_str(prompt->title), sp_fmt_str(top_tail)); + sp_prompt_line_fmt(ctx, "│ {}│", sp_fmt_str(spacer)); sp_da_for(message_lines, it) { sp_str_t line = message_lines[it]; u32 line_width = sp_prompt_text_width(line); sp_str_t pad = sp_prompt_repeat(ctx, ' ', width - line_width); - sp_prompt_line(ctx, sp_fmt(s.mem, "│ {}{}│", sp_fmt_str(line), sp_fmt_str(pad)).value); + sp_prompt_line_fmt(ctx, "│ {}{}│", sp_fmt_str(line), sp_fmt_str(pad)); } - sp_prompt_line(ctx, sp_fmt(s.mem, "│ {}│", sp_fmt_str(spacer)).value); - sp_prompt_line(ctx, sp_fmt(s.mem, "├{}╯", sp_fmt_str(bottom)).value); + sp_prompt_line_fmt(ctx, "│ {}│", sp_fmt_str(spacer)); + sp_prompt_line_fmt(ctx, "├{}╯", sp_fmt_str(bottom)); sp_mem_end_scratch(s); } @@ -3148,10 +3160,6 @@ static void sp_prompt_kr_derive_palette(u8 r, u8 g, u8 b, u8 trail[SP_PROMPT_KR_ return; } - f32 base_r = r; - f32 base_g = g; - f32 base_b = b; - sp_for(it, SP_PROMPT_KR_TRAIL_LEN) { f32 alpha; f32 brightness; @@ -3173,21 +3181,21 @@ static void sp_prompt_kr_derive_palette(u8 r, u8 g, u8 b, u8 trail[SP_PROMPT_KR_ brightness = SP_PROMPT_KR_LEAD_BRIGHTNESS; } - f32 r = base_r * brightness; - f32 g = base_g * brightness; - f32 b = base_b * brightness; - if (r > 255.0f) r = 255.0f; - if (g > 255.0f) g = 255.0f; - if (b > 255.0f) b = 255.0f; + f32 r1 = r * brightness; + f32 g1 = g * brightness; + f32 b1 = b * brightness; + if (r1 > 255.0f) r1 = 255.0f; + if (g1 > 255.0f) g1 = 255.0f; + if (b1 > 255.0f) b1 = 255.0f; - trail[it][0] = sp_prompt_kr_clamp_u8(r * alpha); - trail[it][1] = sp_prompt_kr_clamp_u8(g * alpha); - trail[it][2] = sp_prompt_kr_clamp_u8(b * alpha); + trail[it][0] = sp_prompt_kr_clamp_u8(r1 * alpha); + trail[it][1] = sp_prompt_kr_clamp_u8(g1 * alpha); + trail[it][2] = sp_prompt_kr_clamp_u8(b1 * alpha); } - inactive[0] = sp_prompt_kr_clamp_u8(base_r * SP_PROMPT_KR_INACTIVE_ALPHA); - inactive[1] = sp_prompt_kr_clamp_u8(base_g * SP_PROMPT_KR_INACTIVE_ALPHA); - inactive[2] = sp_prompt_kr_clamp_u8(base_b * SP_PROMPT_KR_INACTIVE_ALPHA); + inactive[0] = sp_prompt_kr_clamp_u8(r * SP_PROMPT_KR_INACTIVE_ALPHA); + inactive[1] = sp_prompt_kr_clamp_u8(g * SP_PROMPT_KR_INACTIVE_ALPHA); + inactive[2] = sp_prompt_kr_clamp_u8(b * SP_PROMPT_KR_INACTIVE_ALPHA); } static void sp_prompt_knight_rider_event(sp_prompt_ctx_t* ctx, sp_prompt_event_t event) { diff --git a/spn.lock b/spn.lock deleted file mode 100644 index a879ae1..0000000 --- a/spn.lock +++ /dev/null @@ -1,21 +0,0 @@ -[spn] -version = "1.0.0" -commit = "00c0fa98" - -[package] -system_deps = ["m"] - -[[dep]] -name = "argparse" -version = "1.2.0" -commit = "f71ed6c7b11c" -kind = "index" -visibility = "public" - -[[dep]] -name = "utest" -version = "1.0.0" -commit = "fb622dc480a5" -kind = "index" -visibility = "public" - diff --git a/spn.toml b/spn.toml deleted file mode 100644 index 055eb24..0000000 --- a/spn.toml +++ /dev/null @@ -1,146 +0,0 @@ -[package] -name = "sp" -include = [".", "include", "test/tools", "test/tools/process", "sp"] -define = ["SP_IMPLEMENTATION", "SP_TEST_IMPLEMENTATION"] -version = "1.9.0" -system_deps = ["m"] - -[deps.package] -utest = "1.0.0" -argparse = "1.2.0" - -[[profile]] -name = "debug" -cc = "clang" - -[[profile]] -name = "macos" -cc = "clang" - -[[profile]] -name = "musl-static" -cc = "musl-gcc" -libc = "musl" -linkage = "static" - -[[profile]] -name = "sanity" -mode = "debug" -cc = "tcc" - -[[profile]] -name = "bench" -mode = "release" -cc = "clang" - -[[test]] -name = "app" -source = ["test/app.c"] - -[[test]] -name = "asset" -source = ["test/asset.c"] - -[[test]] -name = "context" -source = ["test/context.c"] - -[[test]] -name = "cv" -source = ["test/cv.c"] - -[[test]] -name = "elf" -source = ["test/elf.c"] - -[[test]] -name = "fmon" -source = ["test/fmon.c"] - -[[test]] -name = "fs" -source = ["test/fs.c"] -include = [".", "include", "test/tools", "test/tools/process", "sp", "test/fs"] - -[[test]] -name = "glob" -source = ["test/glob.c"] -define = ["SP_GLOB_IMPLEMENTATION"] - -[[test]] -name = "ht" -source = ["test/ht.c"] - -[[test]] -name = "io" -source = ["test/io.c"] - -[[test]] -name = "linkage" -source = ["test/linkage.c"] - -[[test]] -name = "ps" -source = ["test/ps.c"] - -[[test]] -name = "rb" -source = ["test/rb.c"] - -[[test]] -name = "str" -source = ["test/str.c"] - -[[test]] -name = "time" -source = ["test/time.c"] - -[[test]] -name = "mem" -source = ["test/mem.c"] - - -[[test]] -name = "amalg" -source = ["test/amalg.c"] - -[[test]] -name = "prompt" -source = ["test/prompt.c"] - - -[[bin]] -name = "process" -source = ["test/tools/process/process.c"] - - - -[[bin]] -name = "extract_fns" -source = ["tools/extract_fns.c"] - -[[bin]] -name = "sleep" -source = ["tools/sleep.c"] - - - -[[bin]] -name = "bench-ht" -source = ["test/bench/ht.c"] -include = ["doc"] - -[[bin]] -name = "bench-glob" -source = ["test/bench/glob.c"] -include = ["test"] -define = ["SP_GLOB_IMPLEMENTATION"] - -[[bin]] -name = "palette" -source = ["example/cli/palette.c"] -include = ["example/palette"] - -[[bin]] -name = "demo-prompt" -source = ["example/cli/prompt.c"] diff --git a/test/amalg.c b/test/amalg.c index a43564b..0ec2273 100644 --- a/test/amalg.c +++ b/test/amalg.c @@ -4,8 +4,8 @@ #include "app.c" #include "asset.c" #include "array.c" +#include "etc.c" #include "cv.c" -#include "elf.c" #include "env.c" #include "fmon.c" #include "format.c" @@ -13,7 +13,6 @@ #include "glob.c" #include "ht.c" #include "io.c" -#include "linkage.c" #include "math.c" #include "mem.c" #include "prompt.c" diff --git a/test/bench/ht.c b/test/bench/ht.c index 62ae2e6..5daca03 100644 --- a/test/bench/ht.c +++ b/test/bench/ht.c @@ -144,8 +144,7 @@ static void run_benchmarks(bench_t* benches, u32 num_benches) { sp_io_writer_t sb = sp_zero; sp_io_dyn_mem_writer_init(sp_mem_get_scratch(), &sb); - sp_mem_arena_marker_t fmt_s = sp_mem_begin_scratch_for(sp_mem_get_scratch()); - sp_fmt_io(&sb, fmt_s.mem, "{}{} {} {} {} {}{}\n", + sp_fmt_io(&sb, "{}{} {} {} {} {}{}\n", sp_fmt_cstr(SP_ANSI_FG_BRIGHT_BLACK), sp_fmt_str(sp_str_pad(sp_mem_get_scratch(), sp_str_lit("test"), max_name)), sp_fmt_str(sp_str_pad(sp_mem_get_scratch(), sp_str_lit("n"), max_n_width)), @@ -167,7 +166,7 @@ static void run_benchmarks(bench_t* benches, u32 num_benches) { sp_str_t ratio_color = color_for_ratio(ratio); sp_str_t ratio_str = sp_str_pad(sp_mem_get_scratch(), sp_fmt(sp_mem_get_scratch(), "{}x", sp_fmt_float(ratio)).value, ratio_width); - sp_fmt_io(&sb, fmt_s.mem, "{} {} {} {} {}{}{}\n", + sp_fmt_io(&sb, "{} {} {} {} {}{}{}\n", sp_fmt_str(sp_str_pad(sp_mem_get_scratch(), r->name, max_name)), sp_fmt_str(sp_str_pad(sp_mem_get_scratch(), n_str, max_n_width)), sp_fmt_str(sp_time_str), @@ -179,7 +178,6 @@ static void run_benchmarks(bench_t* benches, u32 num_benches) { sp_str_t output = sp_io_dyn_mem_writer_as_str(&sb); sp_os_print(output); - sp_mem_end_scratch(fmt_s); } static void kernel_deinit(bench_params_t p, bench_data_t* data) { diff --git a/test/core.c b/test/core.c deleted file mode 100644 index 220e098..0000000 --- a/test/core.c +++ /dev/null @@ -1,44 +0,0 @@ -#define SP_APP -#include "sp.h" -#include "test.h" - -#include "utest.h" - - -SP_TEST_MAIN() - - -///////////////////// -// PARSER TESTS // -///////////////////// - - - -#define SP_TEST_ENUM(X) \ - X(SP_ENUM_FOO) \ - X(SP_ENUM_BAR) \ - X(SP_ENUM_BAZ) \ - X(SP_ENUM_QUX) - -typedef enum { - SP_TEST_ENUM(SP_X_ENUM_DEFINE) -} sp_test_enum_t; - -sp_str_t sp_test_enum_to_str(sp_test_enum_t e) { - switch (e) { - SP_TEST_ENUM(SP_X_ENUM_CASE_TO_STRING) - default: SP_UNREACHABLE_RETURN(sp_str_lit("")); - } -} - -const c8* sp_test_enum_to_cstr(sp_test_enum_t e) { - switch (e) { - SP_TEST_ENUM(SP_X_ENUM_CASE_TO_CSTR) - default: SP_UNREACHABLE_RETURN(""); - } -} - -UTEST(sp_enum_macros, name_generation) { - ASSERT_STREQ(sp_test_enum_to_cstr(SP_ENUM_BAZ), "SP_ENUM_BAZ"); - SP_EXPECT_STR_EQ_CSTR(sp_test_enum_to_str(SP_ENUM_QUX), "SP_ENUM_QUX"); -} diff --git a/test/elf.c b/test/elf.c index ed495ee..cecedee 100644 --- a/test/elf.c +++ b/test/elf.c @@ -661,7 +661,7 @@ UTEST_F(elf, oracle_cc_read) { sp_str_t c_path = sp_test_file_path(&ut.file_manager, sp_str_lit("minimal.c")); sp_io_file_writer_t c_file = sp_zero; - sp_io_file_writer_from_path(&c_file, c_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&c_file, c_path); sp_io_write_str(&c_file.base, c_src, SP_NULLPTR); sp_io_file_writer_close(&c_file); @@ -725,7 +725,7 @@ UTEST_F(elf, oracle_cc_link) { sp_str_t c_path = sp_test_file_path(&ut.file_manager, sp_str_lit("integration.c")); sp_io_file_writer_t f = sp_zero; - sp_io_file_writer_from_path(&f, c_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&f, c_path); sp_io_write_str(&f.base, c_src, SP_NULLPTR); sp_io_file_writer_close(&f); diff --git a/test/env.c b/test/env.c index 5a98ccd..f45b07f 100644 --- a/test/env.c +++ b/test/env.c @@ -6,18 +6,18 @@ SP_TEST_MAIN() -struct sp_env { +struct env { sp_mem_t mem; }; -UTEST_F_SETUP(sp_env) { +UTEST_F_SETUP(env) { ut.mem = sp_mem_os_new(); } -UTEST_F_TEARDOWN(sp_env) { +UTEST_F_TEARDOWN(env) { } -UTEST_F(sp_env, init_empty) { +UTEST_F(env, init_empty) { sp_env_t env; sp_env_init(ut.mem, &env); EXPECT_EQ(sp_env_count(&env), (u32)0); @@ -25,7 +25,7 @@ UTEST_F(sp_env, init_empty) { sp_env_destroy(&env); } -UTEST_F(sp_env, insert_and_get) { +UTEST_F(env, insert_and_get) { sp_env_t env; sp_env_init(ut.mem, &env); @@ -36,7 +36,7 @@ UTEST_F(sp_env, insert_and_get) { sp_env_destroy(&env); } -UTEST_F(sp_env, insert_overwrites) { +UTEST_F(env, insert_overwrites) { sp_env_t env; sp_env_init(ut.mem, &env); @@ -49,7 +49,7 @@ UTEST_F(sp_env, insert_overwrites) { sp_env_destroy(&env); } -UTEST_F(sp_env, get_missing_returns_empty) { +UTEST_F(env, get_missing_returns_empty) { sp_env_t env; sp_env_init(ut.mem, &env); @@ -59,7 +59,7 @@ UTEST_F(sp_env, get_missing_returns_empty) { sp_env_destroy(&env); } -UTEST_F(sp_env, contains) { +UTEST_F(env, contains) { sp_env_t env; sp_env_init(ut.mem, &env); @@ -70,7 +70,7 @@ UTEST_F(sp_env, contains) { sp_env_destroy(&env); } -UTEST_F(sp_env, erase) { +UTEST_F(env, erase) { sp_env_t env; sp_env_init(ut.mem, &env); @@ -86,7 +86,7 @@ UTEST_F(sp_env, erase) { sp_env_destroy(&env); } -UTEST_F(sp_env, erase_nonexistent) { +UTEST_F(env, erase_nonexistent) { sp_env_t env; sp_env_init(ut.mem, &env); @@ -97,7 +97,7 @@ UTEST_F(sp_env, erase_nonexistent) { sp_env_destroy(&env); } -UTEST_F(sp_env, multiple_entries) { +UTEST_F(env, multiple_entries) { sp_env_t env; sp_env_init(ut.mem, &env); @@ -113,7 +113,7 @@ UTEST_F(sp_env, multiple_entries) { sp_env_destroy(&env); } -UTEST_F(sp_env, copy_is_independent) { +UTEST_F(env, copy_is_independent) { sp_env_t env; sp_env_init(ut.mem, &env); sp_env_insert(&env, sp_str_lit("K"), sp_str_lit("V")); @@ -130,7 +130,7 @@ UTEST_F(sp_env, copy_is_independent) { sp_env_destroy(©); } -UTEST_F(sp_env, empty_value) { +UTEST_F(env, empty_value) { sp_env_t env; sp_env_init(ut.mem, &env); @@ -141,7 +141,7 @@ UTEST_F(sp_env, empty_value) { sp_env_destroy(&env); } -UTEST_F(sp_env, value_with_equals) { +UTEST_F(env, value_with_equals) { sp_env_t env; sp_env_init(ut.mem, &env); @@ -151,9 +151,8 @@ UTEST_F(sp_env, value_with_equals) { sp_env_destroy(&env); } -UTEST_F(sp_env, capture_has_path) { +UTEST_F(env, capture_has_path) { SKIP_ON_WASM() - SKIP_ON_FREESTANDING() sp_env_t env = sp_env_capture(ut.mem); EXPECT_TRUE(sp_env_count(&env) > 0); @@ -171,8 +170,7 @@ UTEST_F(sp_env, capture_has_path) { sp_env_destroy(&env); } -UTEST_F(sp_env, capture_is_snapshot) { - SKIP_ON_FREESTANDING() +UTEST_F(env, capture_is_snapshot) { sp_env_t env = sp_env_capture(ut.mem); u32 count = sp_env_count(&env); @@ -186,7 +184,7 @@ UTEST_F(sp_env, capture_is_snapshot) { sp_env_destroy(&env2); } -UTEST_F(sp_env, destroy_then_reinit) { +UTEST_F(env, destroy_then_reinit) { sp_env_t env; sp_env_init(ut.mem, &env); sp_env_insert(&env, sp_str_lit("A"), sp_str_lit("1")); @@ -200,7 +198,7 @@ UTEST_F(sp_env, destroy_then_reinit) { } #if defined(SP_POSIX) -UTEST_F(sp_env, to_posix_envp) { +UTEST_F(env, to_posix_envp) { sp_env_t env; sp_env_init(ut.mem, &env); sp_env_insert(&env, sp_str_lit("AA"), sp_str_lit("11")); @@ -225,7 +223,7 @@ UTEST_F(sp_env, to_posix_envp) { sp_env_destroy(&env); } -UTEST_F(sp_env, to_posix_envp_empty) { +UTEST_F(env, to_posix_envp_empty) { sp_env_t env; sp_env_init(ut.mem, &env); @@ -236,23 +234,20 @@ UTEST_F(sp_env, to_posix_envp_empty) { } #endif -UTEST(sp_os_env, get_path) { +UTEST_F(env, get_path) { SKIP_ON_WASM() - SKIP_ON_FREESTANDING() sp_str_t path = sp_os_env_get(sp_str_lit("PATH")); EXPECT_TRUE(sp_str_valid(path)); EXPECT_TRUE(path.len > 0); } -UTEST(sp_os_env, get_missing) { - SKIP_ON_FREESTANDING() +UTEST_F(env, get_missing) { sp_str_t val = sp_os_env_get(sp_str_lit("SP_DEFINITELY_NOT_SET_12345")); EXPECT_TRUE(sp_str_empty(val)); } -UTEST(sp_os_env, iterate) { +UTEST_F(env, iterate) { SKIP_ON_WASM() - SKIP_ON_FREESTANDING() sp_os_env_it_t it = sp_os_env_it_begin(); #if defined(SP_WIN32) @@ -276,8 +271,7 @@ UTEST(sp_os_env, iterate) { EXPECT_TRUE(found_path); } -UTEST(sp_os_env, iterate_matches_capture) { - SKIP_ON_FREESTANDING() +UTEST_F(env, iterate_matches_capture) { sp_env_t captured = sp_env_capture(sp_mem_os_new()); sp_os_env_it_t it = sp_os_env_it_begin(); diff --git a/test/etc.c b/test/etc.c new file mode 100644 index 0000000..43cacba --- /dev/null +++ b/test/etc.c @@ -0,0 +1,77 @@ +#define SP_APP +#include "sp.h" +#include "test.h" + +#include "utest.h" + + +SP_TEST_MAIN() + + +///////////////////// +// PARSER TESTS // +///////////////////// + + + +#define SP_TEST_ENUM(X) \ + X(SP_ENUM_FOO) \ + X(SP_ENUM_BAR) \ + X(SP_ENUM_BAZ) \ + X(SP_ENUM_QUX) + +typedef enum { + SP_TEST_ENUM(SP_X_ENUM_DEFINE) +} sp_test_enum_t; + +sp_str_t sp_test_enum_to_str(sp_test_enum_t e) { + switch (e) { + SP_TEST_ENUM(SP_X_ENUM_CASE_TO_STRING) + default: SP_UNREACHABLE_RETURN(sp_str_lit("")); + } +} + +const c8* sp_test_enum_to_cstr(sp_test_enum_t e) { + switch (e) { + SP_TEST_ENUM(SP_X_ENUM_CASE_TO_CSTR) + default: SP_UNREACHABLE_RETURN(""); + } +} + +UTEST(sp_enum_macros, name_generation) { + ASSERT_STREQ(sp_test_enum_to_cstr(SP_ENUM_BAZ), "SP_ENUM_BAZ"); + SP_EXPECT_STR_EQ_CSTR(sp_test_enum_to_str(SP_ENUM_QUX), "SP_ENUM_QUX"); +} + +UTEST(sp_minmax, basic) { + ASSERT_EQ(sp_max(1, 2), 2); + ASSERT_EQ(sp_max(2, 1), 2); + ASSERT_EQ(sp_max(3, 3), 3); + ASSERT_EQ(sp_min(1, 2), 1); + ASSERT_EQ(sp_min(2, 1), 1); + ASSERT_EQ(sp_min(3, 3), 3); + ASSERT_EQ(sp_max(-5, -2), -2); + ASSERT_EQ(sp_min(-5, -2), -5); +} + +UTEST(sp_minmax, precedence) { + ASSERT_EQ(sp_max(1, 2) * 3, 6); + ASSERT_EQ(1 + sp_min(2, 3), 3); + ASSERT_EQ(-sp_max(1, 2), -2); + ASSERT_EQ(sp_max(1, 2) + sp_min(3, 4), 5); +} + +UTEST(sp_clamp, basic) { + ASSERT_EQ(sp_clamp(5, 0, 10), 5); + ASSERT_EQ(sp_clamp(-1, 0, 10), 0); + ASSERT_EQ(sp_clamp(11, 0, 10), 10); + ASSERT_EQ(sp_clamp(0, 0, 10), 0); + ASSERT_EQ(sp_clamp(10, 0, 10), 10); + ASSERT_EQ(sp_clamp(-5, -10, -1), -5); + ASSERT_EQ(sp_clamp(-20, -10, -1), -10); +} + +UTEST(sp_clamp, precedence) { + ASSERT_EQ(sp_clamp(5, 0, 10) * 2, 10); + ASSERT_EQ(1 + sp_clamp(11, 0, 10), 11); +} diff --git a/test/fmon.c b/test/fmon.c index 2d4c941..0030d20 100644 --- a/test/fmon.c +++ b/test/fmon.c @@ -134,7 +134,7 @@ UTEST_F(sp_test_file_monitor, detects_file_modification) { #if defined(SP_MACOS) sp_io_file_writer_t s = sp_zero; - sp_io_file_writer_from_path(&s, test_file, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&s, test_file); sp_io_write_str(&s.base, sp_str_lit("modified content"), SP_NULLPTR); sp_io_file_writer_close(&s); @@ -280,7 +280,7 @@ UTEST_F(sp_test_file_monitor, event_filtering) { // Modify the file — should NOT fire since we only watch REMOVED { sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, test_file, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&w, test_file); sp_io_write_str(&w.base, sp_str_lit("modified"), SP_NULLPTR); sp_io_file_writer_close(&w); } @@ -390,7 +390,7 @@ UTEST_F(sp_test_file_monitor, rename_file) { ut.change_detected = false; fmon_history.count = 0; - sp_sys_rename_s(old_file, new_file); + sp_sys_rename_s(sp_fs_open_cwd(), old_file, sp_fs_open_cwd(), new_file); sp_for_n(FMON_POLL_ITERATIONS) { sp_os_sleep_ms(SP_TEST_POLL_SLEEP_MS); diff --git a/test/format.c b/test/format.c index 6b192e3..f721c96 100644 --- a/test/format.c +++ b/test/format.c @@ -7,6 +7,9 @@ SP_TEST_MAIN() +UTEST_EMPTY_FIXTURE(format) +UTEST_EMPTY_FIXTURE(format_specifier) + static sp_ht(sp_str_t, sp_fmt_directive_t) sp_fmt_directives = SP_NULLPTR; void sp_fmt_directive_reset() { @@ -37,13 +40,13 @@ void run_case(s32* utest_result, format_test_t test) { EXPECT_EQ(specifier.precision.some, test.expected.precision.some); EXPECT_EQ(specifier.precision.value, test.expected.precision.value); EXPECT_EQ(specifier.width, test.expected.width); - EXPECT_EQ(specifier.directive_count, test.expected.directive_count); - EXPECT_TRUE(specifier.fill_dynamic == test.expected.fill_dynamic); - EXPECT_TRUE(specifier.width_dynamic == test.expected.width_dynamic); - EXPECT_TRUE(specifier.precision_dynamic == test.expected.precision_dynamic); + EXPECT_EQ(specifier.directive.num, test.expected.directive.num); + EXPECT_TRUE(specifier.dynamic.fill == test.expected.dynamic.fill); + EXPECT_TRUE(specifier.dynamic.width == test.expected.dynamic.width); + EXPECT_TRUE(specifier.dynamic.precision == test.expected.dynamic.precision); } -UTEST(sp_fmt_parse, smoke) { +UTEST_F(format_specifier, smoke) { run_case(utest_result, (format_test_t) { .str = "{:*^9}", .expected = { @@ -54,63 +57,63 @@ UTEST(sp_fmt_parse, smoke) { }); } -UTEST(sp_fmt_parse, empty) { +UTEST_F(format_specifier, empty) { run_case(utest_result, (format_test_t) { .str = "{}", .expected = { .align = SP_FMT_ALIGN_NONE }, }); } -UTEST(sp_fmt_parse, empty_with_colon) { +UTEST_F(format_specifier, empty_with_colon) { run_case(utest_result, (format_test_t) { .str = "{:}", .expected = { .align = SP_FMT_ALIGN_NONE }, }); } -UTEST(sp_fmt_parse, width_only) { +UTEST_F(format_specifier, width_only) { run_case(utest_result, (format_test_t) { .str = "{:42}", .expected = { .width = 42 }, }); } -UTEST(sp_fmt_parse, precision_only) { +UTEST_F(format_specifier, precision_only) { run_case(utest_result, (format_test_t) { .str = "{:.5}", .expected = { .precision = sp_opt_some(5) }, }); } -UTEST(sp_fmt_parse, width_and_precision) { +UTEST_F(format_specifier, width_and_precision) { run_case(utest_result, (format_test_t) { .str = "{:10.3}", .expected = { .width = 10, .precision = sp_opt_some(3) }, }); } -UTEST(sp_fmt_parse, bare_align_left) { +UTEST_F(format_specifier, bare_align_left) { run_case(utest_result, (format_test_t) { .str = "{:<7}", .expected = { .width = 7, .align = SP_FMT_ALIGN_LEFT }, }); } -UTEST(sp_fmt_parse, bare_align_right) { +UTEST_F(format_specifier, bare_align_right) { run_case(utest_result, (format_test_t) { .str = "{:>4}", .expected = { .width = 4, .align = SP_FMT_ALIGN_RIGHT }, }); } -UTEST(sp_fmt_parse, fill_with_left_align) { +UTEST_F(format_specifier, fill_with_left_align) { run_case(utest_result, (format_test_t) { .str = "{:-<8}", .expected = { .width = 8, .align = SP_FMT_ALIGN_LEFT, .fill = '-' }, }); } -UTEST(sp_fmt_parse, everything) { +UTEST_F(format_specifier, everything) { run_case(utest_result, (format_test_t) { .str = "{:*^12.4}", .expected = { @@ -122,109 +125,106 @@ UTEST(sp_fmt_parse, everything) { }); } -UTEST(sp_fmt_parse, zero_leading_width) { +UTEST_F(format_specifier, zero_leading_width) { run_case(utest_result, (format_test_t) { .str = "{:05}", .expected = { .width = 5 }, }); } -UTEST(sp_fmt_parse, dynamic_width) { +UTEST_F(format_specifier, dynamic_width) { run_case(utest_result, (format_test_t) { .str = "{:$}", - .expected = { .width_dynamic = 1 }, + .expected = { .dynamic = { .width = 1 } }, }); } -UTEST(sp_fmt_parse, dynamic_precision) { +UTEST_F(format_specifier, dynamic_precision) { run_case(utest_result, (format_test_t) { .str = "{:.$}", - .expected = { .precision_dynamic = 1 }, + .expected = { .dynamic = { .precision = 1 } }, }); } -UTEST(sp_fmt_parse, dynamic_fill_center) { +UTEST_F(format_specifier, dynamic_fill_center) { run_case(utest_result, (format_test_t) { .str = "{:$^9}", .expected = { .width = 9, .align = SP_FMT_ALIGN_CENTER, - .fill_dynamic = 1, + .dynamic = { .fill = 1 }, }, }); } -UTEST(sp_fmt_parse, dynamic_fill_and_width) { +UTEST_F(format_specifier, dynamic_fill_and_width) { run_case(utest_result, (format_test_t) { .str = "{:$^$}", .expected = { .align = SP_FMT_ALIGN_CENTER, - .fill_dynamic = 1, - .width_dynamic = 1, + .dynamic = { .fill = 1, .width = 1 }, }, }); } -UTEST(sp_fmt_parse, dynamic_everything) { +UTEST_F(format_specifier, dynamic_everything) { run_case(utest_result, (format_test_t) { .str = "{:$<$.$}", .expected = { .align = SP_FMT_ALIGN_LEFT, - .fill_dynamic = 1, - .width_dynamic = 1, - .precision_dynamic = 1, + .dynamic = { .fill = 1, .width = 1, .precision = 1 }, }, }); } -UTEST(sp_fmt_parse, dynamic_width_with_literal_precision) { +UTEST_F(format_specifier, dynamic_width_with_literal_precision) { run_case(utest_result, (format_test_t) { .str = "{:$.4}", .expected = { .precision = sp_opt_some(4), - .width_dynamic = 1, + .dynamic = { .width = 1 }, }, }); } -UTEST(sp_fmt_parse, err_dynamic_precision_without_dot) { +UTEST_F(format_specifier, err_dynamic_precision_without_dot) { run_case(utest_result, (format_test_t) { .str = "{:5$}", .err = SP_ERR_FMT_BAD_PLACEHOLDER }); } -UTEST(sp_fmt_parse, err_missing_open_brace) { +UTEST_F(format, err_missing_open_brace) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec(":5}", &spec), SP_ERR_FMT_BAD_PLACEHOLDER); } -UTEST(sp_fmt_parse, err_missing_close_brace) { +UTEST_F(format, err_missing_close_brace) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{:5", &spec), SP_ERR_FMT_UNTERMINATED_PLACEHOLDER); } -UTEST(sp_fmt_parse, err_dot_without_digits) { +UTEST_F(format, err_dot_without_digits) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{:5.}", &spec), SP_ERR_FMT_BAD_PRECISION); } -UTEST(sp_fmt_parse_directive, single) { +UTEST_F(format, single) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{.red}", &spec), SP_OK); - EXPECT_EQ(spec.directive_count, 1); - EXPECT_TRUE(sp_str_equal_cstr(spec.directive_names[0], "red")); + EXPECT_EQ(spec.directive.num, 1); + EXPECT_TRUE(sp_str_equal_cstr(spec.directive.names[0], "red")); } -UTEST(sp_fmt_parse_directive, after_width) { +UTEST_F(format, after_width) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{:10 .red}", &spec), SP_OK); EXPECT_EQ(spec.width, 10); - EXPECT_EQ(spec.directive_count, 1); - EXPECT_TRUE(sp_str_equal_cstr(spec.directive_names[0], "red")); + EXPECT_EQ(spec.directive.num, 1); + EXPECT_TRUE(sp_str_equal_cstr(spec.directive.names[0], "red")); } -UTEST(sp_fmt_parse_directive, full_spec) { +UTEST_F(format, full_spec) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{:*^9.2 .red}", &spec), SP_OK); EXPECT_EQ(spec.width, 9); @@ -232,347 +232,269 @@ UTEST(sp_fmt_parse_directive, full_spec) { EXPECT_EQ(spec.precision.some, SP_OPT_SOME); EXPECT_EQ(spec.align, SP_FMT_ALIGN_CENTER); EXPECT_EQ(spec.fill, '*'); - EXPECT_EQ(spec.directive_count, 1); - EXPECT_TRUE(sp_str_equal_cstr(spec.directive_names[0], "red")); + EXPECT_EQ(spec.directive.num, 1); + EXPECT_TRUE(sp_str_equal_cstr(spec.directive.names[0], "red")); } -UTEST(sp_fmt_parse_directive, multiple) { +UTEST_F(format, multiple) { sp_fmt_spec_t spec = sp_zero; - EXPECT_EQ(parse_spec("{.red .bold .upper}", &spec), SP_OK); - EXPECT_EQ(spec.directive_count, 3); - EXPECT_TRUE(sp_str_equal_cstr(spec.directive_names[0], "red")); - EXPECT_TRUE(sp_str_equal_cstr(spec.directive_names[1], "bold")); - EXPECT_TRUE(sp_str_equal_cstr(spec.directive_names[2], "upper")); + EXPECT_EQ(parse_spec("{.red .bold .italic}", &spec), SP_OK); + EXPECT_EQ(spec.directive.num, 3); + EXPECT_TRUE(sp_str_equal_cstr(spec.directive.names[0], "red")); + EXPECT_TRUE(sp_str_equal_cstr(spec.directive.names[1], "bold")); + EXPECT_TRUE(sp_str_equal_cstr(spec.directive.names[2], "italic")); } -UTEST(sp_fmt_parse_directive, max_count) { +UTEST_F(format, max_count) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{.a .b .c .d .e .f .g .h}", &spec), SP_OK); - EXPECT_EQ(spec.directive_count, SP_FMT_MAX_DIRECTIVES); + EXPECT_EQ(spec.directive.num, SP_FMT_MAX_DIRECTIVES); } -UTEST(sp_fmt_parse_directive, err_too_many) { +UTEST_F(format, err_too_many) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{.a .b .c .d .e .f .g .h .i}", &spec), SP_ERR_FMT_TOO_MANY_DIRECTIVES); } -UTEST(sp_fmt_parse_directive, err_empty_name) { +UTEST_F(format, err_empty_name) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{:10 .}", &spec), SP_ERR_FMT_BAD_DIRECTIVE); } -UTEST(sp_fmt_parse_directive, err_bad_char) { +UTEST_F(format, err_bad_char) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{:10 .red!}", &spec), SP_ERR_FMT_BAD_PLACEHOLDER); } -UTEST(sp_fmt_parse_directive, err_no_space_between_directives) { +UTEST_F(format, err_no_space_between_directives) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{.red.bold}", &spec), SP_ERR_FMT_BAD_PLACEHOLDER); } -UTEST(sp_fmt_parse_directive, err_no_dot) { +UTEST_F(format, err_no_dot) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{:10 red}", &spec), SP_ERR_FMT_BAD_PLACEHOLDER); } -UTEST(sp_fmt_parse_directive, digit_in_tail) { +UTEST_F(format, digit_in_tail) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{.base64}", &spec), SP_OK); - EXPECT_EQ(spec.directive_count, 1); - EXPECT_TRUE(sp_str_equal_cstr(spec.directive_names[0], "base64")); + EXPECT_EQ(spec.directive.num, 1); + EXPECT_TRUE(sp_str_equal_cstr(spec.directive.names[0], "base64")); } -UTEST(sp_fmt_parse_directive, hyphen_in_name) { +UTEST_F(format, hyphen_in_name) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{.utf-8}", &spec), SP_OK); - EXPECT_EQ(spec.directive_count, 1); - EXPECT_TRUE(sp_str_equal_cstr(spec.directive_names[0], "utf-8")); + EXPECT_EQ(spec.directive.num, 1); + EXPECT_TRUE(sp_str_equal_cstr(spec.directive.names[0], "utf-8")); } -UTEST(sp_fmt_parse_directive, underscore_in_name) { +UTEST_F(format, underscore_in_name) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{.http_url}", &spec), SP_OK); - EXPECT_EQ(spec.directive_count, 1); - EXPECT_TRUE(sp_str_equal_cstr(spec.directive_names[0], "http_url")); + EXPECT_EQ(spec.directive.num, 1); + EXPECT_TRUE(sp_str_equal_cstr(spec.directive.names[0], "http_url")); } -UTEST(sp_fmt_parse_directive, err_leading_digit) { +UTEST_F(format, err_leading_digit) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{: .4red}", &spec), SP_ERR_FMT_BAD_DIRECTIVE); } -UTEST(sp_fmt_parse_directive, err_leading_hyphen) { +UTEST_F(format, err_leading_hyphen) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{.-red}", &spec), SP_ERR_FMT_BAD_DIRECTIVE); } -UTEST(sp_fmt_parse_directive, no_width_just_directive) { +UTEST_F(format, no_width_just_directive) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{: .red}", &spec), SP_OK); EXPECT_EQ(spec.width, 0); - EXPECT_EQ(spec.directive_count, 1); + EXPECT_EQ(spec.directive.num, 1); } static sp_str_t render_value_to_str(sp_fmt_argv_t argv) { sp_fmt_arg_t arg = sp_fmt_arg_from_argv(argv); sp_io_dyn_mem_writer_t io = sp_zero; sp_io_dyn_mem_writer_init(sp_mem_get_scratch(), &io); - sp_fmt_render_default(&io.base, sp_mem_get_scratch(), &arg, SP_NULLPTR); + sp_fmt_render_default(&io.base, &arg, SP_NULLPTR); return sp_io_dyn_mem_writer_as_str(&io); } -UTEST(sp_fmt_render, u64_zero) { +UTEST_F(format, u64_zero) { sp_str_t got = render_value_to_str(sp_fmt_uint(0)); EXPECT_TRUE(sp_str_equal_cstr(got, "0")); } -UTEST(sp_fmt_render, u64_large) { +UTEST_F(format, u64_large) { sp_str_t got = render_value_to_str(sp_fmt_uint(1234567)); EXPECT_TRUE(sp_str_equal_cstr(got, "1234567")); } -UTEST(sp_fmt_render, s64_negative) { +UTEST_F(format, s64_negative) { sp_str_t got = render_value_to_str(sp_fmt_int(-42)); EXPECT_TRUE(sp_str_equal_cstr(got, "-42")); } -UTEST(sp_fmt_render, s64_positive) { +UTEST_F(format, s64_positive) { sp_str_t got = render_value_to_str(sp_fmt_int(42)); EXPECT_TRUE(sp_str_equal_cstr(got, "42")); } -UTEST(sp_fmt_render, str) { +UTEST_F(format, str) { sp_str_t got = render_value_to_str(sp_fmt_cstr("hello")); EXPECT_TRUE(sp_str_equal_cstr(got, "hello")); } -UTEST(sp_fmt_render, empty_str) { +UTEST_F(format, empty_str) { sp_str_t got = render_value_to_str(sp_fmt_cstr("")); EXPECT_TRUE(sp_str_equal_cstr(got, "")); } -UTEST(sp_fmt_render, u64_ten) { +UTEST_F(format, u64_ten) { sp_str_t got = render_value_to_str(sp_fmt_uint(10)); EXPECT_TRUE(sp_str_equal_cstr(got, "10")); } -UTEST(sp_fmt_render, u64_ninety_nine) { +UTEST_F(format, u64_ninety_nine) { sp_str_t got = render_value_to_str(sp_fmt_uint(99)); EXPECT_TRUE(sp_str_equal_cstr(got, "99")); } -UTEST(sp_fmt_render, u64_one_hundred) { +UTEST_F(format, u64_one_hundred) { sp_str_t got = render_value_to_str(sp_fmt_uint(100)); EXPECT_TRUE(sp_str_equal_cstr(got, "100")); } -UTEST(sp_fmt_render, u64_max) { +UTEST_F(format, u64_max) { sp_str_t got = render_value_to_str(sp_fmt_uint(0xffffffffffffffffULL)); EXPECT_TRUE(sp_str_equal_cstr(got, "18446744073709551615")); } -UTEST(sp_fmt_render, s64_min) { +UTEST_F(format, s64_min) { sp_str_t got = render_value_to_str(sp_fmt_int((s64)0x8000000000000000LL)); EXPECT_TRUE(sp_str_equal_cstr(got, "-9223372036854775808")); } -UTEST(sp_fmt_render, s64_max) { +UTEST_F(format, s64_max) { sp_str_t got = render_value_to_str(sp_fmt_int((s64)0x7fffffffffffffffLL)); EXPECT_TRUE(sp_str_equal_cstr(got, "9223372036854775807")); } -UTEST(sp_fmt_render, f64_zero) { +UTEST_F(format, f64_zero) { sp_str_t got = render_value_to_str(sp_fmt_float(0.0)); EXPECT_TRUE(sp_str_equal_cstr(got, "0.000000")); } -UTEST(sp_fmt_render, f64_one) { +UTEST_F(format, f64_one) { sp_str_t got = render_value_to_str(sp_fmt_float(1.0)); EXPECT_TRUE(sp_str_equal_cstr(got, "1.000000")); } -UTEST(sp_fmt_render, f64_half) { +UTEST_F(format, f64_half) { sp_str_t got = render_value_to_str(sp_fmt_float(0.5)); EXPECT_TRUE(sp_str_equal_cstr(got, "0.500000")); } -UTEST(sp_fmt_render, f64_negative) { +UTEST_F(format, f64_negative) { sp_str_t got = render_value_to_str(sp_fmt_float(-3.25)); EXPECT_TRUE(sp_str_equal_cstr(got, "-3.250000")); } -UTEST(sp_fmt_render, f64_carry) { +UTEST_F(format, f64_carry) { sp_str_t got = render_value_to_str(sp_fmt_float(0.9999995)); EXPECT_TRUE(sp_str_equal_cstr(got, "1.000000")); } -UTEST(sp_fmt_render, f64_nan) { +UTEST_F(format, f64_nan) { union { f64 f; u64 u; } nan_bits = { .u = 0x7ff8000000000000ULL }; sp_str_t got = render_value_to_str(sp_fmt_float(nan_bits.f)); EXPECT_TRUE(sp_str_equal_cstr(got, "nan")); } -UTEST(sp_fmt_render, f64_pos_inf) { +UTEST_F(format, f64_pos_inf) { union { f64 f; u64 u; } inf_bits = { .u = 0x7ff0000000000000ULL }; sp_str_t got = render_value_to_str(sp_fmt_float(inf_bits.f)); EXPECT_TRUE(sp_str_equal_cstr(got, "inf")); } -UTEST(sp_fmt_render, f64_neg_inf) { +UTEST_F(format, f64_neg_inf) { union { f64 f; u64 u; } inf_bits = { .u = 0xfff0000000000000ULL }; sp_str_t got = render_value_to_str(sp_fmt_float(inf_bits.f)); EXPECT_TRUE(sp_str_equal_cstr(got, "-inf")); } -UTEST(sp_fmt_render, f64_custom_precision_via_spec) { +UTEST_F(format, f64_custom_precision_via_spec) { sp_fmt_arg_t arg = sp_fmt_arg_from_argv(sp_fmt_float(3.14159)); sp_opt_set(arg.spec.precision, 2); sp_io_dyn_mem_writer_t io = sp_zero; sp_io_dyn_mem_writer_init(sp_mem_get_scratch(), &io); - sp_fmt_render_default(&io.base, sp_mem_get_scratch(), &arg, SP_NULLPTR); + sp_fmt_render_default(&io.base, &arg, SP_NULLPTR); sp_str_t got = sp_io_dyn_mem_writer_as_str(&io); EXPECT_TRUE(sp_str_equal_cstr(got, "3.14")); } -UTEST(sp_fmt_render, ptr_null) { +UTEST_F(format, ptr_null) { sp_str_t got = render_value_to_str(sp_fmt_ptr(SP_NULLPTR)); EXPECT_TRUE(sp_str_equal_cstr(got, "0x0")); } -UTEST(sp_fmt_render, ptr_nonzero) { +UTEST_F(format, ptr_nonzero) { sp_str_t got = render_value_to_str(sp_fmt_ptr((void*)(uintptr_t)0xdeadbeefULL)); EXPECT_TRUE(sp_str_equal_cstr(got, "0xdeadbeef")); } -static sp_str_t apply_spec_to_str(const c8* content, sp_fmt_spec_t spec) { - sp_io_dyn_mem_writer_t io = sp_zero; - sp_io_dyn_mem_writer_init(sp_mem_get_scratch(), &io); - sp_str_t empty = sp_zero; - sp_fmt_apply_spec(&io.base, empty, sp_str_view(content), empty, spec); - return sp_io_dyn_mem_writer_as_str(&io); -} - -UTEST(sp_fmt_pad, no_width) { - sp_str_t got = apply_spec_to_str("hello", sp_zero_s(sp_fmt_spec_t)); - EXPECT_TRUE(sp_str_equal_cstr(got, "hello")); -} - -UTEST(sp_fmt_pad, content_longer_than_width) { - sp_str_t got = apply_spec_to_str("hello", (sp_fmt_spec_t){ .width = 3 }); - EXPECT_TRUE(sp_str_equal_cstr(got, "hello")); -} - -UTEST(sp_fmt_pad, right_align_default) { - sp_str_t got = apply_spec_to_str("42", (sp_fmt_spec_t){ .width = 6 }); - EXPECT_TRUE(sp_str_equal_cstr(got, " 42")); -} - -UTEST(sp_fmt_pad, left_align_fill) { - sp_str_t got = apply_spec_to_str("42", (sp_fmt_spec_t){ - .width = 6, .align = SP_FMT_ALIGN_LEFT, .fill = '-' - }); - EXPECT_TRUE(sp_str_equal_cstr(got, "42----")); -} - -UTEST(sp_fmt_pad, center_even) { - sp_str_t got = apply_spec_to_str("hi", (sp_fmt_spec_t){ - .width = 8, .align = SP_FMT_ALIGN_CENTER, .fill = '*' - }); - EXPECT_TRUE(sp_str_equal_cstr(got, "***hi***")); -} - -UTEST(sp_fmt_pad, center_odd) { - sp_str_t got = apply_spec_to_str("hi", (sp_fmt_spec_t){ - .width = 9, .align = SP_FMT_ALIGN_CENTER, .fill = '*' - }); - EXPECT_TRUE(sp_str_equal_cstr(got, "***hi****")); -} - -UTEST(sp_fmt_pad, wrapped_padding_outside) { - sp_io_dyn_mem_writer_t io = sp_zero; - sp_io_dyn_mem_writer_init(sp_mem_get_scratch(), &io); - sp_fmt_apply_spec(&io.base, - sp_str_lit("<"), - sp_str_lit("42"), - sp_str_lit(">"), - (sp_fmt_spec_t){ .width = 6 } - ); - sp_str_t got = sp_io_dyn_mem_writer_as_str(&io); - EXPECT_TRUE(sp_str_equal_cstr(got, " <42>")); -} - -UTEST(sp_fmt_pad, wrapped_center) { - sp_io_dyn_mem_writer_t io = sp_zero; - sp_io_dyn_mem_writer_init(sp_mem_get_scratch(), &io); - sp_fmt_apply_spec(&io.base, - sp_str_lit("["), - sp_str_lit("hi"), - sp_str_lit("]"), - (sp_fmt_spec_t){ .width = 8, .align = SP_FMT_ALIGN_CENTER, .fill = '*' } - ); - sp_str_t got = sp_io_dyn_mem_writer_as_str(&io); - EXPECT_TRUE(sp_str_equal_cstr(got, "***[hi]***")); -} - -static void _test_before_lt(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { - (void)arg; (void)param; (void)mem; +static void _test_before_lt(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { + (void)arg; (void)param; sp_io_write_cstr(io, "<", SP_NULLPTR); } -static void _test_after_gt(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { - (void)arg; (void)param; (void)mem; +static void _test_after_gt(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { + (void)arg; (void)param; sp_io_write_cstr(io, ">", SP_NULLPTR); } -static void _test_render_x(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { - (void)arg; (void)param; (void)mem; +static void _test_render_x(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { + (void)arg; (void)param; sp_io_write_cstr(io, "X", SP_NULLPTR); } -static void _test_transform_upper(sp_io_writer_t* io, sp_mem_t mem, sp_str_t content, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { - (void)arg; (void)param; (void)mem; - sp_for(k, content.len) { - c8 c = content.data[k]; - sp_io_write_c8(io, (c >= 'a' && c <= 'z') ? (c8)(c - 32) : c); - } -} - static sp_io_dyn_mem_writer_t _test_log; static u32 _test_render_y_calls; -static void _test_before_a(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { - (void)arg; (void)param; (void)mem; +static void _test_before_a(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { + (void)arg; (void)param; sp_io_write_cstr(io, "[a", SP_NULLPTR); sp_io_write_cstr(&_test_log.base, "ba", SP_NULLPTR); } -static void _test_after_a(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { - (void)arg; (void)param; (void)mem; +static void _test_after_a(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { + (void)arg; (void)param; sp_io_write_cstr(io, "a]", SP_NULLPTR); sp_io_write_cstr(&_test_log.base, "aa", SP_NULLPTR); } -static void _test_before_b(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { - (void)arg; (void)param; (void)mem; +static void _test_before_b(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { + (void)arg; (void)param; sp_io_write_cstr(io, "[b", SP_NULLPTR); sp_io_write_cstr(&_test_log.base, "bb", SP_NULLPTR); } -static void _test_after_b(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { - (void)arg; (void)param; (void)mem; +static void _test_after_b(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { + (void)arg; (void)param; sp_io_write_cstr(io, "b]", SP_NULLPTR); sp_io_write_cstr(&_test_log.base, "ab", SP_NULLPTR); } -static void _test_render_y(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { - (void)arg; (void)param; (void)mem; +static void _test_render_y(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { + (void)arg; (void)param; _test_render_y_calls++; sp_io_write_cstr(io, "Y", SP_NULLPTR); } -UTEST(sp_fmt_directive, register_and_lookup) { +UTEST_F(format, register_and_lookup) { sp_fmt_directive_reset(); sp_fmt_register_decorator("wrap", _test_before_lt, _test_after_gt); @@ -587,13 +509,13 @@ UTEST(sp_fmt_directive, register_and_lookup) { sp_fmt_directive_reset(); } -UTEST(sp_fmt_directive, lookup_unknown_returns_null) { +UTEST_F(format, lookup_unknown_returns_null) { sp_fmt_directive_reset(); sp_fmt_directive_t* got = sp_fmt_directive_lookup(sp_str_lit("nope")); EXPECT_EQ(got, SP_NULLPTR); } -UTEST(sp_fmt_directive, reset_clears) { +UTEST_F(format, reset_clears) { sp_fmt_directive_reset(); sp_fmt_register_decorator("foo", _test_before_lt, SP_NULLPTR); EXPECT_TRUE(sp_fmt_directive_lookup(sp_str_lit("foo")) != SP_NULLPTR); @@ -601,7 +523,7 @@ UTEST(sp_fmt_directive, reset_clears) { EXPECT_EQ(sp_fmt_directive_lookup(sp_str_lit("foo")), SP_NULLPTR); } -UTEST(sp_fmt_directive, wraps_content) { +UTEST_F(format, wraps_content) { sp_fmt_directive_reset(); sp_fmt_register_decorator("wrap", _test_before_lt, _test_after_gt); sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.wrap}", sp_fmt_cstr("hi")).value; @@ -609,7 +531,7 @@ UTEST(sp_fmt_directive, wraps_content) { sp_fmt_directive_reset(); } -UTEST(sp_fmt_directive, render_replaces_value) { +UTEST_F(format, render_replaces_value) { sp_fmt_directive_reset(); sp_fmt_register_renderer("x", _test_render_x, sp_fmt_id_none); sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.x}", sp_fmt_int(999)).value; @@ -617,21 +539,13 @@ UTEST(sp_fmt_directive, render_replaces_value) { sp_fmt_directive_reset(); } -UTEST(sp_fmt_directive, transform_uppercase) { - sp_fmt_directive_reset(); - sp_fmt_register_transformer("upper", _test_transform_upper); - sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.upper}", sp_fmt_cstr("hello")).value; - EXPECT_TRUE(sp_str_equal_cstr(got, "HELLO")); - sp_fmt_directive_reset(); -} - -UTEST(sp_fmt_directive, err_unknown_directive) { +UTEST_F(format, err_unknown_directive) { sp_fmt_directive_reset(); sp_str_t str = sp_zero; EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "{.missing}", sp_fmt_int(42)).err, SP_ERR_FMT_UNKNOWN_DIRECTIVE); } -UTEST(sp_fmt_directive, ordering_bracket_nested) { +UTEST_F(format, ordering_bracket_nested) { sp_fmt_directive_reset(); sp_fmt_register_decorator("a", _test_before_a, _test_after_a); sp_fmt_register_decorator("b", _test_before_b, _test_after_b); @@ -646,7 +560,7 @@ UTEST(sp_fmt_directive, ordering_bracket_nested) { sp_fmt_directive_reset(); } -UTEST(sp_fmt_directive, err_multiple_renders) { +UTEST_F(format, err_multiple_renders) { sp_fmt_directive_reset(); _test_render_y_calls = 0; sp_fmt_register_renderer("x", _test_render_x, sp_fmt_id_none); @@ -657,7 +571,7 @@ UTEST(sp_fmt_directive, err_multiple_renders) { sp_fmt_directive_reset(); } -UTEST(sp_fmt_directive, padding_outside_wrappers) { +UTEST_F(format, padding_outside_wrappers) { sp_fmt_directive_reset(); sp_fmt_register_decorator("wrap", _test_before_lt, _test_after_gt); sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:6 .wrap}", sp_fmt_int(42)).value; @@ -665,7 +579,7 @@ UTEST(sp_fmt_directive, padding_outside_wrappers) { sp_fmt_directive_reset(); } -UTEST(sp_fmt_directive, padding_with_center_and_wrapper) { +UTEST_F(format, padding_with_center_and_wrapper) { sp_fmt_directive_reset(); sp_fmt_register_decorator("wrap", _test_before_lt, _test_after_gt); sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:*^8 .wrap}", sp_fmt_cstr("hi")).value; @@ -673,63 +587,63 @@ UTEST(sp_fmt_directive, padding_with_center_and_wrapper) { sp_fmt_directive_reset(); } -UTEST(sp_fmt_v, literal_only) { +UTEST_F(format, literal_only) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "hello, world").value; EXPECT_TRUE(sp_str_equal_cstr(got, "hello, world")); } -UTEST(sp_fmt_v, empty_placeholder_int) { +UTEST_F(format, empty_placeholder_int) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{}", sp_fmt_int(42)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "42")); } -UTEST(sp_fmt_v, empty_placeholder_str) { +UTEST_F(format, empty_placeholder_str) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{}", sp_fmt_cstr("world")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "world")); } -UTEST(sp_fmt_v, multi_arg) { +UTEST_F(format, multi_arg) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{} + {} = {}", sp_fmt_int(2), sp_fmt_int(3), sp_fmt_int(5)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "2 + 3 = 5")); } -UTEST(sp_fmt_v, literals_around_placeholder) { +UTEST_F(format, literals_around_placeholder) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "hello, {}!", sp_fmt_cstr("thomas")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "hello, thomas!")); } -UTEST(sp_fmt_v, width_right_align) { +UTEST_F(format, width_right_align) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:6}", sp_fmt_int(42)).value; EXPECT_TRUE(sp_str_equal_cstr(got, " 42")); } -UTEST(sp_fmt_v, fill_center) { +UTEST_F(format, fill_center) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:*^9}", sp_fmt_int(42)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "***42****")); } -UTEST(sp_fmt_v, brace_escapes) { +UTEST_F(format, brace_escapes) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{{{}}}", sp_fmt_int(7)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "{7}")); } -UTEST(sp_fmt_v, close_brace_escape) { +UTEST_F(format, close_brace_escape) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "hello }} world").value; EXPECT_TRUE(sp_str_equal_cstr(got, "hello } world")); } -UTEST(sp_fmt_v, err_lone_close_brace) { +UTEST_F(format, err_lone_close_brace) { sp_str_t str = sp_zero; EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "oops } here").err, SP_ERR_FMT_BAD_PLACEHOLDER); } -UTEST(sp_fmt_v, str_with_padding) { +UTEST_F(format, str_with_padding) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "[{:->8}]", sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "[------hi]")); } -UTEST(sp_fmt_directive, custom_fn_fallback) { +UTEST_F(format, custom_fn_fallback) { sp_fmt_directive_reset(); sp_fmt_register_decorator("wrap", _test_before_lt, _test_after_gt); u32 value = 0; @@ -739,7 +653,7 @@ UTEST(sp_fmt_directive, custom_fn_fallback) { sp_fmt_directive_reset(); } -UTEST(sp_fmt_directive, default_render_with_wrappers_on_int) { +UTEST_F(format, default_render_with_wrappers_on_int) { sp_fmt_directive_reset(); sp_fmt_register_decorator("wrap", _test_before_lt, _test_after_gt); sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.wrap}", sp_fmt_int(42)).value; @@ -747,7 +661,7 @@ UTEST(sp_fmt_directive, default_render_with_wrappers_on_int) { sp_fmt_directive_reset(); } -UTEST(sp_fmt_directive, content_wider_than_width_with_wrapper) { +UTEST_F(format, content_wider_than_width_with_wrapper) { sp_fmt_directive_reset(); sp_fmt_register_decorator("wrap", _test_before_lt, _test_after_gt); sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:3 .wrap}", sp_fmt_cstr("hello")).value; @@ -755,55 +669,32 @@ UTEST(sp_fmt_directive, content_wider_than_width_with_wrapper) { sp_fmt_directive_reset(); } -static void _test_render_prefixed(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { - (void)arg; (void)param; (void)mem; +static void _test_render_prefixed(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { + (void)arg; (void)param; sp_io_write_cstr(io, "rendered", SP_NULLPTR); } -UTEST(sp_fmt_directive, before_render_then_transform) { - sp_fmt_directive_reset(); - sp_fmt_register_decorator("wrap", _test_before_lt, SP_NULLPTR); - sp_fmt_register_renderer("prefix", _test_render_prefixed, sp_fmt_id_none); - sp_fmt_register_transformer("upper", _test_transform_upper); - sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.wrap .prefix .upper}", sp_fmt_int(0)).value; - EXPECT_TRUE(sp_str_equal_cstr(got, ""), - (sp_fmt_spec_t){ .width = 3 } - ); - sp_str_t got = sp_io_dyn_mem_writer_as_str(&io); - EXPECT_TRUE(sp_str_equal_cstr(got, "")); -} - -UTEST(sp_fmt_v, escaped_braces_around_placeholder) { +UTEST_F(format, escaped_braces_around_placeholder) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{{{:5}}}", sp_fmt_int(42)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "{ 42}")); } -UTEST(sp_fmt_v, dynamic_width) { +UTEST_F(format, dynamic_width) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:$}", sp_fmt_int(6), sp_fmt_int(42)).value; EXPECT_TRUE(sp_str_equal_cstr(got, " 42")); } -UTEST(sp_fmt_v, dynamic_fill_center) { +UTEST_F(format, dynamic_fill_center) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:$^9}", sp_fmt_int('*'), sp_fmt_int(42)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "***42****")); } -UTEST(sp_fmt_v, dynamic_fill_and_width) { +UTEST_F(format, dynamic_fill_and_width) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:$^$}", sp_fmt_int('-'), sp_fmt_int(8), @@ -811,104 +702,104 @@ UTEST(sp_fmt_v, dynamic_fill_and_width) { EXPECT_TRUE(sp_str_equal_cstr(got, "---hi---")); } -UTEST(sp_fmt_v, dynamic_precision) { +UTEST_F(format, dynamic_precision) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:.$}", sp_fmt_int(3), sp_fmt_float(3.14159)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "3.142")); } -UTEST(sp_fmt_v, dynamic_width_with_literal_precision) { +UTEST_F(format, dynamic_width_with_literal_precision) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:$.2}", sp_fmt_int(8), sp_fmt_float(1.5)).value; EXPECT_TRUE(sp_str_equal_cstr(got, " 1.50")); } -UTEST(sp_fmt_v, err_parse_stops_formatting) { +UTEST_F(format, err_parse_stops_formatting) { sp_str_t str = sp_zero; EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "a {:5.} b {}", sp_fmt_int(99)).err, SP_ERR_FMT_BAD_PRECISION); } -UTEST(sp_fmt_v, err_unterminated_placeholder) { +UTEST_F(format, err_unterminated_placeholder) { sp_str_t str = sp_zero; EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "hi {nope", sp_fmt_int(1)).err, SP_ERR_FMT_BAD_PLACEHOLDER); } -UTEST(sp_fmt_v, err_dynamic_fill_wrong_kind) { +UTEST_F(format, err_dynamic_fill_wrong_kind) { sp_str_t str = sp_zero; EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "{:$^5}", sp_fmt_float(1.0), sp_fmt_int(42)).err, SP_ERR_FMT_DIRECTIVE_ARG_WRONG_KIND); } -UTEST(sp_fmt_v, err_dynamic_width_wrong_kind) { +UTEST_F(format, err_dynamic_width_wrong_kind) { sp_str_t str = sp_zero; EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "{:$}", sp_fmt_cstr("oops"), sp_fmt_int(42)).err, SP_ERR_FMT_DIRECTIVE_ARG_WRONG_KIND); } -UTEST(sp_fmt_v, err_dynamic_precision_wrong_kind) { +UTEST_F(format, err_dynamic_precision_wrong_kind) { sp_str_t str = sp_zero; EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "{:.$}", sp_fmt_float(3.0), sp_fmt_float(3.14)).err, SP_ERR_FMT_DIRECTIVE_ARG_WRONG_KIND); } -UTEST(sp_fmt_v, err_stops_subsequent_placeholders) { +UTEST_F(format, err_stops_subsequent_placeholders) { sp_fmt_directive_reset(); sp_str_t str = sp_zero; sp_err_t err = sp_fmt(sp_mem_get_scratch(), "{} {.nope} {}", sp_fmt_int(1), sp_fmt_int(2), sp_fmt_int(3)).err; EXPECT_EQ(err, SP_ERR_FMT_UNKNOWN_DIRECTIVE); } -UTEST(sp_fmt_v, str_precision_truncates) { +UTEST_F(format, str_precision_truncates) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:.3}", sp_fmt_cstr("hello")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "hel")); } -UTEST(sp_fmt_v, str_precision_longer_than_string) { +UTEST_F(format, str_precision_longer_than_string) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:.10}", sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "hi")); } -UTEST(sp_fmt_v, str_dynamic_precision_truncates) { +UTEST_F(format, str_dynamic_precision_truncates) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:.$}", sp_fmt_int(2), sp_fmt_cstr("hello")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "he")); } -UTEST(sp_fmt_v, str_precision_with_width) { +UTEST_F(format, str_precision_with_width) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "[{:>6.3}]", sp_fmt_cstr("hello")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "[ hel]")); } -UTEST(sp_fmt_v, f64_precision_zero_means_zero) { +UTEST_F(format, f64_precision_zero_means_zero) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:.0}", sp_fmt_float(3.7)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "4")); } -UTEST(sp_fmt_v, f64_dynamic_precision_zero) { +UTEST_F(format, f64_dynamic_precision_zero) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:.$}", sp_fmt_int(0), sp_fmt_float(3.7)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "4")); } -UTEST(sp_fmt_v, f64_no_precision_defaults_to_six) { +UTEST_F(format, f64_no_precision_defaults_to_six) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{}", sp_fmt_float(1.5)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "1.500000")); } -UTEST(sp_fmt_v, width_clamped_literal) { +UTEST_F(format, width_clamped_literal) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:99999}", sp_fmt_cstr("x")).value; EXPECT_EQ(got.len, SP_FMT_WIDTH_MAX); } -UTEST(sp_fmt_v, width_clamped_dynamic_huge) { +UTEST_F(format, width_clamped_dynamic_huge) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:$}", sp_fmt_uint(999999999ULL), sp_fmt_cstr("x")).value; EXPECT_EQ(got.len, SP_FMT_WIDTH_MAX); } -UTEST(sp_fmt_v, width_clamped_dynamic_negative) { +UTEST_F(format, width_clamped_dynamic_negative) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:$}", sp_fmt_int(-5), sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "hi")); } -static void _test_render_u64_only(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { - (void)param; (void)mem; +static void _test_render_u64_only(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { + (void)param; sp_fmt_write_u64(io, arg->value.u); } -UTEST(sp_fmt_directive, kinds_single_accepts_match) { +UTEST_F(format, kinds_single_accepts_match) { sp_fmt_directive_reset(); sp_fmt_register_renderer("only_u64", _test_render_u64_only, sp_fmt_id_u64); sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.only_u64}", sp_fmt_uint(42)).value; @@ -916,7 +807,7 @@ UTEST(sp_fmt_directive, kinds_single_accepts_match) { sp_fmt_directive_reset(); } -UTEST(sp_fmt_directive, kinds_single_rejects_mismatch) { +UTEST_F(format, kinds_single_rejects_mismatch) { sp_fmt_directive_reset(); sp_fmt_register_renderer("only_u64", _test_render_u64_only, sp_fmt_id_u64); sp_str_t str = sp_zero; @@ -924,11 +815,11 @@ UTEST(sp_fmt_directive, kinds_single_rejects_mismatch) { sp_fmt_directive_reset(); } -UTEST(sp_fmt_directive, kinds_multiple_accepts_either) { +UTEST_F(format, kinds_multiple_accepts_either) { sp_fmt_directive_reset(); sp_fmt_directive_register("num", (sp_fmt_directive_t){ .kind = sp_fmt_directive_decorator, - .arg_kinds = sp_cast(sp_fmt_arg_kind_t, sp_fmt_id_u64 | sp_fmt_id_s64), + .args = sp_cast(sp_fmt_arg_kind_t, sp_fmt_id_u64 | sp_fmt_id_s64), }); sp_str_t a = sp_fmt(sp_mem_get_scratch(), "{.num}", sp_fmt_uint(7)).value; sp_str_t b = sp_fmt(sp_mem_get_scratch(), "{.num}", sp_fmt_int(-3)).value; @@ -937,18 +828,18 @@ UTEST(sp_fmt_directive, kinds_multiple_accepts_either) { sp_fmt_directive_reset(); } -UTEST(sp_fmt_directive, kinds_multiple_rejects_outsider) { +UTEST_F(format, kinds_multiple_rejects_outsider) { sp_fmt_directive_reset(); sp_fmt_directive_register("num", (sp_fmt_directive_t){ .kind = sp_fmt_directive_decorator, - .arg_kinds = sp_cast(sp_fmt_arg_kind_t, sp_fmt_id_u64 | sp_fmt_id_s64), + .args = sp_cast(sp_fmt_arg_kind_t, sp_fmt_id_u64 | sp_fmt_id_s64), }); sp_str_t str = sp_zero; EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "{.num}", sp_fmt_cstr("nope")).err, SP_ERR_FMT_WRONG_PARAM_KIND); sp_fmt_directive_reset(); } -UTEST(sp_fmt_directive, kinds_unset_accepts_all) { +UTEST_F(format, kinds_unset_accepts_all) { sp_fmt_directive_reset(); sp_fmt_register_decorator("any", _test_before_lt, _test_after_gt); sp_str_t a = sp_fmt(sp_mem_get_scratch(), "{.any}", sp_fmt_int(1)).value; @@ -960,100 +851,100 @@ UTEST(sp_fmt_directive, kinds_unset_accepts_all) { sp_fmt_directive_reset(); } -UTEST(sp_fmt_parse_directive_arg, literal) { +UTEST_F(format, literal) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{.fg red}", &spec), SP_OK); - EXPECT_EQ(spec.directive_count, 1); - EXPECT_TRUE(sp_str_equal_cstr(spec.directive_names[0], "fg")); - EXPECT_TRUE(sp_str_equal_cstr(spec.directive_args[0], "red")); - EXPECT_EQ(spec.directive_arg_dynamic, 0); + EXPECT_EQ(spec.directive.num, 1); + EXPECT_TRUE(sp_str_equal_cstr(spec.directive.names[0], "fg")); + EXPECT_TRUE(sp_str_equal_cstr(spec.directive.args[0], "red")); + EXPECT_EQ(spec.dynamic.directive, 0); } -UTEST(sp_fmt_parse_directive_arg, dynamic) { +UTEST_F(format, dynamic) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{.fg $}", &spec), SP_OK); - EXPECT_EQ(spec.directive_count, 1); - EXPECT_TRUE(sp_str_equal_cstr(spec.directive_names[0], "fg")); - EXPECT_EQ(spec.directive_args[0].len, 0); - EXPECT_EQ(spec.directive_arg_dynamic, 1); + EXPECT_EQ(spec.directive.num, 1); + EXPECT_TRUE(sp_str_equal_cstr(spec.directive.names[0], "fg")); + EXPECT_EQ(spec.directive.args[0].len, 0); + EXPECT_EQ(spec.dynamic.directive, 1); } -UTEST(sp_fmt_parse_directive_arg, literal_then_directive) { +UTEST_F(format, literal_then_directive) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{.fg red .bold}", &spec), SP_OK); - EXPECT_EQ(spec.directive_count, 2); - EXPECT_TRUE(sp_str_equal_cstr(spec.directive_names[0], "fg")); - EXPECT_TRUE(sp_str_equal_cstr(spec.directive_args[0], "red")); - EXPECT_TRUE(sp_str_equal_cstr(spec.directive_names[1], "bold")); - EXPECT_EQ(spec.directive_args[1].len, 0); + EXPECT_EQ(spec.directive.num, 2); + EXPECT_TRUE(sp_str_equal_cstr(spec.directive.names[0], "fg")); + EXPECT_TRUE(sp_str_equal_cstr(spec.directive.args[0], "red")); + EXPECT_TRUE(sp_str_equal_cstr(spec.directive.names[1], "bold")); + EXPECT_EQ(spec.directive.args[1].len, 0); } -UTEST(sp_fmt_parse_directive_arg, dynamic_then_directive) { +UTEST_F(format, dynamic_then_directive) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{.fg $ .bold}", &spec), SP_OK); - EXPECT_EQ(spec.directive_count, 2); - EXPECT_EQ(spec.directive_arg_dynamic, 1); + EXPECT_EQ(spec.directive.num, 2); + EXPECT_EQ(spec.dynamic.directive, 1); } -UTEST(sp_fmt_parse_directive_arg, two_literals) { +UTEST_F(format, two_literals) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{.fg red .bg blue}", &spec), SP_OK); - EXPECT_EQ(spec.directive_count, 2); - EXPECT_TRUE(sp_str_equal_cstr(spec.directive_args[0], "red")); - EXPECT_TRUE(sp_str_equal_cstr(spec.directive_args[1], "blue")); - EXPECT_EQ(spec.directive_arg_dynamic, 0); + EXPECT_EQ(spec.directive.num, 2); + EXPECT_TRUE(sp_str_equal_cstr(spec.directive.args[0], "red")); + EXPECT_TRUE(sp_str_equal_cstr(spec.directive.args[1], "blue")); + EXPECT_EQ(spec.dynamic.directive, 0); } -UTEST(sp_fmt_parse_directive_arg, two_dynamics) { +UTEST_F(format, two_dynamics) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{.fg $ .bg $}", &spec), SP_OK); - EXPECT_EQ(spec.directive_count, 2); - EXPECT_EQ(spec.directive_arg_dynamic, 0x3); + EXPECT_EQ(spec.directive.num, 2); + EXPECT_EQ(spec.dynamic.directive, 0b11); } -UTEST(sp_fmt_parse_directive_arg, mixed) { +UTEST_F(format, mixed) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{.fg red .bg $ .bold}", &spec), SP_OK); - EXPECT_EQ(spec.directive_count, 3); - EXPECT_TRUE(sp_str_equal_cstr(spec.directive_args[0], "red")); - EXPECT_EQ(spec.directive_args[1].len, 0); - EXPECT_EQ(spec.directive_arg_dynamic, 0x2); - EXPECT_TRUE(sp_str_equal_cstr(spec.directive_names[2], "bold")); + EXPECT_EQ(spec.directive.num, 3); + EXPECT_TRUE(sp_str_equal_cstr(spec.directive.args[0], "red")); + EXPECT_EQ(spec.directive.args[1].len, 0); + EXPECT_EQ(spec.dynamic.directive, 0b010); + EXPECT_TRUE(sp_str_equal_cstr(spec.directive.names[2], "bold")); } -UTEST(sp_fmt_parse_directive_arg, with_full_spec) { +UTEST_F(format, with_full_spec) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{:*^9.2 .fg red}", &spec), SP_OK); EXPECT_EQ(spec.width, 9); - EXPECT_EQ(spec.directive_count, 1); - EXPECT_TRUE(sp_str_equal_cstr(spec.directive_args[0], "red")); + EXPECT_EQ(spec.directive.num, 1); + EXPECT_TRUE(sp_str_equal_cstr(spec.directive.args[0], "red")); } -UTEST(sp_fmt_parse_directive_arg, arg_with_digits_and_symbols) { +UTEST_F(format, arg_with_digits_and_symbols) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{.hex 0xff}", &spec), SP_OK); - EXPECT_TRUE(sp_str_equal_cstr(spec.directive_args[0], "0xff")); + EXPECT_TRUE(sp_str_equal_cstr(spec.directive.args[0], "0xff")); } -UTEST(sp_fmt_parse_directive_arg, err_dollar_inside_literal) { +UTEST_F(format, err_dollar_inside_literal) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{.fg r$d}", &spec), SP_ERR_FMT_BAD_PLACEHOLDER); } -UTEST(sp_fmt_parse_directive_arg, err_dollar_followed_by_literal) { +UTEST_F(format, err_dollar_followed_by_literal) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{.fg $red}", &spec), SP_ERR_FMT_BAD_PLACEHOLDER); } -UTEST(sp_fmt_parse_directive_arg, err_space_in_arg) { +UTEST_F(format, err_space_in_arg) { sp_fmt_spec_t spec = sp_zero; EXPECT_EQ(parse_spec("{.fg red blue}", &spec), SP_ERR_FMT_BAD_PLACEHOLDER); } static sp_str_t _last_fg_param; static bool _last_fg_had_param; -static void _test_fg_before(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { - (void)arg; (void)mem; +static void _test_fg_before(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { + (void)arg; _last_fg_had_param = (param != SP_NULLPTR); if (param && param->id == sp_fmt_id_str) { _last_fg_param = param->value.s; @@ -1066,12 +957,12 @@ static void _test_fg_before(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, } } -static void _test_fg_after(sp_io_writer_t* io, sp_mem_t mem, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { - (void)arg; (void)param; (void)mem; +static void _test_fg_after(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { + (void)arg; (void)param; sp_io_write_cstr(io, "", SP_NULLPTR); } -UTEST(sp_fmt_directive_arg, literal_passed_as_str) { +UTEST_F(format, literal_passed_as_str) { sp_fmt_directive_reset(); sp_fmt_register_decorator_p("fg", _test_fg_before, _test_fg_after, sp_fmt_id_str); sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.fg red}", sp_fmt_cstr("hello")).value; @@ -1081,7 +972,7 @@ UTEST(sp_fmt_directive_arg, literal_passed_as_str) { sp_fmt_directive_reset(); } -UTEST(sp_fmt_directive_arg, dynamic_passed_as_str) { +UTEST_F(format, dynamic_passed_as_str) { sp_fmt_directive_reset(); sp_fmt_register_decorator_p("fg", _test_fg_before, _test_fg_after, sp_fmt_id_str); sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.fg $}", sp_fmt_cstr("blue"), sp_fmt_cstr("hello")).value; @@ -1089,7 +980,7 @@ UTEST(sp_fmt_directive_arg, dynamic_passed_as_str) { sp_fmt_directive_reset(); } -UTEST(sp_fmt_directive_arg, dynamic_accepts_u64_with_mask) { +UTEST_F(format, dynamic_accepts_u64_with_mask) { sp_fmt_directive_reset(); sp_fmt_register_decorator_p("fg", _test_fg_before, _test_fg_after, sp_fmt_id_str | sp_fmt_id_u64); sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.fg $}", sp_fmt_uint(31), sp_fmt_cstr("x")).value; @@ -1097,7 +988,7 @@ UTEST(sp_fmt_directive_arg, dynamic_accepts_u64_with_mask) { sp_fmt_directive_reset(); } -UTEST(sp_fmt_directive_arg, err_missing_arg) { +UTEST_F(format, err_missing_arg) { sp_fmt_directive_reset(); sp_fmt_register_decorator_p("fg", _test_fg_before, _test_fg_after, sp_fmt_id_str); sp_str_t str = sp_zero; @@ -1105,7 +996,7 @@ UTEST(sp_fmt_directive_arg, err_missing_arg) { sp_fmt_directive_reset(); } -UTEST(sp_fmt_directive_arg, err_unexpected_arg) { +UTEST_F(format, err_unexpected_arg) { sp_fmt_directive_reset(); sp_fmt_register_decorator("bold", _test_before_lt, _test_after_gt); sp_str_t str = sp_zero; @@ -1113,7 +1004,7 @@ UTEST(sp_fmt_directive_arg, err_unexpected_arg) { sp_fmt_directive_reset(); } -UTEST(sp_fmt_directive_arg, err_wrong_literal_kind) { +UTEST_F(format, err_wrong_literal_kind) { sp_fmt_directive_reset(); sp_fmt_register_decorator_p("numpad", _test_before_lt, _test_after_gt, sp_fmt_id_u64); sp_str_t str = sp_zero; @@ -1121,7 +1012,7 @@ UTEST(sp_fmt_directive_arg, err_wrong_literal_kind) { sp_fmt_directive_reset(); } -UTEST(sp_fmt_directive_arg, err_wrong_dynamic_kind) { +UTEST_F(format, err_wrong_dynamic_kind) { sp_fmt_directive_reset(); sp_fmt_register_decorator_p("fg", _test_fg_before, _test_fg_after, sp_fmt_id_str); sp_str_t str = sp_zero; @@ -1129,7 +1020,7 @@ UTEST(sp_fmt_directive_arg, err_wrong_dynamic_kind) { sp_fmt_directive_reset(); } -UTEST(sp_fmt_directive_arg, chain_of_literal_and_dynamic) { +UTEST_F(format, chain_of_literal_and_dynamic) { sp_fmt_directive_reset(); sp_fmt_register_decorator_p("fg", _test_fg_before, _test_fg_after, sp_fmt_id_str); sp_fmt_register_decorator("bold", _test_before_lt, _test_after_gt); @@ -1138,32 +1029,27 @@ UTEST(sp_fmt_directive_arg, chain_of_literal_and_dynamic) { sp_fmt_directive_reset(); } -UTEST(sp_fmt_builtin_fg, literal_color) { +UTEST_F(format, literal_color) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.fg red}", sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "\033[31mhi\033[0m")); } -UTEST(sp_fmt_builtin_fg, literal_bright_cyan) { +UTEST_F(format, literal_bright_cyan) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.fg brightcyan}", sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "\033[96mhi\033[0m")); } -UTEST(sp_fmt_builtin_fg, dynamic_color) { +UTEST_F(format, dynamic_color) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.fg $}", sp_fmt_cstr("green"), sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "\033[32mhi\033[0m")); } -UTEST(sp_fmt_builtin_fg, composes_with_padding) { +UTEST_F(format, composes_with_padding) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:*^6 .fg red}", sp_fmt_cstr("hi")).value; EXPECT_TRUE(sp_str_equal_cstr(got, "**\033[31mhi\033[0m**")); } -UTEST(sp_fmt_builtin_fg, err_missing_arg) { - sp_str_t str = sp_zero; - EXPECT_EQ(sp_fmt(sp_mem_get_scratch(), "{.fg}", sp_fmt_cstr("hi")).err, SP_ERR_FMT_DIRECTIVE_ARG_MISSING); -} - -UTEST(sp_fmt_directive_arg, dynamic_param_interleaved_with_width) { +UTEST_F(format, dynamic_param_interleaved_with_width) { sp_fmt_directive_reset(); sp_fmt_register_decorator_p("fg", _test_fg_before, _test_fg_after, sp_fmt_id_str); sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:$ .fg $}", sp_fmt_int(4), sp_fmt_cstr("red"), sp_fmt_cstr("hi")).value; @@ -1171,66 +1057,7 @@ UTEST(sp_fmt_directive_arg, dynamic_param_interleaved_with_width) { sp_fmt_directive_reset(); } -static void _test_transform_redact(sp_io_writer_t* io, sp_mem_t mem, sp_str_t content, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { - (void)arg; (void)param; (void)mem; - sp_for(i, content.len) sp_io_write_c8(io, '*'); -} - -UTEST(sp_fmt_transform, composes_with_wrappers) { - sp_fmt_directive_reset(); - sp_fmt_register_decorator("wrap", _test_before_lt, _test_after_gt); - sp_fmt_register_transformer("upper", _test_transform_upper); - sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.wrap .upper}", sp_fmt_cstr("hi")).value; - EXPECT_TRUE(sp_str_equal_cstr(got, "")); - sp_fmt_directive_reset(); -} - -UTEST(sp_fmt_transform, stacked_innermost_first) { - sp_fmt_directive_reset(); - sp_fmt_register_transformer("upper", _test_transform_upper); - sp_fmt_register_transformer("redact", _test_transform_redact); - sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.upper .redact}", sp_fmt_cstr("hi")).value; - EXPECT_TRUE(sp_str_equal_cstr(got, "**")); - sp_str_t got2 = sp_fmt(sp_mem_get_scratch(), "{.redact .upper}", sp_fmt_cstr("hi")).value; - EXPECT_TRUE(sp_str_equal_cstr(got2, "**")); - sp_fmt_directive_reset(); -} - -UTEST(sp_fmt_transform, measures_post_transform_width) { - sp_fmt_directive_reset(); - sp_fmt_register_transformer("redact", _test_transform_redact); - sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:10 .redact}", sp_fmt_cstr("hi")).value; - EXPECT_TRUE(sp_str_equal_cstr(got, " **")); - sp_fmt_directive_reset(); -} - -UTEST(sp_fmt_transform, into_writer_backed_builder) { - sp_fmt_directive_reset(); - sp_fmt_register_transformer("upper", _test_transform_upper); - - c8 buf[64] = sp_zero; - sp_io_mem_writer_t w = sp_zero; - sp_io_mem_writer_from_buffer(&w, buf, sizeof(buf)); - - sp_mem_arena_marker_t s = sp_mem_begin_scratch(); - sp_fmt_io(&w.base, s.mem, "{.upper}", sp_fmt_cstr("hello")); - sp_mem_end_scratch(s); - sp_str_t got = { .data = buf, .len = 5 }; - EXPECT_TRUE(sp_str_equal_cstr(got, "HELLO")); - sp_fmt_directive_reset(); -} - -UTEST(sp_fmt_builtin_transform, upper) { - sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.upper}", sp_fmt_cstr("hello")).value; - EXPECT_TRUE(sp_str_equal_cstr(got, "HELLO")); -} - -UTEST(sp_fmt_builtin_transform, redact) { - sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.redact}", sp_fmt_cstr("secret")).value; - EXPECT_TRUE(sp_str_equal_cstr(got, "******")); -} - -UTEST(fmt, unsigned_integers) { +UTEST_F(format, unsigned_integers) { // sp_parse_u8 ASSERT_EQ(sp_parse_u8(sp_str_lit("0")), 0); ASSERT_EQ(sp_parse_u8(sp_str_lit("255")), 255); @@ -1260,7 +1087,7 @@ UTEST(fmt, unsigned_integers) { // Would assert: "18446744073709551616", "-1", "invalid" } -UTEST(fmt, signed_integers) { +UTEST_F(format, signed_integers) { // sp_parse_s8 ASSERT_EQ(sp_parse_s8(sp_str_lit("0")), 0); ASSERT_EQ(sp_parse_s8(sp_str_lit("127")), 127); @@ -1294,7 +1121,7 @@ UTEST(fmt, signed_integers) { // Would assert: "9223372036854775808", "-9223372036854775809", "abc" } -UTEST(fmt, floating_point) { +UTEST_F(format, floating_point) { // sp_parse_f32 ASSERT_NEAR(sp_parse_f32(sp_str_lit("0")), 0.0f, 1e-5f); ASSERT_NEAR(sp_parse_f32(sp_str_lit("0.0")), 0.0f, 1e-5f); @@ -1320,7 +1147,7 @@ UTEST(fmt, floating_point) { // Would assert: "nan", "inf", "invalid", "" } -UTEST(fmt, hex) { +UTEST_F(format, hex) { ASSERT_EQ(sp_parse_hex(sp_str_lit("0")), 0ULL); ASSERT_EQ(sp_parse_hex(sp_str_lit("F")), 0xFULL); ASSERT_EQ(sp_parse_hex(sp_str_lit("f")), 0xfULL); @@ -1333,7 +1160,7 @@ UTEST(fmt, hex) { // Would assert: "G", "xyz", "-F", "", "0x" prefix, "0123" octal notation } -UTEST(fmt, hash) { +UTEST_F(format, hash) { ASSERT_EQ(sp_parse_hash(sp_str_lit("0")), 0U); ASSERT_EQ(sp_parse_hash(sp_str_lit("FFFFFFFF")), 0xFFFFFFFFU); ASSERT_EQ(sp_parse_hash(sp_str_lit("12345678")), 0x12345678U); @@ -1343,7 +1170,7 @@ UTEST(fmt, hash) { // Would assert: "G", "12345678901", "-1", "" } -UTEST(fmt, boolean) { +UTEST_F(format, boolean) { ASSERT_EQ(sp_parse_bool(sp_str_lit("true")), true); ASSERT_EQ(sp_parse_bool(sp_str_lit("false")), false); ASSERT_EQ(sp_parse_bool(sp_str_lit("1")), true); @@ -1352,7 +1179,7 @@ UTEST(fmt, boolean) { // Would assert: "maybe", "2", "TRUE", "", "yes", "no", "on", "off" } -UTEST(fmt, characters) { +UTEST_F(format, characters) { // sp_parse_c8 - expects single quoted chars like 'A' ASSERT_EQ(sp_parse_c8(sp_str_lit("'A'")), 'A'); ASSERT_EQ(sp_parse_c8(sp_str_lit("'z'")), 'z'); @@ -1370,7 +1197,7 @@ UTEST(fmt, characters) { // Would assert: "AB", "", "abc", "A" (no quotes) } -UTEST(fmt, extended) { +UTEST_F(format, extended) { u32 u32_val; ASSERT_TRUE(sp_parse_u32_ex(sp_str_lit("42"), &u32_val)); ASSERT_EQ(u32_val, 42U); @@ -1500,7 +1327,7 @@ UTEST(fmt, extended) { ASSERT_FALSE(sp_parse_s64_ex(sp_str_lit("invalid"), &s64_val)); } -UTEST(fmt, parse_edge_cases) { +UTEST_F(format, parse_edge_cases) { ASSERT_EQ(sp_parse_u32(sp_str_lit("00042")), 42U); ASSERT_EQ(sp_parse_s32(sp_str_lit("-00042")), -42); ASSERT_NEAR(sp_parse_f32(sp_str_lit("003.14")), 3.14f, 1e-5f); @@ -1518,37 +1345,263 @@ UTEST(fmt, parse_edge_cases) { ASSERT_EQ(sp_parse_u64(sp_str_lit("18446744073709551615")), 18446744073709551615ULL); } -UTEST(fmt, hex_zero) { +UTEST_F(format, hex_zero) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.hex}", sp_fmt_uint(0)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "0x0")); } -UTEST(fmt, hex_small) { +UTEST_F(format, hex_small) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.hex}", sp_fmt_uint(0xa)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "0xA")); } -UTEST(fmt, hex_no_pad) { +UTEST_F(format, hex_no_pad) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.hex}", sp_fmt_uint(0xa5)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "0xA5")); } -UTEST(fmt, hex_word) { +UTEST_F(format, hex_word) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.hex}", sp_fmt_uint(0xdeadbeef)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "0xDEADBEEF")); } -UTEST(fmt, hex_u64_max) { +UTEST_F(format, hex_u64_max) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.hex}", sp_fmt_uint(0xffffffffffffffffULL)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "0xFFFFFFFFFFFFFFFF")); } -UTEST(fmt, hex_signed_negative) { +UTEST_F(format, hex_signed_negative) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.hex}", sp_fmt_int(-1)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "0xFFFFFFFFFFFFFFFF")); } -UTEST(fmt, hex_mixed_digits) { +UTEST_F(format, hex_mixed_digits) { sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.hex}", sp_fmt_uint(0x1234abcd)).value; EXPECT_TRUE(sp_str_equal_cstr(got, "0x1234ABCD")); } + +UTEST_F(format, italic) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.italic}", sp_fmt_cstr("hi")).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "\033[3mhi\033[0m")); +} + +UTEST_F(format, gray) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.gray}", sp_fmt_cstr("hi")).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "\033[90mhi\033[0m")); +} + +UTEST_F(format, bright_red) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.br_red}", sp_fmt_cstr("hi")).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "\033[91mhi\033[0m")); +} + +UTEST_F(format, bold_int) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.bold}", sp_fmt_int(42)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "\033[1m42\033[0m")); +} + +UTEST_F(format, hyperlink) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.hyperlink}", sp_fmt_cstr("https://x")).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "\033]8;;https://x\033\\https://x\033[0m")); +} + +UTEST_F(format, quote_basic) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.quote}", sp_fmt_cstr("hi")).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "\"hi\"")); +} + +UTEST_F(format, quote_with_color) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.quote .red}", sp_fmt_cstr("hi")).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "\"\033[31mhi\033[0m\"")); +} + +UTEST_F(format, red_with_width_pads_outside_ansi) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:>10 .red}", sp_fmt_cstr("hi")).value; + EXPECT_TRUE(sp_str_equal_cstr(got, " \033[31mhi\033[0m")); +} + +UTEST_F(format, cyan_with_fill_center_pads_outside_ansi) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:*^9 .cyan}", sp_fmt_cstr("x")).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "****\033[36mx\033[0m****")); +} + +UTEST_F(format, bold_with_width_on_int) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:>6 .bold}", sp_fmt_int(42)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, " \033[1m42\033[0m")); +} + +UTEST_F(format, quote_with_width_pads_outside_quotes) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:*^8 .quote}", sp_fmt_cstr("hi")).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "***\"hi\"***")); +} + +static void _test_before_only(sp_io_writer_t* io, sp_fmt_arg_t* arg, sp_fmt_arg_t* param) { + (void)arg; (void)param; + sp_io_write_cstr(io, "[", SP_NULLPTR); +} + +UTEST_F(format, null_after_callback) { + sp_fmt_directive_reset(); + sp_fmt_register_decorator("openonly", _test_before_only, SP_NULLPTR); + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.openonly}", sp_fmt_cstr("hi")).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "[hi")); + sp_fmt_directive_reset(); +} + +UTEST_F(format, null_before_callback) { + sp_fmt_directive_reset(); + sp_fmt_register_decorator("closeonly", SP_NULLPTR, _test_after_gt); + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.closeonly}", sp_fmt_cstr("hi")).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "hi>")); + sp_fmt_directive_reset(); +} + +UTEST_F(format, no_allocator) { + c8 buffer[64] = sp_zero; + sp_str_t got = sp_fmt_buf(buffer, 64, "hello, {}", sp_fmt_cstr("world")).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "hello, world")); +} + +UTEST_F(format, with_decorator_and_width) { + c8 buffer[64] = sp_zero; + sp_str_t got = sp_fmt_buf(buffer, 64, "{:>6 .red}", sp_fmt_cstr("hi")).value; + EXPECT_TRUE(sp_str_equal_cstr(got, " \033[31mhi\033[0m")); +} + +UTEST_F(format, multiple_calls_same_writer) { + c8 buffer[64] = sp_zero; + sp_io_mem_writer_t w = sp_zero; + sp_io_mem_writer_from_buffer(&w, buffer, sizeof(buffer)); + sp_fmt_io(&w.base, "[{}]", sp_fmt_cstr("a")); + sp_io_write_c8(&w.base, ' '); + sp_fmt_io(&w.base, "[{}]", sp_fmt_cstr("b")); + sp_str_t got = sp_io_mem_writer_as_str(&w); + EXPECT_TRUE(sp_str_equal_cstr(got, "[a] [b]")); +} + +UTEST_F(format, pointer) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), + "{:>16}", sp_fmt_ptr((void*)(uintptr_t)0xabcdULL)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, " 0xabcd")); +} + +UTEST_F(format, negative_int_right_align) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:>8}", sp_fmt_int(-42)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, " -42")); +} + +UTEST_F(format, negative_int_zero_fill) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:0>8}", sp_fmt_int(-42)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "00000-42")); +} + +UTEST_F(format, empty_string_center_fill) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:*^6}", sp_fmt_cstr("")).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "******")); +} + +UTEST_F(format, float_precision_and_width) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:>10.2}", sp_fmt_float(3.14159)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, " 3.14")); +} + +UTEST_F(format, float_precision_center_fill) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{:*^10.2}", sp_fmt_float(1.5)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "***1.50***")); +} + +UTEST_F(format, bytes_zero) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.bytes}", sp_fmt_uint(0)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "0 B")); +} + +UTEST_F(format, bytes_below_kb) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.bytes}", sp_fmt_uint(1023)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "1023 B")); +} + +UTEST_F(format, bytes_exact_kb) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.bytes}", sp_fmt_uint(1024)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "1 KB")); +} + +UTEST_F(format, bytes_exact_mb) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.bytes}", sp_fmt_uint(1024ULL * 1024ULL)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "1 MB")); +} + +UTEST_F(format, bytes_fractional) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.bytes}", sp_fmt_uint(1536)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "1.5 KB")); +} + +UTEST_F(format, ordinal_1st) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.ordinal}", sp_fmt_int(1)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "1st")); +} + +UTEST_F(format, ordinal_2nd) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.ordinal}", sp_fmt_int(2)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "2nd")); +} + +UTEST_F(format, ordinal_3rd) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.ordinal}", sp_fmt_int(3)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "3rd")); +} + +UTEST_F(format, ordinal_4th) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.ordinal}", sp_fmt_int(4)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "4th")); +} + +UTEST_F(format, ordinal_11th) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.ordinal}", sp_fmt_int(11)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "11th")); +} + +UTEST_F(format, ordinal_12th) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.ordinal}", sp_fmt_int(12)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "12th")); +} + +UTEST_F(format, ordinal_13th) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.ordinal}", sp_fmt_int(13)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "13th")); +} + +UTEST_F(format, ordinal_21st) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.ordinal}", sp_fmt_int(21)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "21st")); +} + +UTEST_F(format, ordinal_negative) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.ordinal}", sp_fmt_int(-1)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "-1st")); +} + +UTEST_F(format, duration_sub_us) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.duration}", sp_fmt_uint(999)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "999 ns")); +} + +UTEST_F(format, duration_exact_us) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.duration}", sp_fmt_uint(1000)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "1 us")); +} + +UTEST_F(format, duration_fractional_ms) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.duration}", sp_fmt_uint(1500000)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "1.5 ms")); +} + +UTEST_F(format, duration_exact_s) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.duration}", sp_fmt_uint(1000000000)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "1 s")); +} + +UTEST_F(format, iso_epoch_zero) { + sp_str_t got = sp_fmt(sp_mem_get_scratch(), "{.iso}", sp_fmt_uint(0)).value; + EXPECT_TRUE(sp_str_equal_cstr(got, "1970-01-01T00:00:00.000Z")); +} + diff --git a/test/fs/canonicalize_path.c b/test/fs/canonicalize_path.c index 6a3f760..7fa5a8f 100644 --- a/test/fs/canonicalize_path.c +++ b/test/fs/canonicalize_path.c @@ -267,8 +267,6 @@ UTEST_F(fs, canon_symlink_with_dotdot) { }); } -// ---- idempotency ---- - UTEST_F(fs, canon_idempotent) { SKIP_ON_WASM() sp_mem_t a = ut.file_manager.mem; @@ -298,8 +296,6 @@ UTEST_F(fs, canon_exe_idempotent) { SP_EXPECT_STR_EQ(canonical, exe); } -// ---- cwd interaction ---- - UTEST_F(fs, canon_cwd_matches_dot) { SKIP_ON_WASM() sp_mem_t a = ut.file_manager.mem; diff --git a/test/fs/links.c b/test/fs/links.c index 59e1e30..357c3af 100644 --- a/test/fs/links.c +++ b/test/fs/links.c @@ -48,7 +48,7 @@ UTEST_F(fs, create_hard_link_file) { sp_str_t link = sp_fs_join_path(a, sandbox, sp_str_lit("file.hard")); sp_io_file_writer_t writer = sp_zero; - sp_io_file_writer_from_path(&writer, source, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&writer, source); sp_io_write_str(&writer.base, sp_str_lit("updated"), SP_NULLPTR); sp_io_file_writer_close(&writer); sp_str_t link_content = sp_zero; diff --git a/test/fs/mod_time.c b/test/fs/mod_time.c index e9472ee..55d1412 100644 --- a/test/fs/mod_time.c +++ b/test/fs/mod_time.c @@ -23,7 +23,7 @@ UTEST_F(fs, mod_time_updates_after_write) { sp_os_sleep_ms(100); sp_io_file_writer_t writer = sp_zero; - sp_io_file_writer_from_path(&writer, file, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&writer, file); sp_io_write_str(&writer.base, sp_str_lit("b"), SP_NULLPTR); sp_io_file_writer_close(&writer); diff --git a/test/fs/system_paths.c b/test/fs/system_paths.c index ba1e978..111c1f8 100644 --- a/test/fs/system_paths.c +++ b/test/fs/system_paths.c @@ -35,18 +35,3 @@ UTEST(fs_system_paths, config_path_normalized) { assert_normalized(&ur, path, "config_path"); } -UTEST(fs_system_paths, storage_path_exists) { - SKIP_ON_WASM() - sp_mem_t a = sp_mem_os_new(); - sp_str_t path = sp_fs_get_storage_path(a); - ASSERT_TRUE(sp_fs_is_dir(path)); -} - -UTEST(fs_system_paths, config_path_exists) { - SKIP_ON_WASM() - sp_mem_t a = sp_mem_os_new(); - sp_str_t path = sp_fs_get_config_path(a); - ASSERT_TRUE(sp_fs_is_dir(path)); -} - - diff --git a/test/io/copy.c b/test/io/copy.c index 1d103c9..241ad58 100644 --- a/test/io/copy.c +++ b/test/io/copy.c @@ -94,10 +94,10 @@ void run_io_mock_copy_writer_test(int* utest_result, io_mock_copy_writer_test_t UTEST_F(io_copy, reader_fails_after_success) { run_io_mock_copy_reader_test(utest_result, (io_mock_copy_reader_test_t){ .results = { - { .bytes = 3, .data = "abc", .err = SP_OK }, + { .bytes = 3, .err = SP_OK, .data = "abc" }, { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, }, - .capacity.writer = 32, .buffer.copy = 8, + .buffer = { .copy = 8 }, .capacity = { .writer = 32 }, .expect = { .err = SP_ERR_IO_READ_FAILED, .copied = 3, .final = "abc" }, }); } @@ -107,9 +107,9 @@ UTEST_F(io_copy, reader_fails_after_success) { UTEST_F(io_copy, reader_bytes_and_error) { run_io_mock_copy_reader_test(utest_result, (io_mock_copy_reader_test_t){ .results = { - { .bytes = 3, .data = "abc", .err = SP_ERR_IO_READ_FAILED }, + { .bytes = 3, .err = SP_ERR_IO_READ_FAILED, .data = "abc" }, }, - .capacity.writer = 32, .buffer.copy = 8, + .buffer = { .copy = 8 }, .capacity = { .writer = 32 }, .expect = { .err = SP_ERR_IO_READ_FAILED, .copied = 3, .final = "abc" }, }); } @@ -122,7 +122,7 @@ UTEST_F(io_copy, writer_partial_in_call) { .responses = { { .bytes = 4, .err = SP_ERR_IO_NO_SPACE }, }, - .buffer.copy = 8, + .buffer = { .copy = 8 }, .expect = { .err = SP_ERR_IO_NO_SPACE, .copied = 4, .received = "0123" }, }); } @@ -135,7 +135,7 @@ UTEST_F(io_copy, writer_fails_immediately) { .responses = { { .bytes = 0, .err = SP_ERR_IO_WRITE_FAILED }, }, - .buffer.copy = 8, + .buffer = { .copy = 8 }, .expect = { .err = SP_ERR_IO_WRITE_FAILED, .copied = 0, .received = "" }, }); } @@ -152,12 +152,12 @@ UTEST_F(io_copy, writer_fails_immediately) { UTEST_F(io_copy, buffered_reader_drains_through_buffer) { run_io_mock_copy_reader_test(utest_result, (io_mock_copy_reader_test_t){ .results = { - { .bytes = 4, .data = "0123", .err = SP_OK }, - { .bytes = 4, .data = "4567", .err = SP_OK }, + { .bytes = 4, .err = SP_OK, .data = "0123" }, + { .bytes = 4, .err = SP_OK, .data = "4567" }, { .bytes = 0, .err = SP_ERR_IO_EOF }, }, - .buffer = { .r = 4, .copy = 2 }, - .capacity.writer = 32, + .buffer = { .copy = 2, .r = 4 }, + .capacity = { .writer = 32 }, .expect = { .err = SP_OK, .copied = 8, .final = "01234567" }, }); } @@ -167,11 +167,11 @@ UTEST_F(io_copy, buffered_reader_drains_through_buffer) { UTEST_F(io_copy, buffered_reader_one_fill_many_drains) { run_io_mock_copy_reader_test(utest_result, (io_mock_copy_reader_test_t){ .results = { - { .bytes = 8, .data = "ABCDEFGH", .err = SP_OK }, + { .bytes = 8, .err = SP_OK, .data = "ABCDEFGH" }, { .bytes = 0, .err = SP_ERR_IO_EOF }, }, - .buffer = { .r = 8, .copy = 2 }, - .capacity.writer = 32, + .buffer = { .copy = 2, .r = 8 }, + .capacity = { .writer = 32 }, .expect = { .err = SP_OK, .copied = 8, .final = "ABCDEFGH" }, }); } @@ -186,7 +186,7 @@ UTEST_F(io_copy, buffered_writer_overflow_flushes_partial) { .responses = { { .bytes = 4, .err = SP_OK }, }, - .buffer = { .write = 4, .copy = 2 }, + .buffer = { .copy = 2, .write = 4 }, .expect = { .err = SP_OK, .copied = 8, .received = "0123" }, }); } @@ -247,7 +247,7 @@ UTEST_F(io_copy, fast_path_taken_when_both_sides_support) { u64 n = sp_cstr_len(content); { sp_io_file_writer_t fw = sp_zero; - sp_io_file_writer_from_path(&fw, path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&fw, path); sp_io_write(&fw.base, content, n, SP_NULLPTR); sp_io_file_writer_close(&fw); } @@ -302,7 +302,7 @@ UTEST_F(io_copy, fast_path_unimplemented_falls_through) { u64 n = sp_cstr_len(content); { sp_io_file_writer_t fw = sp_zero; - sp_io_file_writer_from_path(&fw, path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&fw, path); sp_io_write(&fw.base, content, n, SP_NULLPTR); sp_io_file_writer_close(&fw); } @@ -325,6 +325,60 @@ UTEST_F(io_copy, fast_path_unimplemented_falls_through) { sp_test_file_manager_cleanup(&fm); } +// End-to-end: regular file -> pipe via stream_writer. On Linux this exercises +// the sendfile fast path; elsewhere it falls back to the generic loop. The +// bytes that emerge on the pipe's read end must match the source content. +UTEST_F(io_copy, file_to_pipe_sendfile) { + SKIP_ON_WASM() + sp_test_file_manager_t fm = sp_zero; + sp_test_file_manager_init(&fm); + sp_str_t path = sp_test_file_create_empty(&fm, sp_str_lit("sendfile_src.bin")); + + u8 source [4096]; + sp_for(i, sizeof(source)) source[i] = (u8)((i * 2654435761u + 7) >> 8); + { + sp_io_file_writer_t fw = sp_zero; + sp_io_file_writer_from_path(&fw, path); + EXPECT_EQ(sp_io_write(&fw.base, source, sizeof(source), SP_NULLPTR), SP_OK); + sp_io_file_writer_close(&fw); + } + + sp_sys_fd_t pipe_r = SP_SYS_INVALID_FD; + sp_sys_fd_t pipe_w = SP_SYS_INVALID_FD; + EXPECT_EQ(sp_sys_pipe(&pipe_r, &pipe_w), 0); + + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_path(&r, path); + + sp_io_stream_writer_t w = sp_zero; + sp_io_stream_writer_from_fd(&w, pipe_w, SP_IO_CLOSE_MODE_AUTO); + + u64 copied = 0; + EXPECT_EQ(sp_io_copy(&w.base, &r.base, &copied), SP_OK); + EXPECT_EQ(copied, sizeof(source)); + + sp_io_file_reader_close(&r); + sp_io_stream_writer_close(&w); + + sp_io_stream_reader_t sr = sp_zero; + sp_io_stream_reader_from_fd(&sr, pipe_r, SP_IO_CLOSE_MODE_AUTO); + + u8 received [sizeof(source)] = sp_zero; + u64 got = 0; + while (got < sizeof(received)) { + u64 chunk = 0; + sp_err_t err = sp_io_read(&sr.base, received + got, sizeof(received) - got, &chunk); + got += chunk; + if (err == SP_ERR_IO_EOF) break; + EXPECT_EQ(err, SP_OK); + } + EXPECT_EQ(got, sizeof(source)); + sp_for(i, sizeof(source)) EXPECT_EQ(received[i], source[i]); + + sp_io_stream_reader_close(&sr); + sp_test_file_manager_cleanup(&fm); +} + // Writer buffer attached; backend errors on the flush that the wrapper // triggers when the buffer overflows. Copy surfaces the error. UTEST_F(io_copy, buffered_writer_flush_error) { @@ -333,7 +387,7 @@ UTEST_F(io_copy, buffered_writer_flush_error) { .responses = { { .bytes = 0, .err = SP_ERR_IO_WRITE_FAILED }, }, - .buffer = { .write = 4, .copy = 2 }, + .buffer = { .copy = 2, .write = 4 }, .expect = { .err = SP_ERR_IO_WRITE_FAILED, .copied = 4, .received = "" }, }); } diff --git a/test/io/dyn.c b/test/io/dyn.c index b27092c..72eb715 100644 --- a/test/io/dyn.c +++ b/test/io/dyn.c @@ -63,34 +63,36 @@ void run_io_dyn_test(int* utest_result, sp_mem_t mem, io_dyn_test_t t) { } UTEST_F(io, dyn_write) { - run_io_dyn_test(utest_result, ut.mem, (io_dyn_test_t){ + run_io_dyn_test(utest_result, ut.mem, SP_RVAL(io_dyn_test_t){ .steps = { { .kind = IO_STEP_WRITE, .write = { "abcd", SP_OK, 4 } }, { .kind = IO_STEP_SIZE, .size = { SP_OK, 4 } }, }, - .expect.content = "abcd", + .expect = { + .content = "abcd", + } }); } UTEST_F(io, dyn_multiple_writes) { - run_io_dyn_test(utest_result, ut.mem, (io_dyn_test_t){ + run_io_dyn_test(utest_result, ut.mem, SP_RVAL(io_dyn_test_t){ .steps = { { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, { .kind = IO_STEP_WRITE, .write = { "def", SP_OK, 3 } }, { .kind = IO_STEP_WRITE, .write = { "ghi", SP_OK, 3 } }, { .kind = IO_STEP_SIZE, .size = { SP_OK, 9 } }, }, - .expect.content = "abcdefghi", + .expect = { .content = "abcdefghi" }, }); } UTEST_F(io, dyn_seek) { - run_io_dyn_test(utest_result, ut.mem, (io_dyn_test_t){ + run_io_dyn_test(utest_result, ut.mem, SP_RVAL(io_dyn_test_t){ .steps = { { .kind = IO_STEP_WRITE, .write = { "12345678", SP_OK, 8 } }, { .kind = IO_STEP_SEEK, .seek = { 4, SP_IO_SEEK_SET, SP_OK, 4 } }, }, - .expect.content = "12345678", + .expect = { .content = "12345678" }, }); } diff --git a/test/io/file.c b/test/io/file.c index 9cc25be..7e61c2c 100644 --- a/test/io/file.c +++ b/test/io/file.c @@ -12,7 +12,7 @@ typedef struct { void run_io_file_reader_test(int* utest_result, sp_str_t path, io_file_reader_test_t t) { { sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&w, path); if (t.content) sp_io_write(&w.base, t.content, sp_cstr_len(t.content), SP_NULLPTR); sp_io_file_writer_close(&w); } @@ -145,7 +145,6 @@ UTEST_F(io, file_reader_nonexistent) { //////////// typedef struct { const c8* pre_content; - sp_io_write_mode_t mode; u64 buffer; io_step_t steps [IO_MAX_STEPS]; struct { @@ -156,13 +155,13 @@ typedef struct { void run_io_file_writer_test(int* utest_result, sp_mem_t mem, sp_str_t path, io_file_writer_test_t t) { if (t.pre_content) { sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&w, path); sp_io_write(&w.base, t.pre_content, sp_cstr_len(t.pre_content), SP_NULLPTR); sp_io_file_writer_close(&w); } sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, path, t.mode); + sp_io_file_writer_from_path(&w, path); u8 wrapper_buf [64] = sp_zero; if (t.buffer) sp_io_writer_set_buffer(&w.base, wrapper_buf, t.buffer); @@ -222,66 +221,50 @@ void run_io_file_writer_test(int* utest_result, sp_mem_t mem, sp_str_t path, io_ UTEST_F(io, file_writer_write) { run_io_file_writer_test(utest_result, ut.mem, ut.file_path, (io_file_writer_test_t){ - .mode = SP_IO_WRITE_MODE_OVERWRITE, .steps = { { .kind = IO_STEP_WRITE, .write = { "test data", SP_OK, 9 } }, }, - .expect.content = "test data", + .expect = { .content = "test data" }, }); } UTEST_F(io, file_writer_overwrite) { run_io_file_writer_test(utest_result, ut.mem, ut.file_path, (io_file_writer_test_t){ .pre_content = "XXXXXXXX", - .mode = SP_IO_WRITE_MODE_OVERWRITE, .steps = { { .kind = IO_STEP_WRITE, .write = { "1234", SP_OK, 4 } }, }, - .expect.content = "1234", - }); -} - -UTEST_F(io, file_writer_append) { - run_io_file_writer_test(utest_result, ut.mem, ut.file_path, (io_file_writer_test_t){ - .pre_content = "first", - .mode = SP_IO_WRITE_MODE_APPEND, - .steps = { - { .kind = IO_STEP_WRITE, .write = { "second", SP_OK, 6 } }, - }, - .expect.content = "firstsecond", + .expect = { .content = "1234" }, }); } UTEST_F(io, file_writer_size) { run_io_file_writer_test(utest_result, ut.mem, ut.file_path, (io_file_writer_test_t){ - .mode = SP_IO_WRITE_MODE_OVERWRITE, .steps = { { .kind = IO_STEP_WRITE, .write = { "0123456789ABCDEF", SP_OK, 16 } }, { .kind = IO_STEP_SIZE, .size = { SP_OK, 16 } }, }, - .expect.content = "0123456789ABCDEF", + .expect = { .content = "0123456789ABCDEF" }, }); } UTEST_F(io, file_writer_buffered_implicit_flush) { run_io_file_writer_test(utest_result, ut.mem, ut.file_path, (io_file_writer_test_t){ - .mode = SP_IO_WRITE_MODE_OVERWRITE, .buffer = 64, .steps = { { .kind = IO_STEP_WRITE, .write = { "hello", SP_OK, 5 } }, }, - .expect.content = "hello", + .expect = { .content = "hello" }, }); } UTEST_F(io, file_writer_buffered_larger_than_buffer) { run_io_file_writer_test(utest_result, ut.mem, ut.file_path, (io_file_writer_test_t){ - .mode = SP_IO_WRITE_MODE_OVERWRITE, .buffer = 4, .steps = { { .kind = IO_STEP_WRITE, .write = { "0123456789ABCDEF", SP_OK, 16 } }, }, - .expect.content = "0123456789ABCDEF", + .expect = { .content = "0123456789ABCDEF" }, }); } @@ -296,7 +279,7 @@ UTEST_F(io, file_writer_buffered_larger_than_buffer) { UTEST_F(io, file_writer_pad) { SKIP_ON_WASM() sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&w, ut.file_path); sp_io_write(&w.base, "AA", 2, SP_NULLPTR); sp_io_pad(&w.base, 3, SP_NULLPTR); sp_io_write(&w.base, "BB", 2, SP_NULLPTR); @@ -323,7 +306,7 @@ UTEST_F(io, file_writer_pad) { UTEST_F(io, file_reader_as_fd) { SKIP_ON_WASM() sp_io_file_writer_t fw = sp_zero; - sp_io_file_writer_from_path(&fw, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&fw, ut.file_path); sp_io_write(&fw.base, "x", 1, SP_NULLPTR); sp_io_file_writer_close(&fw); @@ -331,22 +314,15 @@ UTEST_F(io, file_reader_as_fd) { sp_io_file_reader_from_path(&r, ut.file_path); EXPECT_TRUE(r.base.as_fd != SP_NULLPTR); - sp_io_file_t fd = SP_SYS_INVALID_FD; - EXPECT_EQ(r.base.as_fd(&r.base, &fd), SP_OK); + sp_sys_fd_t fd = SP_SYS_INVALID_FD; + u64* pos = SP_NULLPTR; + EXPECT_EQ(r.base.as_fd(&r.base, &fd, &pos), SP_OK); EXPECT_EQ((s64)fd, (s64)r.file); + EXPECT_EQ(pos, &r.pos); sp_io_file_reader_close(&r); } -// File writer's read_from callback is set at construction. -UTEST_F(io, file_writer_read_from_set) { - SKIP_ON_WASM() - sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); - EXPECT_TRUE(w.base.read_from != SP_NULLPTR); - sp_io_file_writer_close(&w); -} - // End-to-end: copy a non-trivial file via sp_io_copy and verify the // destination matches. On Linux this exercises copy_file_range; elsewhere it // falls back to the generic loop. Either way, the contents should be @@ -359,7 +335,7 @@ UTEST_F(io, file_to_file_copy) { sp_for(i, sizeof(source)) source[i] = (u8)((i * 1103515245u + 12345u) >> 8); sp_io_file_writer_t sw = sp_zero; - sp_io_file_writer_from_path(&sw, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&sw, ut.file_path); EXPECT_EQ(sp_io_write(&sw.base, source, sizeof(source), SP_NULLPTR), SP_OK); sp_io_file_writer_close(&sw); @@ -368,7 +344,7 @@ UTEST_F(io, file_to_file_copy) { sp_io_file_reader_t r = sp_zero; sp_io_file_reader_from_path(&r, ut.file_path); sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, dst_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&w, dst_path); u64 copied = 0; EXPECT_EQ(sp_io_copy(&w.base, &r.base, &copied), SP_OK); @@ -395,7 +371,7 @@ UTEST_F(io, file_copy_fast_path_falls_back_for_mem_source) { EXPECT_TRUE(r.as_fd == SP_NULLPTR); // Mem reader has no fd to hand out. sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&w, ut.file_path); u64 copied = 0; EXPECT_EQ(sp_io_copy(&w.base, &r, &copied), SP_OK); @@ -409,6 +385,36 @@ UTEST_F(io, file_copy_fast_path_falls_back_for_mem_source) { sp_for(i, n) EXPECT_EQ(loaded.data[i], content[i]); } +// This legitimately fails on Windows, but it's just because Windows doesn't give us the +// same guarantee as POSIX when it comes to the state of the kernel's cursor when you +// do positional IO. It's not a bug. If you're using the file reader, you're ignoring +// the kernel's cursor by definition. +// +// The only way this surfaces is if you mix positional and streaming IO on the same kernel +// handle, which is definitely a user error. +UTEST_F(io, file_reader_positional_does_not_touch_kernel_cursor) { + SKIP_ON_WASM() + SKIP_ON_WIN32() + sp_io_file_writer_t w = sp_zero; + sp_io_file_writer_from_path(&w, ut.file_path); + sp_io_write(&w.base, "0123456789ABCDEF", 16, SP_NULLPTR); + sp_io_file_writer_close(&w); + + sp_sys_fd_t fd = sp_sys_open_s(sp_fs_open_cwd(), ut.file_path, SP_O_RDONLY | SP_O_BINARY, 0); + const s64 parked = 7; + sp_sys_lseek(fd, parked, SP_IO_SEEK_SET); + + sp_io_file_reader_t r = sp_zero; + sp_io_file_reader_from_file(&r, fd, SP_IO_CLOSE_MODE_NONE); + + u8 buf [4] = sp_zero; + sp_io_read(&r.base, buf, 4, SP_NULLPTR); + EXPECT_EQ(sp_sys_lseek(fd, 0, SP_IO_SEEK_CUR), parked); + + sp_io_file_reader_close(&r); + sp_sys_close(fd); +} + // Two file handles (writer then reader) operating on the same large offset; // doesn't fit the single-subject runner pattern. UTEST_F(io, file_seek_beyond_4gb) { @@ -417,7 +423,7 @@ UTEST_F(io, file_seek_beyond_4gb) { u8 marker [4] = {0xDE, 0xAD, 0xBE, 0xEF}; sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, ut.file_path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&w, ut.file_path); s64 seek_pos = 0; EXPECT_EQ(sp_io_file_writer_seek(&w, offset, SP_IO_SEEK_SET, &seek_pos), SP_OK); EXPECT_EQ(seek_pos, offset); @@ -426,9 +432,9 @@ UTEST_F(io, file_seek_beyond_4gb) { sp_io_file_reader_t r = sp_zero; sp_io_file_reader_from_path(&r, ut.file_path); - s64 end = sp_sys_lseek(r.file, 0, SP_IO_SEEK_END); - sp_sys_lseek(r.file, 0, SP_IO_SEEK_SET); - EXPECT_EQ((u64)end, (u64)offset + 4); + u64 end = 0; + EXPECT_EQ(sp_io_file_reader_size(&r, &end), SP_OK); + EXPECT_EQ(end, (u64)offset + 4); s64 read_pos = 0; EXPECT_EQ(sp_io_file_reader_seek(&r, offset, SP_IO_SEEK_SET, &read_pos), SP_OK); diff --git a/test/io/mem.c b/test/io/mem.c index 5217ba3..d444a4a 100644 --- a/test/io/mem.c +++ b/test/io/mem.c @@ -167,7 +167,7 @@ UTEST_F(io_mem, write_fits) { .steps = { { .kind = IO_STEP_WRITE, .write = { "hello", SP_OK, 5 } }, }, - .expect.content = "hello", + .expect = { .content = "hello" }, }); } @@ -177,7 +177,7 @@ UTEST_F(io_mem, write_exact_fit) { .steps = { { .kind = IO_STEP_WRITE, .write = { "abcd", SP_OK, 4 } }, }, - .expect.content = "abcd", + .expect = { .content = "abcd" }, }); } @@ -187,7 +187,7 @@ UTEST_F(io_mem, write_overflow) { .steps = { { .kind = IO_STEP_WRITE, .write = { "abcdefgh", SP_ERR_IO_NO_SPACE, 4 } }, }, - .expect.content = "abcd", + .expect = { .content = "abcd" }, }); } @@ -197,7 +197,7 @@ UTEST_F(io_mem, write_barely_overflows) { .steps = { { .kind = IO_STEP_WRITE, .write = { "abcde", SP_ERR_IO_NO_SPACE, 4 } }, }, - .expect.content = "abcd", + .expect = { .content = "abcd" }, }); } @@ -208,7 +208,7 @@ UTEST_F(io_mem, write_smaller_after_overflow) { { .kind = IO_STEP_WRITE, .write = { "abcd", SP_OK, 4 } }, { .kind = IO_STEP_WRITE, .write = { "x", SP_ERR_IO_NO_SPACE, 0 } }, }, - .expect.content = "abcd", + .expect = { .content = "abcd" }, }); } @@ -220,7 +220,7 @@ UTEST_F(io_mem, write_appends) { { .kind = IO_STEP_WRITE, .write = { "def", SP_OK, 3 } }, { .kind = IO_STEP_WRITE, .write = { "ghi", SP_OK, 3 } }, }, - .expect.content = "abcdefghi", + .expect = { .content = "abcdefghi" }, }); } @@ -253,7 +253,7 @@ UTEST_F(io_mem, copy_full) { .steps = { { .kind = IO_STEP_COPY, .copy = { 8, SP_OK, 10 } }, }, - .expect.content = "0123456789", + .expect = { .content = "0123456789" }, }); } @@ -264,7 +264,7 @@ UTEST_F(io_mem, copy_loops) { .steps = { { .kind = IO_STEP_COPY, .copy = { 4, SP_OK, 16 } }, }, - .expect.content = "ABCDEFGHIJKLMNOP", + .expect = { .content = "ABCDEFGHIJKLMNOP" }, }); } @@ -285,7 +285,7 @@ UTEST_F(io_mem, copy_writer_no_space) { .steps = { { .kind = IO_STEP_COPY, .copy = { 8, SP_ERR_IO_NO_SPACE, 4 } }, }, - .expect.content = "0123", + .expect = { .content = "0123" }, }); } diff --git a/test/io/read.c b/test/io/read.c index 3b96d9e..109ab41 100644 --- a/test/io/read.c +++ b/test/io/read.c @@ -44,7 +44,7 @@ void run_io_mock_read_test(int* utest_result, io_mock_read_test_t t) { UTEST_F(io_read, smoke) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ .results = { - { .bytes = 3, .data = "abc", .err = SP_OK }, + { .bytes = 3, .err = SP_OK, .data = "abc" }, }, .steps = { { .kind = IO_STEP_READ, .read = { 8, SP_OK, "abc" } }, @@ -56,7 +56,7 @@ UTEST_F(io_read, smoke) { UTEST_F(io_read, short_then_eof) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ .results = { - { .bytes = 3, .data = "abc", .err = SP_OK }, + { .bytes = 3, .err = SP_OK, .data = "abc" }, { .bytes = 0, .err = SP_ERR_IO_EOF }, }, .steps = { @@ -110,7 +110,7 @@ UTEST_F(io_read, error_zero_bytes) { UTEST_F(io_read, error_after_success) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ .results = { - { .bytes = 4, .data = "abcd", .err = SP_OK }, + { .bytes = 4, .err = SP_OK, .data = "abcd" }, { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, }, .steps = { @@ -125,7 +125,7 @@ UTEST_F(io_read, error_after_success) { UTEST_F(io_read, bytes_and_error) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ .results = { - { .bytes = 3, .data = "abc", .err = SP_ERR_IO_READ_FAILED }, + { .bytes = 3, .err = SP_ERR_IO_READ_FAILED, .data = "abc" }, }, .steps = { { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_READ_FAILED, "abc" } }, @@ -159,7 +159,7 @@ UTEST_F(io_read, error_then_error) { UTEST_F(io_read, buffered_first_read_fills) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ .results = { - { .bytes = 8, .data = "01234567", .err = SP_OK }, + { .bytes = 8, .err = SP_OK, .data = "01234567" }, }, .buffer = 8, .steps = { @@ -173,7 +173,7 @@ UTEST_F(io_read, buffered_first_read_fills) { UTEST_F(io_read, buffered_drains_without_backend_call) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ .results = { - { .bytes = 8, .data = "01234567", .err = SP_OK }, + { .bytes = 8, .err = SP_OK, .data = "01234567" }, }, .buffer = 8, .steps = { @@ -188,8 +188,8 @@ UTEST_F(io_read, buffered_drains_without_backend_call) { UTEST_F(io_read, buffered_refill_after_exhaustion) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ .results = { - { .bytes = 4, .data = "abcd", .err = SP_OK }, - { .bytes = 4, .data = "efgh", .err = SP_OK }, + { .bytes = 4, .err = SP_OK, .data = "abcd" }, + { .bytes = 4, .err = SP_OK, .data = "efgh" }, }, .buffer = 4, .steps = { @@ -204,7 +204,7 @@ UTEST_F(io_read, buffered_refill_after_exhaustion) { UTEST_F(io_read, buffered_large_request_bypasses) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ .results = { - { .bytes = 16, .data = "0123456789ABCDEF", .err = SP_OK }, + { .bytes = 16, .err = SP_OK, .data = "0123456789ABCDEF" }, }, .buffer = 4, .steps = { @@ -218,8 +218,8 @@ UTEST_F(io_read, buffered_large_request_bypasses) { UTEST_F(io_read, buffered_drain_then_bypass) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ .results = { - { .bytes = 4, .data = "abcd", .err = SP_OK }, - { .bytes = 12, .data = "EFGHIJKLMNOP", .err = SP_OK }, + { .bytes = 4, .err = SP_OK, .data = "abcd" }, + { .bytes = 12, .err = SP_OK, .data = "EFGHIJKLMNOP" }, }, .buffer = 4, .steps = { @@ -247,7 +247,7 @@ UTEST_F(io_read, buffered_eof_immediate) { UTEST_F(io_read, buffered_eof_after_partial_drain) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ .results = { - { .bytes = 4, .data = "abcd", .err = SP_OK }, + { .bytes = 4, .err = SP_OK, .data = "abcd" }, { .bytes = 0, .err = SP_ERR_IO_EOF }, { .bytes = 0, .err = SP_ERR_IO_EOF }, }, @@ -279,7 +279,7 @@ UTEST_F(io_read, buffered_error_immediate) { UTEST_F(io_read, buffered_short_fill) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ .results = { - { .bytes = 2, .data = "ab", .err = SP_OK }, + { .bytes = 2, .err = SP_OK, .data = "ab" }, }, .buffer = 8, .steps = { @@ -294,7 +294,7 @@ UTEST_F(io_read, error_then_recovery) { run_io_mock_read_test(utest_result, (io_mock_read_test_t){ .results = { { .bytes = 0, .err = SP_ERR_IO_READ_FAILED }, - { .bytes = 3, .data = "abc", .err = SP_OK }, + { .bytes = 3, .err = SP_OK, .data = "abc" }, }, .steps = { { .kind = IO_STEP_READ, .read = { 8, SP_ERR_IO_READ_FAILED } }, diff --git a/test/io/seeking_reader.c b/test/io/seeking_reader.c index c21516f..b669df7 100644 --- a/test/io/seeking_reader.c +++ b/test/io/seeking_reader.c @@ -86,7 +86,7 @@ typedef struct { void run_io_seeking_reader_file_test(int* utest_result, sp_str_t path, io_seeking_reader_file_test_t t) { { sp_io_file_writer_t w = sp_zero; - sp_io_file_writer_from_path(&w, path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&w, path); if (t.content) sp_io_write(&w.base, t.content, sp_cstr_len(t.content), SP_NULLPTR); sp_io_file_writer_close(&w); } @@ -166,7 +166,7 @@ UTEST_F(io, seeking_reader_file_seek_invalid) { run_io_seeking_reader_file_test(utest_result, ut.file_path, (io_seeking_reader_file_test_t){ .content = "0123456789", .steps = { - { .kind = IO_STEP_SEEK, .seek = { -10, SP_IO_SEEK_SET, SP_ERR_IO_SEEK_FAILED, -1 } }, + { .kind = IO_STEP_SEEK, .seek = { -10, SP_IO_SEEK_SET, SP_ERR_IO_SEEK_INVALID, -1 } }, }, }); } diff --git a/test/io/write.c b/test/io/write.c index e6f1a2a..09b259a 100644 --- a/test/io/write.c +++ b/test/io/write.c @@ -71,7 +71,7 @@ UTEST_F(io_write, smoke) { .steps = { { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, }, - .expect.received = "abc", + .expect = { .received = "abc" }, }); } @@ -85,7 +85,7 @@ UTEST_F(io_write, partial_no_space) { .steps = { { .kind = IO_STEP_WRITE, .write = { "abcdefgh", SP_ERR_IO_NO_SPACE, 4 } }, }, - .expect.received = "abcd", + .expect = { .received = "abcd" }, }); } @@ -112,7 +112,7 @@ UTEST_F(io_write, error_after_success) { { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, { .kind = IO_STEP_WRITE, .write = { "xyz", SP_ERR_IO_WRITE_FAILED, 0 } }, }, - .expect.received = "abc", + .expect = { .received = "abc" }, }); } @@ -125,7 +125,7 @@ UTEST_F(io_write, bytes_and_error) { .steps = { { .kind = IO_STEP_WRITE, .write = { "abc", SP_ERR_IO_WRITE_FAILED, 2 } }, }, - .expect.received = "ab", + .expect = { .received = "ab" }, }); } @@ -141,7 +141,7 @@ UTEST_F(io_write, smaller_after_overflow) { { .kind = IO_STEP_WRITE, .write = { "abcd", SP_ERR_IO_NO_SPACE, 0 } }, { .kind = IO_STEP_WRITE, .write = { "xy", SP_OK, 2 } }, }, - .expect.received = "xy", + .expect = { .received = "xy" }, }); } @@ -195,7 +195,7 @@ UTEST_F(io_write, buffered_flush_drains_buffer) { { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, { .kind = IO_STEP_FLUSH, .flush = { SP_OK } }, }, - .expect.received = "abc", + .expect = { .received = "abc" }, }); } @@ -212,7 +212,7 @@ UTEST_F(io_write, buffered_multiple_writes_one_flush) { { .kind = IO_STEP_WRITE, .write = { "def", SP_OK, 3 } }, { .kind = IO_STEP_FLUSH, .flush = { SP_OK } }, }, - .expect.received = "abcdef", + .expect = { .received = "abcdef" }, }); } @@ -240,7 +240,7 @@ UTEST_F(io_write, buffered_overflow_flushes_then_buffers) { { .kind = IO_STEP_WRITE, .write = { "abcde", SP_OK, 5 } }, { .kind = IO_STEP_WRITE, .write = { "fghij", SP_OK, 5 } }, }, - .expect.received = "abcde", + .expect = { .received = "abcde" }, }); } @@ -255,7 +255,7 @@ UTEST_F(io_write, buffered_large_write_bypasses) { .steps = { { .kind = IO_STEP_WRITE, .write = { "0123456789ABCDEF", SP_OK, 16 } }, }, - .expect.received = "0123456789ABCDEF", + .expect = { .received = "0123456789ABCDEF" }, }); } @@ -273,7 +273,7 @@ UTEST_F(io_write, buffered_drain_then_bypass) { { .kind = IO_STEP_WRITE, .write = { "abc", SP_OK, 3 } }, { .kind = IO_STEP_WRITE, .write = { "0123456789ABCDEF", SP_OK, 16 } }, }, - .expect.received = "abc0123456789ABCDEF", + .expect = { .received = "abc0123456789ABCDEF" }, }); } @@ -308,7 +308,7 @@ UTEST_F(io_write, buffered_flush_partial_drops_tail) { { .kind = IO_STEP_WRITE, .write = { "xy", SP_OK, 2 } }, { .kind = IO_STEP_FLUSH, .flush = { SP_OK } }, }, - .expect.received = "abxy", + .expect = { .received = "abxy" }, }); } diff --git a/test/linkage.c b/test/linkage.c index 3605845..6d6b101 100644 --- a/test/linkage.c +++ b/test/linkage.c @@ -2,410 +2,4 @@ #include "test.h" #include "utest.h" -#if defined(SP_FREESTANDING) SP_TEST_MAIN() -#else - -typedef struct linkage { - sp_test_file_manager_t files; - sp_str_t root; - sp_str_t source; - sp_opt(bool) has_compiler; - sp_mem_arena_t* arena; - sp_mem_t mem; -} linkage; - -#if defined(SP_WIN32) -bool check_path(sp_str_t program) { - return SearchPathA(SP_NULLPTR, sp_str_to_cstr(sp_mem_get_scratch(), program), SP_NULLPTR, 0, SP_NULLPTR, SP_NULLPTR) > 0; -} -#else -bool check_path(sp_str_t program) { - return true; -} -#endif - -UTEST_F_SETUP(linkage) { - SKIP_ON_WASM() - ut.arena = sp_mem_arena_new(sp_mem_os_new()); - ut.mem = sp_mem_arena_as_allocator(ut.arena); - sp_test_file_manager_init(&ut.files); - - ut.root = ut.files.paths.root; - ut.source = sp_fs_join_path(ut.mem, ut.root, sp_str_lit("test/tools/linkage")); - - if (!ut.has_compiler.some) { - sp_opt_set(ut.has_compiler, check_path(sp_str_lit("cl"))); - } - - if (!ut.has_compiler.value) { - UTEST_SKIP("cl.exe is not on path"); - } -} - -UTEST_F_TEARDOWN(linkage) { - SKIP_ON_WASM() - sp_test_file_manager_cleanup(&ut.files); - sp_mem_arena_destroy(ut.arena); -} - -sp_str_t linkage_exe(sp_str_t stem) { - #if defined(SP_WIN32) - return sp_fmt(sp_mem_get_scratch(), "{}.exe", sp_fmt_str(stem)).value; - #else - return stem; - #endif -} - -sp_str_t linkage_obj(sp_str_t stem) { - #if defined(SP_WIN32) - return sp_fmt(sp_mem_get_scratch(), "{}.obj", sp_fmt_str(stem)).value; - #else - return sp_fmt(sp_mem_get_scratch(), "{}.o", sp_fmt_str(stem)).value; - #endif -} - -sp_str_t linkage_shared(sp_str_t stem) { - #if defined(SP_WIN32) - return sp_fmt(sp_mem_get_scratch(), "{}.dll", sp_fmt_str(stem)).value; - #else - return sp_fmt(sp_mem_get_scratch(), "{}.so", sp_fmt_str(stem)).value; - #endif -} - -sp_str_t linkage_static(sp_str_t stem) { - #if defined(SP_WIN32) - return sp_fmt(sp_mem_get_scratch(), "{}.lib", sp_fmt_str(stem)).value; - #else - return sp_fmt(sp_mem_get_scratch(), "{}.a", sp_fmt_str(stem)).value; - #endif -} - -void linkage_add_win32_link_libs(sp_ps_config_t* cfg) { - #if defined(SP_WIN32) - sp_ps_config_add_arg(sp_mem_get_scratch(), cfg, sp_str_lit("/link")); - sp_ps_config_add_arg(sp_mem_get_scratch(), cfg, sp_str_lit("shell32.lib")); - #else - SP_UNUSED(cfg); - #endif -} - -bool compile_to_exe(linkage* ctx, const c8* file, sp_str_t output) { - #if defined(SP_WIN32) - sp_ps_config_t cfg = { - .command = sp_str_lit("cl"), - .args = { - sp_str_lit("/nologo"), - sp_str_lit("/TC"), - sp_fmt(sp_mem_get_scratch(), "/I{}", sp_fmt_str(ctx->root)).value, - sp_fs_join_path(sp_mem_get_scratch(), ctx->source, sp_str_view(file)), - sp_fmt(sp_mem_get_scratch(), "/Fe:{}", sp_fmt_str(output)).value, - }, - }; - - linkage_add_win32_link_libs(&cfg); - sp_ps_output_t out = sp_ps_run(sp_mem_get_scratch(), cfg); - - return !out.status.exit_code; - #else - sp_ps_output_t out = sp_ps_run(sp_mem_get_scratch(), (sp_ps_config_t) { - .command = sp_str_lit("cc"), - .args = { - sp_fs_join_path(sp_mem_get_scratch(), ctx->source, sp_str_view(file)), - sp_fmt(sp_mem_get_scratch(), "-I{}", sp_fmt_str(ctx->root)).value, - sp_str_lit("-o"), output, - sp_str_lit("-g"), - }, - }); - - return !out.status.exit_code; - #endif -} - -bool compile_to_object(linkage* ctx, const c8* file, sp_str_t output) { - #if defined(SP_WIN32) - sp_ps_output_t out = sp_ps_run(sp_mem_get_scratch(), (sp_ps_config_t) { - .command = sp_str_lit("cl"), - .args = { - sp_str_lit("/nologo"), - sp_str_lit("/TC"), - sp_str_lit("/c"), - sp_fmt(sp_mem_get_scratch(), "/I{}", sp_fmt_str(ctx->root)).value, - sp_fs_join_path(sp_mem_get_scratch(), ctx->source, sp_str_view(file)), - sp_fmt(sp_mem_get_scratch(), "/Fo:{}", sp_fmt_str(output)).value, - }, - }); - - return !out.status.exit_code; - #else - sp_ps_output_t out = sp_ps_run(sp_mem_get_scratch(), (sp_ps_config_t) { - .command = sp_str_lit("cc"), - .args = { - sp_str_lit("-c"), - sp_fs_join_path(sp_mem_get_scratch(), ctx->source, sp_str_view(file)), - sp_fmt(sp_mem_get_scratch(), "-I{}", sp_fmt_str(ctx->root)).value, - sp_str_lit("-o"), output, - sp_str_lit("-g"), - }, - }); - - return !out.status.exit_code; - #endif -} - -bool compile_objects_to_exe(sp_str_t output, sp_str_t* objs, u32 len) { - #if defined(SP_WIN32) - sp_ps_config_t cfg = { - .command = sp_str_lit("cl"), - .args = { sp_str_lit("/nologo"), sp_fmt(sp_mem_get_scratch(), "/Fe:{}", sp_fmt_str(output)).value }, - }; - - sp_for(it, len) { - sp_ps_config_add_arg(sp_mem_get_scratch(), &cfg, objs[it]); - } - - linkage_add_win32_link_libs(&cfg); - - sp_ps_output_t out = sp_ps_run(sp_mem_get_scratch(), cfg); - return !out.status.exit_code; - #else - sp_ps_config_t cfg = { - .command = sp_str_lit("cc"), - .args = { sp_str_lit("-o"), output } - }; - - for (u32 it = 0; it < len; it++) { - sp_ps_config_add_arg(sp_mem_get_scratch(), &cfg, objs[it]); - } - - sp_ps_output_t out = sp_ps_run(sp_mem_get_scratch(), cfg); - return !out.status.exit_code; - #endif -} - -bool compile_to_linked_exe(linkage* ctx, const c8* file, sp_str_t output, sp_str_t* libs, u32 len) { - #if defined(SP_WIN32) - sp_ps_config_t cfg = { - .command = sp_str_lit("cl"), - .args = { - sp_str_lit("/nologo"), - sp_str_lit("/TC"), - sp_fs_join_path(sp_mem_get_scratch(), ctx->source, sp_str_view(file)), - sp_fmt(sp_mem_get_scratch(), "/I{}", sp_fmt_str(ctx->root)).value, - sp_fmt(sp_mem_get_scratch(), "/Fe:{}", sp_fmt_str(output)).value, - }, - }; - - sp_ps_config_add_arg(sp_mem_get_scratch(), &cfg, sp_str_lit("/link")); - - sp_for(it, len) { - sp_ps_config_add_arg(sp_mem_get_scratch(), &cfg, libs[it]); - } - - sp_ps_config_add_arg(sp_mem_get_scratch(), &cfg, sp_str_lit("shell32.lib")); - - sp_ps_output_t out = sp_ps_run(sp_mem_get_scratch(), cfg); - return !out.status.exit_code; - #else - sp_ps_config_t cfg = { - .command = sp_str_lit("cc"), - .args = { - sp_fs_join_path(sp_mem_get_scratch(), ctx->source, sp_str_view(file)), - sp_fmt(sp_mem_get_scratch(), "-I{}", sp_fmt_str(ctx->root)).value, - sp_str_lit("-o"), output, - sp_str_lit("-g"), - sp_str_lit("-lpthread"), sp_str_lit("-lm") - }, - }; - - for (u32 it = 0; it < len; it++) { - sp_ps_config_add_arg(sp_mem_get_scratch(), &cfg, libs[it]); - } - - sp_ps_output_t out = sp_ps_run(sp_mem_get_scratch(), cfg); - return !out.status.exit_code; - #endif -} - -bool create_archive(linkage* ctx, sp_str_t archive, sp_str_t* objs, u32 len) { - #if defined(SP_WIN32) - SP_UNUSED(ctx); - sp_ps_config_t cfg = { - .command = sp_str_lit("lib"), - .args = { - sp_str_lit("/nologo"), - sp_fmt(sp_mem_get_scratch(), "/OUT:{}", sp_fmt_str(archive)).value, - }, - }; - - sp_for(it, len) { - sp_ps_config_add_arg(sp_mem_get_scratch(), &cfg, objs[it]); - } - - sp_ps_output_t out = sp_ps_run(sp_mem_get_scratch(), cfg); - return !out.status.exit_code; - #else - sp_ps_config_t cfg = { - .command = sp_str_lit("ar"), - .args = { - sp_str_lit("rcs") - }, - }; - - sp_ps_config_add_arg(sp_mem_get_scratch(), &cfg, archive); - for (u32 it = 0; it < len; it++) { - sp_ps_config_add_arg(sp_mem_get_scratch(), &cfg, objs[it]); - } - - sp_ps_output_t out = sp_ps_run(sp_mem_get_scratch(), cfg); - return !out.status.exit_code; - #endif -} - -bool is_symbol_in_binary(sp_str_t binary, sp_str_t symbol) { - #if defined(SP_WIN32) - HMODULE module = LoadLibraryA(sp_str_to_cstr(sp_mem_get_scratch(), binary)); - if (!module) { - return false; - } - - bool found = GetProcAddress(module, sp_str_to_cstr(sp_mem_get_scratch(), symbol)) != SP_NULLPTR; - FreeLibrary(module); - return found; - #else - sp_ps_output_t nm = sp_ps_run(sp_mem_get_scratch(), (sp_ps_config_t){ - .command = sp_str_lit("nm"), - .args = { sp_str_lit("-g"), binary }, - }); - - return !nm.status.exit_code && sp_str_contains(nm.out, symbol); - #endif -} - -UTEST_F(linkage, single_tu) { - SKIP_ON_WASM() - sp_str_t bin = linkage_exe(sp_test_file_path(&ut.files, sp_str_lit("header-single"))); - EXPECT_TRUE(compile_to_exe(&ut, "main.c", bin)); - - sp_ps_output_t run = sp_ps_run(ut.mem, (sp_ps_config_t){ .command = bin }); - ASSERT_EQ(run.status.exit_code, 0); -} - -UTEST_F(linkage, multi_tu) { - SKIP_ON_WASM() - typedef struct { - const c8* file; - sp_str_t output; - } target_t; - - target_t targets[] = { - { "lib-decl.c", linkage_obj(sp_test_file_path(&ut.files, sp_str_lit("multi-decl"))) }, - { "main-impl.c", linkage_obj(sp_test_file_path(&ut.files, sp_str_lit("multi-impl"))) }, - }; - sp_carr_for(targets, it) { - EXPECT_TRUE(compile_to_object(&ut, targets[it].file, targets[it].output)); - } - - sp_str_t objs[] = { targets[0].output, targets[1].output }; - sp_str_t bin = linkage_exe(sp_test_file_path(&ut.files, sp_str_lit("header-multi"))); - EXPECT_TRUE(compile_objects_to_exe(bin, objs, sp_carr_len(objs))); - - sp_ps_output_t run = sp_ps_run(ut.mem, (sp_ps_config_t){ .command = bin }); - EXPECT_EQ(run.status.exit_code, 0); -} - -UTEST_F(linkage, shared_lib) { - SKIP_ON_WASM() - sp_str_t so = linkage_shared(sp_test_file_path(&ut.files, sp_str_lit("shared"))); - - #if defined(SP_WIN32) - sp_ps_output_t out = sp_ps_run(ut.mem, (sp_ps_config_t){ - .command = sp_str_lit("cl"), - .args = { - sp_str_lit("/nologo"), - sp_str_lit("/LD"), - sp_str_lit("/TC"), - sp_str_lit("/DSP_SHARED_LIB"), - sp_fs_join_path(ut.mem, ut.source, sp_str_lit("lib-impl.c")), - sp_fmt(ut.mem, "/I{}", sp_fmt_str(ut.root)).value, - sp_fmt(ut.mem, "/Fe:{}", sp_fmt_str(so)).value, - sp_str_lit("/link"), - sp_str_lit("shell32.lib"), - }, - }); - #else - sp_ps_output_t out = sp_ps_run(ut.mem, (sp_ps_config_t){ - .command = sp_str_lit("cc"), - .args = { - sp_str_lit("-shared"), sp_str_lit("-fPIC"), - sp_fs_join_path(ut.mem, ut.source, sp_str_lit("lib-impl.c")), - sp_fmt(ut.mem, "-I{}", sp_fmt_str(ut.root)).value, - sp_str_lit("-o"), so, - sp_str_lit("-lpthread"), sp_str_lit("-lm") - }, - }); - #endif - ASSERT_EQ(out.status.exit_code, 0); - EXPECT_TRUE(is_symbol_in_binary(so, sp_str_lit("sp_mem_allocator_alloc"))); -} - -UTEST_F(linkage, static_lib) { - SKIP_ON_WASM() - sp_str_t obj = linkage_obj(sp_test_file_path(&ut.files, sp_str_lit("sp"))); - EXPECT_TRUE(compile_to_object(&ut, "lib-impl.c", obj)); - - sp_str_t archive = linkage_static(sp_test_file_path(&ut.files, sp_str_lit("static"))); - EXPECT_TRUE(create_archive(&ut, archive, &obj, 1)); - - sp_str_t bin = linkage_exe(sp_test_file_path(&ut.files, sp_str_lit("static-single"))); - EXPECT_TRUE(compile_to_linked_exe(&ut, "main-decl.c", bin, &archive, 1)); - - sp_ps_output_t run = sp_ps_run(ut.mem, (sp_ps_config_t){ .command = bin }); - ASSERT_EQ(run.status.exit_code, 0); -} - -UTEST_F(linkage, cpp_compat) { - SKIP_ON_WASM() - sp_str_t obj = linkage_obj(sp_test_file_path(&ut.files, sp_str_lit("cpp-main"))); - - #if defined(SP_WIN32) - sp_ps_output_t out = sp_ps_run(ut.mem, (sp_ps_config_t){ - .command = sp_str_lit("cl"), - .args = { - sp_str_lit("/nologo"), - sp_str_lit("/TP"), - sp_str_lit("/std:c++20"), - sp_str_lit("/c"), - sp_fs_join_path(ut.mem, ut.source, sp_str_lit("cpp-compat.c")), - sp_fmt(ut.mem, "/I{}", sp_fmt_str(ut.root)).value, - sp_fmt(ut.mem, "/Fo:{}", sp_fmt_str(obj)).value, - sp_str_lit("/WX"), - }, - }); - #else - sp_ps_output_t out = sp_ps_run(ut.mem, (sp_ps_config_t){ - .command = sp_str_lit("c++"), - .args = { - sp_str_lit("-c"), - sp_str_lit("-x"), sp_str_lit("c++"), - sp_fs_join_path(ut.mem, ut.source, sp_str_lit("cpp-compat.c")), - sp_fmt(ut.mem, "-I{}", sp_fmt_str(ut.root)).value, - sp_str_lit("-o"), obj, - sp_str_lit("-g"), - sp_str_lit("-Werror"), - }, - }); - #endif - - EXPECT_EQ(out.status.exit_code, 0); -} - -UTEST_F(linkage, format_bare_args_should_fail) { - SKIP_ON_WASM() - // sp_str_t bin = sp_test_file_path(&ut.files, sp_str_lit("format-bare-args")); - // bool compiled = compile_to_exe(&ut, "format-bare-args.c", bin); - // EXPECT_FALSE(compiled); -} - -SP_TEST_MAIN() -#endif /* !SP_FREESTANDING */ diff --git a/test/tools/process/process.c b/test/process.c similarity index 99% rename from test/tools/process/process.c rename to test/process.c index 17a03e3..f8d3c22 100644 --- a/test/tools/process/process.c +++ b/test/process.c @@ -78,8 +78,7 @@ s32 main(s32 num_args, const c8** args) { num_args = argparse_parse(&argparse, num_args, args); if (!function_str) { - argparse_usage(&argparse); - SP_EXIT_FAILURE(); + return 0; } test_proc_function_t function = test_proc_function_from_str(sp_str_view(function_str)); diff --git a/test/tools/process/process.h b/test/process.h similarity index 100% rename from test/tools/process/process.h rename to test/process.h diff --git a/test/prompt.c b/test/prompt.c index e73bff4..2fb7ae9 100644 --- a/test/prompt.c +++ b/test/prompt.c @@ -1,5 +1,4 @@ #define SP_UNIMPLEMENTED() ((void)0) -#define LINENOISE_IMPLEMENTATION #define SP_PROMPT_IMPLEMENTATION #include "sp/sp_prompt.h" #include "test.h" diff --git a/test/ps.c b/test/ps.c index 95ba27b..fd8a9b5 100644 --- a/test/ps.c +++ b/test/ps.c @@ -165,7 +165,7 @@ void sp_test_proc_io(sp_ps* utest_fixture, s32* utest_result, sp_test_proc_io_co sp_ps_t ps = sp_ps_create(ut.mem, config); SP_ASSERT(ps.os); - sp_io_file_writer_t* in = sp_ps_io_in(&ps); + sp_io_stream_writer_t* in = sp_ps_io_in(&ps); sp_io_reader_t* out = sp_ps_io_out(&ps); sp_io_reader_t* err = sp_ps_io_err(&ps); @@ -183,7 +183,7 @@ void sp_test_proc_io(sp_ps* utest_fixture, s32* utest_result, sp_test_proc_io_co sp_assert(bytes_written == test.input.len); } - sp_io_file_writer_close(in); + sp_io_stream_writer_close(in); } if (!sp_str_empty(test.output.out.expected)) { @@ -285,7 +285,7 @@ UTEST_F(ps, io_stdout_stderr) { // SP_PS_IO_MODE_EXISTING UTEST_F(ps, io_create_file_null) { sp_str_t file_path = sp_test_file_create_empty(&ut.file_manager, sp_str_lit("stdout.file")); - sp_sys_fd_t fd = sp_sys_open_s(file_path, SP_O_RDWR | SP_O_CREAT, 0644); + sp_sys_fd_t fd = sp_sys_open_s(sp_fs_open_cwd(), file_path, SP_O_RDWR | SP_O_CREAT, 0644); sp_test_proc_io(&ut, &ur, (sp_test_proc_io_config_t) { .io = { @@ -316,7 +316,7 @@ UTEST_F(ps, io_create_file_null) { UTEST_F(ps, io_file_create_null) { sp_str_t file_path = sp_test_file_create_empty(&ut.file_manager, sp_str_lit("stdin.file")); - sp_sys_fd_t fd = sp_sys_open_s(file_path, SP_O_RDWR | SP_O_CREAT, 0644); + sp_sys_fd_t fd = sp_sys_open_s(sp_fs_open_cwd(), file_path, SP_O_RDWR | SP_O_CREAT, 0644); sp_sys_write(fd, sp_test_ps_canary.data, sp_test_ps_canary.len); sp_sys_lseek(fd, 0, SP_SEEK_SET); @@ -340,7 +340,7 @@ UTEST_F(ps, io_file_create_null) { UTEST_F(ps, io_create_null_file) { sp_str_t file_path = sp_test_file_create_empty(&ut.file_manager, sp_str_lit("stderr.file")); - sp_sys_fd_t fd = sp_sys_open_s(file_path, SP_O_RDWR | SP_O_CREAT, 0644); + sp_sys_fd_t fd = sp_sys_open_s(sp_fs_open_cwd(), file_path, SP_O_RDWR | SP_O_CREAT, 0644); sp_test_proc_io(&ut, &ur, (sp_test_proc_io_config_t) { .io = { @@ -371,12 +371,12 @@ UTEST_F(ps, io_create_null_file) { UTEST_F(ps, io_file_null_file) { sp_str_t in_path = sp_test_file_create_empty(&ut.file_manager, sp_str_lit("stdin.file")); - sp_sys_fd_t in_fd = sp_sys_open_s(in_path, SP_O_RDWR | SP_O_CREAT, 0644); + sp_sys_fd_t in_fd = sp_sys_open_s(sp_fs_open_cwd(), in_path, SP_O_RDWR | SP_O_CREAT, 0644); sp_sys_write(in_fd, sp_test_ps_canary.data, sp_test_ps_canary.len); sp_sys_lseek(in_fd, 0, SP_SEEK_SET); sp_str_t err_path = sp_test_file_create_empty(&ut.file_manager, sp_str_lit("stderr.file")); - sp_sys_fd_t err_fd = sp_sys_open_s(err_path, SP_O_RDWR | SP_O_CREAT, 0644); + sp_sys_fd_t err_fd = sp_sys_open_s(sp_fs_open_cwd(), err_path, SP_O_RDWR | SP_O_CREAT, 0644); sp_test_proc_io(&ut, &ur, (sp_test_proc_io_config_t) { .io = { @@ -469,7 +469,7 @@ void sp_test_proc_env_verify(sp_ps* utest_fixture, s32* utest_result, sp_test_pr sp_ps_t ps = sp_ps_create(ut.mem, config); SP_ASSERT(ps.os); - sp_io_file_writer_t* in = sp_ps_io_in(&ps); + sp_io_stream_writer_t* in = sp_ps_io_in(&ps); sp_io_reader_t* out = sp_ps_io_out(&ps); for (u32 i = 0; i < 8; i++) { @@ -481,7 +481,7 @@ void sp_test_proc_env_verify(sp_ps* utest_fixture, s32* utest_result, sp_test_pr sp_io_write_c8(&in->base, '\n'); } - sp_io_file_writer_close(in); + sp_io_stream_writer_close(in); sp_test_proc_stream_context_t ctx = { .stream = out, @@ -826,7 +826,7 @@ UTEST_F(ps, poll_with_io) { sp_ps_status_t r1 = sp_ps_poll(&ps, 10); EXPECT_EQ(r1.state, SP_PS_STATE_RUNNING); - sp_io_file_writer_t* in = sp_ps_io_in(&ps); + sp_io_stream_writer_t* in = sp_ps_io_in(&ps); EXPECT_NE(in, SP_NULLPTR); sp_ps_status_t r2 = sp_ps_wait(&ps); @@ -847,7 +847,7 @@ UTEST_F(ps, interleaved_read_write) { } }); - sp_io_file_writer_t* in = sp_ps_io_in(&ps); + sp_io_stream_writer_t* in = sp_ps_io_in(&ps); sp_io_reader_t* out = sp_ps_io_out(&ps); EXPECT_NE(in, SP_NULLPTR); @@ -868,7 +868,7 @@ UTEST_F(ps, interleaved_read_write) { EXPECT_TRUE(sp_mem_is_equal(ut.buffer.data, expected.data, expected.len)); } - sp_io_file_writer_close(in); + sp_io_stream_writer_close(in); sp_ps_status_t result = sp_ps_wait(&ps); EXPECT_EQ(result.state, SP_PS_STATE_DONE); diff --git a/test/tools/stress.c b/test/tools/stress.c index 80d5bb4..568de61 100644 --- a/test/tools/stress.c +++ b/test/tools/stress.c @@ -444,7 +444,7 @@ UTEST(stress, fmon) { sp_str_t path = sp_fs_join_path(sp_mem_get_scratch(), dir, name); sp_io_writer_t writer = sp_zero; - sp_io_file_writer_from_path(&writer, path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&writer, path); sp_io_write_str(&writer, sp_str_lit("initial"), SP_NULLPTR); sp_io_writer_close(&writer); @@ -469,7 +469,7 @@ UTEST(stress, fmon) { while (files_modified < batch_end) { sp_str_t path = files[files_modified]; sp_io_writer_t writer = sp_zero; - sp_io_file_writer_from_path(&writer, path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&writer, path); sp_io_write_str(&writer, sp_str_lit("modified"), SP_NULLPTR); sp_io_writer_close(&writer); files_modified++; diff --git a/test/tools/test.h b/test/tools/test.h index b44d5ad..34cd672 100644 --- a/test/tools/test.h +++ b/test/tools/test.h @@ -84,10 +84,8 @@ void sp_byte_buffer_zero(sp_byte_buffer_t* buffer); typedef struct { - sp_str_t root; - sp_str_t build; - sp_str_t bin; - sp_str_t test; + sp_str_t bin; + sp_str_t test; } sp_test_file_paths_t; typedef struct { @@ -151,37 +149,7 @@ bool sp_mem_tracking_ok(sp_mem_tracking_t* mem); static sp_str_t sp_test_file_manager_top_level = sp_zero; -static sp_str_t sp_test_file_manager_get_repo_root(sp_mem_t a) { - sp_str_t path = sp_fs_get_exe_path(a); - if (sp_fs_exists(path) && sp_fs_is_file(path)) { - path = sp_fs_parent_path(path); - } - - while (!sp_str_empty(path)) { - if (sp_str_equal(sp_fs_get_name(path), sp_str_lit("sp"))) { - sp_str_t marker = sp_fs_join_path(a, path, sp_str_lit("sp.h")); - if (sp_fs_exists(marker)) { - return sp_fs_canonicalize_path(a, path); - } - } - - if (sp_fs_is_root(path)) { - break; - } - - sp_str_t parent = sp_fs_parent_path(path); - if (sp_str_equal(parent, path)) { - break; - } - - path = parent; - } - - SP_ASSERT(false); - return sp_fs_canonicalize_path(a, sp_fs_get_cwd(a)); -} - -static sp_str_t sp_test_file_manager_get_top_level(sp_mem_t a, sp_str_t repo_root) { +static sp_str_t sp_test_file_manager_get_top_level(sp_mem_t a) { if (!sp_str_empty(sp_test_file_manager_top_level)) { if (!sp_fs_exists(sp_test_file_manager_top_level)) { sp_fs_create_dir(sp_test_file_manager_top_level); @@ -189,7 +157,7 @@ static sp_str_t sp_test_file_manager_get_top_level(sp_mem_t a, sp_str_t repo_roo return sp_test_file_manager_top_level; } - sp_str_t tmp = sp_fs_join_path(a, repo_root, sp_str_lit(".tmp")); + sp_str_t tmp = sp_fs_join_path(a, sp_fs_get_cwd(a), sp_str_lit(".tmp")); if (!sp_fs_exists(tmp)) { sp_fs_create_dir(tmp); } @@ -213,9 +181,7 @@ void sp_test_file_manager_init(sp_test_file_manager_t* fs) { fs->arena = sp_mem_arena_new(sp_mem_os_new()); fs->mem = sp_mem_arena_as_allocator(fs->arena); fs->paths.bin = sp_fs_get_exe_path(fs->mem); - fs->paths.root = sp_test_file_manager_get_repo_root(fs->mem); - fs->paths.build = fs->paths.root; - fs->paths.test = sp_test_file_manager_get_top_level(fs->mem, fs->paths.root); + fs->paths.test = sp_test_file_manager_get_top_level(fs->mem); if (!sp_fs_exists(fs->paths.test)) { sp_fs_create_dir(fs->paths.test); @@ -235,7 +201,7 @@ void sp_test_file_create_ex(sp_test_file_config_t config) { sp_fs_remove_file(config.path); sp_io_file_writer_t stream = sp_zero; - sp_io_file_writer_from_path(&stream, config.path, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&stream, config.path); SP_ASSERT(stream.fd != 0); if (config.content.len > 0) { @@ -262,11 +228,6 @@ void sp_test_file_manager_cleanup(sp_test_file_manager_t* manager) { return; } - if (sp_str_equal(manager->paths.test, manager->paths.root)) { - SP_ASSERT(false); - return; - } - if (sp_fs_exists(manager->paths.test)) { sp_fs_remove_dir(manager->paths.test); } diff --git a/tools/utest.h b/test/tools/utest.h similarity index 99% rename from tools/utest.h rename to test/tools/utest.h index c52a552..290a4b6 100644 --- a/tools/utest.h +++ b/test/tools/utest.h @@ -166,11 +166,6 @@ UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(double d) { UTEST_PRINTF("{:.3}", sp_fmt_float(d)); } -UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long double d); -UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long double d) { - UTEST_PRINTF("{:.3}", sp_fmt_float(UTEST_CAST(f64, d))); -} - UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(int i); UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(int i) { UTEST_PRINTF("{}", sp_fmt_int(UTEST_CAST(s32, i))); @@ -919,8 +914,7 @@ s32 utest_main(s32 argc, const c8 **argv) { } else if (0 == UTEST_STRNCMP(argv[index], output_str, sizeof(output_str) - 1)) { sp_str_t path = sp_str_view(argv[index] + sizeof(output_str) - 1); - sp_io_file_writer_from_path(&utest_state.output, path, - SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&utest_state.output, path); utest_state.has_output = 1; } else if (0 == UTEST_STRNCMP(argv[index], list_str, sizeof(list_str) - 1)) { diff --git a/tools/aarch64/alpine.sh b/tools/aarch64/alpine.sh new file mode 100755 index 0000000..cfb69d4 --- /dev/null +++ b/tools/aarch64/alpine.sh @@ -0,0 +1 @@ +docker run --rm -it --platform linux/arm64 -v $PWD:/sp -w /sp alpine:latest /sp/build/aarch64-linux-musl/test/$1 diff --git a/tools/aarch64/debian.sh b/tools/aarch64/debian.sh new file mode 100755 index 0000000..fcb333b --- /dev/null +++ b/tools/aarch64/debian.sh @@ -0,0 +1 @@ +docker run --rm -it --platform linux/arm64 -v $PWD:/sp -w /sp debian:stable-slim /sp/build/aarch64-linux-gnu/test/$1 diff --git a/tools/windows/sp/elf.vcxproj b/tools/windows/sp/etc.vcxproj similarity index 92% rename from tools/windows/sp/elf.vcxproj rename to tools/windows/sp/etc.vcxproj index dd8e293..ea933f7 100644 --- a/tools/windows/sp/elf.vcxproj +++ b/tools/windows/sp/etc.vcxproj @@ -13,12 +13,12 @@ 17.0 Win32Proj - {06F3F75F-659D-43B7-876E-831D95B910CC} - elf - elf + {21A5F3D1-2C70-4A11-9F8B-1A2B3C4D5E67} + etc + etc 10.0 - elf - ..\..\..\test\elf.c + etc + ..\..\..\test\etc.c ..\..\..\sp.h;..\..\..\test\tools\utest.h @@ -57,4 +57,4 @@ - + diff --git a/tools/windows/sp/examples/elf.vcxproj b/tools/windows/sp/examples/embed.vcxproj similarity index 89% rename from tools/windows/sp/examples/elf.vcxproj rename to tools/windows/sp/examples/embed.vcxproj index 0727a5a..f0a77e0 100644 --- a/tools/windows/sp/examples/elf.vcxproj +++ b/tools/windows/sp/examples/embed.vcxproj @@ -1,62 +1,62 @@ - - - - - Debug - x64 - - - Release - x64 - - - - 17.0 - Win32Proj - {10000002-0002-4002-8002-000000000002} - elf - elf - 10.0 - elf - ..\..\..\..\example\elf.c - ..\..\..\..\sp.h;..\..\..\..\sp\sp_elf.h - false - ..\..\..\..;..\..\..\..\sp - - - - Debug - x64 - - - Application - true - v143 - MultiByte - - - Application - false - v143 - true - MultiByte - - - - - - - - - - - - - - $(SolutionDir)build\vs\$(Configuration)\bin\examples\ - $(SolutionDir)build\vs\$(Configuration)\obj\examples\$(MSBuildProjectName)\ - - - - - + + + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {10000014-0014-4014-8014-000000000014} + embed + embed + 10.0 + embed + ..\..\..\..\example\embed.c + ..\..\..\..\sp.h + false + ..\..\..\..;..\..\..\..\sp + + + + Debug + x64 + + + Application + true + v143 + MultiByte + + + Application + false + v143 + true + MultiByte + + + + + + + + + + + + + + $(SolutionDir)build\vs\$(Configuration)\bin\examples\ + $(SolutionDir)build\vs\$(Configuration)\obj\examples\$(MSBuildProjectName)\ + + + + + diff --git a/tools/windows/sp/linkage.vcxproj b/tools/windows/sp/examples/io_copy_perf.vcxproj similarity index 76% rename from tools/windows/sp/linkage.vcxproj rename to tools/windows/sp/examples/io_copy_perf.vcxproj index 4554025..c30d77f 100644 --- a/tools/windows/sp/linkage.vcxproj +++ b/tools/windows/sp/examples/io_copy_perf.vcxproj @@ -13,17 +13,15 @@ 17.0 Win32Proj - {A3B2C1D0-1234-5678-9ABC-DEF012345678} - linkage - linkage + {10000015-0015-4015-8015-000000000015} + io_copy_perf + io_copy_perf 10.0 - linkage - ..\..\..\test\linkage.c - ..\..\..\sp.h;..\..\..\test\tools\utest.h - - - - + io_copy_perf + ..\..\..\..\example\io_copy_perf.c + ..\..\..\..\sp.h + false + ..\..\..\..;..\..\..\..\sp @@ -53,8 +51,12 @@ - + + + $(SolutionDir)build\vs\$(Configuration)\bin\examples\ + $(SolutionDir)build\vs\$(Configuration)\obj\examples\$(MSBuildProjectName)\ + - + - + diff --git a/tools/windows/sp/sp.sln b/tools/windows/sp/sp.sln index dac58ff..9d446f8 100644 --- a/tools/windows/sp/sp.sln +++ b/tools/windows/sp/sp.sln @@ -10,7 +10,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "asset", "asset.vcxproj", "{ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cv", "cv.vcxproj", "{F12504B9-5257-4BA3-8C0F-B713CA5959AA}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "elf", "elf.vcxproj", "{06F3F75F-659D-43B7-876E-831D95B910CC}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "etc", "etc.vcxproj", "{21A5F3D1-2C70-4A11-9F8B-1A2B3C4D5E67}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fmon", "fmon.vcxproj", "{C4D5E6F7-A1B2-4C3D-8E9F-0A1B2C3D4E5F}" EndProject @@ -22,8 +22,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ht", "ht.vcxproj", "{78ED29 EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "io", "io.vcxproj", "{F7A2B8C1-3D4E-4F56-A1B2-C3D4E5F60718}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "linkage", "linkage.vcxproj", "{A3B2C1D0-1234-5678-9ABC-DEF012345678}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mem", "mem.vcxproj", "{98937EB7-5079-4A16-B455-1044E54AE0FE}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "process", "process.vcxproj", "{A1B2C3D4-E5F6-4A8B-9C0D-E1F2A3B4C5D6}" @@ -54,7 +52,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{FF EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "array", "examples\array.vcxproj", "{10000001-0001-4001-8001-000000000001}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "elf", "examples\elf.vcxproj", "{10000002-0002-4002-8002-000000000002}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "embed", "examples\embed.vcxproj", "{10000014-0014-4014-8014-000000000014}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "io_copy_perf", "examples\io_copy_perf.vcxproj", "{10000015-0015-4015-8015-000000000015}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "format", "examples\format.vcxproj", "{10000003-0003-4003-8003-000000000003}" EndProject @@ -100,10 +100,10 @@ Global {F12504B9-5257-4BA3-8C0F-B713CA5959AA}.Debug|x64.Build.0 = Debug|x64 {F12504B9-5257-4BA3-8C0F-B713CA5959AA}.Release|x64.ActiveCfg = Release|x64 {F12504B9-5257-4BA3-8C0F-B713CA5959AA}.Release|x64.Build.0 = Release|x64 - {06F3F75F-659D-43B7-876E-831D95B910CC}.Debug|x64.ActiveCfg = Debug|x64 - {06F3F75F-659D-43B7-876E-831D95B910CC}.Debug|x64.Build.0 = Debug|x64 - {06F3F75F-659D-43B7-876E-831D95B910CC}.Release|x64.ActiveCfg = Release|x64 - {06F3F75F-659D-43B7-876E-831D95B910CC}.Release|x64.Build.0 = Release|x64 + {21A5F3D1-2C70-4A11-9F8B-1A2B3C4D5E67}.Debug|x64.ActiveCfg = Debug|x64 + {21A5F3D1-2C70-4A11-9F8B-1A2B3C4D5E67}.Debug|x64.Build.0 = Debug|x64 + {21A5F3D1-2C70-4A11-9F8B-1A2B3C4D5E67}.Release|x64.ActiveCfg = Release|x64 + {21A5F3D1-2C70-4A11-9F8B-1A2B3C4D5E67}.Release|x64.Build.0 = Release|x64 {C4D5E6F7-A1B2-4C3D-8E9F-0A1B2C3D4E5F}.Debug|x64.ActiveCfg = Debug|x64 {C4D5E6F7-A1B2-4C3D-8E9F-0A1B2C3D4E5F}.Debug|x64.Build.0 = Debug|x64 {C4D5E6F7-A1B2-4C3D-8E9F-0A1B2C3D4E5F}.Release|x64.ActiveCfg = Release|x64 @@ -124,14 +124,6 @@ Global {F7A2B8C1-3D4E-4F56-A1B2-C3D4E5F60718}.Debug|x64.Build.0 = Debug|x64 {F7A2B8C1-3D4E-4F56-A1B2-C3D4E5F60718}.Release|x64.ActiveCfg = Release|x64 {F7A2B8C1-3D4E-4F56-A1B2-C3D4E5F60718}.Release|x64.Build.0 = Release|x64 - {B81D009F-8386-4B3D-9C9E-8589A86498AF}.Debug|x64.ActiveCfg = Debug|x64 - {B81D009F-8386-4B3D-9C9E-8589A86498AF}.Debug|x64.Build.0 = Debug|x64 - {B81D009F-8386-4B3D-9C9E-8589A86498AF}.Release|x64.ActiveCfg = Release|x64 - {B81D009F-8386-4B3D-9C9E-8589A86498AF}.Release|x64.Build.0 = Release|x64 - {A3B2C1D0-1234-5678-9ABC-DEF012345678}.Debug|x64.ActiveCfg = Debug|x64 - {A3B2C1D0-1234-5678-9ABC-DEF012345678}.Debug|x64.Build.0 = Debug|x64 - {A3B2C1D0-1234-5678-9ABC-DEF012345678}.Release|x64.ActiveCfg = Release|x64 - {A3B2C1D0-1234-5678-9ABC-DEF012345678}.Release|x64.Build.0 = Release|x64 {98937EB7-5079-4A16-B455-1044E54AE0FE}.Debug|x64.ActiveCfg = Debug|x64 {98937EB7-5079-4A16-B455-1044E54AE0FE}.Debug|x64.Build.0 = Debug|x64 {98937EB7-5079-4A16-B455-1044E54AE0FE}.Release|x64.ActiveCfg = Release|x64 @@ -164,10 +156,14 @@ Global {10000001-0001-4001-8001-000000000001}.Debug|x64.Build.0 = Debug|x64 {10000001-0001-4001-8001-000000000001}.Release|x64.ActiveCfg = Release|x64 {10000001-0001-4001-8001-000000000001}.Release|x64.Build.0 = Release|x64 - {10000002-0002-4002-8002-000000000002}.Debug|x64.ActiveCfg = Debug|x64 - {10000002-0002-4002-8002-000000000002}.Debug|x64.Build.0 = Debug|x64 - {10000002-0002-4002-8002-000000000002}.Release|x64.ActiveCfg = Release|x64 - {10000002-0002-4002-8002-000000000002}.Release|x64.Build.0 = Release|x64 + {10000014-0014-4014-8014-000000000014}.Debug|x64.ActiveCfg = Debug|x64 + {10000014-0014-4014-8014-000000000014}.Debug|x64.Build.0 = Debug|x64 + {10000014-0014-4014-8014-000000000014}.Release|x64.ActiveCfg = Release|x64 + {10000014-0014-4014-8014-000000000014}.Release|x64.Build.0 = Release|x64 + {10000015-0015-4015-8015-000000000015}.Debug|x64.ActiveCfg = Debug|x64 + {10000015-0015-4015-8015-000000000015}.Debug|x64.Build.0 = Debug|x64 + {10000015-0015-4015-8015-000000000015}.Release|x64.ActiveCfg = Release|x64 + {10000015-0015-4015-8015-000000000015}.Release|x64.Build.0 = Release|x64 {10000003-0003-4003-8003-000000000003}.Debug|x64.ActiveCfg = Debug|x64 {10000003-0003-4003-8003-000000000003}.Debug|x64.Build.0 = Debug|x64 {10000003-0003-4003-8003-000000000003}.Release|x64.ActiveCfg = Release|x64 @@ -242,7 +238,8 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {10000001-0001-4001-8001-000000000001} = {FFFFFFFF-EEEE-DDDD-CCCC-BBBBBBBBBBBB} - {10000002-0002-4002-8002-000000000002} = {FFFFFFFF-EEEE-DDDD-CCCC-BBBBBBBBBBBB} + {10000014-0014-4014-8014-000000000014} = {FFFFFFFF-EEEE-DDDD-CCCC-BBBBBBBBBBBB} + {10000015-0015-4015-8015-000000000015} = {FFFFFFFF-EEEE-DDDD-CCCC-BBBBBBBBBBBB} {10000003-0003-4003-8003-000000000003} = {FFFFFFFF-EEEE-DDDD-CCCC-BBBBBBBBBBBB} {10000004-0004-4004-8004-000000000004} = {FFFFFFFF-EEEE-DDDD-CCCC-BBBBBBBBBBBB} {10000005-0005-4005-8005-000000000005} = {FFFFFFFF-EEEE-DDDD-CCCC-BBBBBBBBBBBB} diff --git a/example/elf.c b/tools/wip/elf.c similarity index 100% rename from example/elf.c rename to tools/wip/elf.c diff --git a/example/embed.c b/tools/wip/embed.c similarity index 93% rename from example/embed.c rename to tools/wip/embed.c index 28c19bf..f955880 100644 --- a/example/embed.c +++ b/tools/wip/embed.c @@ -160,26 +160,26 @@ s32 embed_main(s32 argc, const c8** argv) { } sp_io_file_writer_t hdr = sp_zero; - sp_io_file_writer_from_path(&hdr, out_hdr, SP_IO_WRITE_MODE_OVERWRITE); + sp_io_file_writer_from_path(&hdr, out_hdr); sp_da_for(entries, it) { embed_entry_t entry = entries[it]; - sp_fmt_io(&hdr.base, mem, + sp_fmt_io(&hdr.base, "extern const u8 {} [{}];\n", sp_fmt_str(entry.symbol), sp_fmt_uint(entry.size) ); - sp_fmt_io(&hdr.base, mem, + sp_fmt_io(&hdr.base, "extern const u64 {}_size;\n\n", sp_fmt_str(entry.symbol) ); } sp_io_write_str(&hdr.base, sp_str_lit("typedef struct {\n const char* path;\n const void* data;\n unsigned long long size;\n} spn_embed_entry_t;\n"), SP_NULLPTR); - sp_fmt_io(&hdr.base, mem, "static const unsigned int spn_embed_count = {};\n", sp_fmt_uint(sp_da_size(entries))); + sp_fmt_io(&hdr.base, "static const unsigned int spn_embed_count = {};\n", sp_fmt_uint(sp_da_size(entries))); sp_io_write_str(&hdr.base, sp_str_lit("static const spn_embed_entry_t spn_embed_manifest[] = {\n"), SP_NULLPTR); sp_da_for(entries, it) { embed_entry_t entry = entries[it]; sp_io_write_cstr(&hdr.base, " { ", SP_NULLPTR); - sp_fmt_io(&hdr.base, mem, "\"{}\", {}, {}", sp_fmt_str(entry.path), sp_fmt_str(entry.symbol), sp_fmt_uint(entry.size)); + sp_fmt_io(&hdr.base, "\"{}\", {}, {}", sp_fmt_str(entry.path), sp_fmt_str(entry.symbol), sp_fmt_uint(entry.size)); sp_io_write_cstr(&hdr.base, " },\n", SP_NULLPTR); } sp_io_write_str(&hdr.base, sp_str_lit("};\n"), SP_NULLPTR); diff --git a/sp/sp_elf.h b/tools/wip/sp_elf.h similarity index 99% rename from sp/sp_elf.h rename to tools/wip/sp_elf.h index bda9a96..f4ed53c 100644 --- a/sp/sp_elf.h +++ b/tools/wip/sp_elf.h @@ -597,7 +597,7 @@ sp_err_t sp_elf_write(sp_elf_t* elf, sp_io_writer_t* out) { sp_err_t sp_elf_write_to_file(sp_elf_t* elf, sp_str_t path) { sp_io_file_writer_t f = sp_zero; - sp_try(sp_io_file_writer_from_path(&f, path, SP_IO_WRITE_MODE_OVERWRITE)); + sp_try(sp_io_file_writer_from_path(&f, path)); sp_err_t err = sp_elf_write(elf, &f.base); sp_io_file_writer_close(&f); return err;