From 3a34b1b8d06e13bae22617e0c24c6e939b5a4814 Mon Sep 17 00:00:00 2001 From: Stefan Ostermann Date: Sun, 2 Nov 2025 20:31:05 +0100 Subject: [PATCH] [ai] quickwin memory optimiziations, wip --- src/DirectoryNode.cpp | 81 ++++++++++++++++++++++++------------------- src/DirectoryNode.h | 14 ++++---- 2 files changed, 52 insertions(+), 43 deletions(-) diff --git a/src/DirectoryNode.cpp b/src/DirectoryNode.cpp index d6de8f5..ecd21e2 100644 --- a/src/DirectoryNode.cpp +++ b/src/DirectoryNode.cpp @@ -37,19 +37,20 @@ const std::vector &DirectoryNode::getMP3Files() const return mp3Files; } -void DirectoryNode::setCurrentPlaying(const String mp3File) +void DirectoryNode::setCurrentPlaying(const String &mp3File) { currentPlaying = mp3File; - for (int i = 0; i < mp3Files.size(); i++) + for (size_t i = 0; i < mp3Files.size(); i++) { if (mp3Files[i] == mp3File && ids.size() > i) { currentPlayingId = ids[i]; + break; } } } -const String DirectoryNode::getCurrentPlaying() const +const String &DirectoryNode::getCurrentPlaying() const { return currentPlaying; } @@ -82,7 +83,7 @@ void DirectoryNode::setSecondsPlayed(const uint32_t seconds) secondsPlayed = seconds; } -uint32_t DirectoryNode::getSecondsPlayed() +uint32_t DirectoryNode::getSecondsPlayed() const { return secondsPlayed; } @@ -97,6 +98,9 @@ void DirectoryNode::buildDirectoryTree(const char *currentPath) subdirectories.clear(); mp3Files.clear(); ids.clear(); + subdirectories.shrink_to_fit(); + mp3Files.shrink_to_fit(); + ids.shrink_to_fit(); // First collect entries so we can sort them alphabetically std::vector dirNames; @@ -113,27 +117,33 @@ void DirectoryNode::buildDirectoryTree(const char *currentPath) if (entry.isDirectory() && entry.name()[0] != '.' && strcmp(entry.name(), sys_dir)) { - dirNames.push_back(String(entry.name())); + dirNames.emplace_back(entry.name()); } else { String entryName = entry.name(); if (entryName.endsWith(".mp3") || entryName.endsWith(".MP3")) { - fileNames.push_back(entryName); + fileNames.push_back(std::move(entryName)); } } entry.close(); } rootDir.close(); - // Case-insensitive alphabetical sort + // Case-insensitive alphabetical sort without allocations auto ciLess = [](const String &a, const String &b) { - String al = a; - String bl = b; - al.toLowerCase(); - bl.toLowerCase(); - return al.compareTo(bl) < 0; + const char* pa = a.c_str(); + const char* pb = b.c_str(); + while (*pa && *pb) { + char ca = *pa++; + char cb = *pb++; + if (ca >= 'A' && ca <= 'Z') ca += 'a' - 'A'; + if (cb >= 'A' && cb <= 'Z') cb += 'a' - 'A'; + if (ca < cb) return true; + if (ca > cb) return false; + } + return *pa < *pb; }; std::sort(dirNames.begin(), dirNames.end(), ciLess); std::sort(fileNames.begin(), fileNames.end(), ciLess); @@ -165,7 +175,7 @@ void DirectoryNode::buildDirectoryTree(const char *currentPath) fullPath += "/"; fullPath += fileName; - mp3Files.push_back(fullPath); + mp3Files.push_back(std::move(fullPath)); ids.push_back(getNextId()); } } @@ -174,7 +184,7 @@ void DirectoryNode::printDirectoryTree(int level) const { for (int i = 0; i < level; i++) { - Serial.print(" "); + Serial.print(F(" ")); } Serial.println(name); @@ -182,7 +192,7 @@ void DirectoryNode::printDirectoryTree(int level) const { for (int i = 0; i <= level; i++) { - Serial.print(" "); + Serial.print(F(" ")); } Serial.println(mp3File); } @@ -256,15 +266,16 @@ DirectoryNode *DirectoryNode::advanceToMP3(const uint16_t id) } // If we get here, no song with this ID was found - Serial.println("advanceToMP3: No song found for ID: " + String(id)); + Serial.print(F("advanceToMP3: No song found for ID: ")); + Serial.println(id); return nullptr; } -DirectoryNode *DirectoryNode::advanceToMP3(const String songName) +DirectoryNode *DirectoryNode::advanceToMP3(const String &songName) { if (songName.isEmpty()) { - Serial.println("advanceToMP3: songName is empty"); + Serial.println(F("advanceToMP3: songName is empty")); return nullptr; } @@ -279,8 +290,6 @@ DirectoryNode *DirectoryNode::advanceToMP3(const String songName) // Lowercased copies for case-insensitive comparisons (FAT can uppercase names) String lowTarget = songName; lowTarget.toLowerCase(); - String lowNormPath = normalizedPath; - lowNormPath.toLowerCase(); // First, search in the current directory's MP3 files for (size_t i = 0; i < mp3Files.size(); i++) @@ -359,7 +368,8 @@ DirectoryNode *DirectoryNode::advanceToMP3(const String songName) } // If we get here, no matching song was found - Serial.println("advanceToMP3: No song found for: " + songName); + Serial.print(F("advanceToMP3: No song found for: ")); + Serial.println(songName); return nullptr; } @@ -376,14 +386,14 @@ DirectoryNode *DirectoryNode::goToPreviousMP3(uint32_t thresholdSeconds) // Safety check for null pointer if (currentPlaying.isEmpty()) { - Serial.println("goToPreviousMP3: currentPlaying is empty"); + Serial.println(F("goToPreviousMP3: currentPlaying is empty")); return nullptr; } // If we've been playing for more than threshold seconds, restart current song if (secondsPlayed > thresholdSeconds) { - Serial.println("goToPreviousMP3: Restarting current song (played > threshold)"); + Serial.println(F("goToPreviousMP3: Restarting current song (played > threshold)")); return this; } @@ -401,7 +411,7 @@ DirectoryNode *DirectoryNode::goToPreviousMP3(uint32_t thresholdSeconds) // If current song found and not the first song, move to previous if (currentIndex > 0) { - Serial.print("goToPreviousMP3: Moving to previous song in same directory: "); + Serial.print(F("goToPreviousMP3: Moving to previous song in same directory: ")); Serial.println(mp3Files[currentIndex - 1]); setCurrentPlaying(mp3Files[currentIndex - 1]); return this; @@ -409,15 +419,15 @@ DirectoryNode *DirectoryNode::goToPreviousMP3(uint32_t thresholdSeconds) // If we're at the first song or song not found in current directory, // we need to find the previous song globally - Serial.println("goToPreviousMP3: At first song or song not found, looking for previous globally"); + Serial.println(F("goToPreviousMP3: At first song or song not found, looking for previous globally")); return nullptr; // Let the caller handle global previous logic } -DirectoryNode *DirectoryNode::findPreviousMP3Globally(const String currentGlobal) +DirectoryNode *DirectoryNode::findPreviousMP3Globally(const String ¤tGlobal) { if (currentGlobal.isEmpty()) { - Serial.println("findPreviousMP3Globally: currentGlobal is null"); + Serial.println(F("findPreviousMP3Globally: currentGlobal is null")); return nullptr; } @@ -444,23 +454,25 @@ DirectoryNode *DirectoryNode::findPreviousMP3Globally(const String currentGlobal DirectoryNode *prevNode = allMP3s[currentGlobalIndex - 1].first; int prevFileIndex = allMP3s[currentGlobalIndex - 1].second; - Serial.print("findPreviousMP3Globally: Moving to previous song globally: "); + Serial.print(F("findPreviousMP3Globally: Moving to previous song globally: ")); Serial.println(prevNode->mp3Files[prevFileIndex]); prevNode->setCurrentPlaying(prevNode->mp3Files[prevFileIndex]); return prevNode; } - Serial.println("findPreviousMP3Globally: No previous song found globally"); + Serial.println(F("findPreviousMP3Globally: No previous song found globally")); return nullptr; } void DirectoryNode::buildFlatMP3List(std::vector> &allMP3s) { + // Pre-reserve to reduce reallocations + allMP3s.reserve(allMP3s.size() + mp3Files.size()); // Add all MP3 files from this directory for (size_t i = 0; i < mp3Files.size(); i++) { - allMP3s.push_back(std::make_pair(this, i)); + allMP3s.emplace_back(this, i); } // Recursively add MP3 files from subdirectories @@ -470,12 +482,12 @@ void DirectoryNode::buildFlatMP3List(std::vector } } -const size_t DirectoryNode::getNumOfFiles() +size_t DirectoryNode::getNumOfFiles() const { return subdirectories.size(); } -DirectoryNode *DirectoryNode::advanceToNextMP3(const String currentGlobal) +DirectoryNode *DirectoryNode::advanceToNextMP3(const String ¤tGlobal) { bool useFirst = false; Serial.println(currentGlobal.c_str()); @@ -534,7 +546,7 @@ DirectoryNode *DirectoryNode::advanceToNextMP3(const String currentGlobal) // If we get here, there were no MP3 files or subdirectories left to check currentPlaying = ""; - Serial.println("no more nodes found"); + Serial.println(F("no more nodes found")); return this; } @@ -581,6 +593,3 @@ void DirectoryNode::streamDirectoryHTML(Print &out) const { yield(); // Final yield before completing } } - - - diff --git a/src/DirectoryNode.h b/src/DirectoryNode.h index 8f8e0b2..518b68b 100644 --- a/src/DirectoryNode.h +++ b/src/DirectoryNode.h @@ -34,14 +34,14 @@ public: const std::vector& getSubdirectories() const; const std::vector& getMP3Files() const; - const size_t getNumOfFiles(); + size_t getNumOfFiles() const; - void setCurrentPlaying(const String mp3File); - const String getCurrentPlaying() const; + void setCurrentPlaying(const String& mp3File); + const String& getCurrentPlaying() const; const uint16_t getCurrentPlayingId() const; void setSecondsPlayed(const uint32_t seconds); - uint32_t getSecondsPlayed(); + uint32_t getSecondsPlayed() const; uint16_t getNextId(); @@ -49,10 +49,10 @@ public: void addMP3File(const String& mp3File); void buildDirectoryTree(const char* currentPath); void printDirectoryTree(int level = 0) const; - DirectoryNode* advanceToMP3(const String songName); - DirectoryNode* advanceToNextMP3(const String currentGlobal); + DirectoryNode* advanceToMP3(const String& songName); + DirectoryNode* advanceToNextMP3(const String& currentGlobal); DirectoryNode* goToPreviousMP3(uint32_t thresholdSeconds = 3); - DirectoryNode* findPreviousMP3Globally(const String currentGlobal); + DirectoryNode* findPreviousMP3Globally(const String& currentGlobal); void buildFlatMP3List(std::vector>& allMP3s); DirectoryNode* advanceToMP3(const uint16_t id); void advanceToFirstMP3InThisNode();