mirror of
https://code.blicky.net/yorhel/ncdu.git
synced 2026-01-13 01:08:41 -09:00
Correct int truncating/saturating + avoid one toPosixPath()
This commit is contained in:
parent
097f49d9e6
commit
3e27d37012
3 changed files with 42 additions and 30 deletions
|
|
@ -132,6 +132,8 @@ fn writeTree(out: anytype, e: *model.Entry, indent: u32) @TypeOf(out).Error!void
|
||||||
} else if (e.link()) |l| {
|
} else if (e.link()) |l| {
|
||||||
try out.print(" ino={x} nlinks={d}", .{ l.ino, l.nlink });
|
try out.print(" ino={x} nlinks={d}", .{ l.ino, l.nlink });
|
||||||
}
|
}
|
||||||
|
if (e.ext()) |ext|
|
||||||
|
try out.print(" mtime={d} uid={d} gid={d} mode={o}", .{ ext.mtime, ext.uid, ext.gid, ext.mode });
|
||||||
|
|
||||||
try out.writeByte('\n');
|
try out.writeByte('\n');
|
||||||
if (e.dir()) |d| {
|
if (e.dir()) |d| {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,16 @@ const main = @import("main.zig");
|
||||||
// memory overhead turns out to be insignificant.)
|
// memory overhead turns out to be insignificant.)
|
||||||
var allocator = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
var allocator = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||||
|
|
||||||
|
fn saturateAdd(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
|
||||||
|
std.debug.assert(@typeInfo(@TypeOf(a)).Int.signedness == .unsigned);
|
||||||
|
return std.math.add(@TypeOf(a), a, b) catch std.math.maxInt(@TypeOf(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn saturateSub(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
|
||||||
|
std.debug.assert(@typeInfo(@TypeOf(a)).Int.signedness == .unsigned);
|
||||||
|
return std.math.sub(@TypeOf(a), a, b) catch std.math.minInt(@TypeOf(a));
|
||||||
|
}
|
||||||
|
|
||||||
pub const EType = packed enum(u2) { dir, link, file };
|
pub const EType = packed enum(u2) { dir, link, file };
|
||||||
|
|
||||||
// Memory layout:
|
// Memory layout:
|
||||||
|
|
@ -122,22 +132,22 @@ pub const Entry = packed struct {
|
||||||
// First time we encounter this file in this dir, count it.
|
// First time we encounter this file in this dir, count it.
|
||||||
if (d.entry.key.num_files == 1) {
|
if (d.entry.key.num_files == 1) {
|
||||||
add_total = true;
|
add_total = true;
|
||||||
p.shared_size += self.size;
|
p.shared_size = saturateAdd(p.shared_size, self.size);
|
||||||
p.shared_blocks += self.blocks;
|
p.shared_blocks = saturateAdd(p.shared_blocks, self.blocks);
|
||||||
p.shared_items += 1;
|
p.shared_items = saturateAdd(p.shared_items, 1);
|
||||||
// Encountered this file in this dir the same number of times as its link count, meaning it's not shared with other dirs.
|
// Encountered this file in this dir the same number of times as its link count, meaning it's not shared with other dirs.
|
||||||
} else if(d.entry.key.num_files == l.nlink) {
|
} else if(d.entry.key.num_files == l.nlink) {
|
||||||
p.shared_size -= self.size;
|
p.shared_size = saturateSub(p.shared_size, self.size);
|
||||||
p.shared_blocks -= self.blocks;
|
p.shared_blocks = saturateSub(p.shared_blocks, self.blocks);
|
||||||
p.shared_items -= 1;
|
p.shared_items = saturateSub(p.shared_items, 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
add_total = true;
|
add_total = true;
|
||||||
}
|
}
|
||||||
if(add_total) {
|
if(add_total) {
|
||||||
p.total_size += self.size;
|
p.total_size = saturateAdd(p.total_size, self.size);
|
||||||
p.total_blocks += self.blocks;
|
p.total_blocks = saturateAdd(p.total_blocks, self.blocks);
|
||||||
p.total_items += 1;
|
p.total_items = saturateAdd(p.total_items, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -200,8 +210,8 @@ pub const File = packed struct {
|
||||||
|
|
||||||
pub const Ext = packed struct {
|
pub const Ext = packed struct {
|
||||||
mtime: u64,
|
mtime: u64,
|
||||||
uid: i32,
|
uid: u32,
|
||||||
gid: i32,
|
gid: u32,
|
||||||
mode: u16,
|
mode: u16,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
36
src/scan.zig
36
src/scan.zig
|
|
@ -16,8 +16,7 @@ const Stat = struct {
|
||||||
ext: model.Ext,
|
ext: model.Ext,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Cast any integer type to the target type, clamping the
|
// Cast any integer type to the target type, clamping the value to the supported maximum if necessary.
|
||||||
// value to the supported maximum if necessary.
|
|
||||||
fn castClamp(comptime T: type, x: anytype) T {
|
fn castClamp(comptime T: type, x: anytype) T {
|
||||||
// (adapted from std.math.cast)
|
// (adapted from std.math.cast)
|
||||||
if (std.math.maxInt(@TypeOf(x)) > std.math.maxInt(T) and x > std.math.maxInt(T)) {
|
if (std.math.maxInt(@TypeOf(x)) > std.math.maxInt(T) and x > std.math.maxInt(T)) {
|
||||||
|
|
@ -29,17 +28,20 @@ fn castClamp(comptime T: type, x: anytype) T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cast any integer type to the unsigned target type, wrapping/truncating as necessary.
|
// Cast any integer type to the target type, truncating if necessary.
|
||||||
fn castWrap(comptime T: type, x: anytype) T {
|
fn castTruncate(comptime T: type, x: anytype) T {
|
||||||
return @intCast(T, x); // TODO
|
const Ti = @typeInfo(T).Int;
|
||||||
|
const Xi = @typeInfo(@TypeOf(x)).Int;
|
||||||
|
const nx = if (Xi.signedness != Ti.signedness) @bitCast(std.meta.Int(Ti.signedness, Xi.bits), x) else x;
|
||||||
|
return if (Xi.bits > Ti.bits) @truncate(T, nx) else nx;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clamp(comptime T: type, comptime field: anytype, x: anytype) std.meta.fieldInfo(T, field).field_type {
|
fn clamp(comptime T: type, comptime field: anytype, x: anytype) std.meta.fieldInfo(T, field).field_type {
|
||||||
return castClamp(std.meta.fieldInfo(T, field).field_type, x);
|
return castClamp(std.meta.fieldInfo(T, field).field_type, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrap(comptime T: type, comptime field: anytype, x: anytype) std.meta.fieldInfo(T, field).field_type {
|
fn truncate(comptime T: type, comptime field: anytype, x: anytype) std.meta.fieldInfo(T, field).field_type {
|
||||||
return castWrap(std.meta.fieldInfo(T, field).field_type, x);
|
return castTruncate(std.meta.fieldInfo(T, field).field_type, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn readStat(parent: std.fs.Dir, name: [:0]const u8, follow: bool) !Stat {
|
fn readStat(parent: std.fs.Dir, name: [:0]const u8, follow: bool) !Stat {
|
||||||
|
|
@ -47,17 +49,17 @@ fn readStat(parent: std.fs.Dir, name: [:0]const u8, follow: bool) !Stat {
|
||||||
return Stat{
|
return Stat{
|
||||||
.blocks = clamp(Stat, .blocks, stat.blocks),
|
.blocks = clamp(Stat, .blocks, stat.blocks),
|
||||||
.size = clamp(Stat, .size, stat.size),
|
.size = clamp(Stat, .size, stat.size),
|
||||||
.dev = wrap(Stat, .dev, stat.dev),
|
.dev = truncate(Stat, .dev, stat.dev),
|
||||||
.ino = wrap(Stat, .ino, stat.ino),
|
.ino = truncate(Stat, .ino, stat.ino),
|
||||||
.nlink = clamp(Stat, .nlink, stat.nlink),
|
.nlink = clamp(Stat, .nlink, stat.nlink),
|
||||||
.dir = std.os.system.S_ISDIR(stat.mode),
|
.dir = std.os.system.S_ISDIR(stat.mode),
|
||||||
.reg = std.os.system.S_ISREG(stat.mode),
|
.reg = std.os.system.S_ISREG(stat.mode),
|
||||||
.symlink = std.os.system.S_ISLNK(stat.mode),
|
.symlink = std.os.system.S_ISLNK(stat.mode),
|
||||||
.ext = .{
|
.ext = .{
|
||||||
.mtime = clamp(model.Ext, .mtime, stat.mtime().tv_sec),
|
.mtime = clamp(model.Ext, .mtime, stat.mtime().tv_sec),
|
||||||
.uid = wrap(model.Ext, .uid, stat.uid),
|
.uid = truncate(model.Ext, .uid, stat.uid),
|
||||||
.gid = wrap(model.Ext, .gid, stat.gid),
|
.gid = truncate(model.Ext, .gid, stat.gid),
|
||||||
.mode = clamp(model.Ext, .mode, stat.mode & 0xffff),
|
.mode = truncate(model.Ext, .mode, stat.mode),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -157,19 +159,17 @@ fn scanDir(parents: *model.Parents, dir: std.fs.Dir) std.mem.Allocator.Error!voi
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scanRoot(path: []const u8) !void {
|
pub fn scanRoot(path: []const u8) !void {
|
||||||
// XXX: Both realpathAlloc() and toPosixPath are limited to PATH_MAX.
|
const full_path = std.fs.realpathAlloc(main.allocator, path) catch path;
|
||||||
// Oh well, I suppose we can accept that as limitation for the top-level dir we're scanning.
|
model.root = (try model.Entry.create(.dir, false, full_path)).dir().?;
|
||||||
const full_path = try std.os.toPosixPath(try std.fs.realpathAlloc(main.allocator, path));
|
|
||||||
|
|
||||||
const stat = try readStat(std.fs.cwd(), &full_path, true);
|
const stat = try readStat(std.fs.cwd(), model.root.entry.name(), true);
|
||||||
if (!stat.dir) return error.NotADirectory;
|
if (!stat.dir) return error.NotADirectory;
|
||||||
model.root = (try model.Entry.create(.dir, false, &full_path)).dir().?;
|
|
||||||
model.root.entry.blocks = stat.blocks;
|
model.root.entry.blocks = stat.blocks;
|
||||||
model.root.entry.size = stat.size;
|
model.root.entry.size = stat.size;
|
||||||
model.root.dev = try model.getDevId(stat.dev);
|
model.root.dev = try model.getDevId(stat.dev);
|
||||||
if (model.root.entry.ext()) |ext| ext.* = stat.ext;
|
if (model.root.entry.ext()) |ext| ext.* = stat.ext;
|
||||||
|
|
||||||
var parents = model.Parents{};
|
var parents = model.Parents{};
|
||||||
const dir = try std.fs.cwd().openDirZ(&full_path, .{ .access_sub_paths = true, .iterate = true });
|
const dir = try std.fs.cwd().openDirZ(model.root.entry.name(), .{ .access_sub_paths = true, .iterate = true });
|
||||||
try scanDir(&parents, dir);
|
try scanDir(&parents, dir);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue