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(
211211 var vecs: [8][]const u8 = undefined; // Arbitrarily chosen size.
212212 var i: usize = 1;
213213 vecs[0] = header;
214 for (data) |buf| {
214 for (data[0 .. data.len - 1]) |buf| {
215215 if (buf.len == 0) continue;
216216 vecs[i] = buf;
217217 i += 1;
218218 if (vecs.len - i == 0) break;
219219 }
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 };
221227 return w.vtable.drain(w, vecs[0..i], new_splat);
222228}
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
224245/// Equivalent to `writeSplatHeader` but writes at most `limit` bytes.
225246pub fn writeSplatHeaderLimit(
226247 w: *Writer,
......@@ -2037,8 +2058,8 @@ test "printFloat with comptime_float" {
20372058 var buf: [20]u8 = undefined;
20382059 var w: Writer = .fixed(&buf);
20392060 try w.printFloat(@as(comptime_float, 1.0), std.fmt.Options.toNumber(.{}, .scientific, .lower));
2040 try std.testing.expectEqualStrings(w.buffered(), "1e0");
2041 try std.testing.expectFmt("1", "{}", .{1.0});
2061 try testing.expectEqualStrings(w.buffered(), "1e0");
2062 try testing.expectFmt("1", "{}", .{1.0});
20422063}
20432064
20442065fn testPrintIntCase(expected: []const u8, value: anytype, base: u8, case: std.fmt.Case, options: std.fmt.Options) !void {
......@@ -2065,12 +2086,12 @@ test printByteSize {
20652086
20662087test "bytes.hex" {
20672088 const some_bytes = "\xCA\xFE\xBA\xBE";
2068 try std.testing.expectFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes});
2069 try std.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]});
2071 try std.testing.expectFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]});
2089 try testing.expectFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes});
2090 try testing.expectFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes});
2091 try testing.expectFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]});
2092 try testing.expectFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]});
20722093 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});
20742095}
20752096
20762097test fixed {
......@@ -2479,7 +2500,7 @@ pub const Allocating = struct {
24792500 }
24802501
24812502 test Allocating {
2482 var a: Allocating = .init(std.testing.allocator);
2503 var a: Allocating = .init(testing.allocator);
24832504 defer a.deinit();
24842505 const w = &a.writer;
24852506