#if !defined(DN_HELPERS_H) #define DN_HELPERS_H #if !defined(DN_BASE_H) #error dn_base_inc.h must be included before this #endif #if !defined(DN_MATH_H) #error dn_math.h must be included before this #endif /* //////////////////////////////////////////////////////////////////////////////////////////////////// // // $$\ $$\ $$$$$$$$\ $$\ $$$$$$$\ $$$$$$$$\ $$$$$$$\ $$$$$$\ // $$ | $$ |$$ _____|$$ | $$ __$$\ $$ _____|$$ __$$\ $$ __$$\ // $$ | $$ |$$ | $$ | $$ | $$ |$$ | $$ | $$ |$$ / \__| // $$$$$$$$ |$$$$$\ $$ | $$$$$$$ |$$$$$\ $$$$$$$ |\$$$$$$\ // $$ __$$ |$$ __| $$ | $$ ____/ $$ __| $$ __$$< \____$$\ // $$ | $$ |$$ | $$ | $$ | $$ | $$ | $$ |$$\ $$ | // $$ | $$ |$$$$$$$$\ $$$$$$$$\ $$ | $$$$$$$$\ $$ | $$ |\$$$$$$ | // \__| \__|\________|\________|\__| \________|\__| \__| \______/ // // dn_helpers.h -- Helper functions/data structures // //////////////////////////////////////////////////////////////////////////////////////////////////// */ // NOTE: DN_PCG32 ////////////////////////////////////////////////////////////////////////////////// struct DN_PCG32 { uint64_t state; }; #if !defined(DN_NO_JSON_BUILDER) // NOTE: DN_JSONBuilder //////////////////////////////////////////////////////////////////////////// enum DN_JSONBuilderItem { DN_JSONBuilderItem_Empty, DN_JSONBuilderItem_OpenContainer, DN_JSONBuilderItem_CloseContainer, DN_JSONBuilderItem_KeyValue, }; struct DN_JSONBuilder { bool use_stdout; // When set, ignore the string builder and dump immediately to stdout DN_Str8Builder string_builder; // (Internal) int indent_level; // (Internal) int spaces_per_indent; // The number of spaces per indent level DN_JSONBuilderItem last_item; }; #endif // !defined(DN_NO_JSON_BUIDLER) // NOTE: DN_BinarySearch /////////////////////////////////////////////////////////////////////////// template using DN_BinarySearchLessThanProc = bool(T const &lhs, T const &rhs); template bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs); enum DN_BinarySearchType { // Index of the match. If no match is found, found is set to false and the // index is set to the index where the match should be inserted/exist, if // it were in the array DN_BinarySearchType_Match, // Index of the first element in the array that is `element >= find`. If no such // item is found or the array is empty, then, the index is set to the array // size and found is set to `false`. // // For example: // int array[] = {0, 1, 2, 3, 4, 5}; // DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_LowerBound); // printf("%zu\n", result.index); // Prints index '4' DN_BinarySearchType_LowerBound, // Index of the first element in the array that is `element > find`. If no such // item is found or the array is empty, then, the index is set to the array // size and found is set to `false`. // // For example: // int array[] = {0, 1, 2, 3, 4, 5}; // DN_BinarySearchResult result = DN_BinarySearch(array, DN_ArrayCountU(array), 4, DN_BinarySearchType_UpperBound); // printf("%zu\n", result.index); // Prints index '5' DN_BinarySearchType_UpperBound, }; struct DN_BinarySearchResult { bool found; DN_USize index; }; template using DN_QSortLessThanProc = bool(T const &a, T const &b, void *user_context); // NOTE: Misc ////////////////////////////////////////////////////////////////////////////////////// // NOTE: DN_JobQueue /////////////////////////////////////////////////////////////////////////////// typedef void(DN_JobQueueFunc)(DN_OSThread *thread, void *user_context); struct DN_Job { DN_JobQueueFunc *func; // The function to invoke for the job void *user_context; // Pointer user can set to use in their `job_func` uint64_t elapsed_tsc; uint16_t user_tag; // Arbitrary value the user can set to identiy the type of `user_context` this job has bool add_to_completion_queue; // When true, on job completion, job must be dequeued from the completion queue via `GetFinishedJobs` }; #if !defined(DN_JOB_QUEUE_SPMC_SIZE) #define DN_JOB_QUEUE_SPMC_SIZE 128 #endif struct DN_JobQueueSPMC { DN_OSMutex mutex; DN_OSSemaphore thread_wait_for_job_semaphore; DN_OSSemaphore wait_for_completion_semaphore; DN_U32 threads_waiting_for_completion; DN_Job jobs[DN_JOB_QUEUE_SPMC_SIZE]; DN_B32 quit; DN_U32 quit_exit_code; DN_U32 volatile read_index; DN_U32 volatile finish_index; DN_U32 volatile write_index; DN_OSSemaphore complete_queue_write_semaphore; DN_Job complete_queue[DN_JOB_QUEUE_SPMC_SIZE]; DN_U32 volatile complete_read_index; DN_U32 volatile complete_write_index; }; // NOTE: DN_PCG32 ////////////////////////////////////////////////////////////////////////////////// DN_API DN_PCG32 DN_PCG32_Init (uint64_t seed); DN_API uint32_t DN_PCG32_Next (DN_PCG32 *rng); DN_API uint64_t DN_PCG32_Next64 (DN_PCG32 *rng); DN_API uint32_t DN_PCG32_Range (DN_PCG32 *rng, uint32_t low, uint32_t high); DN_API DN_F32 DN_PCG32_NextF32 (DN_PCG32 *rng); DN_API DN_F64 DN_PCG32_NextF64 (DN_PCG32 *rng); DN_API void DN_PCG32_Advance (DN_PCG32 *rng, uint64_t delta); #if !defined(DN_NO_JSON_BUILDER) // NOTE: DN_JSONBuilder //////////////////////////////////////////////////////////////////////////// #define DN_JSONBuilder_Object(builder) \ DN_DEFER_LOOP(DN_JSONBuilder_ObjectBegin(builder), \ DN_JSONBuilder_ObjectEnd(builder)) #define DN_JSONBuilder_ObjectNamed(builder, name) \ DN_DEFER_LOOP(DN_JSONBuilder_ObjectBeginNamed(builder, name), \ DN_JSONBuilder_ObjectEnd(builder)) #define DN_JSONBuilder_Array(builder) \ DN_DEFER_LOOP(DN_JSONBuilder_ArrayBegin(builder), \ DN_JSONBuilder_ArrayEnd(builder)) #define DN_JSONBuilder_ArrayNamed(builder, name) \ DN_DEFER_LOOP(DN_JSONBuilder_ArrayBeginNamed(builder, name), \ DN_JSONBuilder_ArrayEnd(builder)) DN_API DN_JSONBuilder DN_JSONBuilder_Init (DN_Arena *arena, int spaces_per_indent); DN_API DN_Str8 DN_JSONBuilder_Build (DN_JSONBuilder const *builder, DN_Arena *arena); DN_API void DN_JSONBuilder_KeyValue (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value); DN_API void DN_JSONBuilder_KeyValueF (DN_JSONBuilder *builder, DN_Str8 key, char const *value_fmt, ...); DN_API void DN_JSONBuilder_ObjectBeginNamed (DN_JSONBuilder *builder, DN_Str8 name); DN_API void DN_JSONBuilder_ObjectEnd (DN_JSONBuilder *builder); DN_API void DN_JSONBuilder_ArrayBeginNamed (DN_JSONBuilder *builder, DN_Str8 name); DN_API void DN_JSONBuilder_ArrayEnd (DN_JSONBuilder *builder); DN_API void DN_JSONBuilder_Str8Named (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value); DN_API void DN_JSONBuilder_LiteralNamed (DN_JSONBuilder *builder, DN_Str8 key, DN_Str8 value); DN_API void DN_JSONBuilder_U64Named (DN_JSONBuilder *builder, DN_Str8 key, uint64_t value); DN_API void DN_JSONBuilder_I64Named (DN_JSONBuilder *builder, DN_Str8 key, int64_t value); DN_API void DN_JSONBuilder_F64Named (DN_JSONBuilder *builder, DN_Str8 key, double value, int decimal_places); DN_API void DN_JSONBuilder_BoolNamed (DN_JSONBuilder *builder, DN_Str8 key, bool value); #define DN_JSONBuilder_ObjectBegin(builder) DN_JSONBuilder_ObjectBeginNamed(builder, DN_STR8("")) #define DN_JSONBuilder_ArrayBegin(builder) DN_JSONBuilder_ArrayBeginNamed(builder, DN_STR8("")) #define DN_JSONBuilder_Str8(builder, value) DN_JSONBuilder_Str8Named(builder, DN_STR8(""), value) #define DN_JSONBuilder_Literal(builder, value) DN_JSONBuilder_LiteralNamed(builder, DN_STR8(""), value) #define DN_JSONBuilder_U64(builder, value) DN_JSONBuilder_U64Named(builder, DN_STR8(""), value) #define DN_JSONBuilder_I64(builder, value) DN_JSONBuilder_I64Named(builder, DN_STR8(""), value) #define DN_JSONBuilder_F64(builder, value) DN_JSONBuilder_F64Named(builder, DN_STR8(""), value) #define DN_JSONBuilder_Bool(builder, value) DN_JSONBuilder_BoolNamed(builder, DN_STR8(""), value) #endif // !defined(DN_NO_JSON_BUILDER) // NOTE: DN_BinarySearch /////////////////////////////////////////////////////////////////////////// template bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs); template DN_BinarySearchResult DN_BinarySearch (T const *array, DN_USize array_size, T const &find, DN_BinarySearchType type = DN_BinarySearchType_Match, DN_BinarySearchLessThanProc less_than = DN_BinarySearch_DefaultLessThan); // NOTE: DN_QSort ////////////////////////////////////////////////////////////////////////////////// template bool DN_QSort_DefaultLessThan(T const &lhs, T const &rhs); template void DN_QSort (T *array, DN_USize array_size, void *user_context, DN_QSortLessThanProc less_than = DN_QSort_DefaultLessThan); // NOTE: DN_JobQueue /////////////////////////////////////////////////////////////////////////////// DN_API DN_JobQueueSPMC DN_OS_JobQueueSPMCInit (); DN_API bool DN_OS_JobQueueSPMCCanAdd (DN_JobQueueSPMC const *queue, uint32_t count); DN_API bool DN_OS_JobQueueSPMCAddArray (DN_JobQueueSPMC *queue, DN_Job *jobs, uint32_t count); DN_API bool DN_OS_JobQueueSPMCAdd (DN_JobQueueSPMC *queue, DN_Job job); DN_API void DN_OS_JobQueueSPMCWaitForCompletion (DN_JobQueueSPMC *queue); DN_API int32_t DN_OS_JobQueueSPMCThread (DN_OSThread *thread); DN_API DN_USize DN_OS_JobQueueSPMCGetFinishedJobs (DN_JobQueueSPMC *queue, DN_Job *jobs, DN_USize jobs_size); // NOTE: DN_BinarySearch /////////////////////////////////////////////////////////////////////////// template bool DN_BinarySearch_DefaultLessThan(T const &lhs, T const &rhs) { bool result = lhs < rhs; return result; } template DN_BinarySearchResult DN_BinarySearch(T const *array, DN_USize array_size, T const &find, DN_BinarySearchType type, DN_BinarySearchLessThanProc less_than) { DN_BinarySearchResult result = {}; if (!array || array_size <= 0 || !less_than) return result; T const *end = array + array_size; T const *first = array; T const *last = end; while (first != last) { DN_USize count = last - first; T const *it = first + (count / 2); bool advance_first = false; if (type == DN_BinarySearchType_UpperBound) advance_first = !less_than(find, it[0]); else advance_first = less_than(it[0], find); if (advance_first) first = it + 1; else last = it; } switch (type) { case DN_BinarySearchType_Match: { result.found = first != end && !less_than(find, *first); } break; case DN_BinarySearchType_LowerBound: /*FALLTHRU*/ case DN_BinarySearchType_UpperBound: { result.found = first != end; } break; } result.index = first - array; return result; } // NOTE: DN_QSort ////////////////////////////////////////////////////////////////////////////////// template bool DN_QSort_DefaultLessThan(T const &lhs, T const &rhs, void *user_context) { (void)user_context; bool result = lhs < rhs; return result; } template void DN_QSort(T *array, DN_USize array_size, void *user_context, DN_QSortLessThanProc less_than) { if (!array || array_size <= 1 || !less_than) return; // NOTE: Insertion Sort, under 24->32 is an optimal amount ///////////////////////////////////// const DN_USize QSORT_THRESHOLD = 24; if (array_size < QSORT_THRESHOLD) { for (DN_USize item_to_insert_index = 1; item_to_insert_index < array_size; item_to_insert_index++) { for (DN_USize index = 0; index < item_to_insert_index; index++) { if (!less_than(array[index], array[item_to_insert_index], user_context)) { T item_to_insert = array[item_to_insert_index]; for (DN_USize i = item_to_insert_index; i > index; i--) array[i] = array[i - 1]; array[index] = item_to_insert; break; } } } return; } // NOTE: Quick sort, under 24->32 is an optimal amount ///////////////////////////////////////// DN_USize last_index = array_size - 1; DN_USize pivot_index = array_size / 2; DN_USize partition_index = 0; DN_USize start_index = 0; // Swap pivot with last index, so pivot is always at the end of the array. // This makes logic much simpler. DN_Swap(array[last_index], array[pivot_index]); pivot_index = last_index; // 4^, 8, 7, 5, 2, 3, 6 if (less_than(array[start_index], array[pivot_index], user_context)) partition_index++; start_index++; // 4, |8, 7, 5^, 2, 3, 6* // 4, 5, |7, 8, 2^, 3, 6* // 4, 5, 2, |8, 7, ^3, 6* // 4, 5, 2, 3, |7, 8, ^6* for (DN_USize index = start_index; index < last_index; index++) { if (less_than(array[index], array[pivot_index], user_context)) { DN_Swap(array[partition_index], array[index]); partition_index++; } } // Move pivot to right of partition // 4, 5, 2, 3, |6, 8, ^7* DN_Swap(array[partition_index], array[pivot_index]); DN_QSort(array, partition_index, user_context, less_than); // Skip the value at partion index since that is guaranteed to be sorted. // 4, 5, 2, 3, (x), 8, 7 DN_USize one_after_partition_index = partition_index + 1; DN_QSort(array + one_after_partition_index, (array_size - one_after_partition_index), user_context, less_than); } #endif // !defined(DN_HELPERS_H)