summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2021-05-29 13:18:21 +0200
committerYorhel <git@yorhel.nl>2021-05-29 13:18:23 +0200
commit23903088833447bbe558ad1c93abe47505635383 (patch)
treeda2654425c2542133ebab3d8f75b0dcb06670fa3 /src
parentc077c5bed5378b298501da8054824fdef7c3a8ed (diff)
Handle allocation failures
In a similar way to the C version of ncdu: by wrapping malloc(). It's simpler to handle allocation failures at the source to allow for easy retries, pushing the retries up the stack will complicate code somewhat more. Likewise, this is a best-effort approach to handling OOM, allocation failures in ncurses aren't handled and display glitches may occur when we get an OOM inside a drawing function. This is a somewhat un-Zig-like way of handling errors and adds scary-looking 'catch unreachable's all over the code, but that's okay.
Diffstat (limited to 'src')
-rw-r--r--src/browser.zig36
-rw-r--r--src/main.zig43
-rw-r--r--src/model.zig37
-rw-r--r--src/scan.zig53
-rw-r--r--src/ui.zig38
-rw-r--r--src/util.zig4
6 files changed, 128 insertions, 83 deletions
diff --git a/src/browser.zig b/src/browser.zig
index 042537e..7ed05f7 100644
--- a/src/browser.zig
+++ b/src/browser.zig
@@ -120,24 +120,24 @@ fn sortDir() void {
// - dir_parents changes (i.e. we change directory)
// - config.show_hidden changes
// - files in this dir have been added or removed
-pub fn loadDir() !void {
+pub fn loadDir() void {
dir_items.shrinkRetainingCapacity(0);
dir_max_size = 1;
dir_max_blocks = 1;
if (dir_parents.top() != model.root)
- try dir_items.append(null);
+ dir_items.append(null) catch unreachable;
var it = dir_parents.top().sub;
while (it) |e| {
if (e.blocks > dir_max_blocks) dir_max_blocks = e.blocks;
if (e.size > dir_max_size) dir_max_size = e.size;
if (main.config.show_hidden) // fast path
- try dir_items.append(e)
+ dir_items.append(e) catch unreachable
else {
const excl = if (e.file()) |f| f.excluded else false;
const name = e.name();
if (!excl and name[0] != '.' and name[name.len-1] != '~')
- try dir_items.append(e);
+ dir_items.append(e) catch unreachable;
}
it = e.next;
}
@@ -271,19 +271,19 @@ const Row = struct {
ui.addstr(" no mtime");
}
- fn name(self: *Self) !void {
+ fn name(self: *Self) void {
ui.move(self.row, self.col);
if (self.item) |i| {
self.bg.fg(if (i.etype == .dir) .dir else .default);
ui.addch(if (i.etype == .dir) '/' else ' ');
- ui.addstr(try ui.shorten(try ui.toUtf8(i.name()), saturateSub(ui.cols, self.col + 1)));
+ ui.addstr(ui.shorten(ui.toUtf8(i.name()), saturateSub(ui.cols, self.col + 1)));
} else {
self.bg.fg(.dir);
ui.addstr("/..");
}
}
- fn draw(self: *Self) !void {
+ fn draw(self: *Self) void {
if (self.bg == .sel) {
self.bg.fg(.default);
ui.move(self.row, 0);
@@ -294,7 +294,7 @@ const Row = struct {
self.graph();
self.items();
self.mtime();
- try self.name();
+ self.name();
}
};
@@ -314,7 +314,7 @@ fn drawQuit() void {
ui.addch(')');
}
-pub fn draw() !void {
+pub fn draw() void {
ui.style(.hd);
ui.move(0,0);
ui.hline(' ', ui.cols);
@@ -340,8 +340,8 @@ pub fn draw() !void {
ui.style(.dir);
var pathbuf = std.ArrayList(u8).init(main.allocator);
- try dir_parents.path(pathbuf.writer());
- ui.addstr(try ui.shorten(try ui.toUtf8(try arrayListBufZ(&pathbuf)), saturateSub(ui.cols, 5)));
+ dir_parents.path(&pathbuf);
+ ui.addstr(ui.shorten(ui.toUtf8(arrayListBufZ(&pathbuf)), saturateSub(ui.cols, 5)));
pathbuf.deinit();
ui.style(.default);
@@ -361,7 +361,7 @@ pub fn draw() !void {
.bg = if (i+current_view.top == cursor_idx) .sel else .default,
};
if (row.bg == .sel) sel_row = i+2;
- try row.draw();
+ row.draw();
}
ui.style(.hd);
@@ -389,7 +389,7 @@ fn sortToggle(col: main.config.SortCol, default_order: main.config.SortOrder) vo
sortDir();
}
-pub fn keyInput(ch: i32) !void {
+pub fn keyInput(ch: i32) void {
if (need_confirm_quit) {
switch (ch) {
'y', 'Y' => if (need_confirm_quit) ui.quit(),
@@ -422,7 +422,7 @@ pub fn keyInput(ch: i32) !void {
'M' => if (main.config.extended) sortToggle(.mtime, .desc),
'e' => {
main.config.show_hidden = !main.config.show_hidden;
- try loadDir();
+ loadDir();
},
't' => {
main.config.sort_dirsfirst = !main.config.sort_dirsfirst;
@@ -445,18 +445,18 @@ pub fn keyInput(ch: i32) !void {
if (dir_items.items.len == 0) {
} else if (dir_items.items[cursor_idx]) |e| {
if (e.dir()) |d| {
- try dir_parents.push(d);
- try loadDir();
+ dir_parents.push(d);
+ loadDir();
}
} else if (dir_parents.top() != model.root) {
dir_parents.pop();
- try loadDir();
+ loadDir();
}
},
'h', '<', ui.c.KEY_BACKSPACE, ui.c.KEY_LEFT => {
if (dir_parents.top() != model.root) {
dir_parents.pop();
- try loadDir();
+ loadDir();
}
},
diff --git a/src/main.zig b/src/main.zig
index e0bdba6..195e3d9 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -7,7 +7,29 @@ const ui = @import("ui.zig");
const browser = @import("browser.zig");
const c = @cImport(@cInclude("locale.h"));
-pub const allocator = std.heap.c_allocator;
+// "Custom" allocator that wraps the libc allocator and calls ui.oom() on error.
+// This allocator never returns an error, it either succeeds or causes ncdu to quit.
+// (Which means you'll find a lot of "catch unreachable" sprinkled through the code,
+// they look scarier than they are)
+fn wrapAlloc(alloc: *std.mem.Allocator, len: usize, alignment: u29, len_align: u29, return_address: usize) error{OutOfMemory}![]u8 {
+ while (true) {
+ if (std.heap.c_allocator.allocFn(alloc, len, alignment, len_align, return_address)) |r|
+ return r
+ else |_| {}
+ ui.oom();
+ }
+}
+
+fn wrapResize(alloc: *std.mem.Allocator, buf: []u8, buf_align: u29, new_len: usize, len_align: u29, return_address: usize) std.mem.Allocator.Error!usize {
+ // AFAIK, all uses of resizeFn to grow an allocation will fall back to allocFn on failure.
+ return std.heap.c_allocator.resizeFn(alloc, buf, buf_align, new_len, len_align, return_address);
+}
+
+var allocator_state = std.mem.Allocator{
+ .allocFn = wrapAlloc,
+ .resizeFn = wrapResize,
+};
+pub const allocator = &allocator_state;
pub const config = struct {
pub const SortCol = enum { name, blocks, size, items, mtime };
@@ -158,7 +180,7 @@ fn readExcludeFile(path: []const u8) !void {
rd.readUntilDelimiterArrayList(&buf, '\n', 4096)
catch |e| if (e != error.EndOfStream) return e else if (buf.items.len == 0) break;
if (buf.items.len > 0)
- try config.exclude_patterns.append(try buf.toOwnedSliceSentinel(0));
+ config.exclude_patterns.append(buf.toOwnedSliceSentinel(0) catch unreachable) catch unreachable;
}
}
@@ -203,7 +225,7 @@ pub fn main() !void {
else if(opt.is("-f")) import_file = args.arg()
else if(opt.is("--si")) config.si = true
else if(opt.is("-L") or opt.is("--follow-symlinks")) config.follow_symlinks = true
- else if(opt.is("--exclude")) try config.exclude_patterns.append(args.arg())
+ else if(opt.is("--exclude")) config.exclude_patterns.append(args.arg()) catch unreachable
else if(opt.is("-X") or opt.is("--exclude-from")) {
const arg = args.arg();
readExcludeFile(arg) catch |e| ui.die("Error reading excludes from {s}: {}.\n", .{ arg, e });
@@ -249,22 +271,21 @@ pub fn main() !void {
config.scan_ui = .full; // in case we're refreshing from the UI, always in full mode.
ui.init();
state = .browse;
- try browser.loadDir();
+ browser.loadDir();
- // TODO: Handle OOM errors
- while (true) try handleEvent(true, false);
+ while (true) handleEvent(true, false);
}
var event_delay_timer: std.time.Timer = undefined;
// Draw the screen and handle the next input event.
// In non-blocking mode, screen drawing is rate-limited to keep this function fast.
-pub fn handleEvent(block: bool, force_draw: bool) !void {
+pub fn handleEvent(block: bool, force_draw: bool) void {
if (block or force_draw or event_delay_timer.read() > config.update_delay) {
if (ui.inited) _ = ui.c.erase();
switch (state) {
- .scan => try scan.draw(),
- .browse => try browser.draw(),
+ .scan => scan.draw(),
+ .browse => browser.draw(),
}
if (ui.inited) _ = ui.c.refresh();
event_delay_timer.reset();
@@ -280,8 +301,8 @@ pub fn handleEvent(block: bool, force_draw: bool) !void {
if (ch == 0) return;
if (ch == -1) return handleEvent(firstblock, true);
switch (state) {
- .scan => try scan.keyInput(ch),
- .browse => try browser.keyInput(ch),
+ .scan => scan.keyInput(ch),
+ .browse => browser.keyInput(ch),
}
firstblock = false;
}
diff --git a/src/model.zig b/src/model.zig
index 760300e..842e220 100644
--- a/src/model.zig
+++ b/src/model.zig
@@ -1,5 +1,6 @@
const std = @import("std");
const main = @import("main.zig");
+const ui = @import("ui.zig");
usingnamespace @import("util.zig");
// While an arena allocator is optimimal for almost all scenarios in which ncdu
@@ -67,17 +68,23 @@ pub const Entry = packed struct {
return @intToPtr(*Ext, std.mem.alignForward(@ptrToInt(self) + nameOffset(self.etype) + n.len + 1, @alignOf(Ext)));
}
- pub fn create(etype: EType, isext: bool, ename: []const u8) !*Entry {
+ pub fn create(etype: EType, isext: bool, ename: []const u8) *Entry {
const base_size = nameOffset(etype) + ename.len + 1;
const size = (if (isext) std.mem.alignForward(base_size, @alignOf(Ext))+@sizeOf(Ext) else base_size);
- var ptr = try allocator.allocator.allocWithOptions(u8, size, @alignOf(Entry), null);
+ var ptr = blk: {
+ while (true) {
+ if (allocator.allocator.allocWithOptions(u8, size, @alignOf(Entry), null)) |p|
+ break :blk p
+ else |_| {}
+ ui.oom();
+ }
+ };
std.mem.set(u8, ptr, 0); // kind of ugly, but does the trick
var e = @ptrCast(*Entry, ptr);
e.etype = etype;
e.isext = isext;
var name_ptr = @intToPtr([*]u8, @ptrToInt(e) + nameOffset(etype));
std.mem.copy(u8, name_ptr[0..ename.len], ename);
- //std.debug.warn("{any}\n", .{ @ptrCast([*]u8, e)[0..size] });
return e;
}
@@ -95,8 +102,7 @@ pub const Entry = packed struct {
}
// Insert this entry into the tree at the given directory, updating parent sizes and item counts.
- // (TODO: This function creates an unrecoverable mess on OOM, need to do something better)
- pub fn insert(self: *Entry, parents: *const Parents) !void {
+ pub fn insert(self: *Entry, parents: *const Parents) void {
self.next = parents.top().sub;
parents.top().sub = self;
if (self.dir()) |d| std.debug.assert(d.sub == null);
@@ -121,7 +127,7 @@ pub const Entry = packed struct {
} else if (self.link()) |l| {
const n = HardlinkNode{ .ino = l.ino, .dir = p, .num_files = 1 };
- var d = try devices.items[dev].hardlinks.getOrPut(n);
+ var d = devices.items[dev].hardlinks.getOrPut(n) catch unreachable;
new_hl = !d.found_existing;
if (d.found_existing) d.entry.key.num_files += 1;
// First time we encounter this file in this dir, count it.
@@ -285,12 +291,12 @@ const Device = struct {
var devices: std.ArrayList(Device) = std.ArrayList(Device).init(main.allocator);
var dev_lookup: std.AutoHashMap(u64, DevId) = std.AutoHashMap(u64, DevId).init(main.allocator);
-pub fn getDevId(dev: u64) !DevId {
- var d = try dev_lookup.getOrPut(dev);
+pub fn getDevId(dev: u64) DevId {
+ var d = dev_lookup.getOrPut(dev) catch unreachable;
if (!d.found_existing) {
errdefer dev_lookup.removeAssertDiscard(dev);
d.entry.value = @intCast(DevId, devices.items.len);
- try devices.append(.{ .dev = dev });
+ devices.append(.{ .dev = dev }) catch unreachable;
}
return d.entry.value;
}
@@ -308,8 +314,8 @@ pub const Parents = struct {
const Self = @This();
- pub fn push(self: *Self, dir: *Dir) !void {
- return self.stack.append(dir);
+ pub fn push(self: *Self, dir: *Dir) void {
+ return self.stack.append(dir) catch unreachable;
}
// Attempting to remove the root node is considered a bug.
@@ -338,13 +344,14 @@ pub const Parents = struct {
return .{ .lst = self };
}
- pub fn path(self: *const Self, wr: anytype) !void {
+ // Append the path to the given arraylist. The list is assumed to use main.allocator, so it can't fail.
+ pub fn path(self: *const Self, out: *std.ArrayList(u8)) void {
const r = root.entry.name();
- try wr.writeAll(r);
+ out.appendSlice(r) catch unreachable;
var i: usize = 0;
while (i < self.stack.items.len) {
- if (i != 0 or r[r.len-1] != '/') try wr.writeByte('/');
- try wr.writeAll(self.stack.items[i].entry.name());
+ if (i != 0 or r[r.len-1] != '/') out.append('/') catch unreachable;
+ out.appendSlice(self.stack.items[i].entry.name()) catch unreachable;
i += 1;
}
}
diff --git a/src/scan.zig b/src/scan.zig
index b90b847..6323ce5 100644
--- a/src/scan.zig
+++ b/src/scan.zig
@@ -132,7 +132,7 @@ const Context = struct {
const Self = @This();
fn initFile(out: std.fs.File) !Self {
- var buf = try main.allocator.create(Writer);
+ var buf = main.allocator.create(Writer) catch unreachable;
errdefer main.allocator.destroy(buf);
buf.* = std.io.bufferedWriter(out.writer());
var wr = buf.writer();
@@ -154,13 +154,13 @@ const Context = struct {
}
// Add the name of the file/dir entry we're currently inspecting
- fn pushPath(self: *Self, name: []const u8) !void {
- try self.path_indices.append(self.path.items.len);
- if (self.path.items.len > 1) try self.path.append('/');
+ fn pushPath(self: *Self, name: []const u8) void {
+ self.path_indices.append(self.path.items.len) catch unreachable;
+ if (self.path.items.len > 1) self.path.append('/') catch unreachable;
const start = self.path.items.len;
- try self.path.appendSlice(name);
+ self.path.appendSlice(name) catch unreachable;
- try self.path.append(0);
+ self.path.append(0) catch unreachable;
self.name = self.path.items[start..self.path.items.len-1:0];
self.path.items.len -= 1;
}
@@ -177,7 +177,7 @@ const Context = struct {
}
fn pathZ(self: *Self) [:0]const u8 {
- return arrayListBufZ(&self.path) catch unreachable;
+ return arrayListBufZ(&self.path);
}
// Set a flag to indicate that there was an error listing file entries in the current directory.
@@ -195,12 +195,12 @@ const Context = struct {
if (t == .err) {
if (self.last_error) |p| main.allocator.free(p);
- self.last_error = try main.allocator.dupeZ(u8, self.path.items);
+ self.last_error = main.allocator.dupeZ(u8, self.path.items) catch unreachable;
}
if (self.parents) |*p| {
- var e = try model.Entry.create(.file, false, self.name);
- e.insert(p) catch unreachable;
+ var e = model.Entry.create(.file, false, self.name);
+ e.insert(p);
var f = e.file().?;
switch (t) {
.err => e.set_err(p),
@@ -233,10 +233,10 @@ const Context = struct {
const etype = if (self.stat.dir) model.EType.dir
else if (self.stat.hlinkc) model.EType.link
else model.EType.file;
- var e = try model.Entry.create(etype, main.config.extended, self.name);
+ var e = model.Entry.create(etype, main.config.extended, self.name);
e.blocks = self.stat.blocks;
e.size = self.stat.size;
- if (e.dir()) |d| d.dev = try model.getDevId(self.stat.dev);
+ if (e.dir()) |d| d.dev = model.getDevId(self.stat.dev);
if (e.file()) |f| f.notreg = !self.stat.dir and !self.stat.reg;
// TODO: Handle the scenario where we don't know the hard link count
// (i.e. on imports from old ncdu versions that don't have the "nlink" field)
@@ -249,8 +249,8 @@ const Context = struct {
if (self.items_seen == 0)
model.root = e.dir().?
else {
- try e.insert(p);
- if (e.dir()) |d| try p.push(d); // Enter the directory
+ e.insert(p);
+ if (e.dir()) |d| p.push(d); // Enter the directory
}
} else if (self.wr) |wr| {
@@ -286,8 +286,7 @@ const Context = struct {
var active_context: ?*Context = null;
// Read and index entries of the given dir.
-// (TODO: shouldn't error on OOM but instead call a function that waits or something)
-fn scanDir(ctx: *Context, dir: std.fs.Dir, dir_dev: u64) (std.fs.File.Writer.Error || std.mem.Allocator.Error)!void {
+fn scanDir(ctx: *Context, dir: std.fs.Dir, dir_dev: u64) std.fs.File.Writer.Error!void {
// XXX: The iterator allocates 8k+ bytes on the stack, may want to do heap allocation here?
var it = dir.iterate();
while(true) {
@@ -297,9 +296,9 @@ fn scanDir(ctx: *Context, dir: std.fs.Dir, dir_dev: u64) (std.fs.File.Writer.Err
} orelse break;
ctx.stat.dir = false;
- try ctx.pushPath(entry.name);
+ ctx.pushPath(entry.name);
defer ctx.popPath();
- try main.handleEvent(false, false);
+ main.handleEvent(false, false);
// XXX: This algorithm is extremely slow, can be optimized with some clever pattern parsing.
const excluded = blk: {
@@ -378,7 +377,7 @@ pub fn scanRoot(path: []const u8, out: ?std.fs.File) !void {
const full_path = std.fs.realpathAlloc(main.allocator, path) catch null;
defer if (full_path) |p| main.allocator.free(p);
- try ctx.pushPath(full_path orelse path);
+ ctx.pushPath(full_path orelse path);
ctx.stat = try Stat.read(std.fs.cwd(), ctx.pathZ(), true);
if (!ctx.stat.dir) return error.NotADirectory;
@@ -703,7 +702,7 @@ const Import = struct {
else => self.die("expected ',' or '}'"),
}
}
- if (name) |n| self.ctx.pushPath(n) catch unreachable
+ if (name) |n| self.ctx.pushPath(n)
else self.die("missing \"name\" field");
if (special) |s| self.ctx.addSpecial(s) catch unreachable
else self.ctx.addStat(dir_dev) catch unreachable;
@@ -733,7 +732,7 @@ const Import = struct {
self.ctx.popPath();
if ((self.ctx.items_seen & 1023) == 0)
- main.handleEvent(false, false) catch unreachable;
+ main.handleEvent(false, false);
}
fn root(self: *Self) void {
@@ -791,7 +790,7 @@ pub fn importRoot(path: [:0]const u8, out: ?std.fs.File) !void {
var animation_pos: u32 = 0;
var need_confirm_quit = false;
-fn drawBox() !void {
+fn drawBox() void {
ui.init();
const ctx = active_context.?;
const width = saturateSub(ui.cols, 5);
@@ -808,7 +807,7 @@ fn drawBox() !void {
box.move(3, 2);
ui.addstr("Current item: ");
- ui.addstr(try ui.shorten(try ui.toUtf8(ctx.pathZ()), saturateSub(width, 18)));
+ ui.addstr(ui.shorten(ui.toUtf8(ctx.pathZ()), saturateSub(width, 18)));
if (ctx.last_error) |path| {
box.move(5, 2);
@@ -816,7 +815,7 @@ fn drawBox() !void {
ui.addstr("Warning: ");
ui.style(.default);
ui.addstr("error scanning ");
- ui.addstr(try ui.shorten(try ui.toUtf8(path), saturateSub(width, 28)));
+ ui.addstr(ui.shorten(ui.toUtf8(path), saturateSub(width, 28)));
box.move(6, 3);
ui.addstr("some directory sizes may not be correct.");
}
@@ -855,7 +854,7 @@ fn drawBox() !void {
}
}
-pub fn draw() !void {
+pub fn draw() void {
switch (main.config.scan_ui) {
.none => {},
.line => {
@@ -873,11 +872,11 @@ pub fn draw() !void {
}
_ = std.io.getStdErr().write(line) catch {};
},
- .full => try drawBox(),
+ .full => drawBox(),
}
}
-pub fn keyInput(ch: i32) !void {
+pub fn keyInput(ch: i32) void {
if (need_confirm_quit) {
switch (ch) {
'y', 'Y' => if (need_confirm_quit) ui.quit(),
diff --git a/src/ui.zig b/src/ui.zig
index 4a2f761..f5b7a07 100644
--- a/src/ui.zig
+++ b/src/ui.zig
@@ -29,6 +29,24 @@ pub fn quit() noreturn {
std.process.exit(0);
}
+// Should be called when malloc fails. Will show a message to the user, wait
+// for a second and return to give it another try.
+// Glitch: this function may be called while we're in the process of drawing
+// the ncurses window, in which case the deinit/reinit will cause the already
+// drawn part to be discarded. A redraw will fix that, but that tends to only
+// happen after user input.
+// Also, init() and other ncurses-related functions may have hidden allocation,
+// no clue if ncurses will consistently report OOM, but we're not handling that
+// right now.
+pub fn oom() void {
+ const haveui = inited;
+ deinit();
+ _ = std.io.getStdErr().writer().writeAll("\x1b7\x1b[JOut of memory, trying again in 1 second. Hit Ctrl-C to abort.\x1b8") catch {};
+ std.time.sleep(std.time.ns_per_s);
+ if (haveui)
+ init();
+}
+
var to_utf8_buf = std.ArrayList(u8).init(main.allocator);
fn toUtf8BadChar(ch: u8) bool {
@@ -44,7 +62,7 @@ fn toUtf8BadChar(ch: u8) bool {
// internal buffer that will be invalidated on the next call.
// (Doesn't check for non-printable Unicode characters)
// (This program assumes that the console locale is UTF-8, but file names may not be)
-pub fn toUtf8(in: [:0]const u8) ![:0]const u8 {
+pub fn toUtf8(in: [:0]const u8) [:0]const u8 {
const hasBadChar = blk: {
for (in) |ch| if (toUtf8BadChar(ch)) break :blk true;
break :blk false;
@@ -56,16 +74,16 @@ pub fn toUtf8(in: [:0]const u8) ![:0]const u8 {
if (std.unicode.utf8ByteSequenceLength(in[i])) |cp_len| {
if (!toUtf8BadChar(in[i]) and i + cp_len <= in.len) {
if (std.unicode.utf8Decode(in[i .. i + cp_len])) |_| {
- try to_utf8_buf.appendSlice(in[i .. i + cp_len]);
+ to_utf8_buf.appendSlice(in[i .. i + cp_len]) catch unreachable;
i += cp_len;
continue;
} else |_| {}
}
} else |_| {}
- try to_utf8_buf.writer().print("\\x{X:0>2}", .{in[i]});
+ to_utf8_buf.writer().print("\\x{X:0>2}", .{in[i]}) catch unreachable;
i += 1;
}
- return try arrayListBufZ(&to_utf8_buf);
+ return arrayListBufZ(&to_utf8_buf);
}
var shorten_buf = std.ArrayList(u8).init(main.allocator);
@@ -75,7 +93,7 @@ var shorten_buf = std.ArrayList(u8).init(main.allocator);
// Input is assumed to be valid UTF-8.
// Return value points to the input string or to an internal buffer that is
// invalidated on a subsequent call.
-pub fn shorten(in: [:0]const u8, max_width: u32) ![:0] const u8 {
+pub fn shorten(in: [:0]const u8, max_width: u32) [:0] const u8 {
if (max_width < 4) return "...";
var total_width: u32 = 0;
var prefix_width: u32 = 0;
@@ -98,8 +116,8 @@ pub fn shorten(in: [:0]const u8, max_width: u32) ![:0] const u8 {
if (total_width <= max_width) return in;
shorten_buf.shrinkRetainingCapacity(0);
- try shorten_buf.appendSlice(in[0..prefix_end]);
- try shorten_buf.appendSlice("...");
+ shorten_buf.appendSlice(in[0..prefix_end]) catch unreachable;
+ shorten_buf.appendSlice("...") catch unreachable;
var start_width: u32 = prefix_width;
var start_len: u32 = prefix_end;
@@ -111,15 +129,15 @@ pub fn shorten(in: [:0]const u8, max_width: u32) ![:0] const u8 {
start_width += cp_width;
start_len += cp_len;
if (total_width - start_width <= max_width - prefix_width - 3) {
- try shorten_buf.appendSlice(in[start_len..]);
+ shorten_buf.appendSlice(in[start_len..]) catch unreachable;
break;
}
}
- return try arrayListBufZ(&shorten_buf);
+ return arrayListBufZ(&shorten_buf);
}
fn shortenTest(in: [:0]const u8, max_width: u32, out: [:0]const u8) !void {
- try std.testing.expectEqualStrings(out, try shorten(in, max_width));
+ try std.testing.expectEqualStrings(out, shorten(in, max_width));
}
test "shorten" {
diff --git a/src/util.zig b/src/util.zig
index 9b73de5..277a006 100644
--- a/src/util.zig
+++ b/src/util.zig
@@ -38,8 +38,8 @@ pub fn blocksToSize(b: u64) u64 {
// Ensure the given arraylist buffer gets zero-terminated and returns a slice
// into the buffer. The returned buffer is invalidated whenever the arraylist
// is freed or written to.
-pub fn arrayListBufZ(buf: *std.ArrayList(u8)) ![:0]const u8 {
- try buf.append(0);
+pub fn arrayListBufZ(buf: *std.ArrayList(u8)) [:0]const u8 {
+ buf.append(0) catch unreachable;
defer buf.items.len -= 1;
return buf.items[0..buf.items.len-1:0];
}