Compile on Linux again, fix comment warning, implement output capture on exec on posix
This commit is contained in:
parent
b1eab3abdf
commit
3bca236a74
@ -1,5 +1,7 @@
|
||||
#if !defined(DQN_KECCAK_H)
|
||||
#define DQN_KECCAK_H
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\
|
||||
@ -64,6 +66,9 @@
|
||||
// code of the header file.
|
||||
//
|
||||
// #define DQN_KECCAK_IMPLEMENTATION
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
#if !defined(DQN_KECCAK_MEMCPY)
|
||||
#include <string.h>
|
||||
|
@ -1,6 +1,7 @@
|
||||
#if !defined(DQN_UTEST_H)
|
||||
#define DQN_UTEST_H
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$$\
|
||||
@ -45,6 +46,9 @@
|
||||
// #define DQN_UTEST_GOOD_COLOR
|
||||
// Define this to a terminal color code to specify what color sucess will be
|
||||
// presented as.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: Macros ////////////////////////////////////////////////////////////////////////////////////
|
||||
#include <stdio.h>
|
||||
|
14
build.sh
14
build.sh
@ -1,8 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
|
||||
code_dir=${PWD}
|
||||
mkdir -p Build
|
||||
|
||||
pushd Build
|
||||
g++ ${code_dir}/dqn_unit_tests.cpp -D DQN_TEST_WITH_MAIN -std=c++17 -o dqn_unit_tests
|
||||
g++ \
|
||||
-Wall \
|
||||
-Werror \
|
||||
-fsanitize=address \
|
||||
-std=c++17 \
|
||||
-D DQN_IMPLEMENTATION \
|
||||
-D DQN_UNIT_TESTS_WITH_MAIN \
|
||||
-D DQN_UNIT_TESTS_WITH_KECCAK \
|
||||
-x c++ ${code_dir}/dqn.h \
|
||||
-g \
|
||||
-o dqn_unit_tests
|
||||
popd
|
||||
|
5
dqn.h
5
dqn.h
@ -1,6 +1,7 @@
|
||||
#if !defined(DQN_H)
|
||||
#define DQN_H
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$\ $$$$$$\ $$\ $$\
|
||||
@ -207,6 +208,7 @@
|
||||
// library from being included. This might be useful if you are including the
|
||||
// library in your project yourself. The library must still be defined and
|
||||
// visible before this header.
|
||||
*/
|
||||
|
||||
#if defined(DQN_ONLY_VARRAY) || \
|
||||
defined(DQN_ONLY_SARRAY) || \
|
||||
@ -344,6 +346,7 @@
|
||||
#endif // DQN_H
|
||||
|
||||
#if defined(DQN_IMPLEMENTATION)
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// /$$$$$$\ $$\ $$\ $$$$$$$\ $$\
|
||||
@ -358,6 +361,7 @@
|
||||
// Implementation
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
#if defined(DQN_WITH_CGEN)
|
||||
#if !defined(DQN_NO_METADESK)
|
||||
@ -383,6 +387,7 @@
|
||||
#include "dqn_debug.cpp"
|
||||
#include "dqn_string.cpp"
|
||||
#include "dqn_containers.cpp"
|
||||
#include "dqn_type_info.cpp"
|
||||
|
||||
#if defined(DQN_PLATFORM_EMSCRIPTEN) || defined(DQN_PLATFORM_POSIX) || defined(DQN_PLATFORM_ARM64)
|
||||
#include "dqn_os_posix.cpp"
|
||||
|
@ -1,3 +1,4 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\ $$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$\
|
||||
@ -12,6 +13,7 @@
|
||||
// dqn_allocator.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$AREN] Dqn_Arena /////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_ArenaBlock *Dqn_Arena_BlockInit(uint64_t reserve, uint64_t commit, bool track_alloc, bool alloc_can_leak)
|
||||
@ -300,7 +302,7 @@ DQN_API void *Dqn_ChunkPool_Alloc(Dqn_ChunkPool *pool, Dqn_usize size)
|
||||
#if defined(DQN_OS_WIN32)
|
||||
Dqn_usize dist_to_next_msb = __lzcnt64(required_size) + 1;
|
||||
#else
|
||||
Dqn_usize dist_to_next_msb = __builtin_clz(required_size) + 1;
|
||||
Dqn_usize dist_to_next_msb = __builtin_clzll(required_size) + 1;
|
||||
#endif
|
||||
|
||||
// NOTE: Round up if not PoT as the low bits are set.
|
||||
|
@ -1,3 +1,4 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$\ $$\ $$$$$$\ $$$$$$\ $$$$$$\ $$$$$$$$\ $$$$$$\ $$$$$$$\
|
||||
@ -17,6 +18,9 @@
|
||||
// [$CHUN] Dqn_ChunkPool -- Allocates reusable, free-able memory in PoT chunks
|
||||
// [$ACAT] Dqn_ArenaCatalog -- Collate, create & manage arenas in a catalog
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$AREN] Dqn_Arena /////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_ARENA_RESERVE_SIZE)
|
||||
#define DQN_ARENA_RESERVE_SIZE DQN_MEGABYTES(64)
|
||||
|
49
dqn_base.cpp
49
dqn_base.cpp
@ -1,3 +1,4 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$\
|
||||
@ -12,6 +13,7 @@
|
||||
// dqn_base.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$INTR] Intrinsics ////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_PLATFORM_ARM64) && !defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
@ -303,25 +305,25 @@ DQN_API Dqn_Str8 Dqn_Log_MakeStr8(Dqn_Arena *arena,
|
||||
Dqn_Str8 file_name = Dqn_Str8_FileNameFromPath(call_site.file);
|
||||
Dqn_OSDateTimeStr8 const time = Dqn_OS_DateLocalTimeStr8Now();
|
||||
Dqn_Str8 header = Dqn_Str8_InitF(arena,
|
||||
"%.*s " // date
|
||||
"%.*s " // hms
|
||||
"%.*s" // colour
|
||||
"%.*s" // bold
|
||||
"%.*s" // type
|
||||
"%*s" // type padding
|
||||
"%.*s" // reset
|
||||
" %.*s" // file name
|
||||
":%05I32u " // line number
|
||||
,
|
||||
DQN_CAST(int)time.date_size - 2, time.date + 2, // date
|
||||
DQN_CAST(int)time.hms_size, time.hms, // hms
|
||||
DQN_STR_FMT(colour_esc), // colour
|
||||
DQN_STR_FMT(bold_esc), // bold
|
||||
DQN_STR_FMT(type), // type
|
||||
DQN_CAST(int)type_padding, "", // type padding
|
||||
DQN_STR_FMT(reset_esc), // reset
|
||||
DQN_STR_FMT(file_name), // file name
|
||||
call_site.line); // line number
|
||||
"%.*s " // date
|
||||
"%.*s " // hms
|
||||
"%.*s" // colour
|
||||
"%.*s" // bold
|
||||
"%.*s" // type
|
||||
"%*s" // type padding
|
||||
"%.*s" // reset
|
||||
" %.*s" // file name
|
||||
":%05I32u " // line number
|
||||
,
|
||||
DQN_CAST(int)time.date_size - 2, time.date + 2, // date
|
||||
DQN_CAST(int)time.hms_size, time.hms, // hms
|
||||
DQN_STR_FMT(colour_esc), // colour
|
||||
DQN_STR_FMT(bold_esc), // bold
|
||||
DQN_STR_FMT(type), // type
|
||||
DQN_CAST(int)type_padding, "", // type padding
|
||||
DQN_STR_FMT(reset_esc), // reset
|
||||
DQN_STR_FMT(file_name), // file name
|
||||
call_site.line); // line number
|
||||
Dqn_usize header_size_no_ansi_codes = header.size - colour_esc.size - Dqn_Print_ESCResetStr8.size;
|
||||
|
||||
// NOTE: Header padding ////////////////////////////////////////////////////////////////////////
|
||||
@ -332,9 +334,12 @@ DQN_API Dqn_Str8 Dqn_Log_MakeStr8(Dqn_Arena *arena,
|
||||
// NOTE: Construct final log ///////////////////////////////////////////////////////////////////
|
||||
Dqn_Str8 user_msg = Dqn_Str8_InitFV(arena, fmt, args);
|
||||
Dqn_Str8 result = Dqn_Str8_Alloc(arena, header.size + header_padding + user_msg.size, Dqn_ZeroMem_No);
|
||||
DQN_MEMCPY(result.data, header.data, header.size);
|
||||
DQN_MEMSET(result.data + header.size, ' ', header_padding);
|
||||
DQN_MEMCPY(result.data + header.size + header_padding, user_msg.data, user_msg.size);
|
||||
if (Dqn_Str8_HasData(result)) {
|
||||
DQN_MEMCPY(result.data, header.data, header.size);
|
||||
DQN_MEMSET(result.data + header.size, ' ', header_padding);
|
||||
if (Dqn_Str8_HasData(user_msg))
|
||||
DQN_MEMCPY(result.data + header.size + header_padding, user_msg.data, user_msg.size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
14
dqn_base.h
14
dqn_base.h
@ -1,4 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/*
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$\
|
||||
// $$ __$$\
|
||||
@ -21,6 +22,9 @@
|
||||
// [$PRIN] Dqn_Print -- Console printing
|
||||
// [$LLOG] Dqn_Log -- Console logging macros
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$MACR] Macros ////////////////////////////////////////////////////////////////////////////
|
||||
#define DQN_STRINGIFY(x) #x
|
||||
#define DQN_TOKEN_COMBINE2(x, y) x ## y
|
||||
@ -214,10 +218,10 @@
|
||||
#define DQN_CHAR_COUNT(string) (sizeof(string) - 1)
|
||||
|
||||
// NOTE: SI Byte ///////////////////////////////////////////////////////////////////////////////////
|
||||
#define DQN_BYTES(val) (val)
|
||||
#define DQN_KILOBYTES(val) (1024ULL * DQN_BYTES(val))
|
||||
#define DQN_MEGABYTES(val) (1024ULL * DQN_KILOBYTES(val))
|
||||
#define DQN_GIGABYTES(val) (1024ULL * DQN_MEGABYTES(val))
|
||||
#define DQN_BYTES(val) ((uint64_t)val)
|
||||
#define DQN_KILOBYTES(val) ((uint64_t)1024 * DQN_BYTES(val))
|
||||
#define DQN_MEGABYTES(val) ((uint64_t)1024 * DQN_KILOBYTES(val))
|
||||
#define DQN_GIGABYTES(val) ((uint64_t)1024 * DQN_MEGABYTES(val))
|
||||
|
||||
// NOTE: Time //////////////////////////////////////////////////////////////////////////////////////
|
||||
#define DQN_SECONDS_TO_MS(val) ((val) * 1000)
|
||||
|
@ -1,3 +1,4 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$\ $$$$$$\
|
||||
@ -12,6 +13,7 @@
|
||||
// dqn_containers.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$SLIC] Dqn_Slice /////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_Str8 Dqn_Slice_Str8Render(Dqn_Arena *arena, Dqn_Slice<Dqn_Str8> array, Dqn_Str8 separator)
|
||||
|
@ -1,3 +1,4 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$\ $$$$$$\
|
||||
@ -22,6 +23,7 @@
|
||||
// [$LIST] Dqn_List -- DQN_LIST -- Chunked linked lists, append only
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$CARR] Dqn_CArray ////////////////////////////////////////////////////////////////////////
|
||||
enum Dqn_ArrayErase
|
||||
|
@ -1,3 +1,4 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$\ $$$$$$$$\ $$$$$$$\ $$\ $$\ $$$$$$\
|
||||
@ -12,6 +13,7 @@
|
||||
// dqn_debug.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$ASAN] Dqn_Asan ////////////////////////////////////////////////////////////////////////// ///
|
||||
DQN_API void Dqn_ASAN_PoisonMemoryRegion(void const volatile *ptr, Dqn_usize size)
|
||||
@ -123,7 +125,7 @@ static void Dqn_StackTrace_AddWalkToStr8Builder_(Dqn_StackTraceWalkResult const
|
||||
for (Dqn_usize index = skip; index < walk->size; index++) {
|
||||
raw_frame.base_addr = walk->base_addr[index];
|
||||
Dqn_StackTraceFrame frame = Dqn_StackTrace_RawFrameToFrame(builder->arena, raw_frame);
|
||||
Dqn_Str8Builder_AppendF(builder, "%.*s(%I64u): %.*s%s", DQN_STR_FMT(frame.file_name), frame.line_number, DQN_STR_FMT(frame.function_name), (index == walk->size - 1) ? "" : "\n");
|
||||
Dqn_Str8Builder_AppendF(builder, "%.*s(%zu): %.*s%s", DQN_STR_FMT(frame.file_name), frame.line_number, DQN_STR_FMT(frame.function_name), (DQN_CAST(int)index == walk->size - 1) ? "" : "\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$\ $$$$$$$$\ $$$$$$$\ $$\ $$\ $$$$$$\
|
||||
@ -18,6 +19,7 @@
|
||||
// [$DEBG] Dqn_Debug -- Allocation leak tracking API
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$ASAN] Dqn_Asan //////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_ASAN_POISON)
|
||||
|
@ -1,3 +1,4 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\
|
||||
@ -24,6 +25,7 @@
|
||||
// and/or conciseness of being able to learn the breadth of the APIs.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
DQN_MSVC_WARNING_PUSH
|
||||
DQN_MSVC_WARNING_DISABLE(4702) // unreachable code
|
||||
@ -577,7 +579,7 @@ void Dqn_Docs_Demo()
|
||||
|
||||
// Print the result like so
|
||||
if (0) {
|
||||
printf("%.*s[%u] %llu cycles (%.1fms)\n",
|
||||
printf("%.*s[%u] %" PRIu64 " cycles (%.1fms)\n",
|
||||
DQN_STR_FMT(anchor->name),
|
||||
anchor->hit_count,
|
||||
anchor->tsc_inclusive,
|
||||
@ -691,7 +693,7 @@ void Dqn_Docs_Demo()
|
||||
|
||||
// You may then print out the frame like so
|
||||
if (0)
|
||||
printf("%.*s(%llu): %.*s\n", DQN_STR_FMT(frame.file_name), frame.line_number, DQN_STR_FMT(frame.function_name));
|
||||
printf("%.*s(%" PRIu64 "): %.*s\n", DQN_STR_FMT(frame.file_name), frame.line_number, DQN_STR_FMT(frame.function_name));
|
||||
}
|
||||
|
||||
// If you load new shared-libraries into the address space it maybe
|
||||
|
@ -1,3 +1,4 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$$$$$$\ $$\ $$\ $$$$$$\ $$\
|
||||
@ -12,6 +13,7 @@
|
||||
// dqn_external.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
#if !defined(DQN_USE_STD_PRINTF) && !defined(DQN_STB_SPRINTF_HEADER_ONLY)
|
||||
// NOTE: [$STBS] stb_sprintf ///////////////////////////////////////////////////////////////////////
|
||||
|
@ -1,3 +1,4 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$$$$$$\ $$\ $$\ $$$$$$\ $$\
|
||||
@ -12,6 +13,7 @@
|
||||
// dqn_external.h -- Third party dependencies
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$OS_H] OS Headers ////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_OS_WIN32) || defined(DQN_OS_WIN32_USE_PTHREADS)
|
||||
@ -56,7 +58,7 @@
|
||||
#define DQN_VSNPRINTF(...) STB_SPRINTF_DECORATE(vsnprintf)(__VA_ARGS__)
|
||||
|
||||
#if (DQN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)) && defined(DQN_COMPILER_MSVC)
|
||||
#error The STB implementation of sprintf triggers MSVC's implementation of ASAN. Compiling ASAN with STB sprintf is not supported.
|
||||
#error The STB implementation of sprintf triggers MSVCs implementation of ASAN. Compiling ASAN with STB sprintf is not supported.
|
||||
|
||||
// NOTE: stb_sprintf assumes c-string literals are 4 byte aligned which is
|
||||
// always true, however, reading past the end of a string whose size is not
|
||||
|
@ -1,3 +1,4 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$\ $$$$$$\ $$\ $$\
|
||||
@ -12,6 +13,7 @@
|
||||
// dqn_hash.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$FNV1] Dqn_FNV1A /////////////////////////////////////////////////////////////////////////
|
||||
// Default values recommended by: http://isthe.com/chongo/tech/comp/fnv/
|
||||
|
@ -1,3 +1,4 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$\ $$$$$$\ $$\ $$\
|
||||
@ -17,6 +18,7 @@
|
||||
// [$MMUR] Dqn_MurmurHash3 -- Hash(x) -> 32/128bit via MurmurHash3
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$FNV1] Dqn_FNV1A /////////////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_FNV1A32_SEED)
|
||||
|
@ -1,3 +1,4 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$$$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$$\ $$$$$$\
|
||||
@ -12,6 +13,7 @@
|
||||
// dqn_helpers.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$PCGX] Dqn_PCG32 /////////////////////////////////////////////////////////////////////////
|
||||
#define DQN_PCG_DEFAULT_MULTIPLIER_64 6364136223846793005ULL
|
||||
|
@ -1,3 +1,4 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$$$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$$\ $$$$$$\
|
||||
@ -24,6 +25,7 @@
|
||||
// [$PROF] Dqn_Profiler -- DQN_PROFILER -- Profiler that measures using a timestamp counter
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$PCGX] Dqn_PCG32 /////////////////////////////////////////////////////////////////////////
|
||||
struct Dqn_PCG32 { uint64_t state; };
|
||||
|
@ -1,3 +1,4 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$\ $$$$$$$$\ $$\ $$\
|
||||
@ -12,6 +13,7 @@
|
||||
// dqn_math.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
#if !defined(DQN_NO_V2)
|
||||
// NOTE: [$VEC2] Vector2 ///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1,3 +1,4 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$\ $$\ $$$$$$\ $$$$$$$$\ $$\ $$\
|
||||
@ -22,6 +23,7 @@
|
||||
// [$MATH] Other --
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
DQN_MSVC_WARNING_PUSH
|
||||
DQN_MSVC_WARNING_DISABLE(4201) // warning C4201: nonstandard extension used: nameless struct/union
|
||||
|
10
dqn_os.cpp
10
dqn_os.cpp
@ -1,3 +1,4 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\
|
||||
@ -12,6 +13,7 @@
|
||||
// dqn_os.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$DATE] Date //////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_OSDateTimeStr8 Dqn_OS_DateLocalTimeStr8(Dqn_OSDateTime time, char date_separator, char hms_separator)
|
||||
@ -442,19 +444,19 @@ DQN_API Dqn_Str8 Dqn_OS_PathBuildWithSeparator(Dqn_Arena *arena, Dqn_OSPath cons
|
||||
// NOTE: [$EXEC] Dqn_OSExec ////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_Exec(Dqn_Slice<Dqn_Str8> cmd_line,
|
||||
Dqn_Str8 working_dir,
|
||||
uint8_t exec_flag,
|
||||
uint8_t exec_flags,
|
||||
Dqn_Arena *arena,
|
||||
Dqn_ErrorSink *error)
|
||||
{
|
||||
Dqn_OSExecAsyncHandle async_handle = Dqn_OS_ExecAsync(cmd_line, working_dir, exec_flag, error);
|
||||
Dqn_OSExecAsyncHandle async_handle = Dqn_OS_ExecAsync(cmd_line, working_dir, exec_flags, error);
|
||||
Dqn_OSExecResult result = Dqn_OS_ExecWait(async_handle, arena, error);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_ExecOrAbort(Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir, uint8_t exec_flag, Dqn_Arena *arena)
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_ExecOrAbort(Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir, uint8_t exec_flags, Dqn_Arena *arena)
|
||||
{
|
||||
Dqn_ErrorSink *error = Dqn_ErrorSink_Begin(Dqn_ErrorSinkMode_Nil);
|
||||
Dqn_OSExecResult result = Dqn_OS_Exec(cmd_line, working_dir, exec_flag, arena, error);
|
||||
Dqn_OSExecResult result = Dqn_OS_Exec(cmd_line, working_dir, exec_flags, arena, error);
|
||||
if (result.os_error_code) {
|
||||
Dqn_ErrorSink_EndAndExitIfErrorF(
|
||||
error,
|
||||
|
10
dqn_os.h
10
dqn_os.h
@ -1,3 +1,4 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\
|
||||
@ -24,6 +25,7 @@
|
||||
// [$HTTP] Dqn_OSHttp -- --
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$OMEM] Dqn_OSMem //////////////////////////////////////////////////////////////////////////
|
||||
enum Dqn_OSMemCommit
|
||||
@ -184,12 +186,10 @@ struct Dqn_OSExecAsyncHandle
|
||||
uint32_t os_error_code;
|
||||
uint32_t exit_code;
|
||||
void *process;
|
||||
#if defined(DQN_OS_WIN32)
|
||||
void *stdout_read;
|
||||
void *stdout_write;
|
||||
void *stderr_read;
|
||||
void *stderr_write;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct Dqn_OSExecResult
|
||||
@ -364,9 +364,9 @@ DQN_API Dqn_Str8 Dqn_OS_PathConvertF (Dqn_Arena *arena
|
||||
// NOTE: [$EXEC] Dqn_OSExec ////////////////////////////////////////////////////////////////////////
|
||||
DQN_API void Dqn_OS_Exit (int32_t exit_code);
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_ExecWait (Dqn_OSExecAsyncHandle handle, Dqn_Arena *arena, Dqn_ErrorSink *error);
|
||||
DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync (Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir, uint8_t exec_flag, Dqn_ErrorSink *error);
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_Exec (Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir, uint8_t exec_flag, Dqn_Arena *arena, Dqn_ErrorSink *error);
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_ExecOrAbort(Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir, uint8_t exec_flag, Dqn_Arena *arena);
|
||||
DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync (Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir, uint8_t exec_flags, Dqn_ErrorSink *error);
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_Exec (Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir, uint8_t exec_flags, Dqn_Arena *arena, Dqn_ErrorSink *error);
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_ExecOrAbort(Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir, uint8_t exec_flags, Dqn_Arena *arena);
|
||||
|
||||
// NOTE: [$SEMA] Dqn_OSSemaphore ///////////////////////////////////////////////////////////////////
|
||||
#if !defined(DQN_NO_SEMAPHORE)
|
||||
|
454
dqn_os_posix.cpp
454
dqn_os_posix.cpp
@ -1,3 +1,4 @@
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// $$$$$$\ $$$$$$\ $$$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\ $$\ $$\
|
||||
@ -9,11 +10,13 @@
|
||||
// $$$$$$ |\$$$$$$ | $$ | $$$$$$ |\$$$$$$ |$$$$$$\ $$ / $$ |
|
||||
// \______/ \______/ \__| \______/ \______/ \______|\__| \__|
|
||||
//
|
||||
// dqn_os_posix.cpp -- Posix implementation of the OS layer
|
||||
// dqn_os_posix.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
// NOTE: [$VMEM] Dqn_OSMem //////////////////////////////////////////////////////////////////////////
|
||||
// NOTE: [$VMEM] Dqn_OSMem
|
||||
// //////////////////////////////////////////////////////////////////////////
|
||||
static uint32_t Dqn_OS_MemConvertPageToOSFlags_(uint32_t protect)
|
||||
{
|
||||
DQN_ASSERT((protect & ~Dqn_OSMemPage_All) == 0);
|
||||
@ -38,7 +41,7 @@ DQN_API void *Dqn_OS_MemReserve(Dqn_usize size, Dqn_OSMemCommit commit, uint32_t
|
||||
if (commit == Dqn_OSMemCommit_Yes)
|
||||
os_page_flags |= (PROT_READ | PROT_WRITE);
|
||||
|
||||
void *result = mmap(nullptr, size, os_page_flags, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||
void *result = mmap(nullptr, size, os_page_flags, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (result == MAP_FAILED)
|
||||
result = nullptr;
|
||||
return result;
|
||||
@ -71,11 +74,14 @@ DQN_API int Dqn_OS_MemProtect(void *ptr, Dqn_usize size, uint32_t page_flags)
|
||||
if (!ptr || size == 0)
|
||||
return 0;
|
||||
|
||||
static Dqn_Str8 const ALIGNMENT_ERROR_MSG =
|
||||
DQN_STR8("Page protection requires pointers to be page aligned because we "
|
||||
"can only guard memory at a multiple of the page boundary.");
|
||||
DQN_ASSERTF(Dqn_IsPowerOfTwoAligned(DQN_CAST(uintptr_t)ptr, g_dqn_library->os_page_size), "%s", ALIGNMENT_ERROR_MSG.data);
|
||||
DQN_ASSERTF(Dqn_IsPowerOfTwoAligned(size, g_dqn_library->os_page_size), "%s", ALIGNMENT_ERROR_MSG.data);
|
||||
static Dqn_Str8 const ALIGNMENT_ERROR_MSG = DQN_STR8(
|
||||
"Page protection requires pointers to be page aligned because we "
|
||||
"can only guard memory at a multiple of the page boundary.");
|
||||
DQN_ASSERTF(Dqn_IsPowerOfTwoAligned(DQN_CAST(uintptr_t) ptr, g_dqn_library->os_page_size),
|
||||
"%s",
|
||||
ALIGNMENT_ERROR_MSG.data);
|
||||
DQN_ASSERTF(
|
||||
Dqn_IsPowerOfTwoAligned(size, g_dqn_library->os_page_size), "%s", ALIGNMENT_ERROR_MSG.data);
|
||||
|
||||
unsigned long os_page_flags = Dqn_OS_MemConvertPageToOSFlags_(page_flags);
|
||||
int result = mprotect(ptr, size, os_page_flags);
|
||||
@ -86,7 +92,7 @@ DQN_API int Dqn_OS_MemProtect(void *ptr, Dqn_usize size, uint32_t page_flags)
|
||||
// NOTE: [$DATE] Date //////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_OSDateTime Dqn_OS_DateLocalTimeNow()
|
||||
{
|
||||
Dqn_OSDateTime result = {};
|
||||
Dqn_OSDateTime result = {};
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
|
||||
@ -105,9 +111,9 @@ DQN_API Dqn_OSDateTime Dqn_OS_DateLocalTimeNow()
|
||||
result.minutes = time.tm_min;
|
||||
result.seconds = time.tm_sec;
|
||||
|
||||
result.day = DQN_CAST(uint8_t)time.tm_mday;
|
||||
result.month = DQN_CAST(uint8_t)time.tm_mon + 1;
|
||||
result.year = 1900 + DQN_CAST(int16_t)time.tm_year;
|
||||
result.day = DQN_CAST(uint8_t) time.tm_mday;
|
||||
result.month = DQN_CAST(uint8_t) time.tm_mon + 1;
|
||||
result.year = 1900 + DQN_CAST(int16_t) time.tm_year;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -134,7 +140,8 @@ DQN_API uint64_t Dqn_OS_DateToUnixTime(Dqn_OSDateTime date)
|
||||
DQN_API bool Dqn_OS_SecureRNGBytes(void *buffer, uint32_t size)
|
||||
{
|
||||
#if defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
(void)buffer; (void)size;
|
||||
(void)buffer;
|
||||
(void)size;
|
||||
return false;
|
||||
#else
|
||||
if (!buffer || size < 0)
|
||||
@ -144,13 +151,16 @@ DQN_API bool Dqn_OS_SecureRNGBytes(void *buffer, uint32_t size)
|
||||
return true;
|
||||
|
||||
DQN_ASSERTF(size <= 32,
|
||||
"We can increase this by chunking the buffer and filling 32 bytes at a time. *Nix guarantees 32 "
|
||||
"We can increase this by chunking the buffer and filling 32 bytes at a time. *Nix "
|
||||
"guarantees 32 "
|
||||
"bytes can always be fulfilled by this system at a time");
|
||||
// TODO(doyle): https://github.com/jedisct1/libsodium/blob/master/src/libsodium/randombytes/sysrandom/randombytes_sysrandom.c
|
||||
// TODO(doyle):
|
||||
// https://github.com/jedisct1/libsodium/blob/master/src/libsodium/randombytes/sysrandom/randombytes_sysrandom.c
|
||||
// TODO(doyle): https://man7.org/linux/man-pages/man2/getrandom.2.html
|
||||
uint32_t read_bytes = 0;
|
||||
do {
|
||||
read_bytes = getrandom(buffer, size, 0); // NOTE: EINTR can not be triggered if size <= 32 bytes
|
||||
read_bytes =
|
||||
getrandom(buffer, size, 0); // NOTE: EINTR can not be triggered if size <= 32 bytes
|
||||
} while (read_bytes != size || errno == EAGAIN);
|
||||
return true;
|
||||
#endif
|
||||
@ -164,9 +174,9 @@ DQN_API Dqn_Str8 Dqn_OS_EXEPath(Dqn_Arena *arena)
|
||||
|
||||
int required_size_wo_null_terminator = 0;
|
||||
for (int try_size = 128;; try_size *= 2) {
|
||||
auto scoped_arena = Dqn_ArenaTempMemScope(arena);
|
||||
char *try_buf = Dqn_Arena_NewArray(arena, char, try_size, Dqn_ZeroMem_No);
|
||||
int bytes_written = readlink("/proc/self/exe", try_buf, try_size);
|
||||
auto scoped_arena = Dqn_ArenaTempMemScope(arena);
|
||||
char *try_buf = Dqn_Arena_NewArray(arena, char, try_size, Dqn_ZeroMem_No);
|
||||
int bytes_written = readlink("/proc/self/exe", try_buf, try_size);
|
||||
if (bytes_written == -1) {
|
||||
// Failed, we're unable to determine the executable directory
|
||||
break;
|
||||
@ -184,7 +194,9 @@ DQN_API Dqn_Str8 Dqn_OS_EXEPath(Dqn_Arena *arena)
|
||||
// try_buf around, memcopy the byte and trash the try_buf from the
|
||||
// arena. Instead we just get the size and redo the call one last
|
||||
// time after this "calculate" step.
|
||||
DQN_ASSERTF(bytes_written < try_size, "bytes_written can never be greater than the try size, function writes at most try_size");
|
||||
DQN_ASSERTF(bytes_written < try_size,
|
||||
"bytes_written can never be greater than the try size, function writes at "
|
||||
"most try_size");
|
||||
required_size_wo_null_terminator = bytes_written;
|
||||
break;
|
||||
}
|
||||
@ -192,7 +204,8 @@ DQN_API Dqn_Str8 Dqn_OS_EXEPath(Dqn_Arena *arena)
|
||||
|
||||
if (required_size_wo_null_terminator) {
|
||||
Dqn_ArenaTempMem temp_mem = Dqn_Arena_TempMemBegin(arena);
|
||||
char *exe_path = Dqn_Arena_NewArray(arena, char, required_size_wo_null_terminator + 1, Dqn_ZeroMem_No);
|
||||
char *exe_path =
|
||||
Dqn_Arena_NewArray(arena, char, required_size_wo_null_terminator + 1, Dqn_ZeroMem_No);
|
||||
exe_path[required_size_wo_null_terminator] = 0;
|
||||
|
||||
int bytes_written = readlink("/proc/self/exe", exe_path, required_size_wo_null_terminator);
|
||||
@ -239,7 +252,8 @@ DQN_API Dqn_OSPathInfo Dqn_OS_PathInfo(Dqn_Str8 path)
|
||||
result.last_write_time_in_s = file_stat.st_mtime;
|
||||
// TODO(dqn): Seems linux does not support creation time via stat. We
|
||||
// shoddily deal with this.
|
||||
result.create_time_in_s = DQN_MIN(result.last_access_time_in_s, result.last_write_time_in_s);
|
||||
result.create_time_in_s =
|
||||
DQN_MIN(result.last_access_time_in_s, result.last_write_time_in_s);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -281,7 +295,8 @@ DQN_API bool Dqn_OS_CopyFile(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_Er
|
||||
strerror(error_code));
|
||||
return result;
|
||||
}
|
||||
DQN_DEFER {
|
||||
DQN_DEFER
|
||||
{
|
||||
close(src_fd);
|
||||
};
|
||||
|
||||
@ -296,12 +311,13 @@ DQN_API bool Dqn_OS_CopyFile(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_Er
|
||||
strerror(error_code));
|
||||
return result;
|
||||
}
|
||||
DQN_DEFER {
|
||||
DQN_DEFER
|
||||
{
|
||||
close(dest_fd);
|
||||
};
|
||||
|
||||
struct stat stat_existing;
|
||||
int fstat_result = fstat(src_fd, &stat_existing);
|
||||
int fstat_result = fstat(src_fd, &stat_existing);
|
||||
if (fstat_result == -1) {
|
||||
int error_code = errno;
|
||||
Dqn_ErrorSink_MakeF(error,
|
||||
@ -316,13 +332,16 @@ DQN_API bool Dqn_OS_CopyFile(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_Er
|
||||
ssize_t bytes_written = sendfile64(dest_fd, src_fd, 0, stat_existing.st_size);
|
||||
result = (bytes_written == stat_existing.st_size);
|
||||
if (!result) {
|
||||
int error_code = errno;
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 file_size_str8 = Dqn_U64ToByteSizeStr8(scratch.arena, stat_existing.st_size, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_Str8 bytes_written_str8 = Dqn_U64ToByteSizeStr8(scratch.arena, bytes_written, Dqn_U64ByteSizeType_Auto);
|
||||
int error_code = errno;
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 file_size_str8 =
|
||||
Dqn_U64ToByteSizeStr8(scratch.arena, stat_existing.st_size, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_Str8 bytes_written_str8 =
|
||||
Dqn_U64ToByteSizeStr8(scratch.arena, bytes_written, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_ErrorSink_MakeF(error,
|
||||
error_code,
|
||||
"Failed to copy file '%.*s' to '%.*s', we copied %.*s but the file size is %.*s: (%d) %s",
|
||||
"Failed to copy file '%.*s' to '%.*s', we copied %.*s but the file "
|
||||
"size is %.*s: (%d) %s",
|
||||
DQN_STR_FMT(src),
|
||||
DQN_STR_FMT(dest),
|
||||
DQN_STR_FMT(bytes_written_str8),
|
||||
@ -350,12 +369,13 @@ DQN_API bool Dqn_OS_MoveFile(Dqn_Str8 src, Dqn_Str8 dest, bool overwrite, Dqn_Er
|
||||
int unlink_result = unlink(src.data);
|
||||
if (unlink_result == -1) {
|
||||
int error_code = errno;
|
||||
Dqn_ErrorSink_MakeF(error,
|
||||
error_code,
|
||||
"File '%.*s' was moved but failed to be unlinked from old location: (%d) %s",
|
||||
DQN_STR_FMT(src),
|
||||
error_code,
|
||||
strerror(error_code));
|
||||
Dqn_ErrorSink_MakeF(
|
||||
error,
|
||||
error_code,
|
||||
"File '%.*s' was moved but failed to be unlinked from old location: (%d) %s",
|
||||
DQN_STR_FMT(src),
|
||||
error_code,
|
||||
strerror(error_code));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -378,10 +398,10 @@ DQN_API bool Dqn_OS_MakeDir(Dqn_Str8 path)
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
bool result = true;
|
||||
|
||||
// TODO(doyle): Implement this without using the path indexes, it's not
|
||||
// TODO(doyle): Implement this without using the path indexes, it's not
|
||||
// necessary. See Windows implementation.
|
||||
Dqn_usize path_indexes_size = 0;
|
||||
uint16_t path_indexes[64] = {};
|
||||
uint16_t path_indexes[64] = {};
|
||||
|
||||
Dqn_Str8 copy = Dqn_Str8_Copy(scratch.arena, path);
|
||||
for (Dqn_usize index = copy.size - 1; index < copy.size; index--) {
|
||||
@ -403,34 +423,37 @@ DQN_API bool Dqn_OS_MakeDir(Dqn_Str8 path)
|
||||
// it's not a directory. This request to make a directory is
|
||||
// invalid.
|
||||
return false;
|
||||
} else if (Dqn_OS_DirExists(copy)) {
|
||||
// NOTE: We found a directory, we can stop here and start
|
||||
// building up all the directories that didn't exist up to
|
||||
// this point.
|
||||
break;
|
||||
} else {
|
||||
if (Dqn_OS_DirExists(copy)) {
|
||||
// NOTE: We found a directory, we can stop here and start
|
||||
// building up all the directories that didn't exist up to
|
||||
// this point.
|
||||
break;
|
||||
} else {
|
||||
// NOTE: There's nothing that exists at this path, we can
|
||||
// create a directory here
|
||||
path_indexes[path_indexes_size++] = DQN_CAST(uint16_t)index;
|
||||
}
|
||||
// NOTE: There's nothing that exists at this path, we can
|
||||
// create a directory here
|
||||
path_indexes[path_indexes_size++] = DQN_CAST(uint16_t) index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Dqn_usize index = path_indexes_size - 1; result && index < path_indexes_size; index--) {
|
||||
uint16_t path_index = path_indexes[index];
|
||||
char temp = copy.data[path_index];
|
||||
char temp = copy.data[path_index];
|
||||
|
||||
if (index != 0) copy.data[path_index] = 0;
|
||||
if (index != 0)
|
||||
copy.data[path_index] = 0;
|
||||
result |= mkdir(copy.data, 0774) == 0;
|
||||
if (index != 0) copy.data[path_index] = temp;
|
||||
if (index != 0)
|
||||
copy.data[path_index] = temp;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: R/W Stream API ////////////////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_OSFile Dqn_OS_FileOpen(Dqn_Str8 path, Dqn_OSFileOpen open_mode, uint32_t access, Dqn_ErrorSink *error)
|
||||
DQN_API Dqn_OSFile Dqn_OS_FileOpen(Dqn_Str8 path,
|
||||
Dqn_OSFileOpen open_mode,
|
||||
uint32_t access,
|
||||
Dqn_ErrorSink *error)
|
||||
{
|
||||
Dqn_OSFile result = {};
|
||||
if (!Dqn_Str8_HasData(path) || path.size <= 0)
|
||||
@ -443,7 +466,11 @@ DQN_API Dqn_OSFile Dqn_OS_FileOpen(Dqn_Str8 path, Dqn_OSFileOpen open_mode, uint
|
||||
|
||||
if (access & Dqn_OSFileAccess_Execute) {
|
||||
result.error = true;
|
||||
Dqn_ErrorSink_MakeF(error, 1, "Failed to open file '%.*s': File access flag 'execute' is not supported", DQN_STR_FMT(path));
|
||||
Dqn_ErrorSink_MakeF(
|
||||
error,
|
||||
1,
|
||||
"Failed to open file '%.*s': File access flag 'execute' is not supported",
|
||||
DQN_STR_FMT(path));
|
||||
DQN_INVALID_CODE_PATH; // TODO: Not supported via fopen
|
||||
return result;
|
||||
}
|
||||
@ -456,32 +483,41 @@ DQN_API Dqn_OSFile Dqn_OS_FileOpen(Dqn_Str8 path, Dqn_OSFileOpen open_mode, uint
|
||||
FILE *handle = nullptr;
|
||||
switch (open_mode) {
|
||||
case Dqn_OSFileOpen_CreateAlways: handle = fopen(path.data, "w"); break;
|
||||
case Dqn_OSFileOpen_OpenIfExist: handle = fopen(path.data, "r"); break;
|
||||
case Dqn_OSFileOpen_OpenAlways: handle = fopen(path.data, "a"); break;
|
||||
case Dqn_OSFileOpen_OpenIfExist: handle = fopen(path.data, "r"); break;
|
||||
case Dqn_OSFileOpen_OpenAlways: handle = fopen(path.data, "a"); break;
|
||||
default: DQN_INVALID_CODE_PATH; break;
|
||||
}
|
||||
|
||||
if (!handle) { // TODO(doyle): FileOpen flag to string
|
||||
result.error = true;
|
||||
Dqn_ErrorSink_MakeF(error, 1, "Failed to open file '%.*s': File could not be opened in requested mode 'Dqn_OSFileOpen' flag %d", DQN_STR_FMT(path), open_mode);
|
||||
Dqn_ErrorSink_MakeF(error,
|
||||
1,
|
||||
"Failed to open file '%.*s': File could not be opened in requested "
|
||||
"mode 'Dqn_OSFileOpen' flag %d",
|
||||
DQN_STR_FMT(path),
|
||||
open_mode);
|
||||
return result;
|
||||
}
|
||||
fclose(handle);
|
||||
}
|
||||
|
||||
char const *fopen_mode = nullptr;
|
||||
if (access & Dqn_OSFileAccess_AppendOnly) {
|
||||
if (access & Dqn_OSFileAccess_AppendOnly)
|
||||
fopen_mode = "a+";
|
||||
} else if (access & Dqn_OSFileAccess_Write) {
|
||||
else if (access & Dqn_OSFileAccess_Write)
|
||||
fopen_mode = "w+";
|
||||
} else if (access & Dqn_OSFileAccess_Read) {
|
||||
else if (access & Dqn_OSFileAccess_Read)
|
||||
fopen_mode = "r+";
|
||||
}
|
||||
|
||||
FILE *handle = fopen(path.data, fopen_mode);
|
||||
if (!handle) {
|
||||
result.error = true;
|
||||
Dqn_ErrorSink_MakeF(error, 1, "Failed to open file '%.*s': File could not be opened with requested access mode 'Dqn_OSFileAccess' %d", DQN_STR_FMT(path), fopen_mode);
|
||||
Dqn_ErrorSink_MakeF(error,
|
||||
1,
|
||||
"Failed to open file '%.*s': File could not be opened with requested "
|
||||
"access mode 'Dqn_OSFileAccess' %d",
|
||||
DQN_STR_FMT(path),
|
||||
fopen_mode);
|
||||
return result;
|
||||
}
|
||||
result.handle = handle;
|
||||
@ -493,25 +529,32 @@ DQN_API bool Dqn_OS_FileRead(Dqn_OSFile *file, void *buffer, Dqn_usize size, Dqn
|
||||
if (!file || !file->handle || file->error || !buffer || size <= 0)
|
||||
return false;
|
||||
|
||||
if (fread(buffer, size, 1, DQN_CAST(FILE *)file->handle) != 1) {
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 buffer_size_str8 = Dqn_U64ToByteSizeStr8(scratch.arena, size, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_ErrorSink_MakeF(error, 1, "Failed to read %.*s from file", DQN_STR_FMT(buffer_size_str8));
|
||||
if (fread(buffer, size, 1, DQN_CAST(FILE *) file->handle) != 1) {
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 buffer_size_str8 =
|
||||
Dqn_U64ToByteSizeStr8(scratch.arena, size, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_ErrorSink_MakeF(
|
||||
error, 1, "Failed to read %.*s from file", DQN_STR_FMT(buffer_size_str8));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DQN_API bool Dqn_OS_FileWritePtr(Dqn_OSFile *file, void const *buffer, Dqn_usize size, Dqn_ErrorSink *error)
|
||||
DQN_API bool
|
||||
Dqn_OS_FileWritePtr(Dqn_OSFile *file, void const *buffer, Dqn_usize size, Dqn_ErrorSink *error)
|
||||
{
|
||||
if (!file || !file->handle || file->error || !buffer || size <= 0)
|
||||
return false;
|
||||
bool result = fwrite(buffer, DQN_CAST(Dqn_usize)size, 1 /*count*/, DQN_CAST(FILE *)file->handle) == 1 /*count*/;
|
||||
bool result =
|
||||
fwrite(buffer, DQN_CAST(Dqn_usize) size, 1 /*count*/, DQN_CAST(FILE *) file->handle) ==
|
||||
1 /*count*/;
|
||||
if (!result) {
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 buffer_size_str8 = Dqn_U64ToByteSizeStr8(scratch.arena, size, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_ErrorSink_MakeF(error, 1, "Failed to write buffer (%s) to file handle", DQN_STR_FMT(buffer_size_str8));
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
Dqn_Str8 buffer_size_str8 =
|
||||
Dqn_U64ToByteSizeStr8(scratch.arena, size, Dqn_U64ByteSizeType_Auto);
|
||||
Dqn_ErrorSink_MakeF(
|
||||
error, 1, "Failed to write buffer (%s) to file handle", DQN_STR_FMT(buffer_size_str8));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -520,7 +563,7 @@ DQN_API void Dqn_OS_FileClose(Dqn_OSFile *file)
|
||||
{
|
||||
if (!file || !file->handle || file->error)
|
||||
return;
|
||||
fclose(DQN_CAST(FILE *)file->handle);
|
||||
fclose(DQN_CAST(FILE *) file->handle);
|
||||
*file = {};
|
||||
}
|
||||
#endif // !defined(DQN_NO_OS_FILE_API)
|
||||
@ -528,26 +571,39 @@ DQN_API void Dqn_OS_FileClose(Dqn_OSFile *file)
|
||||
// NOTE: [$EXEC] Dqn_OSExec ////////////////////////////////////////////////////////////////////////
|
||||
DQN_API void Dqn_OS_Exit(int32_t exit_code)
|
||||
{
|
||||
exit(DQN_CAST(int)exit_code);
|
||||
exit(DQN_CAST(int) exit_code);
|
||||
}
|
||||
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_ExecWait(Dqn_OSExecAsyncHandle handle)
|
||||
enum Dqn_OSPipeType_ {
|
||||
Dqn_OSPipeType__Read,
|
||||
Dqn_OSPipeType__Write,
|
||||
Dqn_OSPipeType__Count,
|
||||
};
|
||||
|
||||
DQN_API Dqn_OSExecResult Dqn_OS_ExecWait(Dqn_OSExecAsyncHandle handle,
|
||||
Dqn_Arena *arena,
|
||||
Dqn_ErrorSink *error)
|
||||
{
|
||||
Dqn_OSExecResult result = {};
|
||||
if (!handle.process || handle.os_error_code) {
|
||||
result.os_error_code = handle.os_error_code;
|
||||
if (!handle.process || handle.os_error_code || handle.exit_code) {
|
||||
if (handle.os_error_code)
|
||||
result.os_error_code = handle.os_error_code;
|
||||
else
|
||||
result.exit_code = handle.exit_code;
|
||||
|
||||
DQN_ASSERT(!handle.stdout_read);
|
||||
DQN_ASSERT(!handle.stdout_write);
|
||||
DQN_ASSERT(!handle.stderr_read);
|
||||
DQN_ASSERT(!handle.stderr_write);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (handle.exit_code) {
|
||||
result.exit_code = handle.exit_code;
|
||||
return result;
|
||||
}
|
||||
#if defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
DQN_INVALID_CODE_PATHF("Unsupported operation");
|
||||
#endif
|
||||
|
||||
#if defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
DQN_ASSERTF(false, "Unsupported operation");
|
||||
#else
|
||||
static_assert(sizeof(pid_t) <= sizeof(handle.process), "We store the PID opaquely in a register sized pointer");
|
||||
static_assert(sizeof(pid_t) <= sizeof(handle.process),
|
||||
"We store the PID opaquely in a register sized pointer");
|
||||
pid_t process = {};
|
||||
DQN_MEMCPY(&process, &handle.process, sizeof(process));
|
||||
for (;;) {
|
||||
@ -567,28 +623,143 @@ DQN_API Dqn_OSExecResult Dqn_OS_ExecWait(Dqn_OSExecAsyncHandle handle)
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int stdout_pipe[Dqn_OSPipeType__Count] = {};
|
||||
int stderr_pipe[Dqn_OSPipeType__Count] = {};
|
||||
DQN_MEMCPY(&stdout_pipe[Dqn_OSPipeType__Read],
|
||||
&handle.stdout_read,
|
||||
sizeof(stdout_pipe[Dqn_OSPipeType__Read]));
|
||||
DQN_MEMCPY(&stdout_pipe[Dqn_OSPipeType__Write],
|
||||
&handle.stdout_write,
|
||||
sizeof(stdout_pipe[Dqn_OSPipeType__Write]));
|
||||
DQN_MEMCPY(&stderr_pipe[Dqn_OSPipeType__Read],
|
||||
&handle.stderr_read,
|
||||
sizeof(stderr_pipe[Dqn_OSPipeType__Read]));
|
||||
DQN_MEMCPY(&stderr_pipe[Dqn_OSPipeType__Write],
|
||||
&handle.stderr_write,
|
||||
sizeof(stderr_pipe[Dqn_OSPipeType__Write]));
|
||||
|
||||
// NOTE: Process has finished, stop the write end of the pipe
|
||||
close(stdout_pipe[Dqn_OSPipeType__Write]);
|
||||
close(stderr_pipe[Dqn_OSPipeType__Write]);
|
||||
|
||||
// NOTE: Read the data from the read end of the pipe
|
||||
if (result.os_error_code == 0) {
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(arena);
|
||||
if (arena && handle.stdout_read) {
|
||||
char buffer[4096];
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.arena = scratch.arena;
|
||||
for (;;) {
|
||||
ssize_t bytes_read =
|
||||
read(stdout_pipe[Dqn_OSPipeType__Read], buffer, sizeof(buffer));
|
||||
if (bytes_read <= 0)
|
||||
break;
|
||||
Dqn_Str8Builder_AppendF(&builder, "%.*s", bytes_read, buffer);
|
||||
}
|
||||
|
||||
result.stdout_text = Dqn_Str8Builder_Build(&builder, arena);
|
||||
}
|
||||
|
||||
if (arena && handle.stderr_read) {
|
||||
char buffer[4096];
|
||||
Dqn_Str8Builder builder = {};
|
||||
builder.arena = scratch.arena;
|
||||
for (;;) {
|
||||
ssize_t bytes_read =
|
||||
read(stderr_pipe[Dqn_OSPipeType__Read], buffer, sizeof(buffer));
|
||||
if (bytes_read <= 0)
|
||||
break;
|
||||
Dqn_Str8Builder_AppendF(&builder, "%.*s", bytes_read, buffer);
|
||||
}
|
||||
|
||||
result.stderr_text = Dqn_Str8Builder_Build(&builder, arena);
|
||||
}
|
||||
}
|
||||
|
||||
close(stdout_pipe[Dqn_OSPipeType__Read]);
|
||||
close(stderr_pipe[Dqn_OSPipeType__Read]);
|
||||
return result;
|
||||
}
|
||||
|
||||
DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line, Dqn_Str8 working_dir)
|
||||
DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line,
|
||||
Dqn_Str8 working_dir,
|
||||
uint8_t exec_flags,
|
||||
Dqn_ErrorSink *error)
|
||||
{
|
||||
#if defined(DQN_PLATFORM_EMSCRIPTEN)
|
||||
DQN_INVALID_CODE_PATHF("Unsupported operation");
|
||||
#endif
|
||||
|
||||
Dqn_OSExecAsyncHandle result = {};
|
||||
if (cmd_line.size == 0)
|
||||
return result;
|
||||
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
// TODO: This API will need to switch to an array of strings for unix
|
||||
Dqn_Scratch scratch = Dqn_Scratch_Get(nullptr);
|
||||
int stdout_pipe[Dqn_OSPipeType__Count] = {};
|
||||
int stderr_pipe[Dqn_OSPipeType__Count] = {};
|
||||
|
||||
// NOTE: Open stdout pipe //////////////////////////////////////////////////////////////////////
|
||||
if (Dqn_Bit_IsSet(exec_flags, Dqn_OSExecFlag_SaveStdout)) {
|
||||
if (pipe(stdout_pipe) == -1) {
|
||||
result.os_error_code = errno;
|
||||
return result;
|
||||
}
|
||||
DQN_ASSERT(stdout_pipe[Dqn_OSPipeType__Read] != 0);
|
||||
DQN_ASSERT(stdout_pipe[Dqn_OSPipeType__Write] != 0);
|
||||
}
|
||||
|
||||
DQN_DEFER
|
||||
{
|
||||
if (result.os_error_code == 0 && result.exit_code == 0)
|
||||
return;
|
||||
close(stdout_pipe[Dqn_OSPipeType__Read]);
|
||||
close(stdout_pipe[Dqn_OSPipeType__Write]);
|
||||
};
|
||||
|
||||
// NOTE: Open stderr pipe //////////////////////////////////////////////////////////////////////
|
||||
if (Dqn_Bit_IsSet(exec_flags, Dqn_OSExecFlag_SaveStderr)) {
|
||||
if (Dqn_Bit_IsSet(exec_flags, Dqn_OSExecFlag_MergeStderrToStdout)) {
|
||||
stderr_pipe[Dqn_OSPipeType__Read] = stdout_pipe[Dqn_OSPipeType__Read];
|
||||
stderr_pipe[Dqn_OSPipeType__Write] = stdout_pipe[Dqn_OSPipeType__Write];
|
||||
} else if (pipe(stderr_pipe) == -1) {
|
||||
result.os_error_code = errno;
|
||||
return result;
|
||||
}
|
||||
DQN_ASSERT(stderr_pipe[Dqn_OSPipeType__Read] != 0);
|
||||
DQN_ASSERT(stderr_pipe[Dqn_OSPipeType__Write] != 0);
|
||||
}
|
||||
|
||||
DQN_DEFER
|
||||
{
|
||||
if (result.os_error_code == 0 && result.exit_code == 0)
|
||||
return;
|
||||
close(stderr_pipe[Dqn_OSPipeType__Read]);
|
||||
close(stderr_pipe[Dqn_OSPipeType__Write]);
|
||||
};
|
||||
|
||||
pid_t child_pid = fork();
|
||||
if (child_pid < 0) {
|
||||
result.os_error_code = errno;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (child_pid == 0) {
|
||||
if (child_pid == 0) { // Child process
|
||||
if (Dqn_Bit_IsSet(exec_flags, Dqn_OSExecFlag_SaveStdout) &&
|
||||
(dup2(stdout_pipe[Dqn_OSPipeType__Write], STDOUT_FILENO) == -1)) {
|
||||
result.os_error_code = errno;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (Dqn_Bit_IsSet(exec_flags, Dqn_OSExecFlag_SaveStderr) &&
|
||||
(dup2(stderr_pipe[Dqn_OSPipeType__Write], STDERR_FILENO) == -1)) {
|
||||
result.os_error_code = errno;
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: Convert the command into something suitable for execvp
|
||||
char **argv = Dqn_Arena_NewArray(scratch.arena, char*, cmd_line.size + 1 /*null*/, Dqn_ZeroMem_Yes);
|
||||
char **argv =
|
||||
Dqn_Arena_NewArray(scratch.arena, char *, cmd_line.size + 1 /*null*/, Dqn_ZeroMem_Yes);
|
||||
if (!argv) {
|
||||
result.exit_code = -1;
|
||||
return result;
|
||||
@ -596,16 +767,20 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line, Dqn
|
||||
|
||||
for (Dqn_usize arg_index = 0; arg_index < cmd_line.size; arg_index++) {
|
||||
Dqn_Str8 arg = cmd_line.data[arg_index];
|
||||
argv[arg_index] = Dqn_Str8_Copy(scratch.arena, arg).data; // NOTE: Copy string to guarantee it is null-terminated
|
||||
argv[arg_index] = Dqn_Str8_Copy(scratch.arena, arg)
|
||||
.data; // NOTE: Copy string to guarantee it is null-terminated
|
||||
}
|
||||
|
||||
// NOTE: Change the working directory if there is one
|
||||
char *prev_working_dir = nullptr;
|
||||
DQN_DEFER {
|
||||
DQN_DEFER
|
||||
{
|
||||
if (!prev_working_dir)
|
||||
return;
|
||||
if (result.os_error_code == 0)
|
||||
chdir(prev_working_dir);
|
||||
if (result.os_error_code == 0) {
|
||||
int chdir_result = chdir(prev_working_dir);
|
||||
(void)chdir_result;
|
||||
}
|
||||
free(prev_working_dir);
|
||||
};
|
||||
|
||||
@ -619,12 +794,29 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line, Dqn
|
||||
|
||||
// NOTE: Execute the command. We reuse argv because the first arg, the
|
||||
// binary to execute is guaranteed to be null-terminated.
|
||||
if (execvp(argv[0], argv) < 0) {
|
||||
if (execvp(argv[0], argv) < 0) {
|
||||
result.os_error_code = errno;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
DQN_ASSERT(result.os_error_code == 0);
|
||||
DQN_MEMCPY(&result.stdout_read,
|
||||
&stdout_pipe[Dqn_OSPipeType__Read],
|
||||
sizeof(stdout_pipe[Dqn_OSPipeType__Read]));
|
||||
DQN_MEMCPY(&result.stdout_write,
|
||||
&stdout_pipe[Dqn_OSPipeType__Write],
|
||||
sizeof(stdout_pipe[Dqn_OSPipeType__Write]));
|
||||
|
||||
if (Dqn_Bit_IsSet(exec_flags, Dqn_OSExecFlag_SaveStderr) && Dqn_Bit_IsNotSet(exec_flags, Dqn_OSExecFlag_MergeStderrToStdout)) {
|
||||
DQN_MEMCPY(&result.stderr_read,
|
||||
&stderr_pipe[Dqn_OSPipeType__Read],
|
||||
sizeof(stderr_pipe[Dqn_OSPipeType__Read]));
|
||||
DQN_MEMCPY(&result.stderr_write,
|
||||
&stderr_pipe[Dqn_OSPipeType__Write],
|
||||
sizeof(stderr_pipe[Dqn_OSPipeType__Write]));
|
||||
}
|
||||
result.exec_flags = exec_flags;
|
||||
DQN_MEMCPY(&result.process, &child_pid, sizeof(child_pid));
|
||||
return result;
|
||||
}
|
||||
@ -633,8 +825,8 @@ DQN_API Dqn_OSExecAsyncHandle Dqn_OS_ExecAsync(Dqn_Slice<Dqn_Str8> cmd_line, Dqn
|
||||
// NOTE: [$SEMA] Dqn_OSSemaphore ///////////////////////////////////////////////////////////////////
|
||||
DQN_API Dqn_OSSemaphore Dqn_OS_SemaphoreInit(uint32_t initial_count)
|
||||
{
|
||||
Dqn_OSSemaphore result = {};
|
||||
int pshared = 0; // Share the semaphore across all threads in the process
|
||||
Dqn_OSSemaphore result = {};
|
||||
int pshared = 0; // Share the semaphore across all threads in the process
|
||||
if (sem_init(&result.posix_handle, pshared, initial_count) == 0)
|
||||
result.posix_init = true;
|
||||
return result;
|
||||