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" {...@@ -1876,7 +1876,11 @@ test "rotate" {
18761876
1877/// Replace needle with replacement as many times as possible, writing to an output buffer which is assumed to be of1877/// Replace needle with replacement as many times as possible, writing to an output buffer which is assumed to be of
1878/// appropriate size. Use replacementSize to calculate an appropriate buffer size.1878/// appropriate size. Use replacementSize to calculate an appropriate buffer size.
1879/// The needle must not be empty.
1879pub fn replace(comptime T: type, input: []const T, needle: []const T, replacement: []const T, output: []T) usize {1880pub 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
1880 var i: usize = 0;1884 var i: usize = 0;
1881 var slide: usize = 0;1885 var slide: usize = 0;
1882 var replacements: usize = 0;1886 var replacements: usize = 0;
...@@ -1899,22 +1903,48 @@ pub fn replace(comptime T: type, input: []const T, needle: []const T, replacemen...@@ -1899,22 +1903,48 @@ pub fn replace(comptime T: type, input: []const T, needle: []const T, replacemen
1899test "replace" {1903test "replace" {
1900 var output: [29]u8 = undefined;1904 var output: [29]u8 = undefined;
1901 var replacements = replace(u8, "All your base are belong to us", "base", "Zig", output[0..]);1905 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";
1902 testing.expect(replacements == 1);1907 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
1905 replacements = replace(u8, "Favor reading code over writing code.", "code", "", output[0..]);1910 replacements = replace(u8, "Favor reading code over writing code.", "code", "", output[0..]);
1911 expected = "Favor reading over writing .";
1906 testing.expect(replacements == 2);1912 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]);
1908}1932}
19091933
1910/// Calculate the size needed in an output buffer to perform a replacement.1934/// Calculate the size needed in an output buffer to perform a replacement.
1935/// The needle must not be empty.
1911pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, replacement: []const T) usize {1936pub 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
1912 var i: usize = 0;1940 var i: usize = 0;
1913 var size: usize = input.len;1941 var size: usize = input.len;
1914 while (i < input.len) : (i += 1) {1942 while (i < input.len) {
1915 if (mem.indexOf(T, input[i..], needle) == @as(usize, 0)) {1943 if (mem.indexOf(T, input[i..], needle) == @as(usize, 0)) {
1916 size = size - needle.len + replacement.len;1944 size = size - needle.len + replacement.len;
1917 i += needle.len;1945 i += needle.len;
1946 } else {
1947 i += 1;
1918 }1948 }
1919 }1949 }
19201950
...@@ -1923,9 +1953,15 @@ pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, re...@@ -1923,9 +1953,15 @@ pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, re
19231953
1924test "replacementSize" {1954test "replacementSize" {
1925 testing.expect(replacementSize(u8, "All your base are belong to us", "base", "Zig") == 29);1955 testing.expect(replacementSize(u8, "All your base are belong to us", "base", "Zig") == 29);
1926 testing.expect(replacementSize(u8, "", "", "") == 0);
1927 testing.expect(replacementSize(u8, "Favor reading code over writing code.", "code", "") == 29);1956 testing.expect(replacementSize(u8, "Favor reading code over writing code.", "code", "") == 29);
1928 testing.expect(replacementSize(u8, "Only one obvious way to do things.", "things.", "things in Zig.") == 41);1957 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);
1929}1965}
19301966
1931/// Perform a replacement on an allocated buffer of pre-determined size. Caller must free returned memory.1967/// Perform a replacement on an allocated buffer of pre-determined size. Caller must free returned memory.