diff --git a/Marlin/src/sd/cardreader.cpp b/Marlin/src/sd/cardreader.cpp index 9a5394d5e7..1d6fa06998 100644 --- a/Marlin/src/sd/cardreader.cpp +++ b/Marlin/src/sd/cardreader.cpp @@ -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) diff --git a/Marlin/src/sd/cardreader.h b/Marlin/src/sd/cardreader.h index 5f1d99a561..c6b3dc6384 100644 --- a/Marlin/src/sd/cardreader.h +++ b/Marlin/src/sd/cardreader.h @@ -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 //