mirror of
https://github.com/ziglang/zig.git
synced 2026-01-21 06:45:24 +00:00
rework zig fmt to use less syscalls and open fds
* `std.fs.Dir.Entry.Kind` is moved to `std.fs.File.Kind` * `std.fs.File.Stat` gains the `kind` field, so performing a stat() on a File now tells what kind of file it is. On Windows this only will distinguish between directories and files. * rework zig fmt logic so that in the case of opening a file and discovering it to be a directory, it closes the file descriptor before re-opening it with O_DIRECTORY, using fewer simultaneous open file descriptors when walking a directory tree. * rework zig fmt logic so that it pays attention to the kind of directory entries, and when it sees a sub-directory it attempts to open it as a directory rather than a file, reducing the number of open() syscalls when walking a directory tree.
This commit is contained in:
parent
bc0ca73887
commit
d87cd06296
@ -261,17 +261,7 @@ pub const Dir = struct {
|
||||
name: []const u8,
|
||||
kind: Kind,
|
||||
|
||||
pub const Kind = enum {
|
||||
BlockDevice,
|
||||
CharacterDevice,
|
||||
Directory,
|
||||
NamedPipe,
|
||||
SymLink,
|
||||
File,
|
||||
UnixDomainSocket,
|
||||
Whiteout,
|
||||
Unknown,
|
||||
};
|
||||
pub const Kind = File.Kind;
|
||||
};
|
||||
|
||||
const IteratorError = error{AccessDenied} || os.UnexpectedError;
|
||||
|
||||
@ -29,6 +29,18 @@ pub const File = struct {
|
||||
pub const Mode = os.mode_t;
|
||||
pub const INode = os.ino_t;
|
||||
|
||||
pub const Kind = enum {
|
||||
BlockDevice,
|
||||
CharacterDevice,
|
||||
Directory,
|
||||
NamedPipe,
|
||||
SymLink,
|
||||
File,
|
||||
UnixDomainSocket,
|
||||
Whiteout,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
pub const default_mode = switch (builtin.os.tag) {
|
||||
.windows => 0,
|
||||
.wasi => 0,
|
||||
@ -219,13 +231,14 @@ pub const File = struct {
|
||||
/// unique across time, as some file systems may reuse an inode after its file has been deleted.
|
||||
/// Some systems may change the inode of a file over time.
|
||||
///
|
||||
/// On Linux, the inode _is_ structure that stores the metadata, and the inode _number_ is what
|
||||
/// On Linux, the inode is a structure that stores the metadata, and the inode _number_ is what
|
||||
/// you see here: the index number of the inode.
|
||||
///
|
||||
/// The FileIndex on Windows is similar. It is a number for a file that is unique to each filesystem.
|
||||
inode: INode,
|
||||
size: u64,
|
||||
mode: Mode,
|
||||
kind: Kind,
|
||||
|
||||
/// Access time in nanoseconds, relative to UTC 1970-01-01.
|
||||
atime: i128,
|
||||
@ -254,6 +267,7 @@ pub const File = struct {
|
||||
.inode = info.InternalInformation.IndexNumber,
|
||||
.size = @bitCast(u64, info.StandardInformation.EndOfFile),
|
||||
.mode = 0,
|
||||
.kind = if (info.StandardInformation.Directory == 0) .File else .Directory,
|
||||
.atime = windows.fromSysTime(info.BasicInformation.LastAccessTime),
|
||||
.mtime = windows.fromSysTime(info.BasicInformation.LastWriteTime),
|
||||
.ctime = windows.fromSysTime(info.BasicInformation.CreationTime),
|
||||
@ -268,6 +282,16 @@ pub const File = struct {
|
||||
.inode = st.ino,
|
||||
.size = @bitCast(u64, st.size),
|
||||
.mode = st.mode,
|
||||
.kind = switch (st.mode & os.S_IFMT) {
|
||||
os.S_IFBLK => .BlockDevice,
|
||||
os.S_IFCHR => .CharacterDevice,
|
||||
os.S_IFDIR => .Directory,
|
||||
os.S_IFIFO => .NamedPipe,
|
||||
os.S_IFLNK => .SymLink,
|
||||
os.S_IFREG => .File,
|
||||
os.S_IFSOCK => .UnixDomainSocket,
|
||||
else => .Unknown,
|
||||
},
|
||||
.atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec,
|
||||
.mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec,
|
||||
.ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec,
|
||||
|
||||
@ -670,11 +670,12 @@ const FmtError = error{
|
||||
ReadOnlyFileSystem,
|
||||
LinkQuotaExceeded,
|
||||
FileBusy,
|
||||
EndOfStream,
|
||||
} || fs.File.OpenError;
|
||||
|
||||
fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
|
||||
// get the real path here to avoid Windows failing on relative file paths with . or .. in them
|
||||
var real_path = fs.realpathAlloc(fmt.gpa, file_path) catch |err| {
|
||||
const real_path = fs.realpathAlloc(fmt.gpa, file_path) catch |err| {
|
||||
std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
|
||||
fmt.any_error = true;
|
||||
return;
|
||||
@ -684,47 +685,65 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
|
||||
if (fmt.seen.exists(real_path)) return;
|
||||
try fmt.seen.put(real_path);
|
||||
|
||||
const source_file = fs.cwd().openFile(real_path, .{}) catch |err| {
|
||||
std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
|
||||
fmt.any_error = true;
|
||||
return;
|
||||
};
|
||||
defer source_file.close();
|
||||
|
||||
const stat = source_file.stat() catch |err| {
|
||||
std.debug.warn("unable to stat '{}': {}\n", .{ file_path, err });
|
||||
fmt.any_error = true;
|
||||
return;
|
||||
};
|
||||
|
||||
const source_code = source_file.readAllAlloc(fmt.gpa, stat.size, max_src_size) catch |err| switch (err) {
|
||||
error.IsDir => {
|
||||
var dir = try fs.cwd().openDir(file_path, .{ .iterate = true });
|
||||
defer dir.close();
|
||||
|
||||
var dir_it = dir.iterate();
|
||||
|
||||
while (try dir_it.next()) |entry| {
|
||||
if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) {
|
||||
const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
|
||||
try fmtPath(fmt, full_path, check_mode);
|
||||
}
|
||||
}
|
||||
return;
|
||||
},
|
||||
fmtPathFile(fmt, file_path, check_mode, real_path) catch |err| switch (err) {
|
||||
error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, real_path),
|
||||
else => {
|
||||
std.debug.warn("unable to read '{}': {}\n", .{ file_path, err });
|
||||
std.debug.warn("unable to format '{}': {}\n", .{ file_path, err });
|
||||
fmt.any_error = true;
|
||||
return;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn fmtPathDir(fmt: *Fmt, file_path: []const u8, check_mode: bool, parent_real_path: []const u8) FmtError!void {
|
||||
var dir = try fs.cwd().openDir(parent_real_path, .{ .iterate = true });
|
||||
defer dir.close();
|
||||
|
||||
var dir_it = dir.iterate();
|
||||
while (try dir_it.next()) |entry| {
|
||||
const is_dir = entry.kind == .Directory;
|
||||
if (is_dir or mem.endsWith(u8, entry.name, ".zig")) {
|
||||
const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
|
||||
const sub_real_path = fs.realpathAlloc(fmt.gpa, full_path) catch |err| {
|
||||
std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
|
||||
fmt.any_error = true;
|
||||
return;
|
||||
};
|
||||
defer fmt.gpa.free(sub_real_path);
|
||||
|
||||
if (fmt.seen.exists(sub_real_path)) return;
|
||||
try fmt.seen.put(sub_real_path);
|
||||
|
||||
if (is_dir) {
|
||||
try fmtPathDir(fmt, full_path, check_mode, sub_real_path);
|
||||
} else {
|
||||
fmtPathFile(fmt, full_path, check_mode, sub_real_path) catch |err| {
|
||||
std.debug.warn("unable to format '{}': {}\n", .{ full_path, err });
|
||||
fmt.any_error = true;
|
||||
return;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fmtPathFile(fmt: *Fmt, file_path: []const u8, check_mode: bool, real_path: []const u8) FmtError!void {
|
||||
const source_file = try fs.cwd().openFile(real_path, .{});
|
||||
defer source_file.close();
|
||||
|
||||
const stat = try source_file.stat();
|
||||
|
||||
if (stat.kind == .Directory)
|
||||
return error.IsDir;
|
||||
|
||||
const source_code = source_file.readAllAlloc(fmt.gpa, stat.size, max_src_size) catch |err| switch (err) {
|
||||
error.ConnectionResetByPeer => unreachable,
|
||||
error.ConnectionTimedOut => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
defer fmt.gpa.free(source_code);
|
||||
|
||||
const tree = std.zig.parse(fmt.gpa, source_code) catch |err| {
|
||||
std.debug.warn("error parsing file '{}': {}\n", .{ file_path, err });
|
||||
fmt.any_error = true;
|
||||
return;
|
||||
};
|
||||
const tree = try std.zig.parse(fmt.gpa, source_code);
|
||||
defer tree.deinit();
|
||||
|
||||
for (tree.errors) |parse_error| {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user