Skip to content

Commit 5884a28

Browse files
committed
std.fmt: add hexOptions, make hex work with all runtime integer sizes
Fixes #23799
1 parent 99e2163 commit 5884a28

File tree

1 file changed

+75
-11
lines changed

1 file changed

+75
-11
lines changed

lib/std/fmt.zig

Lines changed: 75 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2756,21 +2756,67 @@ test "recursive format function" {
27562756

27572757
pub const hex_charset = "0123456789abcdef";
27582758

2759-
/// Converts an unsigned integer of any multiple of u8 to an array of lowercase
2760-
/// hex bytes, little endian.
2761-
pub fn hex(x: anytype) [@sizeOf(@TypeOf(x)) * 2]u8 {
2762-
comptime assert(@typeInfo(@TypeOf(x)).int.signedness == .unsigned);
2763-
var result: [@sizeOf(@TypeOf(x)) * 2]u8 = undefined;
2764-
var i: usize = 0;
2765-
while (i < result.len / 2) : (i += 1) {
2766-
const byte: u8 = @truncate(x >> @intCast(8 * i));
2767-
result[i * 2 + 0] = hex_charset[byte >> 4];
2768-
result[i * 2 + 1] = hex_charset[byte & 15];
2769-
}
2759+
/// Deprecated, use `std.fmt.hexOptions(x, .{ .endianness = .little })` instead.
2760+
pub fn hex(x: anytype) HexOptionsResult(@TypeOf(x), .{ .endianness = .little }) {
2761+
return hexOptions(x, .{ .endianness = .little });
2762+
}
2763+
2764+
/// Converts an integer to an array of hex characters.
2765+
pub fn hexOptions(x: anytype, comptime options: HexOptions) HexOptionsResult(@TypeOf(x), options) {
2766+
var result: HexOptionsResult(@TypeOf(x), options) = undefined;
2767+
2768+
comptime switch (@typeInfo(@TypeOf(x))) {
2769+
.comptime_int => if (!(x >= -(1 << (4 * result.len - 1)) and x < 1 << (4 * result.len)))
2770+
@compileError("out of bounds"),
2771+
.int => |int| if (int.bits > 4 * result.len) @compileError("out of bounds"),
2772+
else => @compileError("x is not an integer"),
2773+
};
2774+
2775+
const table = switch (options.case) {
2776+
.lower => "0123456789abcdef",
2777+
.upper => "0123456789ABCDEF",
2778+
};
2779+
2780+
_ = switch (options.endianness) {
2781+
.little => {
2782+
inline for (0..result.len / 2) |i| {
2783+
result[2 * i] = table[@as(u4, @truncate(math.shr(@TypeOf(x), x, 4 * (2 * i + 1))))];
2784+
result[2 * i + 1] = table[@as(u4, @truncate(math.shr(@TypeOf(x), x, 4 * 2 * i)))];
2785+
}
2786+
},
2787+
.big => .{inline for (0..result.len) |i| {
2788+
result[i] = table[@as(u4, @truncate(math.shr(@TypeOf(x), x, 4 * (result.len - 1 - i))))];
2789+
}},
2790+
};
2791+
27702792
return result;
27712793
}
27722794

2795+
pub fn HexOptionsResult(x: type, comptime options: HexOptions) type {
2796+
const hex_count = 2 * (options.octet_count orelse comptime switch (@typeInfo(x)) {
2797+
.int => |int| (int.bits + 7) / 8,
2798+
.comptime_int => @compileError("std.fmt.hexOptions on comptime_int must have options.octet_count set"),
2799+
else => @compileError("type must be an integer"),
2800+
});
2801+
2802+
return [hex_count]u8;
2803+
}
2804+
2805+
pub const HexOptions = struct {
2806+
/// Whether octets are ordered little-endian or big-endian
2807+
endianness: std.builtin.Endian = .big,
2808+
case: enum { lower, upper } = .lower,
2809+
/// Sign pads the result to the number of octets.
2810+
/// Required for `comptime_int`.
2811+
octet_count: ?usize = null,
2812+
};
2813+
27732814
test hex {
2815+
{
2816+
const x = hex(@as(u48, 0x1234_5678_abcd));
2817+
try std.testing.expect(x.len == 12);
2818+
try std.testing.expectEqualStrings("cdab78563412", &x);
2819+
}
27742820
{
27752821
const x = hex(@as(u32, 0xdeadbeef));
27762822
try std.testing.expect(x.len == 8);
@@ -2783,6 +2829,24 @@ test hex {
27832829
}
27842830
}
27852831

2832+
test hexOptions {
2833+
{
2834+
const x = hexOptions(@as(u48, 0x1234_5678_abcd), .{ .octet_count = 8 });
2835+
try std.testing.expect(x.len == 16);
2836+
try std.testing.expectEqualStrings("000012345678abcd", &x);
2837+
}
2838+
{
2839+
const x = hexOptions(@as(u32, 0xdeadbeef), .{ .endianness = .little, .case = .upper });
2840+
try std.testing.expect(x.len == 8);
2841+
try std.testing.expectEqualStrings("EFBEADDE", &x);
2842+
}
2843+
{
2844+
const x = hexOptions(-0x3047abed, .{ .octet_count = 8, .endianness = .little });
2845+
try std.testing.expect(x.len == 16);
2846+
try std.testing.expectEqualStrings("1354b8cfffffffff", &x);
2847+
}
2848+
}
2849+
27862850
test "parser until" {
27872851
{ // return substring till ':'
27882852
var parser: Parser = .{

0 commit comments

Comments
 (0)