100 lines
3.7 KiB
C
100 lines
3.7 KiB
C
#include <c_MSDRadixSort.h>
|
|
#include <c_Memory.h>
|
|
|
|
/**
|
|
* 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;
|
|
}
|