A fork of https://github.com/crosspoint-reader/crosspoint-reader
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

perf: Avoid creating strings for file extension checks (#1303)

## Summary

**What is the goal of this PR?**

This change avoids the pattern of creating a `std::string` using
`.substr` in order to compare against a file extension literal.
```c++
std::string path;
if (path.length() >= 4 && path.substr(path.length() - 4) == ".ext")
```

The `checkFileExtension` utility has moved from StringUtils to
FsHelpers, to be available to code in lib/. The signature now accepts a
`std::string_view` instead of `std::string`, which makes the single
implementation reusable for Arduino `String`.

Added utility functions for commonly repeated extensions.

These changes **save about 2 KB of flash (5,999,427 to 5,997,343)**.

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**NO**_

authored by

Zach Nelson and committed by
GitHub
c3f1dbfa ea88797c

+159 -150
+8 -13
lib/Epub/Epub.cpp
··· 103 103 pos += strlen(pattern); 104 104 const auto endPos = coverPageHtml.find('"', pos); 105 105 if (endPos != std::string::npos) { 106 - const auto ref = coverPageHtml.substr(pos, endPos - pos); 106 + const auto ref = std::string_view{coverPageHtml}.substr(pos, endPos - pos); 107 107 // Check if it's an image file 108 - if (ref.length() >= 4) { 109 - const auto ext = ref.substr(ref.length() - 4); 110 - if (ext == ".png" || ext == ".jpg" || ext == "jpeg" || ext == ".gif") { 111 - imageRef = ref; 112 - break; 113 - } 108 + if (FsHelpers::hasPngExtension(ref) || FsHelpers::hasJpgExtension(ref) || FsHelpers::hasGifExtension(ref)) { 109 + imageRef = ref; 110 + break; 114 111 } 115 112 } 116 113 pos = coverPageHtml.find(pattern, pos); ··· 541 538 return false; 542 539 } 543 540 544 - if (coverImageHref.substr(coverImageHref.length() - 4) == ".jpg" || 545 - coverImageHref.substr(coverImageHref.length() - 5) == ".jpeg") { 541 + if (FsHelpers::hasJpgExtension(coverImageHref)) { 546 542 LOG_DBG("EBP", "Generating BMP from JPG cover image (%s mode)", cropped ? "cropped" : "fit"); 547 543 const auto coverJpgTempPath = getCachePath() + "/.cover.jpg"; 548 544 ··· 575 571 return success; 576 572 } 577 573 578 - if (coverImageHref.substr(coverImageHref.length() - 4) == ".png") { 574 + if (FsHelpers::hasPngExtension(coverImageHref)) { 579 575 LOG_DBG("EBP", "Generating BMP from PNG cover image (%s mode)", cropped ? "cropped" : "fit"); 580 576 const auto coverPngTempPath = getCachePath() + "/.cover.png"; 581 577 ··· 629 625 const auto coverImageHref = bookMetadataCache->coreMetadata.coverItemHref; 630 626 if (coverImageHref.empty()) { 631 627 LOG_DBG("EBP", "No known cover image for thumbnail"); 632 - } else if (coverImageHref.substr(coverImageHref.length() - 4) == ".jpg" || 633 - coverImageHref.substr(coverImageHref.length() - 5) == ".jpeg") { 628 + } else if (FsHelpers::hasJpgExtension(coverImageHref)) { 634 629 LOG_DBG("EBP", "Generating thumb BMP from JPG cover image"); 635 630 const auto coverJpgTempPath = getCachePath() + "/.cover.jpg"; 636 631 ··· 666 661 } 667 662 LOG_DBG("EBP", "Generated thumb BMP from JPG cover image, success: %s", success ? "yes" : "no"); 668 663 return success; 669 - } else if (coverImageHref.substr(coverImageHref.length() - 4) == ".png") { 664 + } else if (FsHelpers::hasPngExtension(coverImageHref)) { 670 665 LOG_DBG("EBP", "Generating thumb BMP from PNG cover image"); 671 666 const auto coverPngTempPath = getCachePath() + "/.cover.png"; 672 667
+2 -5
lib/Epub/Epub/converters/JpegToFramebufferConverter.cpp
··· 1 1 #include "JpegToFramebufferConverter.h" 2 2 3 + #include <FsHelpers.h> 3 4 #include <GfxRenderer.h> 4 5 #include <HalStorage.h> 5 6 #include <JPEGDEC.h> ··· 486 487 } 487 488 488 489 bool JpegToFramebufferConverter::supportsFormat(const std::string& extension) { 489 - std::string ext = extension; 490 - for (auto& c : ext) { 491 - c = tolower(c); 492 - } 493 - return (ext == ".jpg" || ext == ".jpeg"); 490 + return FsHelpers::hasJpgExtension(extension); 494 491 }
+2 -5
lib/Epub/Epub/converters/PngToFramebufferConverter.cpp
··· 1 1 #include "PngToFramebufferConverter.h" 2 2 3 + #include <FsHelpers.h> 3 4 #include <GfxRenderer.h> 4 5 #include <HalStorage.h> 5 6 #include <Logging.h> ··· 391 392 } 392 393 393 394 bool PngToFramebufferConverter::supportsFormat(const std::string& extension) { 394 - std::string ext = extension; 395 - for (auto& c : ext) { 396 - c = tolower(c); 397 - } 398 - return (ext == ".png"); 395 + return FsHelpers::hasPngExtension(extension); 399 396 }
+43 -1
lib/FsHelpers/FsHelpers.cpp
··· 1 1 #include "FsHelpers.h" 2 2 3 + #include <cctype> 4 + #include <cstring> 3 5 #include <vector> 4 6 5 - std::string FsHelpers::normalisePath(const std::string& path) { 7 + namespace FsHelpers { 8 + 9 + std::string normalisePath(const std::string& path) { 6 10 std::vector<std::string> components; 7 11 std::string component; 8 12 ··· 37 41 38 42 return result; 39 43 } 44 + 45 + bool checkFileExtension(std::string_view fileName, const char* extension) { 46 + const size_t extLen = strlen(extension); 47 + if (fileName.length() < extLen) { 48 + return false; 49 + } 50 + 51 + const size_t offset = fileName.length() - extLen; 52 + for (size_t i = 0; i < extLen; i++) { 53 + if (tolower(static_cast<unsigned char>(fileName[offset + i])) != 54 + tolower(static_cast<unsigned char>(extension[i]))) { 55 + return false; 56 + } 57 + } 58 + return true; 59 + } 60 + 61 + bool hasJpgExtension(std::string_view fileName) { 62 + return checkFileExtension(fileName, ".jpg") || checkFileExtension(fileName, ".jpeg"); 63 + } 64 + 65 + bool hasPngExtension(std::string_view fileName) { return checkFileExtension(fileName, ".png"); } 66 + 67 + bool hasBmpExtension(std::string_view fileName) { return checkFileExtension(fileName, ".bmp"); } 68 + 69 + bool hasGifExtension(std::string_view fileName) { return checkFileExtension(fileName, ".gif"); } 70 + 71 + bool hasEpubExtension(std::string_view fileName) { return checkFileExtension(fileName, ".epub"); } 72 + 73 + bool hasXtcExtension(std::string_view fileName) { 74 + return checkFileExtension(fileName, ".xtc") || checkFileExtension(fileName, ".xtch"); 75 + } 76 + 77 + bool hasTxtExtension(std::string_view fileName) { return checkFileExtension(fileName, ".txt"); } 78 + 79 + bool hasMarkdownExtension(std::string_view fileName) { return checkFileExtension(fileName, ".md"); } 80 + 81 + } // namespace FsHelpers
+55 -4
lib/FsHelpers/FsHelpers.h
··· 1 1 #pragma once 2 + #include <WString.h> 3 + 2 4 #include <string> 5 + #include <string_view> 6 + 7 + namespace FsHelpers { 8 + 9 + std::string normalisePath(const std::string& path); 10 + 11 + /** 12 + * Check if the given filename ends with the specified extension (case-insensitive). 13 + */ 14 + bool checkFileExtension(std::string_view fileName, const char* extension); 15 + inline bool checkFileExtension(const String& fileName, const char* extension) { 16 + return checkFileExtension(std::string_view{fileName.c_str(), fileName.length()}, extension); 17 + } 18 + 19 + // Check for either .jpg or .jpeg extension (case-insensitive) 20 + bool hasJpgExtension(std::string_view fileName); 21 + inline bool hasJpgExtension(const String& fileName) { 22 + return hasJpgExtension(std::string_view{fileName.c_str(), fileName.length()}); 23 + } 24 + 25 + // Check for .png extension (case-insensitive) 26 + bool hasPngExtension(std::string_view fileName); 27 + inline bool hasPngExtension(const String& fileName) { 28 + return hasPngExtension(std::string_view{fileName.c_str(), fileName.length()}); 29 + } 30 + 31 + // Check for .bmp extension (case-insensitive) 32 + bool hasBmpExtension(std::string_view fileName); 3 33 4 - class FsHelpers { 5 - public: 6 - static std::string normalisePath(const std::string& path); 7 - }; 34 + // Check for .gif extension (case-insensitive) 35 + bool hasGifExtension(std::string_view fileName); 36 + inline bool hasGifExtension(const String& fileName) { 37 + return hasGifExtension(std::string_view{fileName.c_str(), fileName.length()}); 38 + } 39 + 40 + // Check for .epub extension (case-insensitive) 41 + bool hasEpubExtension(std::string_view fileName); 42 + inline bool hasEpubExtension(const String& fileName) { 43 + return hasEpubExtension(std::string_view{fileName.c_str(), fileName.length()}); 44 + } 45 + 46 + // Check for either .xtc or .xtch extension (case-insensitive) 47 + bool hasXtcExtension(std::string_view fileName); 48 + 49 + // Check for .txt extension (case-insensitive) 50 + bool hasTxtExtension(std::string_view fileName); 51 + inline bool hasTxtExtension(const String& fileName) { 52 + return hasTxtExtension(std::string_view{fileName.c_str(), fileName.length()}); 53 + } 54 + 55 + // Check for .md extension (case-insensitive) 56 + bool hasMarkdownExtension(std::string_view fileName); 57 + 58 + } // namespace FsHelpers
+3 -12
lib/Txt/Txt.cpp
··· 41 41 std::string filename = (lastSlash != std::string::npos) ? filepath.substr(lastSlash + 1) : filepath; 42 42 43 43 // Remove .txt extension 44 - if (filename.length() >= 4 && filename.substr(filename.length() - 4) == ".txt") { 44 + if (FsHelpers::hasTxtExtension(filename)) { 45 45 filename = filename.substr(0, filename.length() - 4); 46 46 } 47 47 ··· 112 112 // Setup cache directory 113 113 setupCacheDir(); 114 114 115 - // Get file extension 116 - const size_t len = coverImagePath.length(); 117 - const bool isJpg = 118 - (len >= 4 && (coverImagePath.substr(len - 4) == ".jpg" || coverImagePath.substr(len - 4) == ".JPG")) || 119 - (len >= 5 && (coverImagePath.substr(len - 5) == ".jpeg" || coverImagePath.substr(len - 5) == ".JPEG")); 120 - const bool isBmp = len >= 4 && (coverImagePath.substr(len - 4) == ".bmp" || coverImagePath.substr(len - 4) == ".BMP"); 121 - 122 - if (isBmp) { 115 + if (FsHelpers::hasBmpExtension(coverImagePath)) { 123 116 // Copy BMP file to cache 124 117 LOG_DBG("TXT", "Copying BMP cover image to cache"); 125 118 FsFile src, dst; ··· 139 132 dst.close(); 140 133 LOG_DBG("TXT", "Copied BMP cover to cache"); 141 134 return true; 142 - } 143 - 144 - if (isJpg) { 135 + } else if (FsHelpers::hasJpgExtension(coverImagePath)) { 145 136 // Convert JPG/JPEG to BMP (same approach as Epub) 146 137 LOG_DBG("TXT", "Generating BMP from JPG cover image"); 147 138 FsFile coverJpg, coverBmp;
-10
lib/Xtc/Xtc/XtcTypes.h
··· 144 144 } 145 145 } 146 146 147 - /** 148 - * Check if filename has XTC/XTCH extension 149 - */ 150 - inline bool isXtcExtension(const char* filename) { 151 - if (!filename) return false; 152 - const char* ext = strrchr(filename, '.'); 153 - if (!ext) return false; 154 - return (strcasecmp(ext, ".xtc") == 0 || strcasecmp(ext, ".xtch") == 0); 155 - } 156 - 157 147 } // namespace xtc
+4 -7
src/RecentBooksStore.cpp
··· 1 1 #include "RecentBooksStore.h" 2 2 3 3 #include <Epub.h> 4 + #include <FsHelpers.h> 4 5 #include <HalStorage.h> 5 6 #include <JsonSettingsIO.h> 6 7 #include <Logging.h> ··· 8 9 #include <Xtc.h> 9 10 10 11 #include <algorithm> 11 - 12 - #include "util/StringUtils.h" 13 12 14 13 namespace { 15 14 constexpr uint8_t RECENT_BOOKS_FILE_VERSION = 3; ··· 71 70 // If epub, try to load the metadata for title/author and cover. 72 71 // Use buildIfMissing=false to avoid heavy epub loading on boot; getTitle()/getAuthor() may be 73 72 // blank until the book is opened, and entries with missing title are omitted from recent list. 74 - if (StringUtils::checkFileExtension(lastBookFileName, ".epub")) { 73 + if (FsHelpers::hasEpubExtension(lastBookFileName)) { 75 74 Epub epub(path, "/.crosspoint"); 76 75 epub.load(false, true); 77 76 return RecentBook{path, epub.getTitle(), epub.getAuthor(), epub.getThumbBmpPath()}; 78 - } else if (StringUtils::checkFileExtension(lastBookFileName, ".xtch") || 79 - StringUtils::checkFileExtension(lastBookFileName, ".xtc")) { 77 + } else if (FsHelpers::hasXtcExtension(lastBookFileName)) { 80 78 // Handle XTC file 81 79 Xtc xtc(path, "/.crosspoint"); 82 80 if (xtc.load()) { 83 81 return RecentBook{path, xtc.getTitle(), xtc.getAuthor(), xtc.getThumbBmpPath()}; 84 82 } 85 - } else if (StringUtils::checkFileExtension(lastBookFileName, ".txt") || 86 - StringUtils::checkFileExtension(lastBookFileName, ".md")) { 83 + } else if (FsHelpers::hasTxtExtension(lastBookFileName) || FsHelpers::hasMarkdownExtension(lastBookFileName)) { 87 84 return RecentBook{path, lastBookFileName, "", ""}; 88 85 } 89 86 return RecentBook{path, "", "", ""};
+5 -6
src/activities/boot_sleep/SleepActivity.cpp
··· 1 1 #include "SleepActivity.h" 2 2 3 3 #include <Epub.h> 4 + #include <FsHelpers.h> 4 5 #include <GfxRenderer.h> 5 6 #include <HalStorage.h> 6 7 #include <I18n.h> ··· 12 13 #include "components/UITheme.h" 13 14 #include "fontIds.h" 14 15 #include "images/Logo120.h" 15 - #include "util/StringUtils.h" 16 16 17 17 void SleepActivity::onEnter() { 18 18 Activity::onEnter(); ··· 61 61 continue; 62 62 } 63 63 64 - if (filename.substr(filename.length() - 4) != ".bmp") { 64 + if (!FsHelpers::hasBmpExtension(filename)) { 65 65 LOG_DBG("SLP", "Skipping non-.bmp file name: %s", name); 66 66 file.close(); 67 67 continue; ··· 228 228 bool cropped = SETTINGS.sleepScreenCoverMode == CrossPointSettings::SLEEP_SCREEN_COVER_MODE::CROP; 229 229 230 230 // Check if the current book is XTC, TXT, or EPUB 231 - if (StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".xtc") || 232 - StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".xtch")) { 231 + if (FsHelpers::hasXtcExtension(APP_STATE.openEpubPath)) { 233 232 // Handle XTC file 234 233 Xtc lastXtc(APP_STATE.openEpubPath, "/.crosspoint"); 235 234 if (!lastXtc.load()) { ··· 243 242 } 244 243 245 244 coverBmpPath = lastXtc.getCoverBmpPath(); 246 - } else if (StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".txt")) { 245 + } else if (FsHelpers::hasTxtExtension(APP_STATE.openEpubPath)) { 247 246 // Handle TXT file - looks for cover image in the same folder 248 247 Txt lastTxt(APP_STATE.openEpubPath, "/.crosspoint"); 249 248 if (!lastTxt.load()) { ··· 257 256 } 258 257 259 258 coverBmpPath = lastTxt.getCoverBmpPath(); 260 - } else if (StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".epub")) { 259 + } else if (FsHelpers::hasEpubExtension(APP_STATE.openEpubPath)) { 261 260 // Handle EPUB file 262 261 Epub lastEpub(APP_STATE.openEpubPath, "/.crosspoint"); 263 262 // Skip loading css since we only need metadata here
+6 -6
src/activities/home/FileBrowserActivity.cpp
··· 1 1 #include "FileBrowserActivity.h" 2 2 3 3 #include <Epub.h> 4 + #include <FsHelpers.h> 4 5 #include <GfxRenderer.h> 5 6 #include <HalStorage.h> 6 7 #include <I18n.h> ··· 11 12 #include "MappedInputManager.h" 12 13 #include "components/UITheme.h" 13 14 #include "fontIds.h" 14 - #include "util/StringUtils.h" 15 15 16 16 namespace { 17 17 constexpr unsigned long GO_HOME_MS = 1000; ··· 91 91 if (file.isDirectory()) { 92 92 files.emplace_back(std::string(name) + "/"); 93 93 } else { 94 - auto filename = std::string(name); 95 - if (StringUtils::checkFileExtension(filename, ".epub") || StringUtils::checkFileExtension(filename, ".xtch") || 96 - StringUtils::checkFileExtension(filename, ".xtc") || StringUtils::checkFileExtension(filename, ".txt") || 97 - StringUtils::checkFileExtension(filename, ".md") || StringUtils::checkFileExtension(filename, ".bmp")) { 94 + std::string_view filename{name}; 95 + if (FsHelpers::hasEpubExtension(filename) || FsHelpers::hasXtcExtension(filename) || 96 + FsHelpers::hasTxtExtension(filename) || FsHelpers::hasMarkdownExtension(filename) || 97 + FsHelpers::hasBmpExtension(filename)) { 98 98 files.emplace_back(filename); 99 99 } 100 100 } ··· 120 120 121 121 void FileBrowserActivity::clearFileMetadata(const std::string& fullPath) { 122 122 // Only clear cache for .epub files 123 - if (StringUtils::checkFileExtension(fullPath, ".epub")) { 123 + if (FsHelpers::hasEpubExtension(fullPath)) { 124 124 Epub(fullPath, "/.crosspoint").clearCache(); 125 125 LOG_DBG("FileBrowser", "Cleared metadata cache for: %s", fullPath.c_str()); 126 126 }
+3 -4
src/activities/home/HomeActivity.cpp
··· 2 2 3 3 #include <Bitmap.h> 4 4 #include <Epub.h> 5 + #include <FsHelpers.h> 5 6 #include <GfxRenderer.h> 6 7 #include <HalStorage.h> 7 8 #include <I18n.h> ··· 17 18 #include "RecentBooksStore.h" 18 19 #include "components/UITheme.h" 19 20 #include "fontIds.h" 20 - #include "util/StringUtils.h" 21 21 22 22 int HomeActivity::getMenuItemCount() const { 23 23 int count = 4; // File Browser, Recents, File transfer, Settings ··· 61 61 std::string coverPath = UITheme::getCoverThumbPath(book.coverBmpPath, coverHeight); 62 62 if (!Storage.exists(coverPath.c_str())) { 63 63 // If epub, try to load the metadata for title/author and cover 64 - if (StringUtils::checkFileExtension(book.path, ".epub")) { 64 + if (FsHelpers::hasEpubExtension(book.path)) { 65 65 Epub epub(book.path, "/.crosspoint"); 66 66 // Skip loading css since we only need metadata here 67 67 epub.load(false, true); ··· 79 79 } 80 80 coverRendered = false; 81 81 requestUpdate(); 82 - } else if (StringUtils::checkFileExtension(book.path, ".xtch") || 83 - StringUtils::checkFileExtension(book.path, ".xtc")) { 82 + } else if (FsHelpers::hasXtcExtension(book.path)) { 84 83 // Handle XTC file 85 84 Xtc xtc(book.path, "/.crosspoint"); 86 85 if (xtc.load()) {
-1
src/activities/home/RecentBooksActivity.cpp
··· 10 10 #include "RecentBooksStore.h" 11 11 #include "components/UITheme.h" 12 12 #include "fontIds.h" 13 - #include "util/StringUtils.h" 14 13 15 14 namespace { 16 15 constexpr unsigned long GO_HOME_MS = 1000;
+5 -7
src/activities/reader/ReaderActivity.cpp
··· 1 1 #include "ReaderActivity.h" 2 2 3 + #include <FsHelpers.h> 3 4 #include <HalStorage.h> 4 5 5 6 #include "CrossPointSettings.h" ··· 11 12 #include "XtcReaderActivity.h" 12 13 #include "activities/util/BmpViewerActivity.h" 13 14 #include "activities/util/FullScreenMessageActivity.h" 14 - #include "util/StringUtils.h" 15 15 16 16 std::string ReaderActivity::extractFolderPath(const std::string& filePath) { 17 17 const auto lastSlash = filePath.find_last_of('/'); ··· 21 21 return filePath.substr(0, lastSlash); 22 22 } 23 23 24 - bool ReaderActivity::isXtcFile(const std::string& path) { 25 - return StringUtils::checkFileExtension(path, ".xtc") || StringUtils::checkFileExtension(path, ".xtch"); 26 - } 24 + bool ReaderActivity::isXtcFile(const std::string& path) { return FsHelpers::hasXtcExtension(path); } 27 25 28 26 bool ReaderActivity::isTxtFile(const std::string& path) { 29 - return StringUtils::checkFileExtension(path, ".txt") || 30 - StringUtils::checkFileExtension(path, ".md"); // Treat .md as txt files (until we have a markdown reader) 27 + return FsHelpers::hasTxtExtension(path) || 28 + FsHelpers::hasMarkdownExtension(path); // Treat .md as txt files (until we have a markdown reader) 31 29 } 32 30 33 - bool ReaderActivity::isBmpFile(const std::string& path) { return StringUtils::checkFileExtension(path, ".bmp"); } 31 + bool ReaderActivity::isBmpFile(const std::string& path) { return FsHelpers::hasBmpExtension(path); } 34 32 35 33 std::unique_ptr<Epub> ReaderActivity::loadEpub(const std::string& path) { 36 34 if (!Storage.exists(path.c_str())) {
+5 -6
src/components/UITheme.cpp
··· 1 1 #include "UITheme.h" 2 2 3 + #include <FsHelpers.h> 3 4 #include <GfxRenderer.h> 4 5 #include <Logging.h> 5 6 ··· 10 11 #include "components/themes/BaseTheme.h" 11 12 #include "components/themes/lyra/Lyra3CoversTheme.h" 12 13 #include "components/themes/lyra/LyraTheme.h" 13 - #include "util/StringUtils.h" 14 14 15 15 namespace { 16 16 constexpr int SKIP_PAGE_MS = 700; ··· 74 74 return coverBmpPath; 75 75 } 76 76 77 - UIIcon UITheme::getFileIcon(std::string filename) { 77 + UIIcon UITheme::getFileIcon(const std::string& filename) { 78 78 if (filename.back() == '/') { 79 79 return Folder; 80 80 } 81 - if (StringUtils::checkFileExtension(filename, ".epub") || StringUtils::checkFileExtension(filename, ".xtch") || 82 - StringUtils::checkFileExtension(filename, ".xtc")) { 81 + if (FsHelpers::hasEpubExtension(filename) || FsHelpers::hasXtcExtension(filename)) { 83 82 return Book; 84 83 } 85 - if (StringUtils::checkFileExtension(filename, ".txt") || StringUtils::checkFileExtension(filename, ".md")) { 84 + if (FsHelpers::hasTxtExtension(filename) || FsHelpers::hasMarkdownExtension(filename)) { 86 85 return Text; 87 86 } 88 - if (StringUtils::checkFileExtension(filename, ".bmp")) { 87 + if (FsHelpers::hasBmpExtension(filename)) { 89 88 return Image; 90 89 } 91 90 return File;
+1 -1
src/components/UITheme.h
··· 21 21 static int getNumberOfItemsPerPage(const GfxRenderer& renderer, bool hasHeader, bool hasTabBar, bool hasButtonHints, 22 22 bool hasSubtitle); 23 23 static std::string getCoverThumbPath(std::string coverBmpPath, int coverHeight); 24 - static UIIcon getFileIcon(std::string filename); 24 + static UIIcon getFileIcon(const std::string& filename); 25 25 static int getStatusBarHeight(); 26 26 static int getProgressBarHeight(); 27 27
+2 -7
src/network/CrossPointWebServer.cpp
··· 16 16 #include "html/FilesPageHtml.generated.h" 17 17 #include "html/HomePageHtml.generated.h" 18 18 #include "html/SettingsPageHtml.generated.h" 19 - #include "util/StringUtils.h" 20 19 21 20 namespace { 22 21 // Folders/files to hide from the web interface file browser ··· 44 43 // Helper function to clear epub cache after upload 45 44 void clearEpubCacheIfNeeded(const String& filePath) { 46 45 // Only clear cache for .epub files 47 - if (StringUtils::checkFileExtension(filePath, ".epub")) { 46 + if (FsHelpers::hasEpubExtension(filePath)) { 48 47 Epub(filePath.c_str(), "/.crosspoint").clearCache(); 49 48 LOG_DBG("WEB", "Cleared epub cache for: %s", filePath.c_str()); 50 49 } ··· 391 390 root.close(); 392 391 } 393 392 394 - bool CrossPointWebServer::isEpubFile(const String& filename) const { 395 - String lower = filename; 396 - lower.toLowerCase(); 397 - return lower.endsWith(".epub"); 398 - } 393 + bool CrossPointWebServer::isEpubFile(const String& filename) const { return FsHelpers::hasEpubExtension(filename); } 399 394 400 395 void CrossPointWebServer::handleFileList() const { 401 396 sendHtmlContent(server.get(), FilesPageHtml, sizeof(FilesPageHtml));
+15 -19
src/network/WebDAVHandler.cpp
··· 6 6 #include <Logging.h> 7 7 #include <esp_task_wdt.h> 8 8 9 - #include "util/StringUtils.h" 10 - 11 9 namespace { 12 10 const char* HIDDEN_ITEMS[] = {"System Volume Information", "XTCache"}; 13 11 constexpr size_t HIDDEN_ITEMS_COUNT = sizeof(HIDDEN_ITEMS) / sizeof(HIDDEN_ITEMS[0]); ··· 801 799 } 802 800 803 801 void WebDAVHandler::clearEpubCacheIfNeeded(const String& path) const { 804 - if (StringUtils::checkFileExtension(path, ".epub")) { 802 + if (FsHelpers::hasEpubExtension(path)) { 805 803 Epub(path.c_str(), "/.crosspoint").clearCache(); 806 804 LOG_DBG("DAV", "Cleared epub cache for: %s", path.c_str()); 807 805 } 808 806 } 809 807 810 808 String WebDAVHandler::getMimeType(const String& path) const { 811 - if (StringUtils::checkFileExtension(path, ".epub")) return "application/epub+zip"; 812 - if (StringUtils::checkFileExtension(path, ".pdf")) return "application/pdf"; 813 - if (StringUtils::checkFileExtension(path, ".txt")) return "text/plain"; 814 - if (StringUtils::checkFileExtension(path, ".html") || StringUtils::checkFileExtension(path, ".htm")) 815 - return "text/html"; 816 - if (StringUtils::checkFileExtension(path, ".css")) return "text/css"; 817 - if (StringUtils::checkFileExtension(path, ".js")) return "application/javascript"; 818 - if (StringUtils::checkFileExtension(path, ".json")) return "application/json"; 819 - if (StringUtils::checkFileExtension(path, ".xml")) return "application/xml"; 820 - if (StringUtils::checkFileExtension(path, ".jpg") || StringUtils::checkFileExtension(path, ".jpeg")) 821 - return "image/jpeg"; 822 - if (StringUtils::checkFileExtension(path, ".png")) return "image/png"; 823 - if (StringUtils::checkFileExtension(path, ".gif")) return "image/gif"; 824 - if (StringUtils::checkFileExtension(path, ".svg")) return "image/svg+xml"; 825 - if (StringUtils::checkFileExtension(path, ".zip")) return "application/zip"; 826 - if (StringUtils::checkFileExtension(path, ".gz")) return "application/gzip"; 809 + if (FsHelpers::hasEpubExtension(path)) return "application/epub+zip"; 810 + if (FsHelpers::checkFileExtension(path, ".pdf")) return "application/pdf"; 811 + if (FsHelpers::hasTxtExtension(path)) return "text/plain"; 812 + if (FsHelpers::checkFileExtension(path, ".html") || FsHelpers::checkFileExtension(path, ".htm")) return "text/html"; 813 + if (FsHelpers::checkFileExtension(path, ".css")) return "text/css"; 814 + if (FsHelpers::checkFileExtension(path, ".js")) return "application/javascript"; 815 + if (FsHelpers::checkFileExtension(path, ".json")) return "application/json"; 816 + if (FsHelpers::checkFileExtension(path, ".xml")) return "application/xml"; 817 + if (FsHelpers::hasJpgExtension(path)) return "image/jpeg"; 818 + if (FsHelpers::hasPngExtension(path)) return "image/png"; 819 + if (FsHelpers::hasGifExtension(path)) return "image/gif"; 820 + if (FsHelpers::checkFileExtension(path, ".svg")) return "image/svg+xml"; 821 + if (FsHelpers::checkFileExtension(path, ".zip")) return "application/zip"; 822 + if (FsHelpers::checkFileExtension(path, ".gz")) return "application/gzip"; 827 823 return "application/octet-stream"; 828 824 }
-28
src/util/StringUtils.cpp
··· 2 2 3 3 #include <Utf8.h> 4 4 5 - #include <cstring> 6 - 7 5 namespace StringUtils { 8 6 9 7 std::string sanitizeFilename(const std::string& name, size_t maxBytes) { ··· 43 41 } 44 42 45 43 return result.empty() ? "book" : result; 46 - } 47 - 48 - bool checkFileExtension(const std::string& fileName, const char* extension) { 49 - if (fileName.length() < strlen(extension)) { 50 - return false; 51 - } 52 - 53 - const std::string fileExt = fileName.substr(fileName.length() - strlen(extension)); 54 - for (size_t i = 0; i < fileExt.length(); i++) { 55 - if (tolower(fileExt[i]) != tolower(extension[i])) { 56 - return false; 57 - } 58 - } 59 - return true; 60 - } 61 - 62 - bool checkFileExtension(const String& fileName, const char* extension) { 63 - if (fileName.length() < strlen(extension)) { 64 - return false; 65 - } 66 - 67 - String localFile(fileName); 68 - String localExtension(extension); 69 - localFile.toLowerCase(); 70 - localExtension.toLowerCase(); 71 - return localFile.endsWith(localExtension); 72 44 } 73 45 74 46 } // namespace StringUtils
-8
src/util/StringUtils.h
··· 1 1 #pragma once 2 2 3 - #include <WString.h> 4 - 5 3 #include <string> 6 4 7 5 namespace StringUtils { ··· 12 10 * and limits length to maxBytes bytes. 13 11 */ 14 12 std::string sanitizeFilename(const std::string& name, size_t maxBytes = 100); 15 - 16 - /** 17 - * Check if the given filename ends with the specified extension (case-insensitive). 18 - */ 19 - bool checkFileExtension(const std::string& fileName, const char* extension); 20 - bool checkFileExtension(const String& fileName, const char* extension); 21 13 22 14 } // namespace StringUtils