diff options
author | Yorhel <git@yorhel.nl> | 2021-05-01 10:39:57 +0200 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2021-05-01 11:10:24 +0200 |
commit | 3e27d3701236a5f3b0e0fe567328e7d383336521 (patch) | |
tree | 7d0e8209727bedf30f0f1c21e27c754935067962 | |
parent | 097f49d9e6afccaeedf510cd6e2de631a33ac0ca (diff) |
Correct int truncating/saturating + avoid one toPosixPath()
-rw-r--r-- | src/main.zig | 4 | ||||
-rw-r--r-- | src/model.zig | 32 | ||||
-rw-r--r-- | src/scan.zig | 36 |
3 files changed, 42 insertions, 30 deletions
diff --git a/src/main.zig b/src/main.zig index 4d5c056..32c53d9 100644 --- a/src/main.zig +++ b/src/main.zig @@ -132,6 +132,8 @@ fn writeTree(out: anytype, e: *model.Entry, indent: u32) @TypeOf(out).Error!void } else if (e.link()) |l| { 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'); if (e.dir()) |d| { @@ -185,7 +187,7 @@ pub fn main() anyerror!void { std.log.info("align={}, Entry={}, Dir={}, Link={}, File={}.", .{@alignOf(model.Dir), @sizeOf(model.Entry), @sizeOf(model.Dir), @sizeOf(model.Link), @sizeOf(model.File)}); try scan.scanRoot(scan_dir orelse "."); - + //var out = std.io.bufferedWriter(std.io.getStdOut().writer()); //try writeTree(out.writer(), &model.root.entry, 0); //try out.flush(); diff --git a/src/model.zig b/src/model.zig index e6d1ea2..34e3aa9 100644 --- a/src/model.zig +++ b/src/model.zig @@ -9,6 +9,16 @@ const main = @import("main.zig"); // memory overhead turns out to be insignificant.) 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 }; // Memory layout: @@ -122,22 +132,22 @@ pub const Entry = packed struct { // First time we encounter this file in this dir, count it. if (d.entry.key.num_files == 1) { add_total = true; - p.shared_size += self.size; - p.shared_blocks += self.blocks; - p.shared_items += 1; + p.shared_size = saturateAdd(p.shared_size, self.size); + p.shared_blocks = saturateAdd(p.shared_blocks, self.blocks); + 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. } else if(d.entry.key.num_files == l.nlink) { - p.shared_size -= self.size; - p.shared_blocks -= self.blocks; - p.shared_items -= 1; + p.shared_size = saturateSub(p.shared_size, self.size); + p.shared_blocks = saturateSub(p.shared_blocks, self.blocks); + p.shared_items = saturateSub(p.shared_items, 1); } } else { add_total = true; } if(add_total) { - p.total_size += self.size; - p.total_blocks += self.blocks; - p.total_items += 1; + p.total_size = saturateAdd(p.total_size, self.size); + p.total_blocks = saturateAdd(p.total_blocks, self.blocks); + p.total_items = saturateAdd(p.total_items, 1); } } } @@ -200,8 +210,8 @@ pub const File = packed struct { pub const Ext = packed struct { mtime: u64, - uid: i32, - gid: i32, + uid: u32, + gid: u32, mode: u16, }; diff --git a/src/scan.zig b/src/scan.zig index 3f19f3a..88b3c10 100644 --- a/src/scan.zig +++ b/src/scan.zig @@ -16,8 +16,7 @@ const Stat = struct { ext: model.Ext, }; -// Cast any integer type to the target type, clamping the -// value to the supported maximum if necessary. +// Cast any integer type to the target type, clamping the value to the supported maximum if necessary. fn castClamp(comptime T: type, x: anytype) T { // (adapted from std.math.cast) 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. -fn castWrap(comptime T: type, x: anytype) T { - return @intCast(T, x); // TODO +// Cast any integer type to the target type, truncating if necessary. +fn castTruncate(comptime T: type, x: anytype) T { + 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 { 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 { - return castWrap(std.meta.fieldInfo(T, field).field_type, x); +fn truncate(comptime T: type, comptime field: anytype, x: anytype) std.meta.fieldInfo(T, field).field_type { + return castTruncate(std.meta.fieldInfo(T, field).field_type, x); } 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{ .blocks = clamp(Stat, .blocks, stat.blocks), .size = clamp(Stat, .size, stat.size), - .dev = wrap(Stat, .dev, stat.dev), - .ino = wrap(Stat, .ino, stat.ino), + .dev = truncate(Stat, .dev, stat.dev), + .ino = truncate(Stat, .ino, stat.ino), .nlink = clamp(Stat, .nlink, stat.nlink), .dir = std.os.system.S_ISDIR(stat.mode), .reg = std.os.system.S_ISREG(stat.mode), .symlink = std.os.system.S_ISLNK(stat.mode), .ext = .{ .mtime = clamp(model.Ext, .mtime, stat.mtime().tv_sec), - .uid = wrap(model.Ext, .uid, stat.uid), - .gid = wrap(model.Ext, .gid, stat.gid), - .mode = clamp(model.Ext, .mode, stat.mode & 0xffff), + .uid = truncate(model.Ext, .uid, stat.uid), + .gid = truncate(model.Ext, .gid, stat.gid), + .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 { - // XXX: Both realpathAlloc() and toPosixPath are limited to PATH_MAX. - // Oh well, I suppose we can accept that as limitation for the top-level dir we're scanning. - const full_path = try std.os.toPosixPath(try std.fs.realpathAlloc(main.allocator, path)); + const full_path = std.fs.realpathAlloc(main.allocator, path) catch path; + model.root = (try model.Entry.create(.dir, false, full_path)).dir().?; - 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; - model.root = (try model.Entry.create(.dir, false, &full_path)).dir().?; model.root.entry.blocks = stat.blocks; model.root.entry.size = stat.size; model.root.dev = try model.getDevId(stat.dev); if (model.root.entry.ext()) |ext| ext.* = stat.ext; 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); } |