human review: Integer.zig
This commit is contained in:
parent
1209a0d80b
commit
880c2e657f
8 changed files with 101 additions and 71 deletions
|
|
@ -25,24 +25,14 @@ width: BitWidth = .bits64,
|
|||
/// Whether the top bit is a sign.
|
||||
signedness: Signedness = .signed,
|
||||
|
||||
/// All bits set within the width.
|
||||
pub fn mask(self: Integer) u128 {
|
||||
return self.width.mask();
|
||||
}
|
||||
|
||||
/// The width in bits.
|
||||
pub fn bits(self: Integer) u8 {
|
||||
return self.width.bits();
|
||||
}
|
||||
|
||||
/// The top bit's position, whether or not this value treats it as a sign.
|
||||
pub fn topBit(self: Integer) u128 {
|
||||
return @as(u128, 1) << @intCast(self.bits() - 1);
|
||||
return @as(u128, 1) << @intCast(self.width.bits() - 1);
|
||||
}
|
||||
|
||||
/// The pattern, truncated to the width.
|
||||
pub fn masked(self: Integer) u128 {
|
||||
return self.raw & self.mask();
|
||||
return self.raw & self.width.mask();
|
||||
}
|
||||
|
||||
/// True when the pattern denotes a negative number. An unsigned value has no
|
||||
|
|
@ -55,7 +45,7 @@ pub fn isNegative(self: Integer) bool {
|
|||
/// Interpret as a signed value, sign-extended from the width.
|
||||
pub fn signedValue(self: Integer) i128 {
|
||||
const m = self.masked();
|
||||
if (self.isNegative()) return @bitCast(m | ~self.mask());
|
||||
if (self.isNegative()) return @bitCast(m | ~self.width.mask());
|
||||
return @intCast(m);
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +57,7 @@ pub fn unsignedValue(self: Integer) u128 {
|
|||
/// The same width and signedness, a different pattern. How operations return their
|
||||
/// result without restating the type.
|
||||
pub fn withRaw(self: Integer, raw: u128) Integer {
|
||||
return .{ .raw = raw & self.mask(), .width = self.width, .signedness = self.signedness };
|
||||
return .{ .raw = raw & self.width.mask(), .width = self.width, .signedness = self.signedness };
|
||||
}
|
||||
|
||||
/// True when two values are the same kind of integer, so an operation between them
|
||||
|
|
@ -83,12 +73,25 @@ pub fn sameTypeAs(self: Integer, other: Integer) bool {
|
|||
// frontends held an `Integer` and then handed the pieces over, so nothing stopped a
|
||||
// pattern being rendered at a width it did not come from.
|
||||
//
|
||||
// `as` returns a view, which is the value plus its formatting options and a `format`
|
||||
// method, so a caller prints it: `w.print("hex: {f}\n", .{int.as(.hex, .{})})`. A
|
||||
// caller that wants bytes calls `render` with a buffer, which is a `Writer.fixed`
|
||||
// underneath. Writing into a buffer too small for the result is
|
||||
// `error.WriteFailed` rather than a walk off the end, which is what the previous
|
||||
// buffer-indexing version did.
|
||||
// `{f}` on the value gives plain decimal. `fmt` returns a `Format`, which is the
|
||||
// value plus its options with a `format` method, so a caller prints that instead:
|
||||
// `w.print("hex: {f}\n", .{int.fmt(.hex, .{})})`. This is the shape `std.ascii`
|
||||
// and `std.zig` use for the same job (`HexEscape`, `FormatId` and its nested
|
||||
// options struct).
|
||||
//
|
||||
// A caller that wants bytes rather than a writer calls `render` with a buffer, which
|
||||
// is a `Writer.fixed` underneath. Too small a buffer is `error.WriteFailed` rather
|
||||
// than a walk off the end, which is what the previous buffer-indexing version did.
|
||||
|
||||
/// Print as decimal, read by the value's own signedness, without separators: the
|
||||
/// number, unadorned. `Notation` below covers the deliberate alternates.
|
||||
pub fn format(self: Integer, w: *Writer) Writer.Error!void {
|
||||
const notation: Notation = switch (self.signedness) {
|
||||
.signed => .decimal_signed,
|
||||
.unsigned => .decimal_unsigned,
|
||||
};
|
||||
return self.fmt(notation, .{ .separators = false }).format(w);
|
||||
}
|
||||
|
||||
/// The notations a fixed-width integer renders in.
|
||||
pub const Notation = enum {
|
||||
|
|
@ -104,8 +107,10 @@ pub const Notation = enum {
|
|||
decimal_unsigned,
|
||||
};
|
||||
|
||||
/// How a notation is decorated.
|
||||
pub const Options = struct {
|
||||
/// How a notation is decorated. Not `std.fmt.Options`, which is the specifier side
|
||||
/// of a placeholder (`fill`, `width`, `alignment`, `precision`); these are the
|
||||
/// value's own display choices.
|
||||
pub const FormatOptions = struct {
|
||||
/// Group digits for reading: a space per byte in hex and ASCII, per nibble in
|
||||
/// binary, per three digits in octal, and thousands commas in decimal.
|
||||
separators: bool = true,
|
||||
|
|
@ -118,29 +123,29 @@ pub const Options = struct {
|
|||
endian: std.builtin.Endian = .big,
|
||||
|
||||
/// What the clipboard wants: no separators, and a prefix where one exists.
|
||||
pub const plain: Options = .{ .separators = false, .prefix = true };
|
||||
pub const plain: FormatOptions = .{ .separators = false, .prefix = true };
|
||||
};
|
||||
|
||||
/// This value in `notation`, ready to print.
|
||||
pub fn as(self: Integer, notation: Notation, options: Options) View {
|
||||
pub fn fmt(self: Integer, notation: Notation, options: FormatOptions) Format {
|
||||
return .{ .value = self, .notation = notation, .options = options };
|
||||
}
|
||||
|
||||
pub const View = struct {
|
||||
pub const Format = struct {
|
||||
value: Integer,
|
||||
notation: Notation,
|
||||
options: Options,
|
||||
options: FormatOptions,
|
||||
|
||||
/// Render into `w`. This is the `{f}` implementation, so `int.as(.hex, .{})`
|
||||
/// Render into `w`. This is the `{f}` implementation, so `int.fmt(.hex, .{})`
|
||||
/// prints directly.
|
||||
pub fn format(self: View, w: *Writer) Writer.Error!void {
|
||||
pub fn format(self: Format, w: *Writer) Writer.Error!void {
|
||||
const value = self.value.masked();
|
||||
switch (self.notation) {
|
||||
.hex => {
|
||||
if (self.options.prefix) try w.writeAll("0x");
|
||||
if (self.options.prefix or !self.options.separators) {
|
||||
// Canonical, zero-padded to the width: two digits per byte.
|
||||
try w.printInt(value, 16, .upper, .{ .fill = '0', .width = self.value.bits() / 4 });
|
||||
try w.printInt(value, 16, .upper, .{ .fill = '0', .width = self.value.width.bits() / 4 });
|
||||
} else {
|
||||
// A byte at a time in `endian` order, space-separated. Only the
|
||||
// byte order swaps; nibbles within a byte are always high-low.
|
||||
|
|
@ -172,7 +177,7 @@ pub const View = struct {
|
|||
},
|
||||
.binary => {
|
||||
if (self.options.prefix) try w.writeAll("0b");
|
||||
const width: usize = self.value.bits();
|
||||
const width: usize = self.value.width.bits();
|
||||
if (!self.options.separators) {
|
||||
try w.printInt(value, 2, .lower, .{ .fill = '0', .width = width });
|
||||
} else {
|
||||
|
|
@ -201,13 +206,13 @@ pub const View = struct {
|
|||
|
||||
/// Render into `buf` and return what was written. For callers that need a slice,
|
||||
/// such as the TUI, which measures and positions text before drawing it.
|
||||
pub fn render(self: View, buf: []u8) Writer.Error![]const u8 {
|
||||
pub fn render(self: Format, buf: []u8) Writer.Error![]const u8 {
|
||||
var w = Writer.fixed(buf);
|
||||
try self.format(&w);
|
||||
return w.buffered();
|
||||
}
|
||||
|
||||
fn printDecimal(self: View, w: *Writer, value: anytype) Writer.Error!void {
|
||||
fn printDecimal(self: Format, w: *Writer, value: anytype) Writer.Error!void {
|
||||
if (!self.options.separators) return w.printInt(value, 10, .lower, .{});
|
||||
// Grouping works on the digits, so write them first. The widest value is
|
||||
// minInt(i128): 39 digits and a sign.
|
||||
|
|
@ -219,19 +224,19 @@ pub const View = struct {
|
|||
|
||||
/// The byte at display position `k` (0 = leftmost), honouring endianness.
|
||||
/// Big-endian shows the most significant byte first, little-endian the least.
|
||||
fn byteAt(self: View, k: usize) u8 {
|
||||
fn byteAt(self: Format, k: usize) u8 {
|
||||
const count = self.byteCount();
|
||||
const msb_index = if (self.options.endian == .big) k else count - 1 - k;
|
||||
const shift: u7 = @intCast((count - 1 - msb_index) * 8);
|
||||
return @intCast((self.value.masked() >> shift) & 0xFF);
|
||||
}
|
||||
|
||||
fn byteCount(self: View) usize {
|
||||
return @as(usize, self.value.bits()) / 8;
|
||||
fn byteCount(self: Format) usize {
|
||||
return @as(usize, self.value.width.bits()) / 8;
|
||||
}
|
||||
|
||||
fn octalDigits(self: View) usize {
|
||||
return (@as(usize, self.value.bits()) + 2) / 3;
|
||||
fn octalDigits(self: Format) usize {
|
||||
return (@as(usize, self.value.width.bits()) + 2) / 3;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -365,9 +370,9 @@ fn at(raw: u128, width: BitWidth) Integer {
|
|||
}
|
||||
|
||||
/// Render through a writer, which is the primary path.
|
||||
fn show(buf: []u8, value: Integer, notation: Notation, options: Options) ![]const u8 {
|
||||
fn show(buf: []u8, value: Integer, notation: Notation, options: FormatOptions) ![]const u8 {
|
||||
var w = Writer.fixed(buf);
|
||||
try w.print("{f}", .{value.as(notation, options)});
|
||||
try w.print("{f}", .{value.fmt(notation, options)});
|
||||
return w.buffered();
|
||||
}
|
||||
|
||||
|
|
@ -394,7 +399,7 @@ test "hex: display groups per byte, plain form carries the prefix" {
|
|||
|
||||
test "hex: little-endian reverses the display, never the prefixed form" {
|
||||
var buf: [256]u8 = undefined;
|
||||
const little: Options = .{ .endian = .little };
|
||||
const little: FormatOptions = .{ .endian = .little };
|
||||
|
||||
try testing.expectEqualStrings("EF BE AD DE", try show(&buf, at(0xDEADBEEF, .bits32), .hex, little));
|
||||
// A prefixed form names the value, so it stays canonical whatever the byte order.
|
||||
|
|
@ -568,12 +573,12 @@ test "render: a buffer too small is an error, not a walk off the end" {
|
|||
var tiny: [4]u8 = undefined;
|
||||
try testing.expectError(
|
||||
error.WriteFailed,
|
||||
at(0xDEADBEEF, .bits32).as(.binary, .{}).render(&tiny),
|
||||
at(0xDEADBEEF, .bits32).fmt(.binary, .{}).render(&tiny),
|
||||
);
|
||||
|
||||
// And the successful path returns exactly what was written.
|
||||
var room: [64]u8 = undefined;
|
||||
const written = try at(0xFF, .bits8).as(.hex, .{}).render(&room);
|
||||
const written = try at(0xFF, .bits8).fmt(.hex, .{}).render(&room);
|
||||
try testing.expectEqualStrings("FF", written);
|
||||
}
|
||||
|
||||
|
|
@ -581,6 +586,31 @@ test "a view prints through any writer, which is how the frontends use it" {
|
|||
var buf: [128]u8 = undefined;
|
||||
var w = Writer.fixed(&buf);
|
||||
const value = at(0xDEAD, .bits16);
|
||||
try w.print("hex: {f} oct: {f}", .{ value.as(.hex, .{}), value.as(.octal, .{}) });
|
||||
try w.print("hex: {f} oct: {f}", .{ value.fmt(.hex, .{}), value.fmt(.octal, .{}) });
|
||||
try testing.expectEqualStrings("hex: DE AD oct: 157 255", w.buffered());
|
||||
}
|
||||
|
||||
test "the default format is the number, read by its own signedness" {
|
||||
// `{f}` on the value itself, for tests, debug output and error messages. No
|
||||
// separators and no prefix: the notations are how you ask for those.
|
||||
var buf: [128]u8 = undefined;
|
||||
|
||||
var signed = Writer.fixed(&buf);
|
||||
try signed.print("{f}", .{at(0xFF, .bits8)});
|
||||
try testing.expectEqualStrings("-1", signed.buffered());
|
||||
|
||||
var unsigned = Writer.fixed(&buf);
|
||||
const as_unsigned: Integer = .{ .raw = 0xFF, .width = .bits8, .signedness = .unsigned };
|
||||
try unsigned.print("{f}", .{as_unsigned});
|
||||
try testing.expectEqualStrings("255", unsigned.buffered());
|
||||
|
||||
// Wide values print in full, ungrouped.
|
||||
var wide = Writer.fixed(&buf);
|
||||
try wide.print("{f}", .{at(1234567, .bits32)});
|
||||
try testing.expectEqualStrings("1234567", wide.buffered());
|
||||
|
||||
// And it composes, which is the point of having it.
|
||||
var pair = Writer.fixed(&buf);
|
||||
try pair.print("{f} and {f}", .{ at(5, .bits8), at(0xFE, .bits8) });
|
||||
try testing.expectEqualStrings("5 and -2", pair.buffered());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ const Distance = union(enum) {
|
|||
fn distance(right: Integer) Error!Distance {
|
||||
if (right.isNegative()) return Error.DomainError;
|
||||
const value = right.masked();
|
||||
if (value >= right.bits()) return .past_width;
|
||||
if (value >= right.width.bits()) return .past_width;
|
||||
return .{ .within = @intCast(value) };
|
||||
}
|
||||
|
||||
|
|
@ -94,7 +94,7 @@ fn distance(right: Integer) Error!Distance {
|
|||
/// saturated: rotating a 64-bit value by 65 is rotating it by 1.
|
||||
fn rotation(right: Integer) Error!u7 {
|
||||
if (right.isNegative()) return Error.DomainError;
|
||||
return @intCast(right.masked() % right.bits());
|
||||
return @intCast(right.masked() % right.width.bits());
|
||||
}
|
||||
|
||||
/// Apply a fixed-width operation to two values of the same integer type.
|
||||
|
|
@ -104,7 +104,7 @@ fn rotation(right: Integer) Error!u7 {
|
|||
pub fn apply(op: Op, left_in: Integer, right_in: Integer) Error!Integer {
|
||||
std.debug.assert(left_in.sameTypeAs(right_in));
|
||||
|
||||
const mask = left_in.mask();
|
||||
const mask = left_in.width.mask();
|
||||
const left = left_in.masked();
|
||||
const right = right_in.masked();
|
||||
|
||||
|
|
@ -139,13 +139,13 @@ pub fn apply(op: Op, left_in: Integer, right_in: Integer) Error!Integer {
|
|||
.rotate_left => blk: {
|
||||
const amt = try rotation(right_in);
|
||||
if (amt == 0) break :blk left;
|
||||
const anti: u7 = @intCast(left_in.bits() - amt);
|
||||
const anti: u7 = @intCast(left_in.width.bits() - amt);
|
||||
break :blk ((left << amt) | (left >> anti)) & mask;
|
||||
},
|
||||
.rotate_right => blk: {
|
||||
const amt = try rotation(right_in);
|
||||
if (amt == 0) break :blk left;
|
||||
const anti: u7 = @intCast(left_in.bits() - amt);
|
||||
const anti: u7 = @intCast(left_in.width.bits() - amt);
|
||||
break :blk ((left >> amt) | (left << anti)) & mask;
|
||||
},
|
||||
};
|
||||
|
|
@ -299,7 +299,7 @@ test "rotate left and rotate right are inverses at every distance and width" {
|
|||
.signedness = .unsigned,
|
||||
};
|
||||
var amt: u128 = 0;
|
||||
while (amt < value.bits()) : (amt += 1) {
|
||||
while (amt < value.width.bits()) : (amt += 1) {
|
||||
const there = try apply(.rotate_left, value, value.withRaw(amt));
|
||||
const back = try apply(.rotate_right, there, value.withRaw(amt));
|
||||
try testing.expectEqual(value.raw, back.raw);
|
||||
|
|
@ -331,7 +331,7 @@ test "not and negate stay inside the width and keep the type" {
|
|||
}
|
||||
|
||||
test "the default value type, which standard mode uses, is 64-bit signed" {
|
||||
const minus_eight = i64v(@bitCast(@as(i128, -8) & @as(i128, @bitCast(i64v(0).mask()))));
|
||||
const minus_eight = i64v(@bitCast(@as(i128, -8) & @as(i128, @bitCast(Integer.BitWidth.bits64.mask()))));
|
||||
try testing.expectEqual(Integer.BitWidth.bits64, minus_eight.width);
|
||||
try testing.expectEqual(Integer.Signedness.signed, minus_eight.signedness);
|
||||
// -8 >> 1 is -4 there, which is the case that used to differ between modes.
|
||||
|
|
|
|||
|
|
@ -937,7 +937,7 @@ test "standard mode: its integer type is fixed at 64-bit signed" {
|
|||
try testing.expectEqual(@as(f64, 1024.0), shifted.toFloat(alloc));
|
||||
|
||||
const projected = try standardInt(1);
|
||||
try testing.expectEqual(@as(u8, 64), projected.bits());
|
||||
try testing.expectEqual(@as(u8, 64), projected.width.bits());
|
||||
try testing.expectEqual(Integer.Signedness.signed, projected.signedness);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ test "displayWidthFor: the chosen width holds the value and sizes the rows" {
|
|||
const bw = displayWidthFor(value);
|
||||
try testing.expectEqual(value, value & bw.mask());
|
||||
const int: Integer = .{ .raw = value, .width = bw, .signedness = .unsigned };
|
||||
const hex = try int.as(.hex, .{}).render(&buf);
|
||||
const hex = try int.fmt(.hex, .{}).render(&buf);
|
||||
// Two hex digits per byte, plus one space between bytes.
|
||||
const bytes: usize = bw.bits() / 8;
|
||||
try testing.expectEqual(bytes * 3 - 1, hex.len);
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ fn evalExpr(config: Config, expr: *const Expr) Error!Integer {
|
|||
/// apart again. What remains is the arithmetic, which genuinely differs between the
|
||||
/// modes: it wraps at the width here and is exact rational arithmetic there.
|
||||
fn evalBinaryOp(op: BinaryOp, left_in: Integer, right_in: Integer) Error!Integer {
|
||||
const mask = left_in.mask();
|
||||
const mask = left_in.width.mask();
|
||||
const left = left_in.masked();
|
||||
const right = right_in.masked();
|
||||
|
||||
|
|
|
|||
16
src/main.zig
16
src/main.zig
|
|
@ -497,9 +497,9 @@ fn formatStandardMultiBase(buf: []u8, dec_display: []const u8, value: f64) CliRe
|
|||
var w = std.Io.Writer.fixed(buf);
|
||||
w.print("{s}\n hex: {f}\n oct: {f}\n bin: {f}", .{
|
||||
dec_copy[0..dec_len],
|
||||
int.as(.hex, .{}),
|
||||
int.as(.octal, .{}),
|
||||
int.as(.binary, .{}),
|
||||
int.fmt(.hex, .{}),
|
||||
int.fmt(.octal, .{}),
|
||||
int.fmt(.binary, .{}),
|
||||
}) catch {
|
||||
return .{ .output = "error: buffer overflow\n", .is_error = true };
|
||||
};
|
||||
|
|
@ -517,11 +517,11 @@ fn formatProgrammerResult(buf: []u8, result: engine.Integer, config: engine.prog
|
|||
\\ bin: {f}
|
||||
\\
|
||||
, .{
|
||||
result.as(.decimal_signed, .{}),
|
||||
result.as(.decimal_unsigned, .{}),
|
||||
result.as(.hex, .{ .endian = config.display_endian }),
|
||||
result.as(.octal, .{}),
|
||||
result.as(.binary, .{}),
|
||||
result.fmt(.decimal_signed, .{}),
|
||||
result.fmt(.decimal_unsigned, .{}),
|
||||
result.fmt(.hex, .{ .endian = config.display_endian }),
|
||||
result.fmt(.octal, .{}),
|
||||
result.fmt(.binary, .{}),
|
||||
}) catch {
|
||||
return .{ .output = "error: buffer overflow\n", .is_error = true };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1051,9 +1051,9 @@ pub const App = struct {
|
|||
.signedness = .unsigned,
|
||||
};
|
||||
details = .{
|
||||
try std.fmt.allocPrint(self.allocator, "hex: {f}", .{int.as(.hex, .{})}),
|
||||
try std.fmt.allocPrint(self.allocator, "oct: {f}", .{int.as(.octal, .{})}),
|
||||
try std.fmt.allocPrint(self.allocator, "bin: {f}", .{int.as(.binary, .{})}),
|
||||
try std.fmt.allocPrint(self.allocator, "hex: {f}", .{int.fmt(.hex, .{})}),
|
||||
try std.fmt.allocPrint(self.allocator, "oct: {f}", .{int.fmt(.octal, .{})}),
|
||||
try std.fmt.allocPrint(self.allocator, "bin: {f}", .{int.fmt(.binary, .{})}),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -1161,7 +1161,7 @@ pub const App = struct {
|
|||
fn submitProgrammer(self: *App, expr_text: []const u8) !void {
|
||||
const is_error, const display_text = if (engine.evalProgrammerString(self.allocator, expr_text, self.prog_config)) |int| blk: {
|
||||
self.prog_value = int.unsignedValue();
|
||||
const text = try std.fmt.allocPrint(self.allocator, "{f}", .{int.as(.decimal_unsigned, .{})});
|
||||
const text = try std.fmt.allocPrint(self.allocator, "{f}", .{int.fmt(.decimal_unsigned, .{})});
|
||||
break :blk .{ false, text };
|
||||
} else |err| blk: {
|
||||
break :blk .{ true, try self.allocator.dupe(u8, errorStr(err)) };
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ pub fn drawProgrammerMode(app: *tui.App, surface: *vxfw.Surface, width: u16, hei
|
|||
|
||||
// DEC(s)
|
||||
var sdec_buf: [256]u8 = undefined;
|
||||
const sdec = int.as(.decimal_signed, .{}).render(&sdec_buf) catch too_wide;
|
||||
const sdec = int.fmt(.decimal_signed, .{}).render(&sdec_buf) catch too_wide;
|
||||
const sdec_style: vaxis.Style = if (focused == .dec_signed)
|
||||
.{ .fg = C.bg, .bg = C.cyan, .bold = true }
|
||||
else
|
||||
|
|
@ -85,7 +85,7 @@ pub fn drawProgrammerMode(app: *tui.App, surface: *vxfw.Surface, width: u16, hei
|
|||
|
||||
// DEC(u)
|
||||
var udec_buf: [256]u8 = undefined;
|
||||
const udec = int.as(.decimal_unsigned, .{}).render(&udec_buf) catch too_wide;
|
||||
const udec = int.fmt(.decimal_unsigned, .{}).render(&udec_buf) catch too_wide;
|
||||
const udec_style: vaxis.Style = if (focused == .dec_unsigned)
|
||||
.{ .fg = C.bg, .bg = C.cyan, .bold = true }
|
||||
else
|
||||
|
|
@ -96,7 +96,7 @@ pub fn drawProgrammerMode(app: *tui.App, surface: *vxfw.Surface, width: u16, hei
|
|||
|
||||
// HEX
|
||||
var hex_buf: [256]u8 = undefined;
|
||||
const hex = int.as(.hex, .{ .endian = app.prog_config.display_endian }).render(&hex_buf) catch too_wide;
|
||||
const hex = int.fmt(.hex, .{ .endian = app.prog_config.display_endian }).render(&hex_buf) catch too_wide;
|
||||
const hex_style: vaxis.Style = if (focused == .hex)
|
||||
.{ .fg = C.bg, .bg = C.green, .bold = true }
|
||||
else
|
||||
|
|
@ -114,13 +114,13 @@ pub fn drawProgrammerMode(app: *tui.App, surface: *vxfw.Surface, width: u16, hei
|
|||
|
||||
// ASCII (derived, read-only): one glyph per byte, aligned under HEX.
|
||||
var ascii_buf: [128]u8 = undefined;
|
||||
const ascii = int.as(.ascii, .{ .endian = app.prog_config.display_endian }).render(&ascii_buf) catch too_wide;
|
||||
const ascii = int.fmt(.ascii, .{ .endian = app.prog_config.display_endian }).render(&ascii_buf) catch too_wide;
|
||||
draw.writeStr(surface, base_start + 3, 2, "ASCII:", .{ .fg = C.cyan });
|
||||
draw.writeStr(surface, base_start + 3, 11, ascii, .{ .fg = C.orange });
|
||||
|
||||
// OCT
|
||||
var oct_buf: [256]u8 = undefined;
|
||||
const oct = int.as(.octal, .{}).render(&oct_buf) catch too_wide;
|
||||
const oct = int.fmt(.octal, .{}).render(&oct_buf) catch too_wide;
|
||||
const oct_style: vaxis.Style = if (focused == .oct)
|
||||
.{ .fg = C.bg, .bg = C.purple, .bold = true }
|
||||
else
|
||||
|
|
@ -136,7 +136,7 @@ pub fn drawProgrammerMode(app: *tui.App, surface: *vxfw.Surface, width: u16, hei
|
|||
|
||||
// BIN
|
||||
var bin_buf: [512]u8 = undefined;
|
||||
const bin = int.as(.binary, .{}).render(&bin_buf) catch too_wide;
|
||||
const bin = int.fmt(.binary, .{}).render(&bin_buf) catch too_wide;
|
||||
const bin_style: vaxis.Style = if (focused == .bin)
|
||||
.{ .fg = C.bg, .bg = C.yellow, .bold = true }
|
||||
else
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue