synthetic: add osc/utf8 generators

This commit is contained in:
Mitchell Hashimoto
2025-07-09 15:00:48 -07:00
parent a28b7e9205
commit a09452bf1b
4 changed files with 158 additions and 6 deletions

View File

@ -7,6 +7,8 @@ const cli = @import("../cli.zig");
/// predictably named files under `cli/`. /// predictably named files under `cli/`.
pub const Action = enum { pub const Action = enum {
ascii, ascii,
osc,
utf8,
/// Returns the struct associated with the action. The struct /// Returns the struct associated with the action. The struct
/// should have a few decls: /// should have a few decls:
@ -19,6 +21,8 @@ pub const Action = enum {
pub fn Struct(comptime action: Action) type { pub fn Struct(comptime action: Action) type {
return switch (action) { return switch (action) {
.ascii => @import("cli/Ascii.zig"), .ascii => @import("cli/Ascii.zig"),
.osc => @import("cli/Osc.zig"),
.utf8 => @import("cli/Utf8.zig"),
}; };
} }
}; };
@ -93,3 +97,12 @@ fn mainActionImpl(
defer impl.destroy(alloc); defer impl.destroy(alloc);
try impl.run(writer, rand); try impl.run(writer, rand);
} }
test {
// Make sure we ref all our actions
inline for (@typeInfo(Action).@"enum".fields) |field| {
const action = @field(Action, field.name);
const Impl = Action.Struct(action);
_ = Impl;
}
}

View File

@ -1,6 +1,3 @@
//! This benchmark tests the throughput of grapheme break calculation.
//! This is a common operation in terminal character printing for terminals
//! that support grapheme clustering.
const Ascii = @This(); const Ascii = @This();
const std = @import("std"); const std = @import("std");
@ -37,9 +34,13 @@ pub fn run(self: *Ascii, writer: anytype, rand: std.Random) !void {
var buf: [1024]u8 = undefined; var buf: [1024]u8 = undefined;
while (true) { while (true) {
const data = try gen.next(&buf); const data = try gen.next(&buf);
writer.writeAll(data) catch |err| switch (err) { writer.writeAll(data) catch |err| {
error.BrokenPipe => return, // stdout closed const Error = error{ NoSpaceLeft, BrokenPipe } || @TypeOf(err);
else => return err, switch (@as(Error, err)) {
error.BrokenPipe => return, // stdout closed
error.NoSpaceLeft => return, // fixed buffer full
else => return err,
}
}; };
} }
} }
@ -50,4 +51,13 @@ test Ascii {
const impl: *Ascii = try .create(alloc, .{}); const impl: *Ascii = try .create(alloc, .{});
defer impl.destroy(alloc); defer impl.destroy(alloc);
var prng = std.Random.DefaultPrng.init(1);
const rand = prng.random();
var buf: [1024]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buf);
const writer = fbs.writer();
try impl.run(writer, rand);
} }

67
src/synthetic/cli/Osc.zig Normal file
View File

@ -0,0 +1,67 @@
const Osc = @This();
const std = @import("std");
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const synthetic = @import("../main.zig");
const log = std.log.scoped(.@"terminal-stream-bench");
pub const Options = struct {
/// Probability of generating a valid value.
@"p-valid": f64 = 0.5,
};
opts: Options,
/// Create a new terminal stream handler for the given arguments.
pub fn create(
alloc: Allocator,
opts: Options,
) !*Osc {
const ptr = try alloc.create(Osc);
errdefer alloc.destroy(ptr);
ptr.* = .{ .opts = opts };
return ptr;
}
pub fn destroy(self: *Osc, alloc: Allocator) void {
alloc.destroy(self);
}
pub fn run(self: *Osc, writer: anytype, rand: std.Random) !void {
var gen: synthetic.Osc = .{
.rand = rand,
.p_valid = self.opts.@"p-valid",
};
var buf: [1024]u8 = undefined;
while (true) {
const data = try gen.next(&buf);
writer.writeAll(data) catch |err| {
const Error = error{ NoSpaceLeft, BrokenPipe } || @TypeOf(err);
switch (@as(Error, err)) {
error.BrokenPipe => return, // stdout closed
error.NoSpaceLeft => return, // fixed buffer full
else => return err,
}
};
}
}
test Osc {
const testing = std.testing;
const alloc = testing.allocator;
const impl: *Osc = try .create(alloc, .{});
defer impl.destroy(alloc);
var prng = std.Random.DefaultPrng.init(1);
const rand = prng.random();
var buf: [1024]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buf);
const writer = fbs.writer();
try impl.run(writer, rand);
}

View File

@ -0,0 +1,62 @@
const Utf8 = @This();
const std = @import("std");
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const synthetic = @import("../main.zig");
const log = std.log.scoped(.@"terminal-stream-bench");
pub const Options = struct {};
/// Create a new terminal stream handler for the given arguments.
pub fn create(
alloc: Allocator,
_: Options,
) !*Utf8 {
const ptr = try alloc.create(Utf8);
errdefer alloc.destroy(ptr);
return ptr;
}
pub fn destroy(self: *Utf8, alloc: Allocator) void {
alloc.destroy(self);
}
pub fn run(self: *Utf8, writer: anytype, rand: std.Random) !void {
_ = self;
var gen: synthetic.Utf8 = .{
.rand = rand,
};
var buf: [1024]u8 = undefined;
while (true) {
const data = try gen.next(&buf);
writer.writeAll(data) catch |err| {
const Error = error{ NoSpaceLeft, BrokenPipe } || @TypeOf(err);
switch (@as(Error, err)) {
error.BrokenPipe => return, // stdout closed
error.NoSpaceLeft => return, // fixed buffer full
else => return err,
}
};
}
}
test Utf8 {
const testing = std.testing;
const alloc = testing.allocator;
const impl: *Utf8 = try .create(alloc, .{});
defer impl.destroy(alloc);
var prng = std.Random.DefaultPrng.init(1);
const rand = prng.random();
var buf: [1024]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buf);
const writer = fbs.writer();
try impl.run(writer, rand);
}