Zig Utility Library
The purpose of this library is to enhance Zig's standard library. Much of zul wraps Zig's std to provide simpler APIs for common tasks (e.g. reading lines from a file). In other cases, new functionality has been added (e.g. a UUID type).
Besides Zig's standard library, there are no dependencies. Most functionality is contained within its own file and can easily be copy and pasted into an existing library or project.
zul.benchmark.run
Simple benchmarking function.
example
docs
const HAYSTACK = "abcdefghijklmnopqrstvuwxyz0123456789";
pub fn main() !void {
(try zul.benchmark.run(indexOfScalar, .{})).print("indexOfScalar");
(try zul.benchmark.run(lastIndexOfScalar, .{})).print("lastIndexOfScalar");
}
fn indexOfScalar(_: Allocator, _: *std.time.Timer) !void {
const i = std.mem.indexOfScalar(u8, HAYSTACK, '9').?;
if (i != 35) {
@panic("fail");
}
}
fn lastIndexOfScalar(_: Allocator, _: *std.time.Timer) !void {
const i = std.mem.lastIndexOfScalar(u8, HAYSTACK, 'a').?;
if (i != 0) {
@panic("fail");
}
}
zul.CommandLineArgs
A simple command line parser.
example
docs
var args = try zul.CommandLineArgs.parse(allocator);
defer args.deinit();
if (args.contains("version")) {
os.exit(0);
}
const host = args.get("host") orelse "127.0.0.1";
...
zul.DateTime
Simple (no leap seconds, UTC-only), DateTime, Date and Time types.
example
docs
const dt = try zul.DateTime.parse("2028-11-05T23:29:10Z", .rfc3339);
const next_week = try dt.add(7, .days);
std.debug.assert(next_week.order(dt) == .gt);
std.debug.print("{d} == {s}", .{dt.unix(.milliseconds), dt});
zul.fs.readDir
Iterates, non-recursively, through a directory.
example
docs
var it = try zul.fs.readDir("/tmp/dir");
defer it.deinit();
while (try it.next()) |entry| {
std.debug.print("{s} {any}\n", .{entry.name, entry.kind});
}
it.reset();
const sorted_entries = try it.all(allocator, .dir_first);
for (sorted_entries) |entry| {
std.debug.print("{s} {any}\n", .{entry.name, entry.kind});
}
zul.fs.readJson
Reads and parses a JSON file.
example
docs
const managed_user = try zul.fs.readJson(User, allocator, "/tmp/data.json", .{});
defer managed_user.deinit();
const user = managed_user.value;
zul.fs.readLines
Iterate over the lines in a file.
example
docs
var line_buffer: [1024]u8 = undefined;
var it = try zul.fs.readLines("/tmp/data.txt", &line_buffer, .{});
defer it.deinit();
while (try it.next()) |line| {
std.debug.print("line: {s}\n", .{line});
}
zul.http.Client
A wrapper around std.http.Client to make it easier to create requests and consume responses.
example
docs
var client = zul.http.Client.init(allocator);
defer client.deinit();
var req = try client.request("https://api.github.com/search/topics");
defer req.deinit();
try req.query("q", "zig");
try req.header("Authorization", "Your Token");
var res = try req.getResponse(.{});
if (res.status != 200) {
return;
}
const managed = try res.json(SearchResult, allocator, .{});
defer managed.deinit();
const search_result = managed.value;
zul.JsonString
Allows the embedding of already-encoded JSON strings into objects in order to avoid double encoded values.
example
docs
const an_encoded_json_value = "{\"over\": 9000}";
const str = try std.json.stringifyAlloc(allocator, .{
.name = "goku",
.power = zul.jsonString(an_encoded_json_value),
}, .{});
zul.pool
A thread-safe object pool which will dynamically grow when empty and revert to the configured size.
example
docs
var pool = try zul.pool.Growing(Expensive, usize).init(allocator, 10_000, .{.count = 100});
defer pool.deinit();
var exp1 = try pool.acquire();
defer pool.release(exp1);
...
const Expensive = struct {
pub fn init(allocator: Allocator, size: usize) !Expensive {
return .{
};
}
pub fn deinit(self: *Expensive) void {
}
pub fn reset(self: *Expensive) void {
}
};
zul.Scheduler
Ephemeral thread-based task scheduler used to run tasks at a specific time.
example
docs
const Task = union(enum) {
say: []const u8,
pub fn run(task: Task, ctx: void, at: i64) void {
_ = at;
_ ctx;
switch (task) {
.say => |msg| {std.debug.print("{s}\n", .{msg}),
}
}
}
...
var s = zul.Scheduler(Task, void).init(allocator);
defer s.deinit();
try s.start({});
try s.scheduleIn(.{.say = "world"}, std.time.ms_per_s * 5);
try s.schedule(.{.say = "hello"}, std.time.milliTimestamp() + 100);
zul.sort
Helpers for sorting strings and integers
example
docs
var values = [_][]const u8{"ABC", "abc", "Dog", "Cat", "horse", "chicken"};
zul.sort.strings(&values, .asc);
zul.sort.asciiIgnoreCase(&values, .desc);
var numbers = [_]i32{10, -20, 33, 0, 2, 6};
zul.sort.numbers(i32, &numbers, .asc);
zul.StringBuilder
Efficiently create/concat strings or binary data, optionally using a thread-safe pool with pre-allocated static buffers.
example
docs
var sb = zul.StringBuilder.init(allocator);
defer sb.deinit();
var view = try sb.skip(4);
try sb.writeByte(10);
try sb.write("hello");
view.writeU32Big(@intCast(sb.len() - 4));
std.debug.print("{any}\n", .{sb.string()});
const t = zul.testing;
test "memcpy" {
defer t.reset();
var buf = try t.arena.allocator().alloc(u8, 5);
try t.expectEqual(5, buf.len);
@memcpy(buf[0..5], "hello");
try t.expectEqual("hello", buf);
}
zul.ThreadPool
Lightweight thread pool with back-pressure and zero allocations after initialization.
example
docs
var tp = try zul.ThreadPool(someTask).init(allocator, .{.count = 4, .backlog = 500});
defer tp.deinit(allocator);
tp.spawn(.{1, true});
fn someTask(i: i32, allow: bool) void {
}
zul.UUID
Parse and generate version 4 and version 7 UUIDs.
example
docs
const uuid1 = zul.UUID.v4();
const hex = uuid1.toHex(.lower);
const uuid2 = try zul.UUID.parse("761e3a9d-4f92-4e0d-9d67-054425c2b5c3");
std.debug.print("{any}\n", uuid1.eql(uuid2));
const uuid3 = zul.UUID.v7();
try std.json.stringify(.{.id = uuid3}, .{}, writer);