Skip to content

Commit 39cd230

Browse files
committed
Optimize file splatting on Windows
1 parent 0a1a738 commit 39cd230

File tree

2 files changed

+81
-47
lines changed

2 files changed

+81
-47
lines changed

lib/std/fs/File.zig

Lines changed: 44 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,63 +1604,60 @@ pub const Writer = struct {
16041604
const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
16051605
const handle = w.file.handle;
16061606
const buffered = io_w.buffered();
1607-
if (is_windows) switch (w.mode) {
1608-
.positional, .positional_reading => {
1609-
if (buffered.len != 0) {
1610-
const n = windows.WriteFile(handle, buffered, w.pos) catch |err| {
1611-
w.err = err;
1612-
return error.WriteFailed;
1613-
};
1614-
w.pos += n;
1615-
return io_w.consume(n);
1616-
}
1617-
for (data[0 .. data.len - 1]) |buf| {
1618-
if (buf.len == 0) continue;
1619-
const n = windows.WriteFile(handle, buf, w.pos) catch |err| {
1620-
w.err = err;
1621-
return error.WriteFailed;
1622-
};
1623-
w.pos += n;
1624-
return io_w.consume(n);
1625-
}
1626-
const pattern = data[data.len - 1];
1627-
if (pattern.len == 0 or splat == 0) return 0;
1628-
const n = windows.WriteFile(handle, pattern, w.pos) catch |err| {
1607+
if (is_windows) {
1608+
const pos: ?u64 = switch (w.mode) {
1609+
.positional, .positional_reading => w.pos,
1610+
.streaming, .streaming_reading => null,
1611+
.failure => return error.WriteFailed,
1612+
};
1613+
if (buffered.len != 0) {
1614+
const n = windows.WriteFile(handle, buffered, pos) catch |err| {
16291615
w.err = err;
16301616
return error.WriteFailed;
16311617
};
16321618
w.pos += n;
16331619
return io_w.consume(n);
1634-
},
1635-
.streaming, .streaming_reading => {
1636-
if (buffered.len != 0) {
1637-
const n = windows.WriteFile(handle, buffered, null) catch |err| {
1638-
w.err = err;
1639-
return error.WriteFailed;
1640-
};
1641-
w.pos += n;
1642-
return io_w.consume(n);
1643-
}
1644-
for (data[0 .. data.len - 1]) |buf| {
1645-
if (buf.len == 0) continue;
1646-
const n = windows.WriteFile(handle, buf, null) catch |err| {
1647-
w.err = err;
1648-
return error.WriteFailed;
1649-
};
1650-
w.pos += n;
1651-
return io_w.consume(n);
1652-
}
1653-
const pattern = data[data.len - 1];
1654-
if (pattern.len == 0 or splat == 0) return 0;
1655-
const n = windows.WriteFile(handle, pattern, null) catch |err| {
1620+
}
1621+
for (data[0 .. data.len - 1]) |buf| {
1622+
if (buf.len == 0) continue;
1623+
const n = windows.WriteFile(handle, buf, pos) catch |err| {
16561624
w.err = err;
16571625
return error.WriteFailed;
16581626
};
16591627
w.pos += n;
16601628
return io_w.consume(n);
1661-
},
1662-
.failure => return error.WriteFailed,
1663-
};
1629+
}
1630+
const pattern = data[data.len - 1];
1631+
if (pattern.len == 0 or splat == 0) return 0;
1632+
const splat_buffer: []const u8 = if (splat > 1) buf: {
1633+
const splat_buffer_candidate = io_w.buffer[io_w.end..];
1634+
var backup_buffer: [64]u8 = undefined;
1635+
const splat_buffer = if (splat_buffer_candidate.len >= backup_buffer.len)
1636+
splat_buffer_candidate
1637+
else
1638+
&backup_buffer;
1639+
if (pattern.len > splat_buffer.len / 2) {
1640+
break :buf pattern;
1641+
}
1642+
if (pattern.len == 1) {
1643+
const memset_len = @min(splat_buffer.len, splat);
1644+
const buf = splat_buffer[0..memset_len];
1645+
@memset(buf, pattern[0]);
1646+
break :buf buf;
1647+
}
1648+
const repeat_count = @min(splat_buffer.len / pattern.len, splat);
1649+
for (0..repeat_count) |i| {
1650+
@memcpy(splat_buffer[i * pattern.len ..][0..pattern.len], pattern);
1651+
}
1652+
break :buf splat_buffer[0 .. pattern.len * repeat_count];
1653+
} else pattern;
1654+
const n = windows.WriteFile(handle, splat_buffer, pos) catch |err| {
1655+
w.err = err;
1656+
return error.WriteFailed;
1657+
};
1658+
w.pos += n;
1659+
return io_w.consume(n);
1660+
}
16641661
var iovecs: [max_buffers_len]std.posix.iovec_const = undefined;
16651662
var len: usize = 0;
16661663
if (buffered.len > 0) {

lib/std/fs/test.zig

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2129,3 +2129,40 @@ test "seek keeping partial buffer" {
21292129

21302130
try testing.expectEqualStrings("6789", &buf);
21312131
}
2132+
2133+
test "splat larger than buffer" {
2134+
var tmp_dir = testing.tmpDir(.{});
2135+
defer tmp_dir.cleanup();
2136+
2137+
const file = try tmp_dir.dir.createFile("test_file", .{ .read = true });
2138+
defer file.close();
2139+
var fixed_buf: [100]u8 = undefined;
2140+
var write_buf: [74]u8 = undefined;
2141+
var read_buf: [100]u8 = undefined;
2142+
{
2143+
const data = "0123456789";
2144+
const splat = 10;
2145+
var file_writer: std.fs.File.Writer = .init(file, &write_buf);
2146+
const n = try file_writer.interface.writeSplat(&.{data}, splat);
2147+
try testing.expectEqual(write_buf.len - write_buf.len % data.len, n);
2148+
2149+
var w: std.Io.Writer = .fixed(&fixed_buf);
2150+
_ = try w.writeSplat(&.{data}, splat);
2151+
var file_reader = file.reader(&read_buf);
2152+
try file_reader.interface.fill(n);
2153+
try testing.expectEqualSlices(u8, w.buffered()[0..n], file_reader.interface.buffered());
2154+
}
2155+
{
2156+
const data = "c";
2157+
const splat = 100;
2158+
var file_writer: std.fs.File.Writer = .init(file, &write_buf);
2159+
const n = try file_writer.interface.writeSplat(&.{data}, splat);
2160+
try testing.expectEqual(write_buf.len - write_buf.len % data.len, n);
2161+
2162+
var w: std.Io.Writer = .fixed(&fixed_buf);
2163+
_ = try w.writeSplat(&.{data}, splat);
2164+
var file_reader = file.reader(&read_buf);
2165+
try file_reader.interface.fill(n);
2166+
try testing.expectEqualSlices(u8, w.buffered()[0..n], file_reader.interface.buffered());
2167+
}
2168+
}

0 commit comments

Comments
 (0)