Skip to content

Commit 6edff50

Browse files
committed
Optimise client-side filtering directory mask evaluation
This PR updates isDirNameExcluded() to reduce redundant directory mask checks during client-side filtering. The change avoids duplicate evaluation of equivalent path candidates and skips unnecessary processing when no directory mask is configured, improving filtering efficiency without changing sync behaviour.
1 parent 9f5fb83 commit 6edff50

1 file changed

Lines changed: 35 additions & 3 deletions

File tree

src/clientSideFiltering.d

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class ClientSideFiltering {
2424
string[] syncListAnywherePathOnly; // These are 'include' rules that do not start with a '/', thus are to be searched anywhere for inclusion
2525
Regex!char fileMask;
2626
Regex!char directoryMask;
27+
bool hasDirectoryMaskEntries = false;
2728
bool skipDirStrictMatch = false;
2829
bool skipDotfiles = false;
2930

@@ -220,6 +221,14 @@ class ClientSideFiltering {
220221

221222
// Configure the regex that will be used for 'skip_dir'
222223
void setDirMask(const(char)[] dirmask) {
224+
hasDirectoryMaskEntries = false;
225+
foreach(entry; to!string(dirmask).split("|")) {
226+
if (!strip(entry).empty) {
227+
hasDirectoryMaskEntries = true;
228+
break;
229+
}
230+
}
231+
223232
directoryMask = wild2regex(dirmask);
224233
if (debugLogging) {addLogEntry("Selective Sync Directory Mask: " ~ to!string(directoryMask), ["debug"]);}
225234
}
@@ -258,6 +267,10 @@ class ClientSideFiltering {
258267
// Returns true if the inputPath matches a skip_dir config entry (directoryMask)
259268
// Returns false if no match
260269

270+
if (!hasDirectoryMaskEntries) {
271+
return false;
272+
}
273+
261274
if (debugLogging) {
262275
addLogEntry("skip_dir evaluation for: " ~ inputPath, ["debug"]);
263276
}
@@ -297,10 +310,18 @@ class ClientSideFiltering {
297310
// Also test trailing-slash equivalence for directory roots
298311
// (treat "Documents" and "Documents/" the same, but do not create "//")
299312
string[] expanded;
300-
foreach (c; candidates) {
313+
void addExpandedCandidate(string c) {
314+
if (c.empty) return;
315+
foreach (e; expanded) {
316+
if (e == c) return;
317+
}
301318
expanded ~= c;
319+
}
320+
321+
foreach (c; candidates) {
322+
addExpandedCandidate(c);
302323
if (c.length > 1 && c[$ - 1] != '/') {
303-
expanded ~= (c ~ "/");
324+
addExpandedCandidate(c ~ "/");
304325
}
305326
}
306327
candidates = expanded;
@@ -321,11 +342,22 @@ class ClientSideFiltering {
321342
if (!skipDirStrictMatch) {
322343
if (debugLogging) addLogEntry("No Strict Matching Enforced - testing individual path segments", ["debug"]);
323344

345+
string[] segmentCandidates;
346+
void addSegmentCandidate(string c) {
347+
if (c.empty) return;
348+
foreach (e; segmentCandidates) {
349+
if (e == c) return;
350+
}
351+
segmentCandidates ~= c;
352+
}
353+
324354
foreach (c; candidates) {
325355
// buildNormalizedPath may introduce a leading '/', so we keep it as-is
326356
// and let pathSplitter do its job. We are matching segments, not full paths here.
327-
string path = buildNormalizedPath(c);
357+
addSegmentCandidate(buildNormalizedPath(c));
358+
}
328359

360+
foreach (path; segmentCandidates) {
329361
if (debugLogging) addLogEntry("skip_dir segment test path: " ~ path, ["debug"]);
330362

331363
foreach_reverse(seg; pathSplitter(path)) {

0 commit comments

Comments
 (0)