mirror of
https://github.com/ziglang/zig.git
synced 2026-01-21 06:45:24 +00:00
std.debug: optimized printLineFromFileAnyOs (#18142)
* std.debug: optimized printLineFromFileAnyOs Uses mem.indexOfScalar to speed line iteration instead of byte for byte. Also prints the whole line in a single write (or up to a page size at a time) Closes #18099 * add test cases for printLineFromFileAnyOs
This commit is contained in:
parent
2a32264533
commit
22d7c7d295
@ -1395,31 +1395,169 @@ fn printLineFromFileAnyOs(out_stream: anytype, line_info: LineInfo) !void {
|
||||
// TODO fstat and make sure that the file has the correct size
|
||||
|
||||
var buf: [mem.page_size]u8 = undefined;
|
||||
var line: usize = 1;
|
||||
var column: usize = 1;
|
||||
while (true) {
|
||||
const amt_read = try f.read(buf[0..]);
|
||||
const slice = buf[0..amt_read];
|
||||
|
||||
for (slice) |byte| {
|
||||
if (line == line_info.line) {
|
||||
switch (byte) {
|
||||
'\t' => try out_stream.writeByte(' '),
|
||||
else => try out_stream.writeByte(byte),
|
||||
}
|
||||
if (byte == '\n') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (byte == '\n') {
|
||||
line += 1;
|
||||
column = 1;
|
||||
var amt_read = try f.read(buf[0..]);
|
||||
const line_start = seek: {
|
||||
var current_line_start: usize = 0;
|
||||
var next_line: usize = 1;
|
||||
while (next_line != line_info.line) {
|
||||
const slice = buf[current_line_start..amt_read];
|
||||
if (mem.indexOfScalar(u8, slice, '\n')) |pos| {
|
||||
next_line += 1;
|
||||
if (pos == slice.len - 1) {
|
||||
amt_read = try f.read(buf[0..]);
|
||||
current_line_start = 0;
|
||||
} else current_line_start += pos + 1;
|
||||
} else if (amt_read < buf.len) {
|
||||
return error.EndOfFile;
|
||||
} else {
|
||||
column += 1;
|
||||
amt_read = try f.read(buf[0..]);
|
||||
current_line_start = 0;
|
||||
}
|
||||
}
|
||||
break :seek current_line_start;
|
||||
};
|
||||
const slice = buf[line_start..amt_read];
|
||||
if (mem.indexOfScalar(u8, slice, '\n')) |pos| {
|
||||
const line = slice[0 .. pos + 1];
|
||||
mem.replaceScalar(u8, line, '\t', ' ');
|
||||
return out_stream.writeAll(line);
|
||||
} else { // Line is the last inside the buffer, and requires another read to find delimiter. Alternatively the file ends.
|
||||
mem.replaceScalar(u8, slice, '\t', ' ');
|
||||
try out_stream.writeAll(slice);
|
||||
while (amt_read == buf.len) {
|
||||
amt_read = try f.read(buf[0..]);
|
||||
if (mem.indexOfScalar(u8, buf[0..amt_read], '\n')) |pos| {
|
||||
const line = buf[0 .. pos + 1];
|
||||
mem.replaceScalar(u8, line, '\t', ' ');
|
||||
return out_stream.writeAll(line);
|
||||
} else {
|
||||
const line = buf[0..amt_read];
|
||||
mem.replaceScalar(u8, line, '\t', ' ');
|
||||
try out_stream.writeAll(line);
|
||||
}
|
||||
}
|
||||
// Make sure printing last line of file inserts extra newline
|
||||
try out_stream.writeByte('\n');
|
||||
}
|
||||
}
|
||||
|
||||
if (amt_read < buf.len) return error.EndOfFile;
|
||||
test "printLineFromFileAnyOs" {
|
||||
var output = std.ArrayList(u8).init(std.testing.allocator);
|
||||
defer output.deinit();
|
||||
const output_stream = output.writer();
|
||||
|
||||
const allocator = std.testing.allocator;
|
||||
const join = std.fs.path.join;
|
||||
const expectError = std.testing.expectError;
|
||||
const expectEqualStrings = std.testing.expectEqualStrings;
|
||||
|
||||
var test_dir = std.testing.tmpDir(.{});
|
||||
defer test_dir.cleanup();
|
||||
// Relies on testing.tmpDir internals which is not ideal, but LineInfo requires paths.
|
||||
const test_dir_path = try join(allocator, &.{ "zig-cache", "tmp", test_dir.sub_path[0..] });
|
||||
defer allocator.free(test_dir_path);
|
||||
|
||||
// Cases
|
||||
{
|
||||
const path = try join(allocator, &.{ test_dir_path, "one_line.zig" });
|
||||
defer allocator.free(path);
|
||||
try test_dir.dir.writeFile("one_line.zig", "no new lines in this file, but one is printed anyway");
|
||||
|
||||
try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }));
|
||||
|
||||
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try expectEqualStrings("no new lines in this file, but one is printed anyway\n", output.items);
|
||||
output.clearRetainingCapacity();
|
||||
}
|
||||
{
|
||||
const path = try fs.path.join(allocator, &.{ test_dir_path, "three_lines.zig" });
|
||||
defer allocator.free(path);
|
||||
try test_dir.dir.writeFile("three_lines.zig",
|
||||
\\1
|
||||
\\2
|
||||
\\3
|
||||
);
|
||||
|
||||
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try expectEqualStrings("1\n", output.items);
|
||||
output.clearRetainingCapacity();
|
||||
|
||||
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 3, .column = 0 });
|
||||
try expectEqualStrings("3\n", output.items);
|
||||
output.clearRetainingCapacity();
|
||||
}
|
||||
{
|
||||
const file = try test_dir.dir.createFile("line_overlaps_page_boundary.zig", .{});
|
||||
defer file.close();
|
||||
const path = try fs.path.join(allocator, &.{ test_dir_path, "line_overlaps_page_boundary.zig" });
|
||||
defer allocator.free(path);
|
||||
|
||||
const overlap = 10;
|
||||
var writer = file.writer();
|
||||
try writer.writeByteNTimes('a', mem.page_size - overlap);
|
||||
try writer.writeByte('\n');
|
||||
try writer.writeByteNTimes('a', overlap);
|
||||
|
||||
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 });
|
||||
try expectEqualStrings(("a" ** overlap) ++ "\n", output.items);
|
||||
output.clearRetainingCapacity();
|
||||
}
|
||||
{
|
||||
const file = try test_dir.dir.createFile("file_ends_on_page_boundary.zig", .{});
|
||||
defer file.close();
|
||||
const path = try fs.path.join(allocator, &.{ test_dir_path, "file_ends_on_page_boundary.zig" });
|
||||
defer allocator.free(path);
|
||||
|
||||
var writer = file.writer();
|
||||
try writer.writeByteNTimes('a', mem.page_size);
|
||||
|
||||
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try expectEqualStrings(("a" ** mem.page_size) ++ "\n", output.items);
|
||||
output.clearRetainingCapacity();
|
||||
}
|
||||
{
|
||||
const file = try test_dir.dir.createFile("very_long_first_line_spanning_multiple_pages.zig", .{});
|
||||
defer file.close();
|
||||
const path = try fs.path.join(allocator, &.{ test_dir_path, "very_long_first_line_spanning_multiple_pages.zig" });
|
||||
defer allocator.free(path);
|
||||
|
||||
var writer = file.writer();
|
||||
try writer.writeByteNTimes('a', 3 * mem.page_size);
|
||||
|
||||
try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }));
|
||||
|
||||
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try expectEqualStrings(("a" ** (3 * mem.page_size)) ++ "\n", output.items);
|
||||
output.clearRetainingCapacity();
|
||||
|
||||
try writer.writeAll("a\na");
|
||||
|
||||
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
|
||||
try expectEqualStrings(("a" ** (3 * mem.page_size)) ++ "a\n", output.items);
|
||||
output.clearRetainingCapacity();
|
||||
|
||||
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 });
|
||||
try expectEqualStrings("a\n", output.items);
|
||||
output.clearRetainingCapacity();
|
||||
}
|
||||
{
|
||||
const file = try test_dir.dir.createFile("file_of_newlines.zig", .{});
|
||||
defer file.close();
|
||||
const path = try fs.path.join(allocator, &.{ test_dir_path, "file_of_newlines.zig" });
|
||||
defer allocator.free(path);
|
||||
|
||||
var writer = file.writer();
|
||||
const real_file_start = 3 * mem.page_size;
|
||||
try writer.writeByteNTimes('\n', real_file_start);
|
||||
try writer.writeAll("abc\ndef");
|
||||
|
||||
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = real_file_start + 1, .column = 0 });
|
||||
try expectEqualStrings("abc\n", output.items);
|
||||
output.clearRetainingCapacity();
|
||||
|
||||
try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = real_file_start + 2, .column = 0 });
|
||||
try expectEqualStrings("def\n", output.items);
|
||||
output.clearRetainingCapacity();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user