From 1f0efe6db6f208e1b134909b20afb7d44868be37 Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Sun, 26 Jul 2026 18:25:28 -0400 Subject: [PATCH] fix(persist): use non-throwing directory_iterator increment in pruneDryRun Replace range-based for over fs::directory_iterator with manual it.increment(ec) form. Mid-iteration failures now break to a best-effort partial list instead of throwing filesystem_error across REAPER's C ABI. Split shared fec into reg_ec/sz_ec. --- src/persist.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/persist.cpp b/src/persist.cpp index 5f7055e..0d24c2c 100644 --- a/src/persist.cpp +++ b/src/persist.cpp @@ -281,18 +281,23 @@ PruneReport ReaSamplerSession::pruneDryRun() const { // and the manifest. Non-recursive: the bank folder is flat (capture writes files // directly here); skip any subdirectory. Size is stat'd here and cached by relative // path so the report's byte tally reuses the same on-disk read. + // Manual iterator form (it.increment(ec)) keeps the loop non-throwing: a mid-iteration + // failure (file removed, permission flip) breaks out with a best-effort partial list + // rather than propagating std::filesystem_error across REAPER's C ABI. std::vector present; std::unordered_map sizeByRel; - for (const auto& entry : fs::directory_iterator(bankDir, ec)) { - if (ec) break; - std::error_code fec; - if (!entry.is_regular_file(fec)) continue; // skip subdirs / specials + fs::directory_iterator it(bankDir, ec); + for (; !ec && it != fs::directory_iterator{}; it.increment(ec)) { + const auto& entry = *it; + std::error_code reg_ec; + if (!entry.is_regular_file(reg_ec)) continue; // skip subdirs / specials const std::string name = entry.path().filename().string(); const std::string rel = bankRelativeForName(name); if (rel.empty()) continue; present.push_back(rel); - const std::uintmax_t sz = entry.file_size(fec); - sizeByRel[rel] = fec ? 0 : static_cast(sz); + std::error_code sz_ec; + const std::uintmax_t sz = entry.file_size(sz_ec); + sizeByRel[rel] = sz_ec ? 0 : static_cast(sz); } // The decision lives in the pure core — read-only inputs from the session's book and