mirror of
https://code.blicky.net/yorhel/ncdu.git
synced 2026-01-13 01:08:41 -09:00
Use integer formatting instead of floating points
This avoids embedding Zig's floating point formatting tables and ancillary code, shaving 17k off the final static binary for x86_64. Also adjusted the cut-off points for the units to be more precise.
This commit is contained in:
parent
8ad61e87c1
commit
90b43755b8
3 changed files with 115 additions and 34 deletions
|
|
@ -216,10 +216,13 @@ const Row = struct {
|
|||
ui.addch('[');
|
||||
if (main.config.show_percent) {
|
||||
self.bg.fg(.num);
|
||||
ui.addprint("{d:>5.1}", .{ 100 *
|
||||
if (main.config.show_blocks) @as(f32, @floatFromInt(item.pack.blocks)) / @as(f32, @floatFromInt(@max(1, dir_parent.entry.pack.blocks)))
|
||||
else @as(f32, @floatFromInt(item.size)) / @as(f32, @floatFromInt(@max(1, dir_parent.entry.size)))
|
||||
});
|
||||
var num : u64 = if (main.config.show_blocks) item.pack.blocks else item.size;
|
||||
var denom : u64 = if (main.config.show_blocks) dir_parent.entry.pack.blocks else dir_parent.entry.size;
|
||||
if (num > (1<<54)) { // avoid overflow
|
||||
num >>= 10;
|
||||
denom >>= 10;
|
||||
}
|
||||
ui.addstr(&util.fmt5dec(@intCast( (num * 1000 + (denom / 2)) / denom )));
|
||||
self.bg.fg(.default);
|
||||
ui.addch('%');
|
||||
}
|
||||
|
|
@ -261,12 +264,12 @@ const Row = struct {
|
|||
ui.addnum(self.bg, n);
|
||||
} else if (n < 100_000)
|
||||
ui.addnum(self.bg, n)
|
||||
else if (n < 1000_000) {
|
||||
ui.addprint("{d:>5.1}", .{ @as(f32, @floatFromInt(n)) / 1000 });
|
||||
else if (n < 999_950) {
|
||||
ui.addstr(&util.fmt5dec(@intCast( (n + 50) / 100 )));
|
||||
self.bg.fg(.default);
|
||||
ui.addch('k');
|
||||
} else if (n < 1000_000_000) {
|
||||
ui.addprint("{d:>5.1}", .{ @as(f32, @floatFromInt(n)) / 1000_000 });
|
||||
} else if (n < 999_950_000) {
|
||||
ui.addstr(&util.fmt5dec(@intCast( (n + 50_000) / 100_000 )));
|
||||
self.bg.fg(.default);
|
||||
ui.addch('M');
|
||||
} else {
|
||||
|
|
|
|||
99
src/ui.zig
99
src/ui.zig
|
|
@ -415,39 +415,86 @@ pub fn addch(ch: c.chtype) void {
|
|||
// unit = " XB" or " XiB"
|
||||
// Concatenated, these take 8 columns in SI mode or 9 otherwise.
|
||||
pub const FmtSize = struct {
|
||||
buf: [8:0]u8,
|
||||
buf: [5:0]u8,
|
||||
unit: [:0]const u8,
|
||||
|
||||
pub fn fmt(v: u64) @This() {
|
||||
var r: @This() = undefined;
|
||||
var f: f32 = @floatFromInt(v);
|
||||
if (main.config.si) {
|
||||
if(f < 1000.0) { r.unit = " B"; }
|
||||
else if(f < 1e6) { r.unit = " KB"; f /= 1e3; }
|
||||
else if(f < 1e9) { r.unit = " MB"; f /= 1e6; }
|
||||
else if(f < 1e12) { r.unit = " GB"; f /= 1e9; }
|
||||
else if(f < 1e15) { r.unit = " TB"; f /= 1e12; }
|
||||
else if(f < 1e18) { r.unit = " PB"; f /= 1e15; }
|
||||
else { r.unit = " EB"; f /= 1e18; }
|
||||
}
|
||||
else {
|
||||
if(f < 1000.0) { r.unit = " B"; }
|
||||
else if(f < 1023e3) { r.unit = " KiB"; f /= 1024.0; }
|
||||
else if(f < 1023e6) { r.unit = " MiB"; f /= 1048576.0; }
|
||||
else if(f < 1023e9) { r.unit = " GiB"; f /= 1073741824.0; }
|
||||
else if(f < 1023e12) { r.unit = " TiB"; f /= 1099511627776.0; }
|
||||
else if(f < 1023e15) { r.unit = " PiB"; f /= 1125899906842624.0; }
|
||||
else { r.unit = " EiB"; f /= 1152921504606846976.0; }
|
||||
}
|
||||
_ = std.fmt.bufPrintZ(&r.buf, "{d:>5.1}", .{f}) catch unreachable;
|
||||
return r;
|
||||
fn init(u: [:0]const u8, n: u64, mul: u64, div: u64) FmtSize {
|
||||
return .{
|
||||
.unit = u,
|
||||
.buf = util.fmt5dec(@intCast( ((n*mul) +| (div / 2)) / div )),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn num(self: *const @This()) [:0]const u8 {
|
||||
return std.mem.sliceTo(&self.buf, 0);
|
||||
pub fn fmt(v: u64) FmtSize {
|
||||
if (main.config.si) {
|
||||
if (v < 1000) { return FmtSize.init(" B", v, 10, 1); }
|
||||
else if (v < 999_950) { return FmtSize.init(" KB", v, 1, 100); }
|
||||
else if (v < 999_950_000) { return FmtSize.init(" MB", v, 1, 100_000); }
|
||||
else if (v < 999_950_000_000) { return FmtSize.init(" GB", v, 1, 100_000_000); }
|
||||
else if (v < 999_950_000_000_000) { return FmtSize.init(" TB", v, 1, 100_000_000_000); }
|
||||
else if (v < 999_950_000_000_000_000) { return FmtSize.init(" PB", v, 1, 100_000_000_000_000); }
|
||||
else { return FmtSize.init(" EB", v, 1, 100_000_000_000_000_000); }
|
||||
} else {
|
||||
// Cutoff values are obtained by calculating 999.949999999999999999999999 * div with an infinite-precision calculator.
|
||||
// (Admittedly, this precision is silly)
|
||||
if (v < 1000) { return FmtSize.init(" B", v, 10, 1); }
|
||||
else if (v < 1023949) { return FmtSize.init(" KiB", v, 10, 1<<10); }
|
||||
else if (v < 1048523572) { return FmtSize.init(" MiB", v, 10, 1<<20); }
|
||||
else if (v < 1073688136909) { return FmtSize.init(" GiB", v, 10, 1<<30); }
|
||||
else if (v < 1099456652194612) { return FmtSize.init(" TiB", v, 10, 1<<40); }
|
||||
else if (v < 1125843611847281869) { return FmtSize.init(" PiB", v, 10, 1<<50); }
|
||||
else { return FmtSize.init(" EiB", v, 1, (1<<60)/10); }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn num(self: *const FmtSize) [:0]const u8 {
|
||||
return &self.buf;
|
||||
}
|
||||
|
||||
fn testEql(self: FmtSize, exp: []const u8) !void {
|
||||
var buf: [10]u8 = undefined;
|
||||
try std.testing.expectEqualStrings(exp, try std.fmt.bufPrint(&buf, "{s}{s}", .{ self.num(), self.unit }));
|
||||
}
|
||||
};
|
||||
|
||||
test "fmtsize" {
|
||||
main.config.si = true;
|
||||
try FmtSize.fmt( 0).testEql(" 0.0 B");
|
||||
try FmtSize.fmt( 999).testEql("999.0 B");
|
||||
try FmtSize.fmt( 1000).testEql(" 1.0 KB");
|
||||
try FmtSize.fmt( 1049).testEql(" 1.0 KB");
|
||||
try FmtSize.fmt( 1050).testEql(" 1.1 KB");
|
||||
try FmtSize.fmt( 999_899).testEql("999.9 KB");
|
||||
try FmtSize.fmt( 999_949).testEql("999.9 KB");
|
||||
try FmtSize.fmt( 999_950).testEql(" 1.0 MB");
|
||||
try FmtSize.fmt( 1000_000).testEql(" 1.0 MB");
|
||||
try FmtSize.fmt( 999_850_009).testEql("999.9 MB");
|
||||
try FmtSize.fmt( 999_899_999).testEql("999.9 MB");
|
||||
try FmtSize.fmt( 999_900_000).testEql("999.9 MB");
|
||||
try FmtSize.fmt( 999_949_999).testEql("999.9 MB");
|
||||
try FmtSize.fmt( 999_950_000).testEql(" 1.0 GB");
|
||||
try FmtSize.fmt( 999_999_999).testEql(" 1.0 GB");
|
||||
try FmtSize.fmt(std.math.maxInt(u64)).testEql(" 18.4 EB");
|
||||
|
||||
main.config.si = false;
|
||||
try FmtSize.fmt( 0).testEql(" 0.0 B");
|
||||
try FmtSize.fmt( 999).testEql("999.0 B");
|
||||
try FmtSize.fmt( 1000).testEql(" 1.0 KiB");
|
||||
try FmtSize.fmt( 1024).testEql(" 1.0 KiB");
|
||||
try FmtSize.fmt( 102400).testEql("100.0 KiB");
|
||||
try FmtSize.fmt( 1023898).testEql("999.9 KiB");
|
||||
try FmtSize.fmt( 1023949).testEql(" 1.0 MiB");
|
||||
try FmtSize.fmt( 1048523571).testEql("999.9 MiB");
|
||||
try FmtSize.fmt( 1048523572).testEql(" 1.0 GiB");
|
||||
try FmtSize.fmt( 1073688136908).testEql("999.9 GiB");
|
||||
try FmtSize.fmt( 1073688136909).testEql(" 1.0 TiB");
|
||||
try FmtSize.fmt( 1099456652194611).testEql("999.9 TiB");
|
||||
try FmtSize.fmt( 1099456652194612).testEql(" 1.0 PiB");
|
||||
try FmtSize.fmt(1125843611847281868).testEql("999.9 PiB");
|
||||
try FmtSize.fmt(1125843611847281869).testEql(" 1.0 EiB");
|
||||
try FmtSize.fmt(std.math.maxInt(u64)).testEql(" 16.0 EiB");
|
||||
}
|
||||
|
||||
// Print a formatted human-readable size string onto the given background.
|
||||
pub fn addsize(bg: Bg, v: u64) void {
|
||||
const r = FmtSize.fmt(v);
|
||||
|
|
|
|||
31
src/util.zig
31
src/util.zig
|
|
@ -37,6 +37,37 @@ pub fn arrayListBufZ(buf: *std.ArrayList(u8)) [:0]const u8 {
|
|||
return buf.items[0..buf.items.len-1:0];
|
||||
}
|
||||
|
||||
// Format an integer as right-aligned '###.#'.
|
||||
// Pretty much equivalent to:
|
||||
// std.fmt.bufPrintZ(.., "{d:>5.1}", @floatFromInt(n)/10.0);
|
||||
// Except this function doesn't pull in large float formatting tables.
|
||||
pub fn fmt5dec(n: u14) [5:0]u8 {
|
||||
std.debug.assert(n <= 9999);
|
||||
var buf: [5:0]u8 = " 0.0".*;
|
||||
var v = n;
|
||||
buf[4] += @intCast(v % 10);
|
||||
v /= 10;
|
||||
buf[2] += @intCast(v % 10);
|
||||
v /= 10;
|
||||
if (v == 0) return buf;
|
||||
buf[1] = '0' + @as(u8, @intCast(v % 10));
|
||||
v /= 10;
|
||||
if (v == 0) return buf;
|
||||
buf[0] = '0' + @as(u8, @intCast(v));
|
||||
return buf;
|
||||
}
|
||||
|
||||
test "fmt5dec" {
|
||||
const eq = std.testing.expectEqualStrings;
|
||||
try eq(" 0.0", &fmt5dec(0));
|
||||
try eq(" 0.5", &fmt5dec(5));
|
||||
try eq(" 9.5", &fmt5dec(95));
|
||||
try eq(" 12.5", &fmt5dec(125));
|
||||
try eq("123.9", &fmt5dec(1239));
|
||||
try eq("999.9", &fmt5dec(9999));
|
||||
}
|
||||
|
||||
|
||||
// Straightforward Zig port of strnatcmp() from https://github.com/sourcefrog/natsort/
|
||||
// (Requiring nul-terminated strings is ugly, but we've got them anyway and it does simplify the code)
|
||||
pub fn strnatcmp(a: [:0]const u8, b: [:0]const u8) std.math.Order {
|
||||
|
|
|
|||
Loading…
Reference in a new issue