dqn: Various bug fixes

This commit is contained in:
doyle 2023-05-31 19:20:33 +10:00
parent de826b3440
commit 582b084617

525
dqn.h
View File

@ -134,6 +134,9 @@
// ================================================================================================= // =================================================================================================
// [$MACR] Macros | | Define macros used in the library // [$MACR] Macros | | Define macros used in the library
// ================================================================================================= // =================================================================================================
#define DQN_FOR_UINDEX(index, size) for (Dqn_usize index = 0; index < size; index++)
#define DQN_FOR_IINDEX(index, size) for (Dqn_isize index = 0; index < size; index++)
#define Dqn_PowerOfTwoAlign(value, power_of_two) (((value) + ((power_of_two) - 1)) & ~((power_of_two) - 1)) #define Dqn_PowerOfTwoAlign(value, power_of_two) (((value) + ((power_of_two) - 1)) & ~((power_of_two) - 1))
#define Dqn_IsPowerOfTwo(value) (((value) & (value - 1)) == 0) #define Dqn_IsPowerOfTwo(value) (((value) & (value - 1)) == 0)
@ -846,7 +849,63 @@ void Dqn_Allocator_Dealloc_(DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allocator, voi
// @return The size required to format the string, not including the null // @return The size required to format the string, not including the null
// terminator. // terminator.
// //
// @proc Dqn_CString8_BinarySplit // @proc Dqn_CString8_Size
// @desc Calculate the string length of the null-terminated string.
// @param[in] a The string whose length is to be determined
// @return The length of the string
DQN_API template <Dqn_usize N> constexpr Dqn_usize Dqn_CString8_ArrayUCount(char const (&literal)[N]) { (void)literal; return N - 1; }
DQN_API template <Dqn_usize N> constexpr Dqn_usize Dqn_CString8_ArrayICount(char const (&literal)[N]) { (void)literal; return N - 1; }
DQN_API Dqn_usize Dqn_CString8_FSize (char const *fmt, ...);
DQN_API Dqn_usize Dqn_CString8_FVSize (char const *fmt, va_list args);
DQN_API Dqn_usize Dqn_CString8_Size (char const *a);
DQN_API Dqn_usize Dqn_CString16_Size (wchar_t const *a);
// =================================================================================================
// [$STR8] Dqn_String8 | | Pointer and length strings
// =================================================================================================
//
// NOTE: API
// @proc Dqn_String8_Init
// @desc Initialise a string from a pointer and length
// The string is invalid (i.e. Dqn_String8_IsValid() returns false) if size is
// negative or the string is null.
// @proc Dqn_String8_InitCString
// @desc Initialise a string from a cstring
// The cstring must be null-terminated as its length is evaluated using
// strlen. The string is invalid (i.e. Dqn_String8_IsValid() returns false) if
// size is negative or the string is null.
// @proc Dqn_String8_InitF
// @desc Create a string from a printf style format string
// @param[in] allocator The allocator the string will be allocated from
// @param[in] fmt The printf style format cstring
// @proc Dqn_String8_InitFV
// @desc Create a string from a printf style format string using a va_list
// @param[in] arena The allocator the string will be allocated from
// @param[in] fmt The printf style format cstring
// @param[in] va The variable argument list
//
// @proc Dqn_String8_IsValid
// @desc Determine if the values of the given string are valid
// A string is invalid if size is negative or the string is null.
// @return True if the string is valid, false otherwise.
// @proc Dqn_String8 Dqn_String8_Slice
// @desc Create a slice from a pre-existing string.
// The requested slice is clamped to within the bounds of the original string.
// @param[in] string The string to slice
// @param[in] offset The starting byte to slice from
// @param[in] size The size of the slice
// @return The sliced string
// @proc Dqn_String8_BinarySplitStringChars
// @desc TODO(doyle): Write description
// @proc Dqn_String8_BinarySplit
// @desc Split a string into the substring occuring prior and after the first // @desc Split a string into the substring occuring prior and after the first
// occurence of the `delimiter`. Neither strings include the `delimiter`. // occurence of the `delimiter`. Neither strings include the `delimiter`.
// //
@ -859,34 +918,79 @@ void Dqn_Allocator_Dealloc_(DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allocator, voi
// //
// @return The left hand side of the split string. The original pointer is // @return The left hand side of the split string. The original pointer is
// returned if the arguments were invalid. // returned if the arguments were invalid.
//
// @proc Dqn_CString8_StartsWith, Dqn_CString8_StartsWithInsensitive, // @proc Dqn_String8_Split
// Dqn_CString8_EndsWith, Dqn_CString8_EndswithInsensitive // @desc Split a string by the delimiting character.
// This function can evaluate the number of splits required in the return value
// by setting `splits` to null and `splits_count` to 0.
// @param[in] string The source string to split
// @param[in] delimiter The substring to split the string on
// @param[out] splits (Optional) The destination array to write the splits to.
// @param[in] splits_count The number of splits that can be written into the
// `splits` array.
// @return The number of splits in the `string`. If the return value is >=
// 'splits_count' then there are more splits in the string than can be written
// to the `splits` array. The number of splits written is capped to the
// capacity given by the caller, i.e. `splits_count`. This function should be
// called again with a sufficiently sized array if all splits are desired.
// @proc Dqn_String8_Segment
// @desc Segment a string by inserting the `segment_char` every `segment_size`
// characters in the string. For example, '123456789' split with
// `segment_char` ' ' and `segment_size` '3' would yield, '123 456 789'.
// @proc Dqn_String8_Allocate
// @desc Create an empty string with the requested size
// @param[in] allocator The allocator the string will be allocated from
// @param[in] size The size in bytes of the string to allocate
// @param[in] zero_mem Enum to indicate if the string's memory should be cleared
// @proc Dqn_String8_CopyCString
// @desc Create a copy of the given cstring
// @param[in] allocator The allocator the string will be allocated from
// @param[in] string The cstring to copy
// @param[in] size The size of the cstring to copy. This cannot be <= 0
// @return A copy of the string, invalid string if any argument was invalid.
// @proc Dqn_String8_Copy
// @desc Create a copy of the given string
// @param[in] allocator The allocator the string will be allocated from
// @param[in] string The string to copy
// @return A copy of the string, invalid string if any argument was invalid.
// @proc Dqn_String8_Eq, Dqn_String8_EqInsensitive
// @desc Compare a string for equality with or without case sensitivity.
// @param[in] lhs The first string to compare equality with
// @param[in] rhs The second string to compare equality with
// @param[in] lhs The first string's size
// @param[in] rhs The second string's size
// @param[in] eq_case Set the comparison to be case sensitive or insensitive
// @return True if the arguments are valid, non-null and the strings
// are equal, false otherwise.
// @proc Dqn_String8_StartsWith, Dqn_String8_StartsWithInsensitive,
// Dqn_String8_EndsWith, Dqn_String8_EndswithInsensitive
// @desc Check if a string starts/ends with the specified prefix // @desc Check if a string starts/ends with the specified prefix
// `EndsWithInsensitive` is case insensitive // `EndsWithInsensitive` is case insensitive
// @param[in] string The string to check for the prefix // @param[in] string The string to check for the prefix
// @param[in] prefix The prefix to check against the string // @param[in] prefix The prefix to check against the string
// @param[in] string_size The size of the string
// @param[in] prefix_size The size of the prefix string
// @param[in] eq_case Set the comparison to be case sensitive or insensitive // @param[in] eq_case Set the comparison to be case sensitive or insensitive
// @return True if the string is valid, non-null and has the specified prefix, // @return True if the string is valid, non-null and has the specified prefix,
// false otherwise. // false otherwise.
//
// @proc Dqn_CString8_TrimPrefix, Dqn_CString8_TrimSuffix // @proc Dqn_String8_TrimPrefix, Dqn_String8_TrimSuffix
// @desc Remove the prefix/suffix respectively from the given `string. // @desc Remove the prefix/suffix respectively from the given `string.
// //
// @param[in] string The string to trim // @param[in] string The string to trim
// @param[in] prefix The prefix to trim from the string // @param[in] prefix The prefix to trim from the string
// @param[in] suffix The suffix to trim from the string // @param[in] suffix The suffix to trim from the string
// @param[in] string_size The size of the string
// @param[in] prefix_size The size of the prefix
// @param[in] eq_case Set the comparison to be case sensitive or insensitive // @param[in] eq_case Set the comparison to be case sensitive or insensitive
// @param[out] trimmed_string The size of the trimmed string // @param[out] trimmed_string The size of the trimmed string
// //
// @return The trimmed string. The original input string is returned if // @return The trimmed string. The original input string is returned if
// arguments are invalid or no trim was possible. // arguments are invalid or no trim was possible.
//
// @proc Dqn_CString8_TrimWhitespaceAround // @proc Dqn_String8_TrimWhitespaceAround
// @desc Trim whitespace from the prefix and suffix of the string // @desc Trim whitespace from the prefix and suffix of the string
// //
// @param[in] string The string to trim // @param[in] string The string to trim
@ -895,8 +999,8 @@ void Dqn_Allocator_Dealloc_(DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allocator, voi
// //
// @return The trimmed string. The original input string is returned if // @return The trimmed string. The original input string is returned if
// arguments are invalid or no trim was possible. // arguments are invalid or no trim was possible.
//
// @proc Dqn_CString8_TrimByteOrderMark // @proc Dqn_String8_TrimByteOrderMark
// @desc Trim UTF8, UTF16 BE/LE, UTF32 BE/LE byte order mark prefix in the string. // @desc Trim UTF8, UTF16 BE/LE, UTF32 BE/LE byte order mark prefix in the string.
// //
// @param[in] string The string to trim // @param[in] string The string to trim
@ -905,8 +1009,8 @@ void Dqn_Allocator_Dealloc_(DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allocator, voi
// //
// @return The trimmed string. The original input string is returned if // @return The trimmed string. The original input string is returned if
// arguments are invalid or no trim was possible. // arguments are invalid or no trim was possible.
//
// @proc Dqn_CString8_FileNameFromPath // @proc Dqn_String8_FileNameFromPath
// @desc Get the file name from a file path. The file name is evaluated by // @desc Get the file name from a file path. The file name is evaluated by
// searching from the end of the string backwards to the first occurring path // searching from the end of the string backwards to the first occurring path
// separator '/' or '\'. If no path separator is found, the original string is // separator '/' or '\'. If no path separator is found, the original string is
@ -919,9 +1023,9 @@ void Dqn_Allocator_Dealloc_(DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allocator, voi
// //
// @return The file name in the file path, if none is found, the original path // @return The file name in the file path, if none is found, the original path
// string is returned. Null pointer if arguments are null or invalid. // string is returned. Null pointer if arguments are null or invalid.
//
// @proc Dqn_CString8_ToI64Checked, Dqn_CString8_ToI64, // @proc Dqn_String8_ToI64Checked, Dqn_String8_ToI64,
// Dqn_CString8_ToU64Checked, Dqn_CString8_ToU64 // Dqn_String8_ToU64Checked, Dqn_String8_ToU64
// @desc Convert a number represented as a string to a signed 64 bit number. // @desc Convert a number represented as a string to a signed 64 bit number.
// //
// The `separator` is an optional digit separator for example, if `separator` // The `separator` is an optional digit separator for example, if `separator`
@ -948,167 +1052,27 @@ void Dqn_Allocator_Dealloc_(DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allocator, voi
// in the string true otherwise. // in the string true otherwise.
// The non-checked variant returns the number that could optimistically be // The non-checked variant returns the number that could optimistically be
// parsed from the string e.g. "1234 dog" will return 1234. // parsed from the string e.g. "1234 dog" will return 1234.
//
// @proc Dqn_CString8_Size
// @desc Calculate the string length of the null-terminated string.
// @param[in] a The string whose length is to be determined
// @return The length of the string
DQN_API template <Dqn_usize N> constexpr Dqn_usize Dqn_CString8_ArrayUCount(char const (&literal)[N]) { (void)literal; return N - 1; } // @proc Dqn_String8_Replace, Dqn_String8_ReplaceInsensitive
DQN_API template <Dqn_usize N> constexpr Dqn_usize Dqn_CString8_ArrayICount(char const (&literal)[N]) { (void)literal; return N - 1; } // @desc TODO(doyle): Write description
DQN_API Dqn_usize Dqn_CString8_FSize (char const *fmt, ...);
DQN_API Dqn_usize Dqn_CString8_FVSize (char const *fmt, va_list args);
DQN_API Dqn_usize Dqn_CString8_Size (char const *a);
DQN_API Dqn_usize Dqn_CString16_Size (wchar_t const *a);
// =================================================================================================
// [$STR8] Dqn_String8 | | Pointer and length strings
// =================================================================================================
//
// NOTE: API
//
// @proc Dqn_String8_Init
// @desc Initialise a string from a pointer and length
// The string is invalid (i.e. Dqn_String8_IsValid() returns false) if size is
// negative or the string is null.
//
// @proc Dqn_String8_InitCString
// @desc Initialise a string from a cstring
// The cstring must be null-terminated as its length is evaluated using
// strlen. The string is invalid (i.e. Dqn_String8_IsValid() returns false) if
// size is negative or the string is null.
//
// @proc Dqn_String8_InitF
// @desc Create a string from a printf style format string
// @param[in] allocator The allocator the string will be allocated from
// @param[in] fmt The printf style format cstring
//
// @proc Dqn_String8_InitFV
// @desc Create a string from a printf style format string using a va_list
// @param[in] arena The allocator the string will be allocated from
// @param[in] fmt The printf style format cstring
// @param[in] va The variable argument list
//
// @proc Dqn_String8_IsValid
// @desc Determine if the values of the given string are valid
// A string is invalid if size is negative or the string is null.
// @return True if the string is valid, false otherwise.
//
// @proc Dqn_String8 Dqn_String8_Slice
// @desc Create a slice from a pre-existing string.
// The requested slice is clamped to within the bounds of the original string.
// @param[in] string The string to slice
// @param[in] offset The starting byte to slice from
// @param[in] size The size of the slice
// @return The sliced string
//
// @proc Dqn_String8_Allocate
// @desc Create an empty string with the requested size
// @param[in] allocator The allocator the string will be allocated from
// @param[in] size The size in bytes of the string to allocate
// @param[in] zero_mem Enum to indicate if the string's memory should be cleared
//
// @proc Dqn_String8_CopyCString
// @desc Create a copy of the given cstring
// @param[in] allocator The allocator the string will be allocated from
// @param[in] string The cstring to copy
// @param[in] size The size of the cstring to copy. This cannot be <= 0
// @return A copy of the string, invalid string if any argument was invalid.
//
// @proc Dqn_String8_Copy
// @desc Create a copy of the given string
// @param[in] allocator The allocator the string will be allocated from
// @param[in] string The string to copy
// @return A copy of the string, invalid string if any argument was invalid.
//
// @proc Dqn_String8 Dqn_String8_BinarySplit
// @desc @see Dqn_CString8_BinarySplit
//
// @proc Dqn_String8_Eq, Dqn_String8_EqInsensitive
// @desc Compare a string for equality with or without case sensitivity.
// @param[in] lhs The first string to compare equality with
// @param[in] rhs The second string to compare equality with
// @param[in] eq_case Set the comparison to be case sensitive or insensitive
// @return True if the arguments are valid, non-null and the strings
// are equal, false otherwise.
//
// @proc Dqn_String8_StartsWith
// @desc @see Dqn_CString8_StartsWith
//
// @proc Dqn_String8_StartsWithInsensitive
// @desc @see Dqn_CString8_StartsWithInsensitive
//
// @proc Dqn_String8_EndsWith
// @desc @see Dqn_CString8_EndsWith
//
// @proc Dqn_String8_EndsWithInsensitive
// @desc @see Dqn_CString8_EndsWithInsensitive
//
// @proc Dqn_String8 Dqn_String8_TrimPrefix
// @desc @see Dqn_CString8_TrimPrefix
//
// @proc Dqn_String8 Dqn_String8_TrimSuffix
// @desc @see Dqn_CString8_TrimSuffix
//
// @proc Dqn_String8 Dqn_String8_TrimWhitespaceAround
// @desc @see Dqn_CString8_TrimWhitespaceAround
//
// @proc Dqn_String8 Dqn_String8_TrimByteOrderMark
// @desc @see Dqn_CString8_TrimByteOrderMark
//
// @proc Dqn_String8 Dqn_String8_FileNameFromPath
// @desc @see Dqn_CString8_FileNameFromPath
//
// @proc Dqn_String8_ToU64Checked
// @desc @see Dqn_CString8_ToU64Checked
//
// @proc Dqn_String8_ToU64
// @desc @see Dqn_CString8_ToU64
//
// @proc Dqn_String8_ToI64Checked
// @desc @see Dqn_CString8_ToI64Checked
//
// @proc Dqn_String8_ToI64
// @desc @see Dqn_CString8_ToI64
//
// @proc Dqn_String8_Split
// @desc Split a string by the delimiting character.
// This function can evaluate the number of splits required in the return value
// by setting `splits` to null and `splits_count` to 0.
// @param[in] string The source string to split
// @param[in] delimiter The substring to split the string on
// @param[out] splits (Optional) The destination array to write the splits to.
// @param[in] splits_count The number of splits that can be written into the
// `splits` array.
// @return The number of splits in the `string`. If the return value is >=
// 'splits_count' then there are more splits in the string than can be written
// to the `splits` array. The number of splits written is capped to the
// capacity given by the caller, i.e. `splits_count`. This function should be
// called again with a sufficiently sized array if all splits are desired.
//
// @proc Dqn_String8_Remove // @proc Dqn_String8_Remove
// @desc Remove the substring denoted by the begin index and the size from the string // @desc Remove the substring denoted by the begin index and the size from the string
// string in-place using MEMMOVE to shift the string back. // string in-place using MEMMOVE to shift the string back.
//
// @proc Dqn_String8_FindOffset
// @desc
// @param[in] start_index Set an index within the string string to start the
// search from, if not desired, set to 0
// @return The index of the matching find, -1 if it is not found
//
// @proc Dqn_String8_Find // @proc Dqn_String8_Find
// @desc @param start_index Set an index within the string string to start the search // @desc @param start_index Set an index within the string string to start the search
// from, if not desired, set to 0 // from, if not desired, set to 0
// @return A string that points to the matching find, otherwise a 0 length string. // @return A string that points to the matching find, otherwise a 0 length string.
//
// @proc DQN_STRING8 // @proc DQN_STRING8
// @desc Construct a UTF8 c-string literal into a Dqn_String8 referencing a // @desc Construct a UTF8 c-string literal into a Dqn_String8 referencing a
// string stored in the data-segment. This string is read-only. // string stored in the data-segment. This string is read-only.
//
// @proc DQN_STRING16 // @proc DQN_STRING16
// @desc Construct a UTF16 c-string literal into a Dqn_String16 referencing a string // @desc Construct a UTF16 c-string literal into a Dqn_String16 referencing a string
// stored in the data-segment. This string is read-only. // stored in the data-segment. This string is read-only.
//
// @proc DQN_STRING_FMT // @proc DQN_STRING_FMT
// @desc Unpack a string into arguments for printing a string into a printf style // @desc Unpack a string into arguments for printing a string into a printf style
// format string. // format string.
@ -1167,13 +1131,15 @@ DQN_API bool Dqn_String8_IsValid (Dqn_String8 stri
DQN_API bool Dqn_String8_IsAll (Dqn_String8 string, Dqn_String8IsAll is_all); DQN_API bool Dqn_String8_IsAll (Dqn_String8 string, Dqn_String8IsAll is_all);
DQN_API Dqn_String8 Dqn_String8_Slice (Dqn_String8 string, Dqn_usize offset, Dqn_usize size); DQN_API Dqn_String8 Dqn_String8_Slice (Dqn_String8 string, Dqn_usize offset, Dqn_usize size);
DQN_API Dqn_String8 Dqn_String8_BinarySplitStringChars(Dqn_String8 string, Dqn_String8 delimiter_chars, Dqn_String8 *rhs);
DQN_API Dqn_String8 Dqn_String8_BinarySplit (Dqn_String8 string, char delimiter, Dqn_String8 *rhs); DQN_API Dqn_String8 Dqn_String8_BinarySplit (Dqn_String8 string, char delimiter, Dqn_String8 *rhs);
DQN_API Dqn_usize Dqn_String8_Split (Dqn_String8 string, Dqn_String8 delimiter, Dqn_String8 *splits, Dqn_usize splits_count); DQN_API Dqn_usize Dqn_String8_Split (Dqn_String8 string, Dqn_String8 delimiter, Dqn_String8 *splits, Dqn_usize splits_count);
DQN_API Dqn_String8 Dqn_String8_Segment (Dqn_Allocator allocator, Dqn_String8 src, Dqn_usize segment_size, char segment_char);
DQN_API bool Dqn_String8_Eq (Dqn_String8 lhs, Dqn_String8 rhs, Dqn_String8EqCase eq_case = Dqn_String8EqCase_Sensitive); DQN_API bool Dqn_String8_Eq (Dqn_String8 lhs, Dqn_String8 rhs, Dqn_String8EqCase eq_case = Dqn_String8EqCase_Sensitive);
DQN_API bool Dqn_String8_EqInsensitive (Dqn_String8 lhs, Dqn_String8 rhs); DQN_API bool Dqn_String8_EqInsensitive (Dqn_String8 lhs, Dqn_String8 rhs);
DQN_API bool Dqn_String8_StartsWith (Dqn_String8 string, Dqn_String8 prefix, Dqn_String8EqCase eq_case = Dqn_String8EqCase_Sensitive); DQN_API bool Dqn_String8_StartsWith (Dqn_String8 string, Dqn_String8 prefix, Dqn_String8EqCase eq_case = Dqn_String8EqCase_Sensitive);
DQN_API bool Dqn_String8_StartsWithInsensitive(Dqn_String8 string, Dqn_String8 prefix); DQN_API bool Dqn_String8_StartsWithInsensitive (Dqn_String8 string, Dqn_String8 prefix);
DQN_API bool Dqn_String8_EndsWith (Dqn_String8 string, Dqn_String8 prefix, Dqn_String8EqCase eq_case = Dqn_String8EqCase_Sensitive); DQN_API bool Dqn_String8_EndsWith (Dqn_String8 string, Dqn_String8 prefix, Dqn_String8EqCase eq_case = Dqn_String8EqCase_Sensitive);
DQN_API bool Dqn_String8_EndsWithInsensitive (Dqn_String8 string, Dqn_String8 prefix); DQN_API bool Dqn_String8_EndsWithInsensitive (Dqn_String8 string, Dqn_String8 prefix);
DQN_API bool Dqn_String8_HasChar (Dqn_String8 string, char ch); DQN_API bool Dqn_String8_HasChar (Dqn_String8 string, char ch);
@ -1924,11 +1890,13 @@ struct Dqn_Library
bool log_to_file; ///< Output logs to file as well as standard out bool log_to_file; ///< Output logs to file as well as standard out
void * log_file; ///< TODO(dqn): Hmmm, how should we do this... ? void * log_file; ///< TODO(dqn): Hmmm, how should we do this... ?
Dqn_TicketMutex log_file_mutex; ///< Is locked when instantiating the log_file for the first time Dqn_TicketMutex log_file_mutex; ///< Is locked when instantiating the log_file for the first time
bool log_no_colour; ///< Disable colours in the logging output
/// The backup arena to use if no arena is passed into Dqn_Library_Init /// The backup arena to use if no arena is passed into Dqn_Library_Init
Dqn_Arena arena_catalog_backup_arena; Dqn_Arena arena_catalog_backup_arena;
Dqn_ArenaCatalog arena_catalog; Dqn_ArenaCatalog arena_catalog;
#if defined(DQN_LEAK_TRACING) #if defined(DQN_LEAK_TRACING)
Dqn_TicketMutex alloc_table_mutex; Dqn_TicketMutex alloc_table_mutex;
Dqn_DSMap<Dqn_LeakTrace> alloc_table; Dqn_DSMap<Dqn_LeakTrace> alloc_table;
@ -3244,6 +3212,50 @@ DQN_API Dqn_FsFile Dqn_Fs_OpenFile(Dqn_String8 path, Dqn_FsFileOpen open_mode, u
DQN_API bool Dqn_Fs_WriteFile(Dqn_FsFile *file, char const *buffer, Dqn_usize size); DQN_API bool Dqn_Fs_WriteFile(Dqn_FsFile *file, char const *buffer, Dqn_usize size);
DQN_API void Dqn_Fs_CloseFile(Dqn_FsFile *file); DQN_API void Dqn_Fs_CloseFile(Dqn_FsFile *file);
// NOTE: Filesystem paths
// =============================================================================
#if !defined(Dqn_FsPathOSSeperator)
#if defined(DQN_OS_WIN32)
#define Dqn_FsPathOSSeperator "\\"
#else
#define Dqn_FsPathOSSeperator "/"
#endif
#define Dqn_FsPathOSSeperatorString DQN_STRING8(Dqn_FsPathOSSeperator)
#endif
struct Dqn_FsPathLink
{
Dqn_String8 string;
Dqn_FsPathLink *next;
Dqn_FsPathLink *prev;
};
struct Dqn_FsPath
{
Dqn_FsPathLink *head;
Dqn_FsPathLink *tail;
Dqn_usize string_size;
uint16_t links_size;
};
DQN_API bool Dqn_FsPath_AddRef (Dqn_Arena *arena, Dqn_FsPath *fs_path, Dqn_String8 path);
DQN_API bool Dqn_FsPath_Add (Dqn_Arena *arena, Dqn_FsPath *fs_path, Dqn_String8 path);
DQN_API bool Dqn_FsPath_Pop (Dqn_FsPath *fs_path);
DQN_API Dqn_String8 Dqn_FsPath_BuildWithSeparator(Dqn_Arena *arena, Dqn_FsPath const *fs_path, Dqn_String8 path_separator);
DQN_API Dqn_String8 Dqn_FsPath_ConvertString8 (Dqn_Arena *arena, Dqn_String8 path);
#define Dqn_FsPath_BuildFwdSlash(arena, fs_path) Dqn_FsPath_BuildWithSeparator(arena, fs_path, DQN_STRING8("/"))
#define Dqn_FsPath_BuildBackSlash(arena, fs_path) Dqn_FsPath_BuildWithSeparator(arena, fs_path, DQN_STRING8("\\"))
#if !defined(Dqn_FsPath_Build)
#if defined(DQN_OS_WIN32)
#define Dqn_FsPath_Build(arena, fs_path) Dqn_FsPath_BuildBackSlash(arena, fs_path)
#else
#define Dqn_FsPath_Build(arena, fs_path) Dqn_FsPath_BuildFwdSlash(arena, fs_path)
#endif
#endif
// ================================================================================================= // =================================================================================================
// [$MISC] Miscellaneous | | General purpose helpers // [$MISC] Miscellaneous | | General purpose helpers
// ================================================================================================= // =================================================================================================
@ -4878,22 +4890,39 @@ DQN_FILE_SCOPE void Dqn_Log_FVDefault_(Dqn_String8 type, int log_type, void *use
int type_padding = DQN_CAST(int)(max_type_length - type.size); int type_padding = DQN_CAST(int)(max_type_length - type.size);
Dqn_String8 colour = {}; Dqn_String8 colour = {};
Dqn_String8 bold = {};
Dqn_String8 reset = {};
if (!dqn_library.log_no_colour) {
bold = Dqn_Print_ESCBoldString;
reset = Dqn_Print_ESCResetString;
switch (log_type) { switch (log_type) {
case Dqn_LogType_Debug: break; case Dqn_LogType_Debug: break;
case Dqn_LogType_Info: colour = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Info); break; case Dqn_LogType_Info: colour = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Info); break;
case Dqn_LogType_Warning: colour = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Warning); break; case Dqn_LogType_Warning: colour = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Warning); break;
case Dqn_LogType_Error: colour = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Error); break; case Dqn_LogType_Error: colour = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Error); break;
} }
}
Dqn_String8 file_name = Dqn_String8_FileNameFromPath(call_site.file); Dqn_String8 file_name = Dqn_String8_FileNameFromPath(call_site.file);
Dqn_DateHMSTimeString const time = Dqn_Date_HMSLocalTimeStringNow(); Dqn_DateHMSTimeString const time = Dqn_Date_HMSLocalTimeStringNow();
header = Dqn_String8_InitF(scratch.allocator, header = Dqn_String8_InitF(scratch.allocator,
"%.*s %.*s " Dqn_Print_ESCBold "%.*s%.*s%*s" Dqn_Print_ESCReset " %.*s:%05u ", "%.*s " // date
"%.*s " // hms
"%.*s" // colour
"%.*s" // bold
"%.*s" // type
"%*s" // type padding
"%.*s" // reset
" %.*s" // file name
":%05u ", // line number
time.date_size - 2, time.date + 2, time.date_size - 2, time.date + 2,
time.hms_size, time.hms, time.hms_size, time.hms,
colour.size, colour.data, colour.size, colour.data,
bold.size, bold.data,
type.size, type.data, type.size, type.data,
type_padding, "", type_padding, "",
reset.size, reset.data,
>>>>>>> 4540f8b (dqn: Various bug fixes)
file_name.size, file_name.data, file_name.size, file_name.data,
call_site.line); call_site.line);
header_size_no_ansi_codes = header.size - colour.size - Dqn_Print_ESCResetString.size; header_size_no_ansi_codes = header.size - colour.size - Dqn_Print_ESCResetString.size;
@ -5117,7 +5146,7 @@ DQN_API Dqn_String8 Dqn_String8_Slice(Dqn_String8 string, Dqn_usize offset, Dqn_
return result; return result;
} }
DQN_API Dqn_String8 Dqn_String8_BinarySplit(Dqn_String8 string, char delimiter, Dqn_String8 *rhs) DQN_API Dqn_String8 Dqn_String8_BinarySplitStringChars(Dqn_String8 string, Dqn_String8 delimiter_chars, Dqn_String8 *rhs)
{ {
Dqn_String8 result = string; Dqn_String8 result = string;
if (rhs) if (rhs)
@ -5127,8 +5156,15 @@ DQN_API Dqn_String8 Dqn_String8_BinarySplit(Dqn_String8 string, char delimiter,
return result; return result;
Dqn_usize offset = 0; Dqn_usize offset = 0;
while (offset < string.size && string.data[offset] != delimiter) for (bool hit_delimiter = false; !hit_delimiter && offset < string.size; ) {
for (Dqn_usize index = 0; !hit_delimiter && index < delimiter_chars.size; index++) {
char delimiter = delimiter_chars.data[index];
hit_delimiter = string.data[offset] == delimiter;
}
if (!hit_delimiter)
offset++; offset++;
}
// NOTE: LHS, the string before the delimiter // NOTE: LHS, the string before the delimiter
result = Dqn_String8_Init(string.data, offset); result = Dqn_String8_Init(string.data, offset);
@ -5136,13 +5172,23 @@ DQN_API Dqn_String8 Dqn_String8_BinarySplit(Dqn_String8 string, char delimiter,
// NOTE: RHS, the string after the delimiter // NOTE: RHS, the string after the delimiter
if (rhs) { if (rhs) {
char *end = string.data + string.size; char *end = string.data + string.size;
rhs->data = DQN_MIN((string.data + offset + 1), end); char *next = DQN_MIN((string.data + offset + 1), end);
if (next != end) {
rhs->data = next;
rhs->size = end - rhs->data; rhs->size = end - rhs->data;
} }
}
return result; return result;
} }
DQN_API Dqn_String8 Dqn_String8_BinarySplit(Dqn_String8 string, char delimiter, Dqn_String8 *rhs)
{
Dqn_String8 delimiter_chars = Dqn_String8_Init(&delimiter, 1);
Dqn_String8 result = Dqn_String8_BinarySplitStringChars(string, delimiter_chars, rhs);
return result;
}
DQN_API Dqn_usize Dqn_String8_Split(Dqn_String8 string, Dqn_String8 delimiter, Dqn_String8 *splits, Dqn_usize splits_count) DQN_API Dqn_usize Dqn_String8_Split(Dqn_String8 string, Dqn_String8 delimiter, Dqn_String8 *splits, Dqn_usize splits_count)
{ {
Dqn_usize result = 0; // The number of splits in the actual string. Dqn_usize result = 0; // The number of splits in the actual string.
@ -5173,13 +5219,37 @@ DQN_API Dqn_usize Dqn_String8_Split(Dqn_String8 string, Dqn_String8 delimiter, D
return result; return result;
} }
DQN_API Dqn_String8 Dqn_String8_Segment(Dqn_Allocator allocator, Dqn_String8 src, Dqn_usize segment_size, char segment_char)
{
Dqn_usize result_size = src.size;
if (result_size > segment_size)
result_size += (src.size / segment_size) - 1; // NOTE: No segment on the first chunk.
Dqn_String8 result = Dqn_String8_Allocate(allocator, result_size, Dqn_ZeroMem_Yes);
Dqn_usize write_index = 0;
DQN_FOR_UINDEX(src_index, src.size) {
result.data[write_index++] = src.data[src_index];
if ((src_index + 1) % segment_size == 0 && (src_index + 1) < src.size)
result.data[write_index++] = segment_char;
DQN_ASSERTF(write_index <= result.size, "result.size=%zu, write_index=%zu", result.size, write_index);
}
DQN_ASSERTF(write_index == result.size, "result.size=%zu, write_index=%zu", result.size, write_index);
return result;
}
DQN_API bool Dqn_String8_Eq(Dqn_String8 lhs, Dqn_String8 rhs, Dqn_String8EqCase eq_case) DQN_API bool Dqn_String8_Eq(Dqn_String8 lhs, Dqn_String8 rhs, Dqn_String8EqCase eq_case)
{ {
bool result = (DQN_CAST(bool)lhs.data == DQN_CAST(bool)rhs.data) && (lhs.size == rhs.size); if (lhs.size != rhs.size)
if (!result) return false;
return result;
if (lhs.data) { if (lhs.size == 0)
return true;
if (!lhs.data || !rhs.data)
return false;
bool result = true;
switch (eq_case) { switch (eq_case) {
case Dqn_String8EqCase_Sensitive: { case Dqn_String8EqCase_Sensitive: {
result = (DQN_MEMCMP(lhs.data, rhs.data, lhs.size) == 0); result = (DQN_MEMCMP(lhs.data, rhs.data, lhs.size) == 0);
@ -5190,9 +5260,6 @@ DQN_API bool Dqn_String8_Eq(Dqn_String8 lhs, Dqn_String8 rhs, Dqn_String8EqCase
result = (Dqn_Char_ToLower(lhs.data[index]) == Dqn_Char_ToLower(rhs.data[index])); result = (Dqn_Char_ToLower(lhs.data[index]) == Dqn_Char_ToLower(rhs.data[index]));
} break; } break;
} }
} else {
DQN_ASSERT(lhs.size == 0);
}
return result; return result;
} }
@ -9013,7 +9080,6 @@ DQN_API bool Dqn_Fs_Move(Dqn_String8 src, Dqn_String8 dest, bool overwrite)
#error Unimplemented #error Unimplemented
#endif #endif
return result; return result;
} }
@ -9387,6 +9453,109 @@ DQN_API void Dqn_Fs_CloseFile(Dqn_FsFile *file)
*file = {}; *file = {};
} }
DQN_API bool Dqn_FsPath_AddRef(Dqn_Arena *arena, Dqn_FsPath *fs_path, Dqn_String8 path)
{
if (!arena || !fs_path || !Dqn_String8_IsValid(path))
return false;
if (path.size <= 0)
return true;
Dqn_String8 delimiter_chars = DQN_STRING8("/\\");
for (;;) {
Dqn_String8 rhs = {};
Dqn_String8 lhs = Dqn_String8_BinarySplitStringChars(path, delimiter_chars, &rhs);
for (; lhs.data; lhs = Dqn_String8_BinarySplitStringChars(rhs, delimiter_chars, &rhs)) {
if (lhs.size <= 0)
continue;
Dqn_FsPathLink *link = Dqn_Arena_New(arena, Dqn_FsPathLink, Dqn_ZeroMem_Yes);
if (!link)
return false;
link->string = 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 += lhs.size;
}
if (!lhs.data)
break;
}
return true;
}
DQN_API bool Dqn_FsPath_Add(Dqn_Arena *arena, Dqn_FsPath *fs_path, Dqn_String8 path)
{
Dqn_String8 copy = Dqn_String8_Copy(Dqn_Arena_Allocator(arena), path);
bool result = Dqn_FsPath_AddRef(arena, fs_path, copy);
return result;
}
DQN_API bool Dqn_FsPath_Pop(Dqn_FsPath *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);
}
return true;
}
DQN_API Dqn_String8 Dqn_FsPath_ConvertString8(Dqn_Arena *arena, Dqn_String8 path)
{
Dqn_FsPath fs_path = {};
Dqn_FsPath_AddRef(arena, &fs_path, path);
Dqn_String8 result = Dqn_FsPath_Build(arena, &fs_path);
return result;
}
DQN_API Dqn_String8 Dqn_FsPath_BuildWithSeparator(Dqn_Arena *arena, Dqn_FsPath const *fs_path, Dqn_String8 path_separator)
{
Dqn_String8 result = {};
if (!fs_path || fs_path->links_size <= 0)
return result;
// NOTE: Each link except the last one needs the path separator appended to it, '/' or '\\'
Dqn_usize string_size = fs_path->string_size + ((fs_path->links_size - 1) * path_separator.size);
result = Dqn_String8_Allocate(Dqn_Arena_Allocator(arena), string_size, Dqn_ZeroMem_No);
if (result.data) {
char *dest = result.data;
for (Dqn_FsPathLink *link = fs_path->head; link; link = link->next) {
Dqn_String8 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;
}
}
}
result.data[string_size] = 0;
return result;
}
// ================================================================================================= // =================================================================================================
// [$MISC] Miscellaneous | | General purpose helpers // [$MISC] Miscellaneous | | General purpose helpers
// ================================================================================================= // =================================================================================================