Skip to content

Commit b7fd8ab

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

File tree

1 file changed

+78
-11
lines changed

1 file changed

+78
-11
lines changed

lib/std/fmt.zig

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2756,21 +2756,70 @@ 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+
_ = @as(@Type(.{ .int = .{
2769+
.bits = 4 * result.len,
2770+
.signedness = if (@TypeOf(x) == comptime_int)
2771+
(if (x >= 0) .unsigned else .signed)
2772+
else
2773+
@typeInfo(@TypeOf(x)).int.signedness,
2774+
} }), x);
2775+
2776+
const table = switch (options.case) {
2777+
.lower => "0123456789abcdef",
2778+
.upper => "0123456789ABCDEF",
2779+
};
2780+
2781+
_ = switch (options.endianness) {
2782+
.little => {
2783+
inline for (0..result.len / 2) |i| {
2784+
result[2 * i] = table[@as(u4, @truncate(@divFloor(x, 1 << 4 * (2 * i + 1))))];
2785+
result[2 * i + 1] = table[@as(u4, @truncate(@divFloor(x, 1 << 4 * 2 * i)))];
2786+
}
2787+
},
2788+
.big => .{inline for (0..result.len) |i| {
2789+
result[i] = table[@as(u4, @truncate(@divFloor(x, 1 << 4 * (result.len - 1 - i))))];
2790+
}},
2791+
};
2792+
27702793
return result;
27712794
}
27722795

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

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

0 commit comments

Comments
 (0)