Extract common Match struct and ApplyMatches logic into StringBuilderChunkMatcher
Reduces duplication between GuidScrubber.cs and DirectoryReplacements_StringBuilder.cs by extracting the common Match struct and match application logic.
Changes
-
New
StringBuilderChunkMatcher.cs: Contains sharedMatchstruct andApplyMatchesmethod -
Updated
GuidScrubber.cs: Uses common Match struct via type alias, delegates toApplyMatches -
Updated
DirectoryReplacements_StringBuilder.cs: Same refactoring
Usage
using Match = StringBuilderChunkMatcher.Match;
// Collect matches
matches.Add(new Match(startPosition, length, replacement));
// Apply all matches (sorted by descending index)
StringBuilderChunkMatcher.ApplyMatches(builder, matches);
Not Changed
DateScrubber.cs uses inline replacement during scanning rather than collecting matches, so it doesn't benefit from this refactor. The carryover buffer management was left in place as abstracting it would add complexity without meaningful benefit given the Span
Original prompt
Refactor common code across GuidScrubber.cs, DateScrubber.cs, and DirectoryReplacements_StringBuilder.cs
These three files have significant duplication around:
- Cross-chunk matching logic for StringBuilder chunks
- Carryover buffer management (tracking last N chars between chunks)
- Match collection and application (sorting by descending position and applying replacements)
- Similar Match struct definitions
Please create a new helper class called
StringBuilderChunkMatcher.csin the same directory (src/Verify/Serialization/Scrubbers/) that contains:
- A common
Matchstruct that can be used by all scrubbers:internal readonly struct Match(int index, int length, string value) { public readonly int Index = index; public readonly int Length = length; public readonly string Value = value; }
- A method
ApplyMatchesthat handles the common logic of sorting and applying matches:public static void ApplyMatches(StringBuilder builder, List<Match> matches) { var orderByDescending = matches.OrderByDescending(_ => _.Index); foreach (var match in orderByDescending) { builder.Overwrite(match.Value, match.Index, match.Length); } }
- Helper methods for cross-chunk buffer management that can be reused:
InitializeCarryoverBuffer- initialize the carryover buffer and related tracking variablesUpdateCarryoverBuffer- update the carryover buffer at the end of each chunk iterationProcessCarryover- common pattern for checking matches that span chunksThen update GuidScrubber.cs, DateScrubber.cs, and DirectoryReplacements_StringBuilder.cs to use these common helpers instead of duplicating the logic.
The goal is to reduce duplication while maintaining the same functionality and performance characteristics (using stackalloc buffers, etc.).
This pull request was created as a result of the following prompt from Copilot chat.
Refactor common code across GuidScrubber.cs, DateScrubber.cs, and DirectoryReplacements_StringBuilder.cs
These three files have significant duplication around:
- Cross-chunk matching logic for StringBuilder chunks
- Carryover buffer management (tracking last N chars between chunks)
- Match collection and application (sorting by descending position and applying replacements)
- Similar Match struct definitions
Please create a new helper class called
StringBuilderChunkMatcher.csin the same directory (src/Verify/Serialization/Scrubbers/) that contains:
- A common
Matchstruct that can be used by all scrubbers:internal readonly struct Match(int index, int length, string value) { public readonly int Index = index; public readonly int Length = length; public readonly string Value = value; }
- A method
ApplyMatchesthat handles the common logic of sorting and applying matches:public static void ApplyMatches(StringBuilder builder, List<Match> matches) { var orderByDescending = matches.OrderByDescending(_ => _.Index); foreach (var match in orderByDescending) { builder.Overwrite(match.Value, match.Index, match.Length); } }
- Helper methods for cross-chunk buffer management that can be reused:
InitializeCarryoverBuffer- initialize the carryover buffer and related tracking variablesUpdateCarryoverBuffer- update the carryover buffer at the end of each chunk iterationProcessCarryover- common pattern for checking matches that span chunksThen update GuidScrubber.cs, DateScrubber.cs, and DirectoryReplacements_StringBuilder.cs to use these common helpers instead of duplicating the logic.
The goal is to reduce duplication while maintaining the same functionality and performance characteristics (using stackalloc buffers, etc.).
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.