fix(pS-usage): close delete-ward residuals — remint on corrupt, named abort keys, truncation protect-all, abort->protect-all set
This commit is contained in:
+27
-8
@@ -69,8 +69,9 @@ using FxParmGetter =
|
||||
|
||||
// True if any FX in the (possibly container-nested) sub-chain rooted at `fxId` is a
|
||||
// ReaSampler 9000. BOTH fx_ident and original_name are checked on BOTH chain kinds (a
|
||||
// renamed instance keeps its original_name; fx_ident carries the module path — the
|
||||
// review's take-path gap is closed by sharing this one walk). Containers are walked via
|
||||
// renamed instance may keep its original_name; fx_ident carries the module path — the
|
||||
// primary identification net is the module filename base via fx_ident, which holds even
|
||||
// after a user renames the FX instance). Containers are walked via
|
||||
// the documented container_count / container_item.X addressing (v7.06+); on a chain
|
||||
// kind or REAPER version without containers the parm read returns empty and recursion
|
||||
// is a no-op. `depth` bounds pathological nesting. fx_ident is queried per FX — chain
|
||||
@@ -83,9 +84,17 @@ bool fxSubtreeHasInstance(const FxParmGetter& parm, int fxId,
|
||||
identityMatches(parm(fxId, "original_name"), id.uidHexUpper, id.nameUpper,
|
||||
id.outputNameUpper))
|
||||
return true;
|
||||
if (depth <= 0) return false;
|
||||
const std::string countStr = parm(fxId, "container_count");
|
||||
if (countStr.empty()) return false; // not a container
|
||||
if (countStr.empty()) return false; // not a container; no children to miss
|
||||
if (depth <= 0) {
|
||||
// This node IS a container but we have exhausted our descent budget. We cannot
|
||||
// prove that none of its children is a ReaSampler 9000 instance — treat the
|
||||
// incomplete walk as a positive identification (the protect direction). This is
|
||||
// defense-in-depth: kMaxContainerDepth = 32 should prevent reaching this branch
|
||||
// in any real project, but if it IS reached the fail-safe fires rather than
|
||||
// silently missing a live nested instance.
|
||||
return true;
|
||||
}
|
||||
const int n = std::atoi(countStr.c_str());
|
||||
for (int k = 0; k < n; ++k) {
|
||||
const std::string item =
|
||||
@@ -98,7 +107,10 @@ bool fxSubtreeHasInstance(const FxParmGetter& parm, int fxId,
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr int kMaxContainerDepth = 8;
|
||||
// Raised from 8 to 32 (defense in depth against truncation). Real-world FX containers
|
||||
// are typically 2–4 levels deep; 32 is unreachable in practice while remaining finite.
|
||||
// Even at 32, the truncation→protect-all guard below is the primary protection.
|
||||
constexpr int kMaxContainerDepth = 32;
|
||||
|
||||
std::string trackFxParm(MediaTrack* tr, int fxId, const char* parm) {
|
||||
char buf[2048] = {0};
|
||||
@@ -183,6 +195,7 @@ UsageScanResult liveInstanceHeldPaths(void* projOpaque) {
|
||||
// undecodable -> the pure fold ABORTS the prune.
|
||||
std::vector<std::string> usageKeys;
|
||||
{
|
||||
const std::string prefix = kProjExtUsageKeyPrefix; // hoisted: one alloc, not N
|
||||
char keyBuf[256];
|
||||
for (int idx = 0;; ++idx) {
|
||||
keyBuf[0] = '\0';
|
||||
@@ -190,7 +203,6 @@ UsageScanResult liveInstanceHeldPaths(void* projOpaque) {
|
||||
static_cast<int>(sizeof(keyBuf)), nullptr, 0))
|
||||
break;
|
||||
const std::string key(keyBuf);
|
||||
const std::string prefix = kProjExtUsageKeyPrefix;
|
||||
if (key.compare(0, prefix.size(), prefix) == 0) usageKeys.push_back(key);
|
||||
}
|
||||
}
|
||||
@@ -198,13 +210,17 @@ UsageScanResult liveInstanceHeldPaths(void* projOpaque) {
|
||||
|
||||
std::vector<std::optional<UsageRecord>> decoded;
|
||||
decoded.reserve(usageKeys.size());
|
||||
for (const std::string& key : usageKeys) {
|
||||
for (std::size_t ki = 0; ki < usageKeys.size(); ++ki) {
|
||||
const std::string& key = usageKeys[ki];
|
||||
const std::optional<std::string> value = readExtStateValue(proj, key.c_str());
|
||||
if (!value) {
|
||||
decoded.push_back(std::nullopt); // unreadable -> abort (pure fold)
|
||||
result.offendingKeys.push_back(key);
|
||||
continue;
|
||||
}
|
||||
decoded.push_back(decodeUsageRecord(*value)); // undecodable -> nullopt -> abort
|
||||
const std::optional<UsageRecord> rec = decodeUsageRecord(*value);
|
||||
if (!rec) result.offendingKeys.push_back(key);
|
||||
decoded.push_back(rec); // undecodable nullopt -> abort
|
||||
}
|
||||
|
||||
// 2. Enumerate live ReaSampler 9000 hosts. One channel-frozen needle set drives
|
||||
@@ -250,6 +266,9 @@ UsageScanResult liveInstanceHeldPaths(void* projOpaque) {
|
||||
const UsageFoldResult fold = foldUsageRecords(decoded, liveTrackGuids, anyLive);
|
||||
result.abortPrune = fold.abortPrune;
|
||||
result.heldPaths = fold.heldPaths;
|
||||
// offendingKeys already populated above (unreadable + undecodable entries);
|
||||
// clear it on success so callers see it only when abortPrune is set.
|
||||
if (!result.abortPrune) result.offendingKeys.clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user