2024-01-31 12:49:23 +00:00
////////////////////////////////////////////////////////////////////////////////////////////////////
//
/ / $ $ $ $ $ $ \ $ $ $ $ $ $ \
/ / $ $ __ $ $ \ $ $ __ $ $ \
// $$ / $$ |$$ / \__|
/ / $ $ | $ $ | \ $ $ $ $ $ $ \
/ / $ $ | $ $ | \ ____ $ $ \
// $$ | $$ |$$\ $$ |
// $$$$$$ |\$$$$$$ |
// \______/ \______/
//
// dqn_os.cpp
//
////////////////////////////////////////////////////////////////////////////////////////////////////
// NOTE: [$DATE] Date //////////////////////////////////////////////////////////////////////////////
DQN_API Dqn_OSDateTimeStr8 Dqn_OS_DateLocalTimeStr8 ( Dqn_OSDateTime time , char date_separator , char hms_separator )
{
Dqn_OSDateTimeStr8 result = { } ;
result . hms_size = DQN_CAST ( uint8_t ) DQN_SNPRINTF ( result . hms ,
DQN_ARRAY_ICOUNT ( result . hms ) ,
" %02hhu%c%02hhu%c%02hhu " ,
time . hour ,
hms_separator ,
time . minutes ,
hms_separator ,
time . seconds ) ;
result . date_size = DQN_CAST ( uint8_t ) DQN_SNPRINTF ( result . date ,
DQN_ARRAY_ICOUNT ( result . date ) ,
" %hu%c%02hhu%c%02hhu " ,
time . year ,
date_separator ,
time . month ,
date_separator ,
time . day ) ;
DQN_ASSERT ( result . hms_size < DQN_ARRAY_UCOUNT ( result . hms ) ) ;
DQN_ASSERT ( result . date_size < DQN_ARRAY_UCOUNT ( result . date ) ) ;
return result ;
}
DQN_API Dqn_OSDateTimeStr8 Dqn_OS_DateLocalTimeStr8Now ( char date_separator , char hms_separator )
2023-10-24 13:11:48 +00:00
{
2024-01-31 12:49:23 +00:00
Dqn_OSDateTime time = Dqn_OS_DateLocalTimeNow ( ) ;
Dqn_OSDateTimeStr8 result = Dqn_OS_DateLocalTimeStr8 ( time , date_separator , hms_separator ) ;
return result ;
2023-10-24 13:11:48 +00:00
}
2024-01-31 12:49:23 +00:00
DQN_API Dqn_Str8 Dqn_OS_EXEDir ( Dqn_Arena * arena )
2023-10-24 13:11:48 +00:00
{
2024-01-31 12:49:23 +00:00
Dqn_Str8 result = { } ;
if ( ! arena )
2023-10-24 13:11:48 +00:00
return result ;
2024-01-31 12:49:23 +00:00
Dqn_Scratch scratch = Dqn_Scratch_Get ( arena ) ;
Dqn_Str8 exe_path = Dqn_OS_EXEPath ( scratch . arena ) ;
Dqn_Str8 separators [ ] = { DQN_STR8 ( " / " ) , DQN_STR8 ( " \\ " ) } ;
Dqn_Str8BinarySplitResult split = Dqn_Str8_BinarySplitReverseArray ( exe_path , separators , DQN_ARRAY_UCOUNT ( separators ) ) ;
result = Dqn_Str8_Copy ( arena , split . lhs ) ;
return result ;
}
DQN_API Dqn_f64 Dqn_OS_PerfCounterS ( uint64_t begin , uint64_t end )
{
uint64_t frequency = Dqn_OS_PerfCounterFrequency ( ) ;
uint64_t ticks = end - begin ;
Dqn_f64 result = ticks / DQN_CAST ( Dqn_f64 ) frequency ;
return result ;
}
DQN_API Dqn_f64 Dqn_OS_PerfCounterMs ( uint64_t begin , uint64_t end )
{
uint64_t frequency = Dqn_OS_PerfCounterFrequency ( ) ;
uint64_t ticks = end - begin ;
Dqn_f64 result = ( ticks * 1'000 ) / DQN_CAST ( Dqn_f64 ) frequency ;
return result ;
}
DQN_API Dqn_f64 Dqn_OS_PerfCounterUs ( uint64_t begin , uint64_t end )
{
uint64_t frequency = Dqn_OS_PerfCounterFrequency ( ) ;
uint64_t ticks = end - begin ;
Dqn_f64 result = ( ticks * 1'000'000 ) / DQN_CAST ( Dqn_f64 ) frequency ;
return result ;
}
DQN_API Dqn_f64 Dqn_OS_PerfCounterNs ( uint64_t begin , uint64_t end )
{
uint64_t frequency = Dqn_OS_PerfCounterFrequency ( ) ;
uint64_t ticks = end - begin ;
Dqn_f64 result = ( ticks * 1'000'000'000 ) / DQN_CAST ( Dqn_f64 ) frequency ;
return result ;
}
DQN_API Dqn_OSTimer Dqn_OS_TimerBegin ( )
{
Dqn_OSTimer result = { } ;
result . start = Dqn_OS_PerfCounterNow ( ) ;
return result ;
}
DQN_API void Dqn_OS_TimerEnd ( Dqn_OSTimer * timer )
{
timer - > end = Dqn_OS_PerfCounterNow ( ) ;
}
DQN_API Dqn_f64 Dqn_OS_TimerS ( Dqn_OSTimer timer )
{
Dqn_f64 result = Dqn_OS_PerfCounterS ( timer . start , timer . end ) ;
return result ;
}
DQN_API Dqn_f64 Dqn_OS_TimerMs ( Dqn_OSTimer timer )
{
Dqn_f64 result = Dqn_OS_PerfCounterMs ( timer . start , timer . end ) ;
return result ;
}
DQN_API Dqn_f64 Dqn_OS_TimerUs ( Dqn_OSTimer timer )
{
Dqn_f64 result = Dqn_OS_PerfCounterUs ( timer . start , timer . end ) ;
return result ;
}
DQN_API Dqn_f64 Dqn_OS_TimerNs ( Dqn_OSTimer timer )
{
Dqn_f64 result = Dqn_OS_PerfCounterNs ( timer . start , timer . end ) ;
return result ;
}
DQN_API uint64_t Dqn_OS_EstimateTSCPerSecond ( uint64_t duration_ms_to_gauge_tsc_frequency )
{
uint64_t os_frequency = Dqn_OS_PerfCounterFrequency ( ) ;
uint64_t os_target_elapsed = duration_ms_to_gauge_tsc_frequency * os_frequency / 1000ULL ;
uint64_t tsc_begin = Dqn_CPU_TSC ( ) ;
uint64_t result = 0 ;
if ( tsc_begin ) {
uint64_t os_elapsed = 0 ;
for ( uint64_t os_begin = Dqn_OS_PerfCounterNow ( ) ; os_elapsed < os_target_elapsed ; )
os_elapsed = Dqn_OS_PerfCounterNow ( ) - os_begin ;
uint64_t tsc_end = Dqn_CPU_TSC ( ) ;
uint64_t tsc_elapsed = tsc_end - tsc_begin ;
result = tsc_elapsed / os_elapsed * os_frequency ;
2023-10-24 13:11:48 +00:00
}
2024-01-31 12:49:23 +00:00
return result ;
}
2023-10-24 13:11:48 +00:00
2024-01-31 12:49:23 +00:00
# if !defined(DQN_NO_OS_FILE_API)
// NOTE: [$FILE] Dqn_OSPathInfo/File ///////////////////////////////////////////////////////////////
DQN_API bool Dqn_OS_WriteFile ( Dqn_OSFile * file , Dqn_Str8 buffer )
{
bool result = Dqn_OS_WriteFileBuffer ( file , buffer . data , buffer . size ) ;
return result ;
}
DQN_API bool Dqn_OS_WriteFileFV ( Dqn_OSFile * file , DQN_FMT_ATTRIB char const * fmt , va_list args )
{
bool result = false ;
if ( ! file | | ! fmt )
2023-10-24 13:11:48 +00:00
return result ;
2024-01-31 12:49:23 +00:00
Dqn_Scratch scratch = Dqn_Scratch_Get ( nullptr ) ;
Dqn_Str8 buffer = Dqn_Str8_InitFV ( scratch . arena , fmt , args ) ;
result = Dqn_OS_WriteFileBuffer ( file , buffer . data , buffer . size ) ;
return result ;
}
DQN_API bool Dqn_OS_WriteFileF ( Dqn_OSFile * file , DQN_FMT_ATTRIB char const * fmt , . . . )
{
va_list args ;
va_start ( args , fmt ) ;
bool result = Dqn_OS_WriteFileFV ( file , fmt , args ) ;
va_end ( args ) ;
return result ;
}
// NOTE: R/W Entire File ///////////////////////////////////////////////////////////////////////////
DQN_API bool Dqn_OS_WriteAll ( Dqn_Str8 path , Dqn_Str8 buffer )
{
Dqn_OSFile file = Dqn_OS_OpenFile ( path , Dqn_OSFileOpen_CreateAlways , Dqn_OSFileAccess_Write ) ;
bool result = Dqn_OS_WriteFile ( & file , buffer ) ;
Dqn_OS_CloseFile ( & file ) ;
return result ;
}
DQN_API bool Dqn_OS_WriteAllFV ( Dqn_Str8 file_path , DQN_FMT_ATTRIB char const * fmt , va_list args )
{
Dqn_Scratch scratch = Dqn_Scratch_Get ( nullptr ) ;
Dqn_Str8 buffer = Dqn_Str8_InitFV ( scratch . arena , fmt , args ) ;
bool result = Dqn_OS_WriteAll ( file_path , buffer ) ;
return result ;
}
DQN_API bool Dqn_OS_WriteAllF ( Dqn_Str8 file_path , DQN_FMT_ATTRIB char const * fmt , . . . )
{
va_list args ;
va_start ( args , fmt ) ;
bool result = Dqn_OS_WriteAllFV ( file_path , fmt , args ) ;
va_end ( args ) ;
return result ;
}
DQN_API bool Dqn_OS_WriteAllSafe ( Dqn_Str8 path , Dqn_Str8 buffer )
{
Dqn_Scratch scratch = Dqn_Scratch_Get ( nullptr ) ;
Dqn_Str8 tmp_path = Dqn_Str8_InitF ( scratch . arena , " %.*s.tmp " , DQN_STR_FMT ( path ) ) ;
if ( ! Dqn_OS_WriteAll ( tmp_path , buffer ) ) {
Dqn_Log_ErrorF ( " Failed to write to temporary file [path=%.*s] " , DQN_STR_FMT ( tmp_path ) ) ;
return false ;
2023-10-24 13:11:48 +00:00
}
2024-01-31 12:49:23 +00:00
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 ;
}
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 true ;
}
DQN_API bool Dqn_OS_WriteAllSafeFV ( Dqn_Str8 path , DQN_FMT_ATTRIB char const * fmt , va_list args )
{
Dqn_Scratch scratch = Dqn_Scratch_Get ( nullptr ) ;
Dqn_Str8 buffer = Dqn_Str8_InitFV ( scratch . arena , fmt , args ) ;
bool result = Dqn_OS_WriteAllSafe ( path , buffer ) ;
return result ;
}
DQN_API bool Dqn_OS_WriteAllSafeF ( Dqn_Str8 path , DQN_FMT_ATTRIB char const * fmt , . . . )
{
va_list args ;
va_start ( args , fmt ) ;
bool result = Dqn_OS_WriteAllSafeFV ( path , fmt , args ) ;
return result ;
}
# endif // !defined(DQN_NO_OS_FILE_API)
// NOTE: [$PATH] Dqn_OSPath ////////////////////////////////////////////////////////////////////////
DQN_API bool Dqn_OS_PathAddRef ( Dqn_Arena * arena , Dqn_OSPath * fs_path , Dqn_Str8 path )
{
if ( ! arena | | ! fs_path | | ! Dqn_Str8_HasData ( path ) )
return false ;
if ( path . size < = 0 )
return true ;
Dqn_Str8 const delimiter_array [ ] = {
DQN_STR8 ( " \\ " ) ,
DQN_STR8 ( " / " )
} ;
if ( fs_path - > links_size = = 0 ) {
fs_path - > has_prefix_path_separator = ( path . data [ 0 ] = = ' / ' ) ;
2023-10-24 13:11:48 +00:00
}
for ( ; ; ) {
2024-01-31 12:49:23 +00:00
Dqn_Str8BinarySplitResult delimiter = Dqn_Str8_BinarySplitArray ( path , delimiter_array , DQN_ARRAY_UCOUNT ( delimiter_array ) ) ;
for ( ; delimiter . lhs . data ; delimiter = Dqn_Str8_BinarySplitArray ( delimiter . rhs , delimiter_array , DQN_ARRAY_UCOUNT ( delimiter_array ) ) ) {
if ( delimiter . lhs . size < = 0 )
continue ;
2023-10-24 13:11:48 +00:00
2024-01-31 12:49:23 +00:00
Dqn_OSPathLink * link = Dqn_Arena_New ( arena , Dqn_OSPathLink , Dqn_ZeroMem_Yes ) ;
if ( ! link )
return false ;
link - > string = delimiter . lhs ;
link - > prev = fs_path - > tail ;
if ( fs_path - > tail ) {
fs_path - > tail - > next = link ;
} else {
fs_path - > head = link ;
}
fs_path - > tail = link ;
fs_path - > links_size + = 1 ;
fs_path - > string_size + = delimiter . lhs . size ;
2023-10-24 13:11:48 +00:00
}
2024-01-31 12:49:23 +00:00
if ( ! delimiter . lhs . data )
2023-10-24 13:11:48 +00:00
break ;
}
2024-01-31 12:49:23 +00:00
return true ;
}
DQN_API bool Dqn_OS_PathAdd ( Dqn_Arena * arena , Dqn_OSPath * fs_path , Dqn_Str8 path )
{
Dqn_Str8 copy = Dqn_Str8_Copy ( arena , path ) ;
bool result = Dqn_Str8_HasData ( copy ) ? true : Dqn_OS_PathAddRef ( arena , fs_path , copy ) ;
2023-10-24 13:11:48 +00:00
return result ;
}
2024-01-31 12:49:23 +00:00
DQN_API bool Dqn_OS_PathAddF ( Dqn_Arena * arena , Dqn_OSPath * fs_path , DQN_FMT_ATTRIB char const * fmt , . . . )
2023-10-24 13:11:48 +00:00
{
2024-01-31 12:49:23 +00:00
va_list args ;
va_start ( args , fmt ) ;
Dqn_Str8 path = Dqn_Str8_InitFV ( arena , fmt , args ) ;
va_end ( args ) ;
bool result = Dqn_OS_PathAddRef ( arena , fs_path , path ) ;
return result ;
}
2023-10-24 13:11:48 +00:00
2024-01-31 12:49:23 +00:00
DQN_API bool Dqn_OS_PathPop ( Dqn_OSPath * fs_path )
{
if ( ! fs_path )
return false ;
if ( fs_path - > tail ) {
DQN_ASSERT ( fs_path - > head ) ;
fs_path - > links_size - = 1 ;
fs_path - > string_size - = fs_path - > tail - > string . size ;
fs_path - > tail = fs_path - > tail - > prev ;
if ( fs_path - > tail ) {
fs_path - > tail - > next = nullptr ;
} else {
fs_path - > head = nullptr ;
}
} else {
DQN_ASSERT ( ! fs_path - > head ) ;
2023-10-24 13:11:48 +00:00
}
2024-01-31 12:49:23 +00:00
return true ;
}
DQN_API Dqn_Str8 Dqn_OS_PathConvertTo ( Dqn_Arena * arena , Dqn_Str8 path , Dqn_Str8 path_separator )
{
Dqn_OSPath fs_path = { } ;
Dqn_OS_PathAddRef ( arena , & fs_path , path ) ;
Dqn_Str8 result = Dqn_OS_PathBuildWithSeparator ( arena , & fs_path , path_separator ) ;
return result ;
}
DQN_API Dqn_Str8 Dqn_OS_PathConvertToF ( Dqn_Arena * arena , Dqn_Str8 path_separator , DQN_FMT_ATTRIB char const * fmt , . . . )
{
Dqn_Scratch scratch = Dqn_Scratch_Get ( arena ) ;
va_list args ;
va_start ( args , fmt ) ;
Dqn_Str8 path = Dqn_Str8_InitFV ( scratch . arena , fmt , args ) ;
va_end ( args ) ;
Dqn_Str8 result = Dqn_OS_PathConvertTo ( arena , path , path_separator ) ;
return result ;
}
DQN_API Dqn_Str8 Dqn_OS_PathConvert ( Dqn_Arena * arena , Dqn_Str8 path )
{
Dqn_Str8 result = Dqn_OS_PathConvertTo ( arena , path , Dqn_OSPathSeperatorString ) ;
return result ;
}
DQN_API Dqn_Str8 Dqn_OS_PathConvertF ( Dqn_Arena * arena , DQN_FMT_ATTRIB char const * fmt , . . . )
{
Dqn_Scratch scratch = Dqn_Scratch_Get ( arena ) ;
va_list args ;
va_start ( args , fmt ) ;
Dqn_Str8 path = Dqn_Str8_InitFV ( scratch . arena , fmt , args ) ;
va_end ( args ) ;
Dqn_Str8 result = Dqn_OS_PathConvert ( arena , path ) ;
return result ;
}
DQN_API Dqn_Str8 Dqn_OS_PathBuildWithSeparator ( Dqn_Arena * arena , Dqn_OSPath const * fs_path , Dqn_Str8 path_separator )
{
Dqn_Str8 result = { } ;
if ( ! fs_path | | fs_path - > links_size < = 0 )
2023-10-24 13:11:48 +00:00
return result ;
2024-01-31 12:49:23 +00:00
// NOTE: Each link except the last one needs the path separator appended to it, '/' or '\\'
Dqn_usize string_size = ( fs_path - > has_prefix_path_separator ? path_separator . size : 0 ) + fs_path - > string_size + ( ( fs_path - > links_size - 1 ) * path_separator . size ) ;
result = Dqn_Str8_Alloc ( arena , string_size , Dqn_ZeroMem_No ) ;
if ( result . data ) {
char * dest = result . data ;
if ( fs_path - > has_prefix_path_separator ) {
DQN_MEMCPY ( dest , path_separator . data , path_separator . size ) ;
dest + = path_separator . size ;
2023-10-24 13:11:48 +00:00
}
2024-01-31 12:49:23 +00:00
for ( Dqn_OSPathLink * link = fs_path - > head ; link ; link = link - > next ) {
Dqn_Str8 string = link - > string ;
DQN_MEMCPY ( dest , string . data , string . size ) ;
dest + = string . size ;
if ( link ! = fs_path - > tail ) {
DQN_MEMCPY ( dest , path_separator . data , path_separator . size ) ;
dest + = path_separator . size ;
}
2023-10-24 13:11:48 +00:00
}
}
2024-01-31 12:49:23 +00:00
result . data [ string_size ] = 0 ;
2023-10-24 13:11:48 +00:00
return result ;
}
2024-01-31 12:49:23 +00:00
// NOTE: [$EXEC] Dqn_OSExec ////////////////////////////////////////////////////////////////////////
DQN_API Dqn_OSExecResult Dqn_OS_Exec ( Dqn_Slice < Dqn_Str8 > cmd_line , Dqn_Str8 working_dir )
2023-10-24 13:11:48 +00:00
{
2024-01-31 12:49:23 +00:00
Dqn_OSExecAsyncHandle async_handle = Dqn_OS_ExecAsync ( cmd_line , working_dir ) ;
2023-10-24 13:11:48 +00:00
Dqn_OSExecResult result = Dqn_OS_ExecWait ( async_handle ) ;
return result ;
}
2024-01-31 12:49:23 +00:00
DQN_API void Dqn_OS_ExecOrAbort ( Dqn_Slice < Dqn_Str8 > cmd_line , Dqn_Str8 working_dir )
2023-10-24 13:11:48 +00:00
{
2024-01-31 12:49:23 +00:00
Dqn_OSExecResult result = Dqn_OS_Exec ( cmd_line , working_dir ) ;
2023-10-24 13:11:48 +00:00
if ( result . os_error_code | | result . exit_code ) {
2024-01-31 12:49:23 +00:00
Dqn_Scratch scratch = Dqn_Scratch_Get ( nullptr ) ;
Dqn_Str8 cmd_combined = Dqn_Slice_Str8Render ( scratch . arena , cmd_line , DQN_STR8 ( " " ) /*separator*/ ) ;
2023-10-24 13:11:48 +00:00
if ( result . os_error_code ) {
2024-01-31 12:49:23 +00:00
Dqn_Log_ErrorF ( " OS failed to execute the requested command returning the error code %u. The command was \n \n %.*s " , result . os_error_code , DQN_STR_FMT ( cmd_combined ) ) ;
2023-10-24 13:11:48 +00:00
Dqn_OS_Exit ( result . os_error_code ) ;
}
if ( result . exit_code ) {
2024-01-31 12:49:23 +00:00
Dqn_Log_ErrorF ( " OS executed command and returned a non-zero status: %u. The command was \n \n %.*s " , result . exit_code , DQN_STR_FMT ( cmd_combined ) ) ;
2023-10-24 13:11:48 +00:00
Dqn_OS_Exit ( result . exit_code ) ;
}
}
}
2024-01-31 12:49:23 +00:00
// NOTE: [$HTTP] Dqn_OSHttp ////////////////////////////////////////////////////////////////////////
DQN_API void Dqn_OS_HttpRequestWait ( Dqn_OSHttpResponse * response )
{
2024-01-31 13:03:06 +00:00
if ( response & & Dqn_OS_SemaphoreIsValid ( & response - > on_complete_semaphore ) )
2024-01-31 12:49:23 +00:00
Dqn_OS_SemaphoreWait ( & response - > on_complete_semaphore , DQN_OS_SEMAPHORE_INFINITE_TIMEOUT ) ;
}
DQN_API Dqn_OSHttpResponse Dqn_OS_HttpRequest ( Dqn_Arena * arena , Dqn_Str8 host , Dqn_Str8 path , Dqn_OSHttpRequestSecure secure , Dqn_Str8 method , Dqn_Str8 body , Dqn_Str8 headers )
{
// TODO(doyle): Revise the memory allocation and its lifetime
Dqn_OSHttpResponse result = { } ;
Dqn_Scratch scratch = Dqn_Scratch_Get ( arena ) ;
result . scratch_arena = scratch . arena ;
Dqn_OS_HttpRequestAsync ( & result , arena , host , path , secure , method , body , headers ) ;
Dqn_OS_HttpRequestWait ( & result ) ;
return result ;
}