🐛 Fix SDSORT_QUICK with stack
Some checks are pending
CI - Build Tests / Build Test (push) Waiting to run
CI - Unit Tests / Unit Test (push) Waiting to run
CI - Validate Source Files / Validate Source Files (push) Waiting to run

Followup to #28069
This commit is contained in:
Scott Lahteine 2025-10-22 23:50:04 -05:00
parent 788e86ccc6
commit 3a77a7eefd
2 changed files with 168 additions and 174 deletions

View file

@ -1328,171 +1328,6 @@ void CardReader::cdroot() {
#endif
#endif
#if ENABLED(SDSORT_QUICK)
// Quick Sort
bool CardReader::sort_cmp_files(const int16_t o1, const int16_t o2) {
auto _sort_cmp_file = [](const char *const n1, const char *const n2) -> bool {
const bool sort = strcasecmp(n1, n2) < 0;
return (TERN(SDSORT_GCODE, sort_alpha == AS_REV, ENABLED(SDSORT_REVERSE))) ? !sort : sort;
};
#if ENABLED(SDSORT_USES_RAM)
const bool dir1 = IS_DIR(o1), dir2 = IS_DIR(o2);
const char *name1 = card.sortnames[o1], *name2 = card.sortnames[o2];
#else
card.selectFileByIndex(o1);
char name1_buffer[LONG_FILENAME_LENGTH];
strcpy(name1_buffer, card.longest_filename());
const char *name1 = name1_buffer;
const bool dir1 = card.flag.filenameIsDir;
card.selectFileByIndex(o2);
const char *name2 = card.longest_filename();
const bool dir2 = card.flag.filenameIsDir;
#endif
#if HAS_FOLDER_SORTING
#if ENABLED(SDSORT_GCODE)
if (card.sort_folders && dir1 != dir2)
return (card.sort_folders > 0) ? dir1 : !dir1;
#else
if (dir1 != dir2)
return (SDSORT_FOLDERS > 0) ? dir1 : !dir1;
#endif
#endif
return _sort_cmp_file(name1, name2);
}
int16_t CardReader::partition(uint8_t* arr, int16_t low, int16_t high) {
int16_t pivotIndex = arr[high];
int16_t i = (low - 1);
for (int16_t j = low; j < high; j++) {
if (sort_cmp_files(arr[j], pivotIndex)) {
i++;
uint8_t temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// Manual swap
uint8_t temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
return (i + 1);
}
void CardReader::quicksort(uint8_t* arr, int16_t low, int16_t high) {
int16_t stack[SDSORT_LIMIT + 1];
int16_t top = -1; // Initialize top of stack
// Push initial values to the stack
stack[++top] = low;
stack[++top] = high;
// Pop from stack while not empty
while (top >= 0) {
high = stack[top--];
low = stack[top--];
// Set pivot element at correct position
const int16_t pivot = partition(arr, low, high);
// If elements are on left side, push to stack
if (pivot - 1 > low) {
stack[++top] = low;
stack[++top] = pivot - 1;
}
// If elements are on right side, push to stack
if (pivot + 1 < high) {
stack[++top] = pivot + 1;
stack[++top] = high;
}
}
}
#else // !SDSORT_QUICK
// Bubble Sort
void CardReader::bubblesort(uint8_t* arr, int16_t fileCnt) {
for (int16_t i = fileCnt; --i;) {
bool didSwap = false;
int16_t o1 = arr[0];
#if DISABLED(SDSORT_USES_RAM)
// By default re-read the names from SD for every compare
// retaining only two filenames at a time. This is very
// slow but is safest and uses minimal RAM.
char name1[LONG_FILENAME_LENGTH];
selectFileByIndex(o1); // Pre-fetch the first entry and save it
strcpy(name1, longest_filename()); // so the loop only needs one fetch
#if HAS_FOLDER_SORTING
bool dir1 = flag.filenameIsDir;
#endif
if ((i & 0x7) == 7) hal.watchdog_refresh();
#endif
for (int16_t j = 0; j < i; ++j) {
const int16_t o2 = arr[j + 1];
// Compare names from the array or just the two buffered names
auto _sort_cmp_file = [](char * const n1, char * const n2) -> bool {
const bool sort = strcasecmp(n1, n2) > 0;
return (TERN(SDSORT_GCODE, sort_alpha == AS_REV, ENABLED(SDSORT_REVERSE))) ? !sort : sort;
};
#define _SORT_CMP_FILE() _sort_cmp_file(TERN(SDSORT_USES_RAM, sortnames[o1], name1), TERN(SDSORT_USES_RAM, sortnames[o2], name2))
#if HAS_FOLDER_SORTING
#if ENABLED(SDSORT_USES_RAM)
// Folder sorting needs an index and bit to test for folder-ness.
#define _SORT_CMP_DIR(fs) (IS_DIR(o1) == IS_DIR(o2) ? _SORT_CMP_FILE() : IS_DIR(fs > 0 ? o1 : o2))
#else
#define _SORT_CMP_DIR(fs) ((dir1 == flag.filenameIsDir) ? _SORT_CMP_FILE() : (fs > 0 ? dir1 : !dir1))
#endif
#endif
// The most economical method reads names as-needed
// throughout the loop. Slow if there are many.
#if DISABLED(SDSORT_USES_RAM)
selectFileByIndex(o2);
const bool dir2 = flag.filenameIsDir;
char * const name2 = longest_filename(); // Use the string in-place
if ((i & 0x7) == 7) hal.watchdog_refresh();
#endif
// Sort the current pair according to settings.
if (
#if HAS_FOLDER_SORTING
#if ENABLED(SDSORT_GCODE)
sort_folders ? _SORT_CMP_DIR(sort_folders) : _SORT_CMP_FILE()
#else
_SORT_CMP_DIR(SDSORT_FOLDERS)
#endif
#else
_SORT_CMP_FILE()
#endif
) {
// Reorder the index, indicate that sorting happened
// Note that the next o1 will be the current o1. No new fetch needed.
arr[j] = o2;
arr[j + 1] = o1;
didSwap = true;
}
else {
// The next o1 is the current o2. No new fetch needed.
o1 = o2;
#if DISABLED(SDSORT_USES_RAM)
TERN_(HAS_FOLDER_SORTING, dir1 = dir2);
strcpy(name1, name2);
#endif
}
}
if (!didSwap) break;
}
}
#endif // !SDSORT_QUICK
/**
* Read all the files and produce a sort key
*
@ -1543,6 +1378,13 @@ void CardReader::cdroot() {
#endif
#endif
#else // !SDSORT_USES_RAM
// By default re-read the names from SD for every compare
// retaining only two filenames at a time. This is very
// slow but is safest and uses minimal RAM.
char name1[LONG_FILENAME_LENGTH];
#endif
if (fileCnt > 1) {
@ -1564,15 +1406,171 @@ void CardReader::cdroot() {
if (flag.filenameIsDir) SBI(isDir[ind], bit);
#endif
#endif
if ((i & 0x7) == 7) hal.watchdog_refresh();
}
// Sorting Algorithm
#if ENABLED(SDSORT_QUICK)
quicksort(sort_order, 0, fileCnt - 1);
{
auto sort_cmp_files = [&](const int16_t o1, const int16_t o2) -> bool {
#if DISABLED(SDSORT_USES_RAM)
char name1[LONG_FILENAME_LENGTH];
selectFileByIndex(o1);
strcpy(name1, longest_filename());
#if HAS_FOLDER_SORTING
const bool dir1 = flag.filenameIsDir;
#endif
selectFileByIndex(o2);
const char *name2 = longest_filename();
#if HAS_FOLDER_SORTING
const bool dir2 = flag.filenameIsDir;
#endif
#else
#if HAS_FOLDER_SORTING
const bool dir1 = IS_DIR(o1), dir2 = IS_DIR(o2);
#endif
const char *name1 = sortnames[o1], *name2 = sortnames[o2];
#endif
#if HAS_FOLDER_SORTING
#if ENABLED(SDSORT_GCODE)
if (sort_folders && dir1 != dir2)
return (sort_folders > 0) ? dir1 : !dir1;
#else
if (dir1 != dir2)
return (SDSORT_FOLDERS > 0) ? dir1 : !dir1;
#endif
#endif
const bool sort = strcasecmp(name1, name2) < 0;
return (TERN(SDSORT_GCODE, sort_alpha == AS_REV, ENABLED(SDSORT_REVERSE))) ? !sort : sort;
};
auto partition = [&](uint8_t* arr, int16_t low, int16_t high) -> int16_t {
int16_t pivotIndex = arr[high];
int16_t i = (low - 1);
for (int16_t j = low; j < high; j++) {
if (sort_cmp_files(arr[j], pivotIndex)) {
i++;
uint8_t temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// Manual swap
uint8_t temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
return (i + 1);
};
// Quick Sort
int16_t stack[SDSORT_LIMIT + 1];
int16_t top = -1; // Initialize top of stack
int16_t low = 0, high = fileCnt - 1;
// Push initial values to the stack
stack[++top] = low;
stack[++top] = high;
// Pop from stack while not empty
while (top >= 0) {
high = stack[top--];
low = stack[top--];
// Set pivot element at correct position
const int16_t pivot = partition(sort_order, low, high);
// If elements are on left side, push to stack
if (pivot - 1 > low) {
stack[++top] = low;
stack[++top] = pivot - 1;
}
// If elements are on right side, push to stack
if (pivot + 1 < high) {
stack[++top] = pivot + 1;
stack[++top] = high;
}
}
}
#else
bubblesort(sort_order, fileCnt);
#endif
{
// Bubble Sort
for (int16_t i = fileCnt; --i;) {
bool didSwap = false;
int16_t o1 = sort_order[0];
#if DISABLED(SDSORT_USES_RAM)
// By default re-read the names from SD for every compare
// retaining only two filenames at a time. This is very
// slow but is safest and uses minimal RAM.
selectFileByIndex(o1); // Pre-fetch the first entry and save it
strcpy(name1, longest_filename()); // so the loop only needs one fetch
#if HAS_FOLDER_SORTING
bool dir1 = flag.filenameIsDir;
#endif
if ((i & 0x7) == 7) hal.watchdog_refresh();
#endif
for (int16_t j = 0; j < i; ++j) {
const int16_t o2 = sort_order[j + 1];
// Compare names from the array or just the two buffered names
auto _sort_cmp_file = [](char * const n1, char * const n2) -> bool {
const bool sort = strcasecmp(n1, n2) > 0;
return (TERN(SDSORT_GCODE, sort_alpha == AS_REV, ENABLED(SDSORT_REVERSE))) ? !sort : sort;
};
#define _SORT_CMP_FILE() _sort_cmp_file(TERN(SDSORT_USES_RAM, sortnames[o1], name1), TERN(SDSORT_USES_RAM, sortnames[o2], name2))
#if HAS_FOLDER_SORTING
#if ENABLED(SDSORT_USES_RAM)
// Folder sorting needs an index and bit to test for folder-ness.
#define _SORT_CMP_DIR(fs) (IS_DIR(o1) == IS_DIR(o2) ? _SORT_CMP_FILE() : IS_DIR(fs > 0 ? o1 : o2))
#else
#define _SORT_CMP_DIR(fs) ((dir1 == flag.filenameIsDir) ? _SORT_CMP_FILE() : (fs > 0 ? dir1 : !dir1))
#endif
#endif
// The most economical method reads names as-needed
// throughout the loop. Slow if there are many.
#if DISABLED(SDSORT_USES_RAM)
selectFileByIndex(o2);
const bool dir2 = flag.filenameIsDir;
char * const name2 = longest_filename(); // Use the string in-place
if ((i & 0x7) == 7) hal.watchdog_refresh();
#endif
// Sort the current pair according to settings.
if (
#if HAS_FOLDER_SORTING
#if ENABLED(SDSORT_GCODE)
sort_folders ? _SORT_CMP_DIR(sort_folders) : _SORT_CMP_FILE()
#else
_SORT_CMP_DIR(SDSORT_FOLDERS)
#endif
#else
_SORT_CMP_FILE()
#endif
) {
// Reorder the index, indicate that sorting happened
// Note that the next o1 will be the current o1. No new fetch needed.
sort_order[j] = o2;
sort_order[j + 1] = o1;
didSwap = true;
}
else {
// The next o1 is the current o2. No new fetch needed.
o1 = o2;
#if DISABLED(SDSORT_USES_RAM)
TERN_(HAS_FOLDER_SORTING, dir1 = dir2);
strcpy(name1, name2);
#endif
}
}
if (!didSwap) break;
}
}
#endif // Bubble Sort
// Using RAM but not keeping names around
#if ENABLED(SDSORT_USES_RAM) && DISABLED(SDSORT_CACHE_NAMES)

View file

@ -403,10 +403,6 @@ private:
#endif // SDSORT_USES_RAM
static void flush_presort();
static bool sort_cmp_files(const int16_t o1, const int16_t o2);
static int16_t partition(uint8_t* arr, int16_t low, int16_t high);
static void quicksort(uint8_t* arr, int16_t low, int16_t high);
static void bubblesort(uint8_t* arr, int16_t fileCnt);
#endif // SDCARD_SORT_ALPHA
//