authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-09 18:28:46-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-09 18:29:10-07:00
log9e52febeebe2a5554e67a961476e19aa01268cb4
treeaa01d647b88ffbbf7f7635a56e5a6e1689170d32
parent09cdcf67e1fb6cc05b1b35710c1652682541c915

std.io.Writer.writeSplatHeader: fix splat alias bug

pointer comparison cannot be used since the buffers may alias

1 files changed, 31 insertions(+), 10 deletions(-)

lib/std/io/Writer.zig+31-10
...@@ -211,16 +211,37 @@ pub fn writeSplatHeader(...@@ -211,16 +211,37 @@ pub fn writeSplatHeader(
211 var vecs: [8][]const u8 = undefined; // Arbitrarily chosen size.211 var vecs: [8][]const u8 = undefined; // Arbitrarily chosen size.
212 var i: usize = 1;212 var i: usize = 1;
213 vecs[0] = header;213 vecs[0] = header;
214 for (data) |buf| {214 for (data[0 .. data.len - 1]) |buf| {
215 if (buf.len == 0) continue;215 if (buf.len == 0) continue;
216 vecs[i] = buf;216 vecs[i] = buf;
217 i += 1;217 i += 1;
218 if (vecs.len - i == 0) break;218 if (vecs.len - i == 0) break;
219 }219 }
220 const new_splat = if (vecs[i - 1].ptr == data[data.len - 1].ptr) splat else 1;220 const pattern = data[data.len - 1];
221 const new_splat = s: {
222 if (pattern.len == 0 or vecs.len - i == 0) break :s 1;
223 vecs[i] = pattern;
224 i += 1;
225 break :s splat;
226 };
221 return w.vtable.drain(w, vecs[0..i], new_splat);227 return w.vtable.drain(w, vecs[0..i], new_splat);
222}228}
223229
230test "writeSplatHeader splatting avoids buffer aliasing temptation" {
231 const initial_buf = try testing.allocator.alloc(u8, 8);
232 var aw: std.io.Writer.Allocating = .initOwnedSlice(testing.allocator, initial_buf);
233 defer aw.deinit();
234 // This test assumes 8 vector buffer in this function.
235 const n = try aw.writer.writeSplatHeader("header which is longer than buf ", &.{
236 "1", "2", "3", "4", "5", "6", "foo", "bar", "foo",
237 }, 3);
238 try testing.expectEqual(41, n);
239 try testing.expectEqualStrings(
240 "header which is longer than buf 123456foo",
241 aw.writer.buffered(),
242 );
243}
244
224/// Equivalent to `writeSplatHeader` but writes at most `limit` bytes.245/// Equivalent to `writeSplatHeader` but writes at most `limit` bytes.
225pub fn writeSplatHeaderLimit(246pub fn writeSplatHeaderLimit(
226 w: *Writer,247 w: *Writer,
...@@ -2037,8 +2058,8 @@ test "printFloat with comptime_float" {...@@ -2037,8 +2058,8 @@ test "printFloat with comptime_float" {
2037 var buf: [20]u8 = undefined;2058 var buf: [20]u8 = undefined;
2038 var w: Writer = .fixed(&buf);2059 var w: Writer = .fixed(&buf);
2039 try w.printFloat(@as(comptime_float, 1.0), std.fmt.Options.toNumber(.{}, .scientific, .lower));2060 try w.printFloat(@as(comptime_float, 1.0), std.fmt.Options.toNumber(.{}, .scientific, .lower));
2040 try std.testing.expectEqualStrings(w.buffered(), "1e0");2061 try testing.expectEqualStrings(w.buffered(), "1e0");
2041 try std.testing.expectFmt("1", "{}", .{1.0});2062 try testing.expectFmt("1", "{}", .{1.0});
2042}2063}
20432064
2044fn testPrintIntCase(expected: []const u8, value: anytype, base: u8, case: std.fmt.Case, options: std.fmt.Options) !void {2065fn testPrintIntCase(expected: []const u8, value: anytype, base: u8, case: std.fmt.Case, options: std.fmt.Options) !void {
...@@ -2065,12 +2086,12 @@ test printByteSize {...@@ -2065,12 +2086,12 @@ test printByteSize {
20652086
2066test "bytes.hex" {2087test "bytes.hex" {
2067 const some_bytes = "\xCA\xFE\xBA\xBE";2088 const some_bytes = "\xCA\xFE\xBA\xBE";
2068 try std.testing.expectFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes});2089 try testing.expectFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes});
2069 try std.testing.expectFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes});2090 try testing.expectFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes});
2070 try std.testing.expectFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]});2091 try testing.expectFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]});
2071 try std.testing.expectFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]});2092 try testing.expectFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]});
2072 const bytes_with_zeros = "\x00\x0E\xBA\xBE";2093 const bytes_with_zeros = "\x00\x0E\xBA\xBE";
2073 try std.testing.expectFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros});2094 try testing.expectFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros});
2074}2095}
20752096
2076test fixed {2097test fixed {
...@@ -2479,7 +2500,7 @@ pub const Allocating = struct {...@@ -2479,7 +2500,7 @@ pub const Allocating = struct {
2479 }2500 }
24802501
2481 test Allocating {2502 test Allocating {
2482 var a: Allocating = .init(std.testing.allocator);2503 var a: Allocating = .init(testing.allocator);
2483 defer a.deinit();2504 defer a.deinit();
2484 const w = &a.writer;2505 const w = &a.writer;
24852506