Add new ErrorSink API to collate error from API calls
This commit is contained in:
parent
f6c836ba84
commit
18a37024da
30
dqn.h
30
dqn.h
@ -24,6 +24,23 @@
|
|||||||
// first-class APIs, a 64bit MMU and in general non-pessimized APIs that aren't
|
// first-class APIs, a 64bit MMU and in general non-pessimized APIs that aren't
|
||||||
// constrained by the language specification and operate closer to the OS).
|
// constrained by the language specification and operate closer to the OS).
|
||||||
//
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// $$$$$$\ $$$$$$$$\ $$$$$$$$\ $$$$$$$$\ $$$$$$\ $$\ $$\ $$$$$$\
|
||||||
|
// $$ __$$\ $$ _____|\__$$ __|\__$$ __|\_$$ _|$$$\ $$ |$$ __$$\
|
||||||
|
// $$ / \__|$$ | $$ | $$ | $$ | $$$$\ $$ |$$ / \__|
|
||||||
|
// $$ |$$$$\ $$$$$\ $$ | $$ | $$ | $$ $$\$$ |$$ |$$$$\
|
||||||
|
// $$ |\_$$ |$$ __| $$ | $$ | $$ | $$ \$$$$ |$$ |\_$$ |
|
||||||
|
// $$ | $$ |$$ | $$ | $$ | $$ | $$ |\$$$ |$$ | $$ |
|
||||||
|
// \$$$$$$ |$$$$$$$$\ $$ | $$ | $$$$$$\ $$ | \$$ |\$$$$$$ |
|
||||||
|
// \______/ \________| \__| \__| \______|\__| \__| \______/
|
||||||
|
//
|
||||||
|
// Getting started -- Compiling with the library and library documentation
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// -- Compiling --
|
||||||
|
//
|
||||||
// Define DQN_IMPLEMENTATION macro in one and only one translation unit to
|
// Define DQN_IMPLEMENTATION macro in one and only one translation unit to
|
||||||
// enable the implementation of this library, for example:
|
// enable the implementation of this library, for example:
|
||||||
//
|
//
|
||||||
@ -47,9 +64,16 @@
|
|||||||
// Compiles the library with all optional APIs turned on except the previously
|
// Compiles the library with all optional APIs turned on except the previously
|
||||||
// mentioned APIs.
|
// mentioned APIs.
|
||||||
//
|
//
|
||||||
// Below is a table of contents that describes what you can find in each header
|
// -- Library documentation --
|
||||||
// of this library and additional macros that can be defined to customise the
|
//
|
||||||
// behaviour of this library.
|
// The header files in this library are intentionally extremely minimal and
|
||||||
|
// concise as to provide a dense reference of the APIs available without
|
||||||
|
// drowning out the library interface with code comments like many other
|
||||||
|
// documentation systems.
|
||||||
|
//
|
||||||
|
// Instead, documentation is laid out in dqn_docs.cpp in alphabetical order
|
||||||
|
// which provides self-contained examples in one contiguous top-down block of
|
||||||
|
// source code with comments.
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -220,6 +220,12 @@ DQN_API void Dqn_Arena_Pop(Dqn_Arena *arena, uint64_t amount)
|
|||||||
Dqn_Arena_PopTo(arena, pop_to);
|
Dqn_Arena_PopTo(arena, pop_to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DQN_API uint64_t Dqn_Arena_Pos(Dqn_Arena *arena)
|
||||||
|
{
|
||||||
|
uint64_t result = (arena && arena->curr) ? arena->curr->reserve_sum + arena->curr->used : 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
DQN_API void Dqn_Arena_Clear(Dqn_Arena *arena)
|
DQN_API void Dqn_Arena_Clear(Dqn_Arena *arena)
|
||||||
{
|
{
|
||||||
Dqn_Arena_PopTo(arena, 0);
|
Dqn_Arena_PopTo(arena, 0);
|
||||||
|
@ -145,6 +145,7 @@ DQN_API void * Dqn_Arena_AllocContiguous (Dqn_Arena *arena,
|
|||||||
DQN_API void * Dqn_Arena_Copy (Dqn_Arena *arena, void const *data, uint64_t size, uint8_t align);
|
DQN_API void * Dqn_Arena_Copy (Dqn_Arena *arena, void const *data, uint64_t size, uint8_t align);
|
||||||
DQN_API void Dqn_Arena_PopTo (Dqn_Arena *arena, uint64_t init_used);
|
DQN_API void Dqn_Arena_PopTo (Dqn_Arena *arena, uint64_t init_used);
|
||||||
DQN_API void Dqn_Arena_Pop (Dqn_Arena *arena, uint64_t amount);
|
DQN_API void Dqn_Arena_Pop (Dqn_Arena *arena, uint64_t amount);
|
||||||
|
DQN_API uint64_t Dqn_Arena_Pos (Dqn_Arena *arena);
|
||||||
DQN_API void Dqn_Arena_Clear (Dqn_Arena *arena);
|
DQN_API void Dqn_Arena_Clear (Dqn_Arena *arena);
|
||||||
DQN_API Dqn_ArenaTempMem Dqn_Arena_TempMemBegin (Dqn_Arena *arena);
|
DQN_API Dqn_ArenaTempMem Dqn_Arena_TempMemBegin (Dqn_Arena *arena);
|
||||||
DQN_API void Dqn_Arena_TempMemEnd (Dqn_ArenaTempMem mem);
|
DQN_API void Dqn_Arena_TempMemEnd (Dqn_ArenaTempMem mem);
|
||||||
|
102
dqn_base.cpp
102
dqn_base.cpp
@ -346,10 +346,10 @@ DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_Str8 type, int log_type, void *user_d
|
|||||||
|
|
||||||
// NOTE: Open log file for appending if requested //////////////////////////
|
// NOTE: Open log file for appending if requested //////////////////////////
|
||||||
Dqn_TicketMutex_Begin(&lib->log_file_mutex);
|
Dqn_TicketMutex_Begin(&lib->log_file_mutex);
|
||||||
if (lib->log_to_file && !lib->log_file.handle && lib->log_file.error_size == 0) {
|
if (lib->log_to_file && !lib->log_file.handle && !lib->log_file.error) {
|
||||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||||
Dqn_Str8 log_path = Dqn_OS_PathConvertF(scratch.arena, "%.*s/dqn.log", DQN_STR_FMT(lib->exe_dir));
|
Dqn_Str8 log_path = Dqn_OS_PathConvertF(scratch.arena, "%.*s/dqn.log", DQN_STR_FMT(lib->exe_dir));
|
||||||
lib->log_file = Dqn_OS_OpenFile(log_path, Dqn_OSFileOpen_CreateAlways, Dqn_OSFileAccess_AppendOnly);
|
lib->log_file = Dqn_OS_OpenFile(log_path, Dqn_OSFileOpen_CreateAlways, Dqn_OSFileAccess_AppendOnly, nullptr);
|
||||||
}
|
}
|
||||||
Dqn_TicketMutex_End(&lib->log_file_mutex);
|
Dqn_TicketMutex_End(&lib->log_file_mutex);
|
||||||
|
|
||||||
@ -361,8 +361,8 @@ DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_Str8 type, int log_type, void *user_d
|
|||||||
Dqn_Print_StdLn(Dqn_PrintStd_Out, log_line);
|
Dqn_Print_StdLn(Dqn_PrintStd_Out, log_line);
|
||||||
|
|
||||||
Dqn_TicketMutex_Begin(&lib->log_file_mutex);
|
Dqn_TicketMutex_Begin(&lib->log_file_mutex);
|
||||||
Dqn_OS_WriteFile(&lib->log_file, log_line);
|
Dqn_OS_WriteFile(&lib->log_file, log_line, nullptr);
|
||||||
Dqn_OS_WriteFile(&lib->log_file, DQN_STR8("\n"));
|
Dqn_OS_WriteFile(&lib->log_file, DQN_STR8("\n"), nullptr);
|
||||||
Dqn_TicketMutex_End(&lib->log_file_mutex);
|
Dqn_TicketMutex_End(&lib->log_file_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,3 +405,97 @@ DQN_API void Dqn_Log_TypeFCallSite(Dqn_LogType type, Dqn_CallSite call_site, DQN
|
|||||||
Dqn_Log_TypeFVCallSite(type, call_site, fmt, args);
|
Dqn_Log_TypeFVCallSite(type, call_site, fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: [$ERRS] Dqn_ErrorSink /////////////////////////////////////////////////////////////////////
|
||||||
|
DQN_API Dqn_ErrorSink *Dqn_ErrorSink_Begin()
|
||||||
|
{
|
||||||
|
Dqn_ThreadContext *thread_context = Dqn_ThreadContext_Get();
|
||||||
|
Dqn_ErrorSink *result = &thread_context->error_sink;
|
||||||
|
Dqn_ErrorSinkNode *node = Dqn_Arena_New(result->arena, Dqn_ErrorSinkNode, Dqn_ZeroMem_Yes);
|
||||||
|
node->next = result->stack;
|
||||||
|
node->arena_pos = Dqn_Arena_Pos(result->arena);
|
||||||
|
result->stack = node;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API Dqn_ErrorSinkNode Dqn_ErrorSink_End(Dqn_Arena *arena, Dqn_ErrorSink *error)
|
||||||
|
{
|
||||||
|
Dqn_ErrorSinkNode result = {};
|
||||||
|
if (!error)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
Dqn_ErrorSinkNode *node = error->stack;
|
||||||
|
if (!node)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
DQN_ASSERTF(arena != error->arena,
|
||||||
|
"You are not allowed to reuse the arena for ending the error sink because the memory would get popped and lost");
|
||||||
|
result = *error->stack;
|
||||||
|
result.next = nullptr;
|
||||||
|
result.msg = Dqn_Str8_Copy(arena, result.msg);
|
||||||
|
error->stack = error->stack->next;
|
||||||
|
Dqn_Arena_PopTo(error->arena, result.arena_pos);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API bool Dqn_ErrorSink_EndAndLogErrorFV(Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||||
|
{
|
||||||
|
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||||
|
Dqn_ErrorSinkNode node = Dqn_ErrorSink_End(scratch.arena, error);
|
||||||
|
if (node.error) {
|
||||||
|
Dqn_Str8 line = Dqn_Str8_InitFV(scratch.arena, fmt, args);
|
||||||
|
if (Dqn_Str8_HasData(line)) {
|
||||||
|
Dqn_Log_TypeFCallSite(Dqn_LogType_Error, node.call_site, "%.*s. %.*s", DQN_STR_FMT(line), DQN_STR_FMT(node.msg));
|
||||||
|
} else {
|
||||||
|
Dqn_Log_TypeFCallSite(Dqn_LogType_Error, node.call_site, "%.*s", DQN_STR_FMT(node.msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool result = node.error;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API bool Dqn_ErrorSink_EndAndLogErrorF(Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
bool result = Dqn_ErrorSink_EndAndLogErrorFV(error, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_ErrorSink_EndAndExitIfErrorFV(Dqn_ErrorSink *error, uint32_t exit_code, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||||
|
{
|
||||||
|
if (Dqn_ErrorSink_EndAndLogErrorFV(error, fmt, args))
|
||||||
|
Dqn_OS_Exit(exit_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_ErrorSink_EndAndExitIfErrorF(Dqn_ErrorSink *error, uint32_t exit_code, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
Dqn_ErrorSink_EndAndExitIfErrorFV(error, exit_code, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_ErrorSink_MakeFV_(Dqn_ErrorSink *error, uint32_t error_code, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||||
|
{
|
||||||
|
if (error) {
|
||||||
|
Dqn_ErrorSinkNode *node = error->stack;
|
||||||
|
DQN_ASSERTF(node, "Error sink must be begun by calling 'Begin' before using this function.");
|
||||||
|
if (!node->error) { // NOTE: Only preserve the first error
|
||||||
|
node->msg = Dqn_Str8_InitFV(error->arena, fmt, args);
|
||||||
|
node->error_code = error_code;
|
||||||
|
node->error = true;
|
||||||
|
node->call_site = Dqn_ThreadContext_Get()->call_site;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_API void Dqn_ErrorSink_MakeF_(Dqn_ErrorSink *error, uint32_t error_code, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
Dqn_ErrorSink_MakeFV_(error, error_code, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
30
dqn_base.h
30
dqn_base.h
@ -385,6 +385,23 @@ struct Dqn_CallSite
|
|||||||
};
|
};
|
||||||
#define DQN_CALL_SITE Dqn_CallSite{DQN_STR8(__FILE__), DQN_STR8(__func__), __LINE__}
|
#define DQN_CALL_SITE Dqn_CallSite{DQN_STR8(__FILE__), DQN_STR8(__func__), __LINE__}
|
||||||
|
|
||||||
|
// NOTE: [$ERRS] Dqn_ErrorSink /////////////////////////////////////////////////////////////////////
|
||||||
|
struct Dqn_ErrorSinkNode
|
||||||
|
{
|
||||||
|
bool error;
|
||||||
|
int32_t error_code;
|
||||||
|
Dqn_Str8 msg;
|
||||||
|
Dqn_CallSite call_site;
|
||||||
|
Dqn_ErrorSinkNode *next;
|
||||||
|
uint64_t arena_pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Dqn_ErrorSink
|
||||||
|
{
|
||||||
|
struct Dqn_Arena *arena;
|
||||||
|
Dqn_ErrorSinkNode *stack;
|
||||||
|
};
|
||||||
|
|
||||||
// NOTE: [$INTR] Intrinsics ////////////////////////////////////////////////////////////////////////
|
// NOTE: [$INTR] Intrinsics ////////////////////////////////////////////////////////////////////////
|
||||||
// NOTE: Dqn_Atomic_Add/Exchange return the previous value store in the target
|
// NOTE: Dqn_Atomic_Add/Exchange return the previous value store in the target
|
||||||
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
|
#if defined(DQN_COMPILER_MSVC) || defined(DQN_COMPILER_CLANG_CL)
|
||||||
@ -595,6 +612,19 @@ DQN_API void Dqn_Log_TypeFCallSite
|
|||||||
DQN_API void Dqn_Log_FVCallSite (Dqn_Str8 type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, va_list va);
|
DQN_API void Dqn_Log_FVCallSite (Dqn_Str8 type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, va_list va);
|
||||||
DQN_API void Dqn_Log_FCallSite (Dqn_Str8 type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, ...);
|
DQN_API void Dqn_Log_FCallSite (Dqn_Str8 type, Dqn_CallSite call_site, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||||
|
|
||||||
|
// NOTE: [$ERRS] Dqn_ErrorSink /////////////////////////////////////////////////////////////////////
|
||||||
|
DQN_API Dqn_ErrorSink * Dqn_ErrorSink_Begin ();
|
||||||
|
DQN_API Dqn_ErrorSinkNode Dqn_ErrorSink_End (Dqn_Arena *arena, Dqn_ErrorSink *error);
|
||||||
|
DQN_API bool Dqn_ErrorSink_EndAndLogErrorFV (Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||||
|
DQN_API bool Dqn_ErrorSink_EndAndLogErrorF (Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||||
|
DQN_API void Dqn_ErrorSink_EndAndExitIfErrorF (Dqn_ErrorSink *error, uint32_t exit_code, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||||
|
DQN_API void Dqn_ErrorSink_EndAndExitIfErrorFV (Dqn_ErrorSink *error, uint32_t exit_code, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||||
|
|
||||||
|
#define Dqn_ErrorSink_MakeFV(error, error_code, fmt, args) do { Dqn_ThreadContext_SaveCallSite; Dqn_ErrorSink_MakeFV_(error, error_code, fmt, args); } while (0)
|
||||||
|
#define Dqn_ErrorSink_MakeF(error, error_code, fmt, ...) do { Dqn_ThreadContext_SaveCallSite; Dqn_ErrorSink_MakeF_(error, error_code, fmt, ## __VA_ARGS__); } while (0)
|
||||||
|
DQN_API void Dqn_ErrorSink_MakeFV_ (Dqn_ErrorSink *error, uint32_t error_code, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||||
|
DQN_API void Dqn_ErrorSink_MakeF_ (Dqn_ErrorSink *error, uint32_t error_code, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||||
|
|
||||||
// NOTE: [$INTR] Intrinsics ////////////////////////////////////////////////////////////////////////
|
// NOTE: [$INTR] Intrinsics ////////////////////////////////////////////////////////////////////////
|
||||||
DQN_FORCE_INLINE uint64_t Dqn_Atomic_SetValue64(uint64_t volatile *target, uint64_t value)
|
DQN_FORCE_INLINE uint64_t Dqn_Atomic_SetValue64(uint64_t volatile *target, uint64_t value)
|
||||||
{
|
{
|
||||||
|
51
dqn_docs.cpp
51
dqn_docs.cpp
@ -236,6 +236,52 @@ void Dqn_Docs_Demo()
|
|||||||
// failure, or the requested size is smaller than the current number of
|
// failure, or the requested size is smaller than the current number of
|
||||||
// elements in the map to resize.
|
// elements in the map to resize.
|
||||||
|
|
||||||
|
// NOTE: Dqn_ErrorSink /////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// A thread-local data structure that collects all errors emitted by APIs
|
||||||
|
// into one unified structure. This library has 2 core tenets when handling
|
||||||
|
// errors
|
||||||
|
//
|
||||||
|
// 1. Pipelining of errors
|
||||||
|
// Errors emitted over the course of several API calls are accumulated
|
||||||
|
// into a thread-local sink which save the error code and message
|
||||||
|
// of the first error encountered.
|
||||||
|
//
|
||||||
|
// 2. Error proof APIs
|
||||||
|
// Functions that produce errors must return objects/handles that are
|
||||||
|
// marked to trigger no-ops used in subsequent functions dependent on it.
|
||||||
|
//
|
||||||
|
// Together this allows end-users of APIs to chain calls and defer error
|
||||||
|
// checking until the end of a sequence of actions. Consider the following
|
||||||
|
// example demonstrating the 2 approaches.
|
||||||
|
|
||||||
|
// (A) Conventional error checking patterns using return/sentinel values
|
||||||
|
#if 0
|
||||||
|
FileHandle *file = OpenFile("/path/to/file");
|
||||||
|
if (!file)
|
||||||
|
// Error handling!
|
||||||
|
if (!WriteFile(file, "abc"))
|
||||||
|
// Error handling!
|
||||||
|
CloseFile(file);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// (B) Error handling using pipelining and and error proof APIs
|
||||||
|
if (0) {
|
||||||
|
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||||
|
Dqn_ErrorSink *error = Dqn_ErrorSink_Begin();
|
||||||
|
Dqn_OSFile file = Dqn_OS_OpenFile(DQN_STR8("/path/to/file"), Dqn_OSFileOpen_OpenIfExist, Dqn_OSFileAccess_ReadWrite, error);
|
||||||
|
Dqn_OS_WriteFile(&file, DQN_STR8("abc"), error);
|
||||||
|
Dqn_OS_CloseFile(&file);
|
||||||
|
|
||||||
|
Dqn_ErrorSinkNode error_node = Dqn_ErrorSink_End(scratch.arena, error);
|
||||||
|
if (error_node.error) {
|
||||||
|
// Do error handling!
|
||||||
|
Dqn_Log_ErrorF("%.*s", DQN_STR_FMT(error_node.msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(doyle): Integrate more into the codebase and provide a concrete example.
|
||||||
|
|
||||||
// NOTE: Dqn_FStr8_Max /////////////////////////////////////////////////////////////////////////
|
// NOTE: Dqn_FStr8_Max /////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Return the maximum capacity of the string, e.g. the 'N' template
|
// Return the maximum capacity of the string, e.g. the 'N' template
|
||||||
@ -370,7 +416,10 @@ void Dqn_Docs_Demo()
|
|||||||
// If 'tmp_path' is written to successfuly, the file will be copied over into
|
// If 'tmp_path' is written to successfuly, the file will be copied over into
|
||||||
// 'path'.
|
// 'path'.
|
||||||
if (0) {
|
if (0) {
|
||||||
Dqn_OS_WriteAllSafe(/*path*/ DQN_STR8("C:/Home/my.txt"), /*buffer*/ DQN_STR8("Hello world"));
|
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||||
|
Dqn_ErrorSink *error = Dqn_ErrorSink_Begin();
|
||||||
|
Dqn_OS_WriteAllSafe(/*path*/ DQN_STR8("C:/Home/my.txt"), /*buffer*/ DQN_STR8("Hello world"), error);
|
||||||
|
Dqn_ErrorSink_EndAndLogErrorF(error, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Dqn_OS_EstimateTSCPerSecond ///////////////////////////////////////////////////////////
|
// NOTE: Dqn_OS_EstimateTSCPerSecond ///////////////////////////////////////////////////////////
|
||||||
|
53
dqn_os.cpp
53
dqn_os.cpp
@ -148,95 +148,84 @@ DQN_API uint64_t Dqn_OS_EstimateTSCPerSecond(uint64_t duration_ms_to_gauge_tsc_f
|
|||||||
|
|
||||||
#if !defined(DQN_NO_OS_FILE_API)
|
#if !defined(DQN_NO_OS_FILE_API)
|
||||||
// NOTE: [$FILE] Dqn_OSPathInfo/File ///////////////////////////////////////////////////////////////
|
// NOTE: [$FILE] Dqn_OSPathInfo/File ///////////////////////////////////////////////////////////////
|
||||||
DQN_API bool Dqn_OS_WriteFile(Dqn_OSFile *file, Dqn_Str8 buffer)
|
DQN_API bool Dqn_OS_WriteFile(Dqn_OSFile *file, Dqn_Str8 buffer, Dqn_ErrorSink *error)
|
||||||
{
|
{
|
||||||
bool result = Dqn_OS_WriteFileBuffer(file, buffer.data, buffer.size);
|
bool result = Dqn_OS_WriteFileBuffer(file, buffer.data, buffer.size, error);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API bool Dqn_OS_WriteFileFV(Dqn_OSFile *file, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
DQN_API bool Dqn_OS_WriteFileFV(Dqn_OSFile *file, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
if (!file || !fmt)
|
if (!file || !fmt)
|
||||||
return result;
|
return result;
|
||||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||||
Dqn_Str8 buffer = Dqn_Str8_InitFV(scratch.arena, fmt, args);
|
Dqn_Str8 buffer = Dqn_Str8_InitFV(scratch.arena, fmt, args);
|
||||||
result = Dqn_OS_WriteFileBuffer(file, buffer.data, buffer.size);
|
result = Dqn_OS_WriteFileBuffer(file, buffer.data, buffer.size, error);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API bool Dqn_OS_WriteFileF(Dqn_OSFile *file, DQN_FMT_ATTRIB char const *fmt, ...)
|
DQN_API bool Dqn_OS_WriteFileF(Dqn_OSFile *file, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
bool result = Dqn_OS_WriteFileFV(file, fmt, args);
|
bool result = Dqn_OS_WriteFileFV(file, error, fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
|
// NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
|
||||||
DQN_API bool Dqn_OS_WriteAll(Dqn_Str8 path, Dqn_Str8 buffer)
|
DQN_API bool Dqn_OS_WriteAll(Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *error)
|
||||||
{
|
{
|
||||||
Dqn_OSFile file = Dqn_OS_OpenFile(path, Dqn_OSFileOpen_CreateAlways, Dqn_OSFileAccess_Write);
|
Dqn_OSFile file = Dqn_OS_OpenFile(path, Dqn_OSFileOpen_CreateAlways, Dqn_OSFileAccess_Write, error);
|
||||||
bool result = Dqn_OS_WriteFile(&file, buffer);
|
bool result = Dqn_OS_WriteFile(&file, buffer, error);
|
||||||
Dqn_OS_CloseFile(&file);
|
Dqn_OS_CloseFile(&file);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API bool Dqn_OS_WriteAllFV(Dqn_Str8 file_path, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
DQN_API bool Dqn_OS_WriteAllFV(Dqn_Str8 file_path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||||
{
|
{
|
||||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||||
Dqn_Str8 buffer = Dqn_Str8_InitFV(scratch.arena, fmt, args);
|
Dqn_Str8 buffer = Dqn_Str8_InitFV(scratch.arena, fmt, args);
|
||||||
bool result = Dqn_OS_WriteAll(file_path, buffer);
|
bool result = Dqn_OS_WriteAll(file_path, buffer, error);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API bool Dqn_OS_WriteAllF(Dqn_Str8 file_path, DQN_FMT_ATTRIB char const *fmt, ...)
|
DQN_API bool Dqn_OS_WriteAllF(Dqn_Str8 file_path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
bool result = Dqn_OS_WriteAllFV(file_path, fmt, args);
|
bool result = Dqn_OS_WriteAllFV(file_path, error, fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API bool Dqn_OS_WriteAllSafe(Dqn_Str8 path, Dqn_Str8 buffer)
|
DQN_API bool Dqn_OS_WriteAllSafe(Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *error)
|
||||||
{
|
{
|
||||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||||
Dqn_Str8 tmp_path = Dqn_Str8_InitF(scratch.arena, "%.*s.tmp", DQN_STR_FMT(path));
|
Dqn_Str8 tmp_path = Dqn_Str8_InitF(scratch.arena, "%.*s.tmp", DQN_STR_FMT(path));
|
||||||
if (!Dqn_OS_WriteAll(tmp_path, buffer)) {
|
if (!Dqn_OS_WriteAll(tmp_path, buffer, error))
|
||||||
Dqn_Log_ErrorF("Failed to write to temporary file [path=%.*s]", DQN_STR_FMT(tmp_path));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
if (!Dqn_OS_FileCopy(tmp_path, path, true /*overwrite*/, error))
|
||||||
|
|
||||||
if (!Dqn_OS_FileCopy(tmp_path, path, true /*overwrite*/)) {
|
|
||||||
Dqn_Log_ErrorF("Failed to overwrite file at '%.*s' with temporary file '%.*s' to complete the safe-write",
|
|
||||||
DQN_STR_FMT(tmp_path),
|
|
||||||
DQN_STR_FMT(path));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
if (!Dqn_OS_PathDelete(tmp_path))
|
||||||
|
|
||||||
if (!Dqn_OS_PathDelete(tmp_path)) {
|
|
||||||
Dqn_Log_ErrorF("Failed to delete the temporary file at '%.*s' to clean-up the safe-write", DQN_STR_FMT(tmp_path));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API bool Dqn_OS_WriteAllSafeFV(Dqn_Str8 path, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
DQN_API bool Dqn_OS_WriteAllSafeFV(Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args)
|
||||||
{
|
{
|
||||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||||
Dqn_Str8 buffer = Dqn_Str8_InitFV(scratch.arena, fmt, args);
|
Dqn_Str8 buffer = Dqn_Str8_InitFV(scratch.arena, fmt, args);
|
||||||
bool result = Dqn_OS_WriteAllSafe(path, buffer);
|
bool result = Dqn_OS_WriteAllSafe(path, buffer, error);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API bool Dqn_OS_WriteAllSafeF(Dqn_Str8 path, DQN_FMT_ATTRIB char const *fmt, ...)
|
DQN_API bool Dqn_OS_WriteAllSafeF(Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
bool result = Dqn_OS_WriteAllSafeFV(path, fmt, args);
|
bool result = Dqn_OS_WriteAllSafeFV(path, error, fmt, args);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
#endif // !defined(DQN_NO_OS_FILE_API)
|
#endif // !defined(DQN_NO_OS_FILE_API)
|
||||||
|
35
dqn_os.h
35
dqn_os.h
@ -120,9 +120,8 @@ struct Dqn_OSPathInfo
|
|||||||
// NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
|
// NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
|
||||||
struct Dqn_OSFile
|
struct Dqn_OSFile
|
||||||
{
|
{
|
||||||
|
bool error;
|
||||||
void *handle;
|
void *handle;
|
||||||
char error[512];
|
|
||||||
uint16_t error_size;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Dqn_OSFileOpen
|
enum Dqn_OSFileOpen
|
||||||
@ -305,27 +304,27 @@ DQN_API uint64_t Dqn_OS_EstimateTSCPerSecond(uint64_t duration_
|
|||||||
DQN_API Dqn_OSPathInfo Dqn_OS_PathInfo (Dqn_Str8 path);
|
DQN_API Dqn_OSPathInfo Dqn_OS_PathInfo (Dqn_Str8 path);
|
||||||
DQN_API bool Dqn_OS_PathDelete(Dqn_Str8 path);
|
DQN_API bool Dqn_OS_PathDelete(Dqn_Str8 path);
|
||||||
DQN_API bool Dqn_OS_FileExists(Dqn_Str8 path);
|
DQN_API bool Dqn_OS_FileExists(Dqn_Str8 path);
|
||||||
DQN_API bool Dqn_OS_FileCopy (Dqn_Str8 src, Dqn_Str8 dest, bool overwrite);
|
DQN_API bool Dqn_OS_FileCopy (Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_ErrorSink *error);
|
||||||
DQN_API bool Dqn_OS_FileMove (Dqn_Str8 src, Dqn_Str8 dest, bool overwrite);
|
DQN_API bool Dqn_OS_FileMove (Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_ErrorSink *error);
|
||||||
DQN_API bool Dqn_OS_DirExists (Dqn_Str8 path);
|
DQN_API bool Dqn_OS_DirExists (Dqn_Str8 path, Dqn_ErrorSink *error);
|
||||||
DQN_API bool Dqn_OS_DirMake (Dqn_Str8 path);
|
DQN_API bool Dqn_OS_DirMake (Dqn_Str8 path, Dqn_ErrorSink *error);
|
||||||
|
|
||||||
// NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
|
// NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
|
||||||
DQN_API Dqn_OSFile Dqn_OS_OpenFile (Dqn_Str8 path, Dqn_OSFileOpen open_mode, uint32_t access);
|
DQN_API Dqn_OSFile Dqn_OS_OpenFile (Dqn_Str8 path, Dqn_OSFileOpen open_mode, uint32_t access, Dqn_ErrorSink *error);
|
||||||
DQN_API bool Dqn_OS_WriteFileBuffer(Dqn_OSFile *file, void const *data, Dqn_usize size);
|
DQN_API bool Dqn_OS_WriteFileBuffer(Dqn_OSFile *file, void const *data, Dqn_usize size, Dqn_ErrorSink *error);
|
||||||
DQN_API bool Dqn_OS_WriteFile (Dqn_OSFile *file, Dqn_Str8 buffer);
|
DQN_API bool Dqn_OS_WriteFile (Dqn_OSFile *file, Dqn_Str8 buffer, Dqn_ErrorSink *error);
|
||||||
DQN_API bool Dqn_OS_WriteFileFV (Dqn_OSFile *file, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
DQN_API bool Dqn_OS_WriteFileFV (Dqn_OSFile *file, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||||
DQN_API bool Dqn_OS_WriteFileF (Dqn_OSFile *file, DQN_FMT_ATTRIB char const *fmt, ...);
|
DQN_API bool Dqn_OS_WriteFileF (Dqn_OSFile *file, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||||
DQN_API void Dqn_OS_CloseFile (Dqn_OSFile *file);
|
DQN_API void Dqn_OS_CloseFile (Dqn_OSFile *file);
|
||||||
|
|
||||||
// NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
|
// NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
|
||||||
DQN_API Dqn_Str8 Dqn_OS_ReadAll (Dqn_Str8 path, Dqn_Arena *arena);
|
DQN_API Dqn_Str8 Dqn_OS_ReadAll (Dqn_Str8 path, Dqn_Arena *arena, Dqn_ErrorSink *error);
|
||||||
DQN_API bool Dqn_OS_WriteAll (Dqn_Str8 path, Dqn_Str8 buffer);
|
DQN_API bool Dqn_OS_WriteAll (Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *error);
|
||||||
DQN_API bool Dqn_OS_WriteAllFV (Dqn_Str8 path, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
DQN_API bool Dqn_OS_WriteAllFV (Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||||
DQN_API bool Dqn_OS_WriteAllF (Dqn_Str8 path, DQN_FMT_ATTRIB char const *fmt, ...);
|
DQN_API bool Dqn_OS_WriteAllF (Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||||
DQN_API bool Dqn_OS_WriteAllSafe (Dqn_Str8 path, Dqn_Str8 buffer);
|
DQN_API bool Dqn_OS_WriteAllSafe (Dqn_Str8 path, Dqn_Str8 buffer, Dqn_ErrorSink *error);
|
||||||
DQN_API bool Dqn_OS_WriteAllSafeFV(Dqn_Str8 path, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
DQN_API bool Dqn_OS_WriteAllSafeFV(Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, va_list args);
|
||||||
DQN_API bool Dqn_OS_WriteAllSafeF (Dqn_Str8 path, DQN_FMT_ATTRIB char const *fmt, ...);
|
DQN_API bool Dqn_OS_WriteAllSafeF (Dqn_Str8 path, Dqn_ErrorSink *error, DQN_FMT_ATTRIB char const *fmt, ...);
|
||||||
#endif // !defined(DQN_NO_OS_FILE_API)
|
#endif // !defined(DQN_NO_OS_FILE_API)
|
||||||
|
|
||||||
// NOTE: File system paths /////////////////////////////////////////////////////////////////////////
|
// NOTE: File system paths /////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -254,7 +254,7 @@ DQN_API bool Dqn_OS_FileExists(Dqn_Str8 path)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API bool Dqn_OS_FileCopy(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite)
|
DQN_API bool Dqn_OS_FileCopy(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_ErrorSink *error)
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||||
@ -265,16 +265,19 @@ DQN_API bool Dqn_OS_FileCopy(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite)
|
|||||||
result = CopyFileW(src16.data, dest16.data, fail_if_exists) != 0;
|
result = CopyFileW(src16.data, dest16.data, fail_if_exists) != 0;
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
Dqn_WinError error = Dqn_Win_LastError(scratch.arena);
|
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena);
|
||||||
Dqn_Log_ErrorF("Failed to copy the file\n\nSource: %.*s\nDestination: %.*s\n\nWindows reported: %.*s",
|
Dqn_ErrorSink_MakeF(error,
|
||||||
|
win_error.code,
|
||||||
|
"Failed to copy file '%.*s' to '%.*s': (%u) %.*s",
|
||||||
DQN_STR_FMT(src),
|
DQN_STR_FMT(src),
|
||||||
DQN_STR_FMT(dest),
|
DQN_STR_FMT(dest),
|
||||||
DQN_STR_FMT(error.msg));
|
win_error.code,
|
||||||
|
DQN_STR_FMT(win_error.msg));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API bool Dqn_OS_FileMove(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite)
|
DQN_API bool Dqn_OS_FileMove(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_ErrorSink *error)
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||||
@ -288,11 +291,14 @@ DQN_API bool Dqn_OS_FileMove(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite)
|
|||||||
|
|
||||||
result = MoveFileExW(src16.data, dest16.data, flags) != 0;
|
result = MoveFileExW(src16.data, dest16.data, flags) != 0;
|
||||||
if (!result) {
|
if (!result) {
|
||||||
Dqn_WinError error = Dqn_Win_LastError(scratch.arena);
|
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena);
|
||||||
Dqn_Log_ErrorF("Failed to move the file\n\nSource: %.*s\nDestination: %.*s\n\nWindows reported: %.*s",
|
Dqn_ErrorSink_MakeF(error,
|
||||||
|
win_error.code,
|
||||||
|
"Failed to move file '%.*s' to '%.*s': (%u) %.*s",
|
||||||
DQN_STR_FMT(src),
|
DQN_STR_FMT(src),
|
||||||
DQN_STR_FMT(dest),
|
DQN_STR_FMT(dest),
|
||||||
DQN_STR_FMT(error.msg));
|
win_error.code,
|
||||||
|
DQN_STR_FMT(win_error.msg));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -385,7 +391,7 @@ DQN_API bool Dqn_OS_PathDelete(Dqn_Str8 path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
|
// NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
|
||||||
DQN_API Dqn_OSFile Dqn_OS_OpenFile(Dqn_Str8 path, Dqn_OSFileOpen open_mode, uint32_t access)
|
DQN_API Dqn_OSFile Dqn_OS_OpenFile(Dqn_Str8 path, Dqn_OSFileOpen open_mode, uint32_t access, Dqn_ErrorSink *error)
|
||||||
{
|
{
|
||||||
Dqn_OSFile result = {};
|
Dqn_OSFile result = {};
|
||||||
if (!Dqn_Str8_HasData(path) || path.size <= 0)
|
if (!Dqn_Str8_HasData(path) || path.size <= 0)
|
||||||
@ -429,13 +435,9 @@ DQN_API Dqn_OSFile Dqn_OS_OpenFile(Dqn_Str8 path, Dqn_OSFileOpen open_mode, uint
|
|||||||
/*HANDLE hTemplateFile*/ nullptr);
|
/*HANDLE hTemplateFile*/ nullptr);
|
||||||
|
|
||||||
if (handle == INVALID_HANDLE_VALUE) {
|
if (handle == INVALID_HANDLE_VALUE) {
|
||||||
Dqn_WinError error = Dqn_Win_LastError(scratch.arena);
|
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena);
|
||||||
result.error_size =
|
result.error = true;
|
||||||
DQN_CAST(uint16_t) Dqn_FmtBuffer3DotTruncate(result.error,
|
Dqn_ErrorSink_MakeF(error, win_error.code, "Failed to open file at '%.*s': '%.*s'", DQN_STR_FMT(path), DQN_STR_FMT(win_error.msg));
|
||||||
DQN_ARRAY_UCOUNT(result.error),
|
|
||||||
"Open file failed: %.*s for \"%.*s\"",
|
|
||||||
DQN_STR_FMT(error.msg),
|
|
||||||
DQN_STR_FMT(path));
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,9 +445,9 @@ DQN_API Dqn_OSFile Dqn_OS_OpenFile(Dqn_Str8 path, Dqn_OSFileOpen open_mode, uint
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API bool Dqn_OS_WriteFileBuffer(Dqn_OSFile *file, void const *buffer, Dqn_usize size)
|
DQN_API bool Dqn_OS_WriteFileBuffer(Dqn_OSFile *file, void const *buffer, Dqn_usize size, Dqn_ErrorSink *error)
|
||||||
{
|
{
|
||||||
if (!file || !file->handle || !buffer || size <= 0 || file->error_size)
|
if (!file || !file->handle || file->error || !buffer || size <= 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool result = true;
|
bool result = true;
|
||||||
@ -459,27 +461,23 @@ DQN_API bool Dqn_OS_WriteFileBuffer(Dqn_OSFile *file, void const *buffer, Dqn_us
|
|||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||||
Dqn_WinError error = Dqn_Win_LastError(scratch.arena);
|
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena);
|
||||||
file->error_size =
|
Dqn_Str8 buffer_size_str8 = Dqn_U64ToByteSizeStr8(scratch.arena, size, Dqn_U64ByteSizeType_Auto);
|
||||||
DQN_CAST(uint16_t) Dqn_FmtBuffer3DotTruncate(file->error,
|
Dqn_ErrorSink_MakeF(error, win_error.code, "Failed to write buffer (%.*s) to file handle: %.*s", DQN_STR_FMT(buffer_size_str8), DQN_STR_FMT(win_error.msg));
|
||||||
DQN_ARRAY_UCOUNT(file->error),
|
|
||||||
"Write file failed (%u): %.*s",
|
|
||||||
error.code,
|
|
||||||
DQN_STR_FMT(error.msg));
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DQN_API void Dqn_OS_CloseFile(Dqn_OSFile *file)
|
DQN_API void Dqn_OS_CloseFile(Dqn_OSFile *file)
|
||||||
{
|
{
|
||||||
if (!file || !file->handle || file->error_size)
|
if (!file || !file->handle || file->error)
|
||||||
return;
|
return;
|
||||||
CloseHandle(file->handle);
|
CloseHandle(file->handle);
|
||||||
*file = {};
|
*file = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
|
// NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
|
||||||
DQN_API Dqn_Str8 Dqn_OS_ReadAll(Dqn_Str8 path, Dqn_Arena *arena)
|
DQN_API Dqn_Str8 Dqn_OS_ReadAll(Dqn_Str8 path, Dqn_Arena *arena, Dqn_ErrorSink *error)
|
||||||
{
|
{
|
||||||
Dqn_Str8 result = {};
|
Dqn_Str8 result = {};
|
||||||
if (!arena)
|
if (!arena)
|
||||||
@ -499,8 +497,8 @@ DQN_API Dqn_Str8 Dqn_OS_ReadAll(Dqn_Str8 path, Dqn_Arena *arena)
|
|||||||
/*DWORD dwFlagsAndAttributes*/ FILE_ATTRIBUTE_READONLY,
|
/*DWORD dwFlagsAndAttributes*/ FILE_ATTRIBUTE_READONLY,
|
||||||
/*HANDLE hTemplateFile*/ nullptr);
|
/*HANDLE hTemplateFile*/ nullptr);
|
||||||
if (file_handle == INVALID_HANDLE_VALUE) {
|
if (file_handle == INVALID_HANDLE_VALUE) {
|
||||||
Dqn_WinError error = Dqn_Win_LastError(scratch.arena);
|
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena);
|
||||||
Dqn_Log_ErrorF("Failed to open file '%.*s' for reading: (%u) %.*s", DQN_STR_FMT(path), error.code, DQN_STR_FMT(error.msg));
|
Dqn_ErrorSink_MakeF(error, win_error.code, "Failed to open file at '%.*s' for reading: %.*s", DQN_STR_FMT(path), DQN_STR_FMT(win_error.msg));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
DQN_DEFER { CloseHandle(file_handle); };
|
DQN_DEFER { CloseHandle(file_handle); };
|
||||||
@ -508,14 +506,14 @@ DQN_API Dqn_Str8 Dqn_OS_ReadAll(Dqn_Str8 path, Dqn_Arena *arena)
|
|||||||
// NOTE: Query the file size ///////////////////////////////////////////////////////////////////
|
// NOTE: Query the file size ///////////////////////////////////////////////////////////////////
|
||||||
LARGE_INTEGER win_file_size;
|
LARGE_INTEGER win_file_size;
|
||||||
if (!GetFileSizeEx(file_handle, &win_file_size)) {
|
if (!GetFileSizeEx(file_handle, &win_file_size)) {
|
||||||
Dqn_WinError error = Dqn_Win_LastError(scratch.arena);
|
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena);
|
||||||
Dqn_Log_ErrorF("Failed to query file size [file=%.*s, reason=%.*s]", DQN_STR_FMT(path), DQN_STR_FMT(error.msg));
|
Dqn_ErrorSink_MakeF(error, win_error.code, "Failed to query file size for reading '%.*s': %.*s", DQN_STR_FMT(path), DQN_STR_FMT(win_error.msg));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long const bytes_desired = DQN_CAST(unsigned long)win_file_size.QuadPart;
|
unsigned long const bytes_desired = DQN_CAST(unsigned long)win_file_size.QuadPart;
|
||||||
if (!DQN_CHECKF(bytes_desired == win_file_size.QuadPart,
|
if (!DQN_CHECK(bytes_desired == win_file_size.QuadPart)) {
|
||||||
"Current implementation doesn't support >4GiB, implement Win32 overlapped IO")) {
|
Dqn_ErrorSink_MakeF(error, 1 /*error_code*/, "Current implementation doesn't support reading >4GiB file at '%.*s', implement Win32 overlapped IO", DQN_STR_FMT(path));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -529,19 +527,22 @@ DQN_API Dqn_Str8 Dqn_OS_ReadAll(Dqn_Str8 path, Dqn_Arena *arena)
|
|||||||
/*LPOVERLAPPED lpOverlapped*/ nullptr);
|
/*LPOVERLAPPED lpOverlapped*/ nullptr);
|
||||||
|
|
||||||
if (read_result == 0) {
|
if (read_result == 0) {
|
||||||
Dqn_WinError error = Dqn_Win_LastError(scratch.arena);
|
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena);
|
||||||
Dqn_Log_ErrorF("Failed to load file '%.*s' (via 'ReadFile'): (%u) %.*s", DQN_STR_FMT(path), error.code, DQN_STR_FMT(error.msg));
|
Dqn_ErrorSink_MakeF(error, win_error.code, "Failed to read bytes from file '%.*s': (%u) %.*s", DQN_STR_FMT(path), win_error.code, DQN_STR_FMT(win_error.msg));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bytes_read != bytes_desired) {
|
if (bytes_read != bytes_desired) {
|
||||||
Dqn_WinError error = Dqn_Win_LastError(scratch.arena);
|
Dqn_WinError win_error = Dqn_Win_LastError(scratch.arena);
|
||||||
Dqn_Log_ErrorF("Loaded file '%.*s' (via 'ReadFile') did not read the desired number of bytes %u, we read %u: (%u) %.*s",
|
Dqn_ErrorSink_MakeF(
|
||||||
|
error,
|
||||||
|
win_error.code,
|
||||||
|
"Failed to read the desired number of bytes from file '%.*s', we read %uB but we expected %uB: (%u) %.*s",
|
||||||
DQN_STR_FMT(path),
|
DQN_STR_FMT(path),
|
||||||
bytes_desired,
|
|
||||||
bytes_read,
|
bytes_read,
|
||||||
error.code,
|
bytes_desired,
|
||||||
DQN_STR_FMT(error.msg));
|
win_error.code,
|
||||||
|
DQN_STR_FMT(win_error.msg));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ DQN_API Dqn_ThreadContext *Dqn_ThreadContext_Get()
|
|||||||
Dqn_ArenaCatalog *catalog = &g_dqn_library->arena_catalog;
|
Dqn_ArenaCatalog *catalog = &g_dqn_library->arena_catalog;
|
||||||
DQN_HARD_ASSERTF(g_dqn_library && g_dqn_library->lib_init, "Library must be initialised by calling Dqn_Library_Init()");
|
DQN_HARD_ASSERTF(g_dqn_library && g_dqn_library->lib_init, "Library must be initialised by calling Dqn_Library_Init()");
|
||||||
|
|
||||||
// NOTE: Setup scratch arenas
|
// NOTE: Setup scratch arenas //////////////////////////////////////////////////////////////////
|
||||||
DQN_FOR_UINDEX (index, DQN_ARRAY_UCOUNT(result->scratch_arenas)) {
|
DQN_FOR_UINDEX (index, DQN_ARRAY_UCOUNT(result->scratch_arenas)) {
|
||||||
|
|
||||||
// NOTE: We allocate arenas so that they all come from the memory
|
// NOTE: We allocate arenas so that they all come from the memory
|
||||||
@ -85,6 +85,20 @@ DQN_API Dqn_ThreadContext *Dqn_ThreadContext_Get()
|
|||||||
result->scratch_arenas[index] = catalog_item->arena;
|
result->scratch_arenas[index] = catalog_item->arena;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: Setup error sink //////////////////////////////////////////////////////////////////////
|
||||||
|
{
|
||||||
|
Dqn_FStr8<128> label = Dqn_FStr8_InitF<128>("T%05u Error Sink", Dqn_OS_ThreadID());
|
||||||
|
Dqn_ArenaCatalogItem *catalog_item = Dqn_ArenaCatalog_Find(catalog, Dqn_FStr8_ToStr8(&label));
|
||||||
|
if (catalog_item == &catalog->sentinel) {
|
||||||
|
result->error_sink_arena = Dqn_ArenaCatalog_AllocLabelCopy(catalog, 0, 0, Dqn_ArenaFlag_AllocCanLeak, Dqn_FStr8_ToStr8(&label));
|
||||||
|
} else {
|
||||||
|
// NOTE: Reuse the arena
|
||||||
|
result->error_sink_arena = catalog_item->arena;
|
||||||
|
}
|
||||||
|
result->error_sink.arena = result->error_sink_arena;
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,9 @@ struct Dqn_ThreadContext
|
|||||||
{
|
{
|
||||||
Dqn_b32 init;
|
Dqn_b32 init;
|
||||||
Dqn_Arena *scratch_arenas[2];
|
Dqn_Arena *scratch_arenas[2];
|
||||||
|
Dqn_Arena *error_sink_arena;
|
||||||
|
Dqn_CallSite call_site;
|
||||||
|
Dqn_ErrorSink error_sink;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Dqn_Scratch
|
struct Dqn_Scratch
|
||||||
@ -40,4 +43,5 @@ struct Dqn_Scratch
|
|||||||
|
|
||||||
DQN_API bool Dqn_ThreadContext_IsInit();
|
DQN_API bool Dqn_ThreadContext_IsInit();
|
||||||
DQN_API Dqn_ThreadContext *Dqn_ThreadContext_Get();
|
DQN_API Dqn_ThreadContext *Dqn_ThreadContext_Get();
|
||||||
|
#define Dqn_ThreadContext_SaveCallSite do { Dqn_ThreadContext_Get()->call_site = DQN_CALL_SITE; } while (0)
|
||||||
DQN_API Dqn_Scratch Dqn_Scratch_Get(void const *conflict_arena);
|
DQN_API Dqn_Scratch Dqn_Scratch_Get(void const *conflict_arena);
|
||||||
|
@ -710,26 +710,26 @@ static Dqn_UTest Dqn_Test_Fs()
|
|||||||
DQN_UTEST_TEST("File write, read, copy, move and delete") {
|
DQN_UTEST_TEST("File write, read, copy, move and delete") {
|
||||||
// NOTE: Write step
|
// NOTE: Write step
|
||||||
Dqn_Str8 const SRC_FILE = DQN_STR8("dqn_test_file");
|
Dqn_Str8 const SRC_FILE = DQN_STR8("dqn_test_file");
|
||||||
Dqn_b32 write_result = Dqn_OS_WriteAll(SRC_FILE, DQN_STR8("test"));
|
Dqn_b32 write_result = Dqn_OS_WriteAll(SRC_FILE, DQN_STR8("test"), nullptr);
|
||||||
DQN_UTEST_ASSERT(&test, write_result);
|
DQN_UTEST_ASSERT(&test, write_result);
|
||||||
DQN_UTEST_ASSERT(&test, Dqn_OS_FileExists(SRC_FILE));
|
DQN_UTEST_ASSERT(&test, Dqn_OS_FileExists(SRC_FILE));
|
||||||
|
|
||||||
// NOTE: Read step
|
// NOTE: Read step
|
||||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||||
Dqn_Str8 read_file = Dqn_OS_ReadAll(SRC_FILE, scratch.arena);
|
Dqn_Str8 read_file = Dqn_OS_ReadAll(SRC_FILE, scratch.arena, nullptr);
|
||||||
DQN_UTEST_ASSERTF(&test, Dqn_Str8_HasData(read_file), "Failed to load file");
|
DQN_UTEST_ASSERTF(&test, Dqn_Str8_HasData(read_file), "Failed to load file");
|
||||||
DQN_UTEST_ASSERTF(&test, read_file.size == 4, "File read wrong amount of bytes");
|
DQN_UTEST_ASSERTF(&test, read_file.size == 4, "File read wrong amount of bytes");
|
||||||
DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(read_file, DQN_STR8("test")), "read(%zu): %.*s", read_file.size, DQN_STR_FMT(read_file));
|
DQN_UTEST_ASSERTF(&test, Dqn_Str8_Eq(read_file, DQN_STR8("test")), "read(%zu): %.*s", read_file.size, DQN_STR_FMT(read_file));
|
||||||
|
|
||||||
// NOTE: Copy step
|
// NOTE: Copy step
|
||||||
Dqn_Str8 const COPY_FILE = DQN_STR8("dqn_test_file_copy");
|
Dqn_Str8 const COPY_FILE = DQN_STR8("dqn_test_file_copy");
|
||||||
Dqn_b32 copy_result = Dqn_OS_FileCopy(SRC_FILE, COPY_FILE, true /*overwrite*/);
|
Dqn_b32 copy_result = Dqn_OS_FileCopy(SRC_FILE, COPY_FILE, true /*overwrite*/, nullptr);
|
||||||
DQN_UTEST_ASSERT(&test, copy_result);
|
DQN_UTEST_ASSERT(&test, copy_result);
|
||||||
DQN_UTEST_ASSERT(&test, Dqn_OS_FileExists(COPY_FILE));
|
DQN_UTEST_ASSERT(&test, Dqn_OS_FileExists(COPY_FILE));
|
||||||
|
|
||||||
// NOTE: Move step
|
// NOTE: Move step
|
||||||
Dqn_Str8 const MOVE_FILE = DQN_STR8("dqn_test_file_move");
|
Dqn_Str8 const MOVE_FILE = DQN_STR8("dqn_test_file_move");
|
||||||
Dqn_b32 move_result = Dqn_OS_FileMove(COPY_FILE, MOVE_FILE, true /*overwrite*/);
|
Dqn_b32 move_result = Dqn_OS_FileMove(COPY_FILE, MOVE_FILE, true /*overwrite*/, nullptr);
|
||||||
DQN_UTEST_ASSERT(&test, move_result);
|
DQN_UTEST_ASSERT(&test, move_result);
|
||||||
DQN_UTEST_ASSERT(&test, Dqn_OS_FileExists(MOVE_FILE));
|
DQN_UTEST_ASSERT(&test, Dqn_OS_FileExists(MOVE_FILE));
|
||||||
DQN_UTEST_ASSERTF(&test, Dqn_OS_FileExists(COPY_FILE) == false, "Moving a file should remove the original");
|
DQN_UTEST_ASSERTF(&test, Dqn_OS_FileExists(COPY_FILE) == false, "Moving a file should remove the original");
|
||||||
|
Loading…
Reference in New Issue
Block a user