#include #include /** * Inline helper to safely extract a character at string offset d. * Automatically maps a string's null terminator to a sentinel value of -1. */ C_STATIC_FORCE_INLINE int c_MSDRadixSort_CharAt(const char* str, c_size_t d) { if (str == NULL) return -1; // Walk down to offset d without triggering a buffer overflow lookup violation c_size_t i = 0; while (i < d && str[i] != '\0') { i++; } if (str[i] == '\0' || i < d) return -1; return (unsigned char)str[i]; } /** * Core Private Recursive Sub-partition Sorting Subroutine. * Shares a single pre-allocated auxiliary buffer across stack frames to prevent heap allocation overhead. * * @param lo Lower boundary index of the target partition array slice (inclusive). * @param hi Upper boundary index of the target partition array slice (inclusive). * @param d The current character string evaluation offset cursor. */ static void c_MSDRadixSort_SortRecursive(char** arr, long long lo, long long hi, c_size_t d, c_size_t R, char** aux, c_size_t* count_buf) { if (hi <= lo) return; // Cutoff to Insertion Sort for tiny sub-arrays can be added here for production fine-tuning. // Calculate sub-slice width and initialize count registers c_size_t n = (c_size_t)(hi - lo + 1); // Elements are shifted forward by +2 slots to gracefully absorb the -1 string end sentinel memset(count_buf, 0, (R + 2) * sizeof(c_size_t)); // Pass A: Compute frequency buckets for (long long i = lo; i <= hi; i++) { int c = c_MSDRadixSort_CharAt(arr[i], d); count_buf[c + 2]++; } // Pass B: Transform frequencies into structural start indexes (Prefix Sums) for (c_size_t r = 0; r < R + 1; r++) { count_buf[r + 1] += count_buf[r]; } // Pass C: Distribute strings stably to the temporary auxiliary workspace slice for (long long i = lo; i <= hi; i++) { int c = c_MSDRadixSort_CharAt(arr[i], d); aux[count_buf[c + 1]++] = arr[i]; } // Pass D: Copy back copies natively to the primary tracking layout pointers for (long long i = lo; i <= hi; i++) { arr[i] = aux[i - lo]; } // Recursively sort sub-arrays for each character bucket // Note: count_buf[0] handles strings that hit a terminal '\0' sentinel, so we skip it to prevent loops for (c_size_t r = 0; r < R; r++) { long long next_lo = lo + (long long)count_buf[r]; long long next_hi = lo + (long long)count_buf[r + 1] - 1; if (next_hi > next_lo) { c_MSDRadixSort_SortRecursive(arr, next_lo, next_hi, d + 1, R, aux, count_buf); } } } /** * Sorts an array of variable-length strings using the MSD Radix Sort pipeline. * Guarantees zero runtime heap thrashing via upfront workspace pooling. * * Time Complexity: O(N * String_Length) optimal | Space Complexity: O(N + R) transient workspace memory */ c_err_t c_MSDRadixSort_Sort(const c_MSDRadixSort_t* sort, char** arr, c_size_t n) { if (sort == NULL || arr == NULL || sort->R == 0) return C_ERR_PARAM; if (n <= 1) return C_ERR_OK; // Upfront Transient Workspace Allocation: Eliminates allocation overhead in deep recursions char** aux = (char**)C_ALLOC(n * sizeof(char*)); c_size_t* count_buf = (c_size_t*)C_ALLOC((sort->R + 2) * sizeof(c_size_t)); if (aux == NULL || count_buf == NULL) { C_FREE(aux); C_FREE(count_buf); return C_ERR_NOMEM; } // Launch the core string-wise recursive partition tree c_MSDRadixSort_SortRecursive(arr, 0, (long long)n - 1, 0, sort->R, aux, count_buf); C_FREE(aux); C_FREE(count_buf); return C_ERR_OK; }