authorgravatar for 49791153+jumpnbrownweasel@users.noreply.github.comjumpnbrownweasel <49791153+jumpnbrownweasel@users.noreply.github.com> 2021-04-25 16:16:24-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-04-25 19:16:24-04:00
logbf67a3fdc9efea7126058b21e687c24868bc1268
treeb1f5f2d6be676d0435024d464b3af7facdd617c3
parentc420eb60ad17599f035594ba877df66b7705fb25
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

#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.

1 files changed, 40 insertions(+), 4 deletions(-)

lib/std/mem.zig+40-4
......@@ -1876,7 +1876,11 @@ test "rotate" {
18761876
18771877/// Replace needle with replacement as many times as possible, writing to an output buffer which is assumed to be of
18781878/// appropriate size. Use replacementSize to calculate an appropriate buffer size.
1879/// The needle must not be empty.
18791880pub fn replace(comptime T: type, input: []const T, needle: []const T, replacement: []const T, output: []T) usize {
1881 // Empty needle will loop until output buffer overflows.
1882 assert(needle.len > 0);
1883
18801884 var i: usize = 0;
18811885 var slide: usize = 0;
18821886 var replacements: usize = 0;
......@@ -1899,22 +1903,48 @@ pub fn replace(comptime T: type, input: []const T, needle: []const T, replacemen
18991903test "replace" {
19001904 var output: [29]u8 = undefined;
19011905 var replacements = replace(u8, "All your base are belong to us", "base", "Zig", output[0..]);
1906 var expected: []const u8 = "All your Zig are belong to us";
19021907 testing.expect(replacements == 1);
1903 testing.expect(eql(u8, output[0..], "All your Zig are belong to us"));
1908 testing.expectEqualStrings(expected, output[0..expected.len]);
19041909
19051910 replacements = replace(u8, "Favor reading code over writing code.", "code", "", output[0..]);
1911 expected = "Favor reading over writing .";
19061912 testing.expect(replacements == 2);
1907 testing.expect(eql(u8, output[0..], "Favor reading over writing ."));
1913 testing.expectEqualStrings(expected, output[0..expected.len]);
1914
1915 // Empty needle is not allowed but input may be empty.
1916 replacements = replace(u8, "", "x", "y", output[0..0]);
1917 expected = "";
1918 testing.expect(replacements == 0);
1919 testing.expectEqualStrings(expected, output[0..expected.len]);
1920
1921 // Adjacent replacements.
1922
1923 replacements = replace(u8, "\\n\\n", "\\n", "\n", output[0..]);
1924 expected = "\n\n";
1925 testing.expect(replacements == 2);
1926 testing.expectEqualStrings(expected, output[0..expected.len]);
1927
1928 replacements = replace(u8, "abbba", "b", "cd", output[0..]);
1929 expected = "acdcdcda";
1930 testing.expect(replacements == 3);
1931 testing.expectEqualStrings(expected, output[0..expected.len]);
19081932}
19091933
19101934/// Calculate the size needed in an output buffer to perform a replacement.
1935/// The needle must not be empty.
19111936pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, replacement: []const T) usize {
1937 // Empty needle will loop forever.
1938 assert(needle.len > 0);
1939
19121940 var i: usize = 0;
19131941 var size: usize = input.len;
1914 while (i < input.len) : (i += 1) {
1942 while (i < input.len) {
19151943 if (mem.indexOf(T, input[i..], needle) == @as(usize, 0)) {
19161944 size = size - needle.len + replacement.len;
19171945 i += needle.len;
1946 } else {
1947 i += 1;
19181948 }
19191949 }
19201950
......@@ -1923,9 +1953,15 @@ pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, re
19231953
19241954test "replacementSize" {
19251955 testing.expect(replacementSize(u8, "All your base are belong to us", "base", "Zig") == 29);
1926 testing.expect(replacementSize(u8, "", "", "") == 0);
19271956 testing.expect(replacementSize(u8, "Favor reading code over writing code.", "code", "") == 29);
19281957 testing.expect(replacementSize(u8, "Only one obvious way to do things.", "things.", "things in Zig.") == 41);
1958
1959 // Empty needle is not allowed but input may be empty.
1960 testing.expect(replacementSize(u8, "", "x", "y") == 0);
1961
1962 // Adjacent replacements.
1963 testing.expect(replacementSize(u8, "\\n\\n", "\\n", "\n") == 2);
1964 testing.expect(replacementSize(u8, "abbba", "b", "cd") == 8);
19291965}
19301966
19311967/// Perform a replacement on an allocated buffer of pre-determined size. Caller must free returned memory.