synthetic: make bytes generation more flexible (#9204)
commit
af05397219
|
|
@ -1,4 +1,4 @@
|
||||||
/// Generates bytes.
|
//! Generates bytes.
|
||||||
const Bytes = @This();
|
const Bytes = @This();
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
@ -7,9 +7,7 @@ const Generator = @import("Generator.zig");
|
||||||
/// Random number generator.
|
/// Random number generator.
|
||||||
rand: std.Random,
|
rand: std.Random,
|
||||||
|
|
||||||
/// The minimum and maximum length of the generated bytes. The maximum
|
/// The minimum and maximum length of the generated bytes.
|
||||||
/// length will be capped to the length of the buffer passed in if the
|
|
||||||
/// buffer length is smaller.
|
|
||||||
min_len: usize = 1,
|
min_len: usize = 1,
|
||||||
max_len: usize = std.math.maxInt(usize),
|
max_len: usize = std.math.maxInt(usize),
|
||||||
|
|
||||||
|
|
@ -18,23 +16,79 @@ max_len: usize = std.math.maxInt(usize),
|
||||||
/// side effect of the generator, not an intended use case.
|
/// side effect of the generator, not an intended use case.
|
||||||
alphabet: ?[]const u8 = null,
|
alphabet: ?[]const u8 = null,
|
||||||
|
|
||||||
/// Predefined alphabets.
|
/// Generate an alphabet given a function that returns true/false for a
|
||||||
pub const Alphabet = struct {
|
/// given byte.
|
||||||
pub const ascii = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-=[]{}|;':\\\",./<>?`~";
|
pub fn generateAlphabet(comptime func: fn (u8) bool) []const u8 {
|
||||||
};
|
@setEvalBranchQuota(3000);
|
||||||
|
var count = 0;
|
||||||
|
for (0..256) |c| {
|
||||||
|
if (func(c)) count += 1;
|
||||||
|
}
|
||||||
|
var alphabet: [count]u8 = undefined;
|
||||||
|
var i = 0;
|
||||||
|
for (0..256) |c| {
|
||||||
|
if (func(c)) {
|
||||||
|
alphabet[i] = c;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const result = alphabet;
|
||||||
|
return &result;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn generator(self: *Bytes) Generator {
|
pub fn generator(self: *Bytes) Generator {
|
||||||
return .init(self, next);
|
return .init(self, next);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next(self: *Bytes, writer: *std.Io.Writer, max_len: usize) Generator.Error!void {
|
/// Return a copy of the Bytes, but with a new alphabet.
|
||||||
std.debug.assert(max_len >= 1);
|
pub fn newAlphabet(self: *const Bytes, new_alphabet: ?[]const u8) Bytes {
|
||||||
const len = @min(
|
return .{
|
||||||
self.rand.intRangeAtMostBiased(usize, self.min_len, self.max_len),
|
.rand = self.rand,
|
||||||
max_len,
|
.alphabet = new_alphabet,
|
||||||
);
|
.min_len = self.min_len,
|
||||||
|
.max_len = self.max_len,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a copy of the Bytes, but with a new min_len. The new min
|
||||||
|
/// len cannot be more than the previous max_len.
|
||||||
|
pub fn atLeast(self: *const Bytes, new_min_len: usize) Bytes {
|
||||||
|
return .{
|
||||||
|
.rand = self.rand,
|
||||||
|
.alphabet = self.alphabet,
|
||||||
|
.min_len = @min(self.max_len, new_min_len),
|
||||||
|
.max_len = self.max_len,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a copy of the Bytes, but with a new max_len. The new max_len cannot
|
||||||
|
/// be more the previous max_len.
|
||||||
|
pub fn atMost(self: *const Bytes, new_max_len: usize) Bytes {
|
||||||
|
return .{
|
||||||
|
.rand = self.rand,
|
||||||
|
.alphabet = self.alphabet,
|
||||||
|
.min_len = @min(self.min_len, @min(self.max_len, new_max_len)),
|
||||||
|
.max_len = @min(self.max_len, new_max_len),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(self: *const Bytes, writer: *std.Io.Writer, max_len: usize) std.Io.Writer.Error!void {
|
||||||
|
_ = try self.atMost(max_len).write(writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format(self: *const Bytes, writer: *std.Io.Writer) std.Io.Writer.Error!void {
|
||||||
|
_ = try self.write(writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write some random data and return the number of bytes written.
|
||||||
|
pub fn write(self: *const Bytes, writer: *std.Io.Writer) std.Io.Writer.Error!usize {
|
||||||
|
std.debug.assert(self.min_len >= 1);
|
||||||
|
std.debug.assert(self.max_len >= self.min_len);
|
||||||
|
|
||||||
|
const len = self.rand.intRangeAtMostBiased(usize, self.min_len, self.max_len);
|
||||||
|
|
||||||
var buf: [8]u8 = undefined;
|
var buf: [8]u8 = undefined;
|
||||||
|
|
||||||
var remaining = len;
|
var remaining = len;
|
||||||
while (remaining > 0) {
|
while (remaining > 0) {
|
||||||
const data = buf[0..@min(remaining, buf.len)];
|
const data = buf[0..@min(remaining, buf.len)];
|
||||||
|
|
@ -45,6 +99,8 @@ pub fn next(self: *Bytes, writer: *std.Io.Writer, max_len: usize) Generator.Erro
|
||||||
try writer.writeAll(data);
|
try writer.writeAll(data);
|
||||||
remaining -= data.len;
|
remaining -= data.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
test "bytes" {
|
test "bytes" {
|
||||||
|
|
@ -52,9 +108,11 @@ test "bytes" {
|
||||||
var prng = std.Random.DefaultPrng.init(0);
|
var prng = std.Random.DefaultPrng.init(0);
|
||||||
var buf: [256]u8 = undefined;
|
var buf: [256]u8 = undefined;
|
||||||
var writer: std.Io.Writer = .fixed(&buf);
|
var writer: std.Io.Writer = .fixed(&buf);
|
||||||
var v: Bytes = .{ .rand = prng.random() };
|
var v: Bytes = .{
|
||||||
v.min_len = buf.len;
|
.rand = prng.random(),
|
||||||
v.max_len = buf.len;
|
.min_len = buf.len,
|
||||||
|
.max_len = buf.len,
|
||||||
|
};
|
||||||
const gen = v.generator();
|
const gen = v.generator();
|
||||||
try gen.next(&writer, buf.len);
|
try gen.next(&writer, buf.len);
|
||||||
try testing.expectEqual(buf.len, writer.buffered().len);
|
try testing.expectEqual(buf.len, writer.buffered().len);
|
||||||
|
|
|
||||||
|
|
@ -35,19 +35,26 @@ p_valid: f64 = 1.0,
|
||||||
p_valid_kind: std.enums.EnumArray(ValidKind, f64) = .initFill(1.0),
|
p_valid_kind: std.enums.EnumArray(ValidKind, f64) = .initFill(1.0),
|
||||||
p_invalid_kind: std.enums.EnumArray(InvalidKind, f64) = .initFill(1.0),
|
p_invalid_kind: std.enums.EnumArray(InvalidKind, f64) = .initFill(1.0),
|
||||||
|
|
||||||
/// The alphabet for random bytes (omitting 0x1B and 0x07).
|
fn checkKvAlphabet(c: u8) bool {
|
||||||
const bytes_alphabet: []const u8 = alphabet: {
|
return switch (c) {
|
||||||
var alphabet: [256]u8 = undefined;
|
std.ascii.control_code.esc, std.ascii.control_code.bel, ';', '=' => false,
|
||||||
for (0..alphabet.len) |i| {
|
else => std.ascii.isPrint(c),
|
||||||
if (i == 0x1B or i == 0x07) {
|
};
|
||||||
alphabet[i] = @intCast(i + 1);
|
}
|
||||||
} else {
|
|
||||||
alphabet[i] = @intCast(i);
|
/// The alphabet for random bytes in OSC key/value pairs (omitting 0x1B,
|
||||||
}
|
/// 0x07, ';', '=').
|
||||||
}
|
pub const kv_alphabet = Bytes.generateAlphabet(checkKvAlphabet);
|
||||||
const result = alphabet;
|
|
||||||
break :alphabet &result;
|
fn checkOscAlphabet(c: u8) bool {
|
||||||
};
|
return switch (c) {
|
||||||
|
std.ascii.control_code.esc, std.ascii.control_code.bel => false,
|
||||||
|
else => true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The alphabet for random bytes in OSCs (omitting 0x1B and 0x07).
|
||||||
|
pub const osc_alphabet = Bytes.generateAlphabet(checkOscAlphabet);
|
||||||
|
|
||||||
pub fn generator(self: *Osc) Generator {
|
pub fn generator(self: *Osc) Generator {
|
||||||
return .init(self, next);
|
return .init(self, next);
|
||||||
|
|
@ -99,35 +106,43 @@ fn nextUnwrapped(self: *Osc, writer: *std.Io.Writer, max_len: usize) Generator.E
|
||||||
|
|
||||||
fn nextUnwrappedValidExact(self: *const Osc, writer: *std.Io.Writer, k: ValidKind, max_len: usize) Generator.Error!void {
|
fn nextUnwrappedValidExact(self: *const Osc, writer: *std.Io.Writer, k: ValidKind, max_len: usize) Generator.Error!void {
|
||||||
switch (k) {
|
switch (k) {
|
||||||
.change_window_title => {
|
.change_window_title => change_window_title: {
|
||||||
try writer.writeAll("0;"); // Set window title
|
if (max_len < 3) break :change_window_title;
|
||||||
var bytes_gen = self.bytes();
|
try writer.print("0;{f}", .{self.bytes().atMost(max_len - 3)}); // Set window title
|
||||||
try bytes_gen.next(writer, max_len - 2);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
.prompt_start => {
|
.prompt_start => prompt_start: {
|
||||||
|
if (max_len < 4) break :prompt_start;
|
||||||
|
var remaining = max_len;
|
||||||
|
|
||||||
try writer.writeAll("133;A"); // Start prompt
|
try writer.writeAll("133;A"); // Start prompt
|
||||||
|
remaining -= 4;
|
||||||
|
|
||||||
// aid
|
// aid
|
||||||
if (self.rand.boolean()) {
|
if (self.rand.boolean()) aid: {
|
||||||
var bytes_gen = self.bytes();
|
if (remaining < 6) break :aid;
|
||||||
bytes_gen.max_len = 16;
|
|
||||||
try writer.writeAll(";aid=");
|
try writer.writeAll(";aid=");
|
||||||
try bytes_gen.next(writer, max_len);
|
remaining -= 5;
|
||||||
|
remaining -= try self.bytes().newAlphabet(kv_alphabet).atMost(@min(16, remaining)).write(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// redraw
|
// redraw
|
||||||
if (self.rand.boolean()) {
|
if (self.rand.boolean()) redraw: {
|
||||||
|
if (remaining < 9) break :redraw;
|
||||||
try writer.writeAll(";redraw=");
|
try writer.writeAll(";redraw=");
|
||||||
if (self.rand.boolean()) {
|
if (self.rand.boolean()) {
|
||||||
try writer.writeAll("1");
|
try writer.writeAll("1");
|
||||||
} else {
|
} else {
|
||||||
try writer.writeAll("0");
|
try writer.writeAll("0");
|
||||||
}
|
}
|
||||||
|
remaining -= 9;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
.prompt_end => try writer.writeAll("133;B"), // End prompt
|
.prompt_end => prompt_end: {
|
||||||
|
if (max_len < 4) break :prompt_end;
|
||||||
|
try writer.writeAll("133;B"); // End prompt
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,14 +154,11 @@ fn nextUnwrappedInvalidExact(
|
||||||
) Generator.Error!void {
|
) Generator.Error!void {
|
||||||
switch (k) {
|
switch (k) {
|
||||||
.random => {
|
.random => {
|
||||||
var bytes_gen = self.bytes();
|
try self.bytes().atMost(max_len).format(writer);
|
||||||
try bytes_gen.next(writer, max_len);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
.good_prefix => {
|
.good_prefix => {
|
||||||
try writer.writeAll("133;");
|
try writer.print("133;{f}", .{self.bytes().atMost(max_len - 4)});
|
||||||
var bytes_gen = self.bytes();
|
|
||||||
try bytes_gen.next(writer, max_len - 4);
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -154,7 +166,7 @@ fn nextUnwrappedInvalidExact(
|
||||||
fn bytes(self: *const Osc) Bytes {
|
fn bytes(self: *const Osc) Bytes {
|
||||||
return .{
|
return .{
|
||||||
.rand = self.rand,
|
.rand = self.rand,
|
||||||
.alphabet = bytes_alphabet,
|
.alphabet = osc_alphabet,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,21 @@ const Ascii = @This();
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const synthetic = @import("../main.zig");
|
const Bytes = @import("../Bytes.zig");
|
||||||
|
|
||||||
const log = std.log.scoped(.@"terminal-stream-bench");
|
const log = std.log.scoped(.@"terminal-stream-bench");
|
||||||
|
|
||||||
pub const Options = struct {};
|
pub const Options = struct {};
|
||||||
|
|
||||||
|
fn checkAsciiAlphabet(c: u8) bool {
|
||||||
|
return switch (c) {
|
||||||
|
' ' => false,
|
||||||
|
else => std.ascii.isPrint(c),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const ascii = Bytes.generateAlphabet(checkAsciiAlphabet);
|
||||||
|
|
||||||
/// Create a new terminal stream handler for the given arguments.
|
/// Create a new terminal stream handler for the given arguments.
|
||||||
pub fn create(
|
pub fn create(
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
|
|
@ -23,12 +32,10 @@ pub fn destroy(self: *Ascii, alloc: Allocator) void {
|
||||||
alloc.destroy(self);
|
alloc.destroy(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(self: *Ascii, writer: *std.Io.Writer, rand: std.Random) !void {
|
pub fn run(_: *Ascii, writer: *std.Io.Writer, rand: std.Random) !void {
|
||||||
_ = self;
|
var gen: Bytes = .{
|
||||||
|
|
||||||
var gen: synthetic.Bytes = .{
|
|
||||||
.rand = rand,
|
.rand = rand,
|
||||||
.alphabet = synthetic.Bytes.Alphabet.ascii,
|
.alphabet = ascii,
|
||||||
};
|
};
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue