diff options
| author | jumpnbrownweasel <49791153+jumpnbrownweasel@users.noreply.github.com> | 2021-04-25 16:16:24 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-04-25 19:16:24 -0400 |
| commit | bf67a3fdc9efea7126058b21e687c24868bc1268 (patch) | |
| tree | b1f5f2d6be676d0435024d464b3af7facdd617c3 /lib/std | |
| parent | c420eb60ad17599f035594ba877df66b7705fb25 (diff) | |
| download | zig-bf67a3fdc9efea7126058b21e687c24868bc1268.tar.gz zig-bf67a3fdc9efea7126058b21e687c24868bc1268.zip | |
#8454 Fix for std.mem.replacementSize adjacent matches bug. (#8455)
* #8454 Fix for std.mem.replacementSize adjacent matches bug.
When two 'needle' values are adjacent in the 'input' slice, the size is not
counted correctly. The 2nd 'needle' value is not matched because the index is
incremented by one after changing the index to account for the first value.
The impact is the the size returned is incorrect, and could cause UB when this
amount is used to size of the buffer passed to std.mem.replace.
* Apply changes from PR review:
- Add assert checking that the needle is non-empty and doc for this.
- Add minimal test that an empty input works.
- Use testing.expectEqualStrings.
Diffstat (limited to 'lib/std')
| -rw-r--r-- | lib/std/mem.zig | 44 |
1 files changed, 40 insertions, 4 deletions
diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 66505f5d29..274da3b8f1 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -1876,7 +1876,11 @@ test "rotate" { /// Replace needle with replacement as many times as possible, writing to an output buffer which is assumed to be of /// appropriate size. Use replacementSize to calculate an appropriate buffer size. +/// The needle must not be empty. pub fn replace(comptime T: type, input: []const T, needle: []const T, replacement: []const T, output: []T) usize { + // Empty needle will loop until output buffer overflows. + assert(needle.len > 0); + var i: usize = 0; var slide: usize = 0; var replacements: usize = 0; @@ -1899,22 +1903,48 @@ pub fn replace(comptime T: type, input: []const T, needle: []const T, replacemen test "replace" { var output: [29]u8 = undefined; var replacements = replace(u8, "All your base are belong to us", "base", "Zig", output[0..]); + var expected: []const u8 = "All your Zig are belong to us"; testing.expect(replacements == 1); - testing.expect(eql(u8, output[0..], "All your Zig are belong to us")); + testing.expectEqualStrings(expected, output[0..expected.len]); replacements = replace(u8, "Favor reading code over writing code.", "code", "", output[0..]); + expected = "Favor reading over writing ."; testing.expect(replacements == 2); - testing.expect(eql(u8, output[0..], "Favor reading over writing .")); + testing.expectEqualStrings(expected, output[0..expected.len]); + + // Empty needle is not allowed but input may be empty. + replacements = replace(u8, "", "x", "y", output[0..0]); + expected = ""; + testing.expect(replacements == 0); + testing.expectEqualStrings(expected, output[0..expected.len]); + + // Adjacent replacements. + + replacements = replace(u8, "\\n\\n", "\\n", "\n", output[0..]); + expected = "\n\n"; + testing.expect(replacements == 2); + testing.expectEqualStrings(expected, output[0..expected.len]); + + replacements = replace(u8, "abbba", "b", "cd", output[0..]); + expected = "acdcdcda"; + testing.expect(replacements == 3); + testing.expectEqualStrings(expected, output[0..expected.len]); } /// Calculate the size needed in an output buffer to perform a replacement. +/// The needle must not be empty. pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, replacement: []const T) usize { + // Empty needle will loop forever. + assert(needle.len > 0); + var i: usize = 0; var size: usize = input.len; - while (i < input.len) : (i += 1) { + while (i < input.len) { if (mem.indexOf(T, input[i..], needle) == @as(usize, 0)) { size = size - needle.len + replacement.len; i += needle.len; + } else { + i += 1; } } @@ -1923,9 +1953,15 @@ pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, re test "replacementSize" { testing.expect(replacementSize(u8, "All your base are belong to us", "base", "Zig") == 29); - testing.expect(replacementSize(u8, "", "", "") == 0); testing.expect(replacementSize(u8, "Favor reading code over writing code.", "code", "") == 29); testing.expect(replacementSize(u8, "Only one obvious way to do things.", "things.", "things in Zig.") == 41); + + // Empty needle is not allowed but input may be empty. + testing.expect(replacementSize(u8, "", "x", "y") == 0); + + // Adjacent replacements. + testing.expect(replacementSize(u8, "\\n\\n", "\\n", "\n") == 2); + testing.expect(replacementSize(u8, "abbba", "b", "cd") == 8); } /// Perform a replacement on an allocated buffer of pre-determined size. Caller must free returned memory. |
