dqn: Various bug fixes

This commit is contained in:
doyle 2023-05-31 19:20:33 +10:00
parent de826b3440
commit 582b084617
1 changed files with 394 additions and 225 deletions

619
dqn.h
View File

@ -134,6 +134,9 @@
// =================================================================================================
// [$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_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
// 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
// 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
// returned if the arguments were invalid.
//
// @proc Dqn_CString8_StartsWith, Dqn_CString8_StartsWithInsensitive,
// Dqn_CString8_EndsWith, Dqn_CString8_EndswithInsensitive
// @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_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
// `EndsWithInsensitive` is case insensitive
// @param[in] string The string to check for the prefix
// @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
// @return True if the string is valid, non-null and has the specified prefix,
// 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.
//
// @param[in] string The string to trim
// @param[in] prefix The prefix 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[out] trimmed_string The size of the trimmed string
//
// @return The trimmed string. The original input string is returned if
// 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
//
// @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
// 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.
//
// @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
// 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
// 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
@ -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
// string is returned. Null pointer if arguments are null or invalid.
//
// @proc Dqn_CString8_ToI64Checked, Dqn_CString8_ToI64,
// Dqn_CString8_ToU64Checked, Dqn_CString8_ToU64
// @proc Dqn_String8_ToI64Checked, Dqn_String8_ToI64,
// Dqn_String8_ToU64Checked, Dqn_String8_ToU64
// @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`
@ -948,167 +1052,27 @@ void Dqn_Allocator_Dealloc_(DQN_LEAK_TRACE_FUNCTION Dqn_Allocator allocator, voi
// in the string true otherwise.
// The non-checked variant returns the number that could optimistically be
// 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; }
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);
// @proc Dqn_String8_Replace, Dqn_String8_ReplaceInsensitive
// @desc TODO(doyle): Write description
// =================================================================================================
// [$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
// @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.
//
// @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
// @desc @param start_index Set an index within the string string to start the search
// from, if not desired, set to 0
// @return A string that points to the matching find, otherwise a 0 length string.
//
// @proc DQN_STRING8
// @desc Construct a UTF8 c-string literal into a Dqn_String8 referencing a
// string stored in the data-segment. This string is read-only.
//
// @proc DQN_STRING16
// @desc Construct a UTF16 c-string literal into a Dqn_String16 referencing a string
// stored in the data-segment. This string is read-only.
//
// @proc DQN_STRING_FMT
// @desc Unpack a string into arguments for printing a string into a printf style
// format string.
@ -1162,33 +1126,35 @@ enum Dqn_String8EqCase
Dqn_String8EqCase_Insensitive,
};
DQN_API Dqn_String8 Dqn_String8_InitCString8 (char const *src);
DQN_API bool Dqn_String8_IsValid (Dqn_String8 string);
DQN_API bool Dqn_String8_IsAll (Dqn_String8 string, Dqn_String8IsAll is_all);
DQN_API Dqn_String8 Dqn_String8_InitCString8 (char const *src);
DQN_API bool Dqn_String8_IsValid (Dqn_String8 string);
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_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_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_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_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_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_EndsWithInsensitive (Dqn_String8 string, Dqn_String8 prefix);
DQN_API bool Dqn_String8_HasChar (Dqn_String8 string, char ch);
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_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_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_HasChar (Dqn_String8 string, char ch);
DQN_API Dqn_String8 Dqn_String8_TrimPrefix (Dqn_String8 string, Dqn_String8 prefix, Dqn_String8EqCase eq_case = Dqn_String8EqCase_Sensitive);
DQN_API Dqn_String8 Dqn_String8_TrimSuffix (Dqn_String8 string, Dqn_String8 suffix, Dqn_String8EqCase eq_case = Dqn_String8EqCase_Sensitive);
DQN_API Dqn_String8 Dqn_String8_TrimWhitespaceAround (Dqn_String8 string);
DQN_API Dqn_String8 Dqn_String8_TrimByteOrderMark (Dqn_String8 string);
DQN_API Dqn_String8 Dqn_String8_TrimPrefix (Dqn_String8 string, Dqn_String8 prefix, Dqn_String8EqCase eq_case = Dqn_String8EqCase_Sensitive);
DQN_API Dqn_String8 Dqn_String8_TrimSuffix (Dqn_String8 string, Dqn_String8 suffix, Dqn_String8EqCase eq_case = Dqn_String8EqCase_Sensitive);
DQN_API Dqn_String8 Dqn_String8_TrimWhitespaceAround (Dqn_String8 string);
DQN_API Dqn_String8 Dqn_String8_TrimByteOrderMark (Dqn_String8 string);
DQN_API Dqn_String8 Dqn_String8_FileNameFromPath (Dqn_String8 path);
DQN_API Dqn_String8 Dqn_String8_FileNameFromPath (Dqn_String8 path);
DQN_API bool Dqn_String8_ToU64Checked (Dqn_String8 string, char separator, uint64_t *output);
DQN_API uint64_t Dqn_String8_ToU64 (Dqn_String8 string, char separator);
DQN_API bool Dqn_String8_ToI64Checked (Dqn_String8 string, char separator, int64_t *output);
DQN_API int64_t Dqn_String8_ToI64 (Dqn_String8 string, char separator);
DQN_API bool Dqn_String8_ToU64Checked (Dqn_String8 string, char separator, uint64_t *output);
DQN_API uint64_t Dqn_String8_ToU64 (Dqn_String8 string, char separator);
DQN_API bool Dqn_String8_ToI64Checked (Dqn_String8 string, char separator, int64_t *output);
DQN_API int64_t Dqn_String8_ToI64 (Dqn_String8 string, char separator);
struct Dqn_String8FindResult
{
@ -1197,14 +1163,14 @@ struct Dqn_String8FindResult
Dqn_String8 string;
};
DQN_API Dqn_String8FindResult Dqn_String8_Find (Dqn_String8 string, Dqn_String8 find, Dqn_usize start_index, Dqn_String8EqCase eq_case = Dqn_String8EqCase_Sensitive);
DQN_API Dqn_String8 Dqn_String8_Replace (Dqn_String8 string, Dqn_String8 find, Dqn_String8 replace, Dqn_usize start_index, Dqn_Allocator allocator, Dqn_String8EqCase eq_case = Dqn_String8EqCase_Sensitive);
DQN_API Dqn_String8 Dqn_String8_ReplaceInsensitive (Dqn_String8 string, Dqn_String8 find, Dqn_String8 replace, Dqn_usize start_index, Dqn_Allocator allocator);
DQN_API void Dqn_String8_Remove (Dqn_String8 *string, Dqn_usize offset, Dqn_usize size);
DQN_API Dqn_String8FindResult Dqn_String8_Find (Dqn_String8 string, Dqn_String8 find, Dqn_usize start_index, Dqn_String8EqCase eq_case = Dqn_String8EqCase_Sensitive);
DQN_API Dqn_String8 Dqn_String8_Replace (Dqn_String8 string, Dqn_String8 find, Dqn_String8 replace, Dqn_usize start_index, Dqn_Allocator allocator, Dqn_String8EqCase eq_case = Dqn_String8EqCase_Sensitive);
DQN_API Dqn_String8 Dqn_String8_ReplaceInsensitive (Dqn_String8 string, Dqn_String8 find, Dqn_String8 replace, Dqn_usize start_index, Dqn_Allocator allocator);
DQN_API void Dqn_String8_Remove (Dqn_String8 *string, Dqn_usize offset, Dqn_usize size);
#if defined(__cplusplus)
DQN_API bool operator== (Dqn_String8 const &lhs, Dqn_String8 const &rhs);
DQN_API bool operator!= (Dqn_String8 const &lhs, Dqn_String8 const &rhs);
DQN_API bool operator== (Dqn_String8 const &lhs, Dqn_String8 const &rhs);
DQN_API bool operator!= (Dqn_String8 const &lhs, Dqn_String8 const &rhs);
#endif
// NOTE: Internal ==================================================================================
@ -1924,11 +1890,13 @@ struct Dqn_Library
bool log_to_file; ///< Output logs to file as well as standard out
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
bool log_no_colour; ///< Disable colours in the logging output
/// The backup arena to use if no arena is passed into Dqn_Library_Init
Dqn_Arena arena_catalog_backup_arena;
Dqn_ArenaCatalog arena_catalog;
#if defined(DQN_LEAK_TRACING)
Dqn_TicketMutex alloc_table_mutex;
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 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
// =================================================================================================
@ -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);
Dqn_String8 colour = {};
switch (log_type) {
case Dqn_LogType_Debug: 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_Error: colour = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Error); break;
Dqn_String8 bold = {};
Dqn_String8 reset = {};
if (!dqn_library.log_no_colour) {
bold = Dqn_Print_ESCBoldString;
reset = Dqn_Print_ESCResetString;
switch (log_type) {
case Dqn_LogType_Debug: 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_Error: colour = Dqn_Print_ESCColourFgU32String(Dqn_LogTypeColourU32_Error); break;
}
}
Dqn_String8 file_name = Dqn_String8_FileNameFromPath(call_site.file);
Dqn_DateHMSTimeString const time = Dqn_Date_HMSLocalTimeStringNow();
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.hms_size, time.hms,
colour.size, colour.data,
colour.size, colour.data,
bold.size, bold.data,
type.size, type.data,
type_padding, "",
reset.size, reset.data,
>>>>>>> 4540f8b (dqn: Various bug fixes)
file_name.size, file_name.data,
call_site.line);
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;
}
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;
if (rhs)
@ -5127,22 +5156,39 @@ DQN_API Dqn_String8 Dqn_String8_BinarySplit(Dqn_String8 string, char delimiter,
return result;
Dqn_usize offset = 0;
while (offset < string.size && string.data[offset] != delimiter)
offset++;
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++;
}
// NOTE: LHS, the string before the delimiter
result = Dqn_String8_Init(string.data, offset);
// NOTE: RHS, the string after the delimiter
if (rhs) {
char *end = string.data + string.size;
rhs->data = DQN_MIN((string.data + offset + 1), end);
rhs->size = end - rhs->data;
char *end = string.data + string.size;
char *next = DQN_MIN((string.data + offset + 1), end);
if (next != end) {
rhs->data = next;
rhs->size = end - rhs->data;
}
}
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_usize result = 0; // The number of splits in the actual string.
@ -5173,25 +5219,46 @@ DQN_API Dqn_usize Dqn_String8_Split(Dqn_String8 string, Dqn_String8 delimiter, D
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)
{
bool result = (DQN_CAST(bool)lhs.data == DQN_CAST(bool)rhs.data) && (lhs.size == rhs.size);
if (!result)
return result;
if (lhs.size != rhs.size)
return false;
if (lhs.data) {
switch (eq_case) {
case Dqn_String8EqCase_Sensitive: {
result = (DQN_MEMCMP(lhs.data, rhs.data, lhs.size) == 0);
} break;
if (lhs.size == 0)
return true;
case Dqn_String8EqCase_Insensitive: {
for (Dqn_usize index = 0; index < lhs.size && result; index++)
result = (Dqn_Char_ToLower(lhs.data[index]) == Dqn_Char_ToLower(rhs.data[index]));
} break;
}
} else {
DQN_ASSERT(lhs.size == 0);
if (!lhs.data || !rhs.data)
return false;
bool result = true;
switch (eq_case) {
case Dqn_String8EqCase_Sensitive: {
result = (DQN_MEMCMP(lhs.data, rhs.data, lhs.size) == 0);
} break;
case Dqn_String8EqCase_Insensitive: {
for (Dqn_usize index = 0; index < lhs.size && result; index++)
result = (Dqn_Char_ToLower(lhs.data[index]) == Dqn_Char_ToLower(rhs.data[index]));
} break;
}
return result;
}
@ -8991,9 +9058,9 @@ DQN_API bool Dqn_Fs_Move(Dqn_String8 src, Dqn_String8 dest, bool overwrite)
result = MoveFileExW(src16.data, dest16.data, flags) != 0;
if (!result) {
Dqn_Log_ErrorF("Failed to move file [from=%.*s, to=%.*s, reason=%.*s]",
DQN_STRING_FMT(src),
DQN_STRING_FMT(dest),
DQN_STRING_FMT(Dqn_Win_LastError()));
DQN_STRING_FMT(src),
DQN_STRING_FMT(dest),
DQN_STRING_FMT(Dqn_Win_LastError()));
}
#elif defined(DQN_OS_UNIX)
@ -9013,7 +9080,6 @@ DQN_API bool Dqn_Fs_Move(Dqn_String8 src, Dqn_String8 dest, bool overwrite)
#error Unimplemented
#endif
return result;
}
@ -9387,6 +9453,109 @@ DQN_API void Dqn_Fs_CloseFile(Dqn_FsFile *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
// =================================================================================================