master
   1//! String formatting and parsing.
   2
   3const builtin = @import("builtin");
   4
   5const std = @import("std.zig");
   6const math = std.math;
   7const assert = std.debug.assert;
   8const mem = std.mem;
   9const meta = std.meta;
  10const lossyCast = math.lossyCast;
  11const expectFmt = std.testing.expectFmt;
  12const testing = std.testing;
  13const Allocator = std.mem.Allocator;
  14const Writer = std.Io.Writer;
  15
  16pub const float = @import("fmt/float.zig");
  17
  18pub const default_max_depth = 3;
  19
  20pub const Alignment = enum {
  21    left,
  22    center,
  23    right,
  24};
  25
  26pub const Case = enum { lower, upper };
  27
  28const default_alignment = .right;
  29const default_fill_char = ' ';
  30
  31pub const Options = struct {
  32    precision: ?usize = null,
  33    width: ?usize = null,
  34    alignment: Alignment = default_alignment,
  35    fill: u8 = default_fill_char,
  36
  37    pub fn toNumber(o: Options, mode: Number.Mode, case: Case) Number {
  38        return .{
  39            .mode = mode,
  40            .case = case,
  41            .precision = o.precision,
  42            .width = o.width,
  43            .alignment = o.alignment,
  44            .fill = o.fill,
  45        };
  46    }
  47};
  48
  49pub const Number = struct {
  50    mode: Mode = .decimal,
  51    /// Affects hex digits as well as floating point "inf"/"INF".
  52    case: Case = .lower,
  53    precision: ?usize = null,
  54    width: ?usize = null,
  55    alignment: Alignment = default_alignment,
  56    fill: u8 = default_fill_char,
  57
  58    pub const Mode = enum {
  59        decimal,
  60        binary,
  61        octal,
  62        hex,
  63        scientific,
  64
  65        pub fn base(mode: Mode) ?u8 {
  66            return switch (mode) {
  67                .decimal => 10,
  68                .binary => 2,
  69                .octal => 8,
  70                .hex => 16,
  71                .scientific => null,
  72            };
  73        }
  74    };
  75};
  76
  77pub const Placeholder = struct {
  78    specifier_arg: []const u8,
  79    fill: u8,
  80    alignment: Alignment,
  81    arg: Specifier,
  82    width: Specifier,
  83    precision: Specifier,
  84
  85    pub fn parse(comptime bytes: []const u8) Placeholder {
  86        var parser: Parser = .{ .bytes = bytes, .i = 0 };
  87        const arg = parser.specifier() catch |err| @compileError(@errorName(err));
  88        const specifier_arg = parser.until(':');
  89        if (parser.char()) |b| {
  90            if (b != ':') @compileError("expected : or }, found '" ++ &[1]u8{b} ++ "'");
  91        }
  92
  93        // Parse the fill byte, if present.
  94        //
  95        // When the width field is also specified, the fill byte must
  96        // be followed by an alignment specifier, unless it's '0' (zero)
  97        // (in which case it's handled as part of the width specifier).
  98        var fill: ?u8 = if (parser.peek(1)) |b|
  99            switch (b) {
 100                '<', '^', '>' => parser.char(),
 101                else => null,
 102            }
 103        else
 104            null;
 105
 106        // Parse the alignment parameter
 107        const alignment: ?Alignment = if (parser.peek(0)) |b| init: {
 108            switch (b) {
 109                '<', '^', '>' => {
 110                    // consume the character
 111                    break :init switch (parser.char().?) {
 112                        '<' => .left,
 113                        '^' => .center,
 114                        else => .right,
 115                    };
 116                },
 117                else => break :init null,
 118            }
 119        } else null;
 120
 121        // When none of the fill character and the alignment specifier have
 122        // been provided, check whether the width starts with a zero.
 123        if (fill == null and alignment == null) {
 124            fill = if (parser.peek(0) == '0') '0' else null;
 125        }
 126
 127        // Parse the width parameter
 128        const width = parser.specifier() catch |err| @compileError(@errorName(err));
 129
 130        // Skip the dot, if present
 131        if (parser.char()) |b| {
 132            if (b != '.') @compileError("expected . or }, found '" ++ &[1]u8{b} ++ "'");
 133        }
 134
 135        // Parse the precision parameter
 136        const precision = parser.specifier() catch |err| @compileError(@errorName(err));
 137
 138        if (parser.char()) |b| @compileError("extraneous trailing character '" ++ &[1]u8{b} ++ "'");
 139
 140        const specifier_array = specifier_arg[0..specifier_arg.len].*;
 141
 142        return .{
 143            .specifier_arg = &specifier_array,
 144            .fill = fill orelse default_fill_char,
 145            .alignment = alignment orelse default_alignment,
 146            .arg = arg,
 147            .width = width,
 148            .precision = precision,
 149        };
 150    }
 151};
 152
 153pub const Specifier = union(enum) {
 154    none,
 155    number: usize,
 156    named: []const u8,
 157};
 158
 159/// A stream based parser for format strings.
 160///
 161/// Allows to implement formatters compatible with std.fmt without replicating
 162/// the standard library behavior.
 163pub const Parser = struct {
 164    bytes: []const u8,
 165    i: usize,
 166
 167    pub fn number(self: *@This()) ?usize {
 168        var r: ?usize = null;
 169        while (self.peek(0)) |byte| {
 170            switch (byte) {
 171                '0'...'9' => {
 172                    if (r == null) r = 0;
 173                    r.? *= 10;
 174                    r.? += byte - '0';
 175                },
 176                else => break,
 177            }
 178            self.i += 1;
 179        }
 180        return r;
 181    }
 182
 183    pub fn until(self: *@This(), delimiter: u8) []const u8 {
 184        const start = self.i;
 185        self.i = std.mem.indexOfScalarPos(u8, self.bytes, self.i, delimiter) orelse self.bytes.len;
 186        return self.bytes[start..self.i];
 187    }
 188
 189    pub fn char(self: *@This()) ?u8 {
 190        const i = self.i;
 191        if (self.bytes.len - i == 0) return null;
 192        self.i = i + 1;
 193        return self.bytes[i];
 194    }
 195
 196    pub fn maybe(self: *@This(), byte: u8) bool {
 197        if (self.peek(0) == byte) {
 198            self.i += 1;
 199            return true;
 200        }
 201        return false;
 202    }
 203
 204    pub fn specifier(self: *@This()) !Specifier {
 205        if (self.maybe('[')) {
 206            const arg_name = self.until(']');
 207            if (!self.maybe(']')) return error.@"Expected closing ]";
 208            return .{ .named = arg_name };
 209        }
 210        if (self.number()) |i| return .{ .number = i };
 211        return .{ .none = {} };
 212    }
 213
 214    pub fn peek(self: *@This(), i: usize) ?u8 {
 215        const peek_index = self.i + i;
 216        if (peek_index >= self.bytes.len) return null;
 217        return self.bytes[peek_index];
 218    }
 219};
 220
 221pub const ArgSetType = u32;
 222
 223pub const ArgState = struct {
 224    next_arg: usize = 0,
 225    used_args: ArgSetType = 0,
 226    args_len: usize,
 227
 228    pub fn hasUnusedArgs(self: *@This()) bool {
 229        return @popCount(self.used_args) != self.args_len;
 230    }
 231
 232    pub fn nextArg(self: *@This(), arg_index: ?usize) ?usize {
 233        const next_index = arg_index orelse init: {
 234            const arg = self.next_arg;
 235            self.next_arg += 1;
 236            break :init arg;
 237        };
 238
 239        if (next_index >= self.args_len) {
 240            return null;
 241        }
 242
 243        // Mark this argument as used
 244        self.used_args |= @as(ArgSetType, 1) << @as(u5, @intCast(next_index));
 245        return next_index;
 246    }
 247};
 248
 249/// Asserts the rendered integer value fits in `buffer`.
 250/// Returns the end index within `buffer`.
 251pub fn printInt(buffer: []u8, value: anytype, base: u8, case: Case, options: Options) usize {
 252    var w: Writer = .fixed(buffer);
 253    w.printInt(value, base, case, options) catch unreachable;
 254    return w.end;
 255}
 256
 257/// Converts values in the range [0, 100) to a base 10 string.
 258pub fn digits2(value: u8) [2]u8 {
 259    if (builtin.mode == .ReleaseSmall) {
 260        return .{ @intCast('0' + value / 10), @intCast('0' + value % 10) };
 261    } else {
 262        return "00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899"[value * 2 ..][0..2].*;
 263    }
 264}
 265
 266/// Creates a type suitable for instantiating and passing to a "{f}" placeholder.
 267pub fn Alt(
 268    comptime Data: type,
 269    comptime formatFn: fn (data: Data, writer: *Writer) Writer.Error!void,
 270) type {
 271    return struct {
 272        data: Data,
 273        pub inline fn format(self: @This(), writer: *Writer) Writer.Error!void {
 274            try formatFn(self.data, writer);
 275        }
 276    };
 277}
 278
 279/// Helper for calling alternate format methods besides one named "format".
 280pub fn alt(
 281    context: anytype,
 282    comptime func_name: @EnumLiteral(),
 283) Alt(@TypeOf(context), @field(@TypeOf(context), @tagName(func_name))) {
 284    return .{ .data = context };
 285}
 286
 287test alt {
 288    const Example = struct {
 289        number: u8,
 290
 291        pub fn other(ex: @This(), w: *Writer) Writer.Error!void {
 292            try w.writeByte(ex.number);
 293        }
 294    };
 295    const ex: Example = .{ .number = 'a' };
 296    try expectFmt("a", "{f}", .{alt(ex, .other)});
 297}
 298
 299pub const ParseIntError = error{
 300    /// The result cannot fit in the type specified.
 301    Overflow,
 302    /// The input was empty or contained an invalid character.
 303    InvalidCharacter,
 304};
 305
 306/// Parses the string `buf` as signed or unsigned representation in the
 307/// specified base of an integral value of type `T`.
 308///
 309/// When `base` is zero the string prefix is examined to detect the true base:
 310///  * A prefix of "0b" implies base=2,
 311///  * A prefix of "0o" implies base=8,
 312///  * A prefix of "0x" implies base=16,
 313///  * Otherwise base=10 is assumed.
 314///
 315/// Ignores '_' character in `buf`.
 316/// See also `parseUnsigned`.
 317pub fn parseInt(comptime T: type, buf: []const u8, base: u8) ParseIntError!T {
 318    return parseIntWithGenericCharacter(T, u8, buf, base);
 319}
 320
 321/// Like `parseInt`, but with a generic `Character` type.
 322pub fn parseIntWithGenericCharacter(
 323    comptime Result: type,
 324    comptime Character: type,
 325    buf: []const Character,
 326    base: u8,
 327) ParseIntError!Result {
 328    if (buf.len == 0) return error.InvalidCharacter;
 329    if (buf[0] == '+') return parseIntWithSign(Result, Character, buf[1..], base, .pos);
 330    if (buf[0] == '-') return parseIntWithSign(Result, Character, buf[1..], base, .neg);
 331    return parseIntWithSign(Result, Character, buf, base, .pos);
 332}
 333
 334test parseInt {
 335    try std.testing.expectEqual(-10, try parseInt(i32, "-10", 10));
 336    try std.testing.expectEqual(10, try parseInt(i32, "+10", 10));
 337    try std.testing.expectEqual(10, try parseInt(u32, "+10", 10));
 338    try std.testing.expectError(error.Overflow, parseInt(u32, "-10", 10));
 339    try std.testing.expectError(error.InvalidCharacter, parseInt(u32, " 10", 10));
 340    try std.testing.expectError(error.InvalidCharacter, parseInt(u32, "10 ", 10));
 341    try std.testing.expectError(error.InvalidCharacter, parseInt(u32, "_10_", 10));
 342    try std.testing.expectError(error.InvalidCharacter, parseInt(u32, "0x_10_", 10));
 343    try std.testing.expectError(error.InvalidCharacter, parseInt(u32, "0x10_", 10));
 344    try std.testing.expectError(error.InvalidCharacter, parseInt(u32, "0x_10", 10));
 345    try std.testing.expectEqual(255, try parseInt(u8, "255", 10));
 346    try std.testing.expectError(error.Overflow, parseInt(u8, "256", 10));
 347
 348    // +0 and -0 should work for unsigned
 349    try std.testing.expectEqual(0, try parseInt(u8, "-0", 10));
 350    try std.testing.expectEqual(0, try parseInt(u8, "+0", 10));
 351
 352    // ensure minInt is parsed correctly
 353    try std.testing.expectEqual(math.minInt(i1), try parseInt(i1, "-1", 10));
 354    try std.testing.expectEqual(math.minInt(i8), try parseInt(i8, "-128", 10));
 355    try std.testing.expectEqual(math.minInt(i43), try parseInt(i43, "-4398046511104", 10));
 356
 357    // empty string or bare +- is invalid
 358    try std.testing.expectError(error.InvalidCharacter, parseInt(u32, "", 10));
 359    try std.testing.expectError(error.InvalidCharacter, parseInt(i32, "", 10));
 360    try std.testing.expectError(error.InvalidCharacter, parseInt(u32, "+", 10));
 361    try std.testing.expectError(error.InvalidCharacter, parseInt(i32, "+", 10));
 362    try std.testing.expectError(error.InvalidCharacter, parseInt(u32, "-", 10));
 363    try std.testing.expectError(error.InvalidCharacter, parseInt(i32, "-", 10));
 364
 365    // autodectect the base
 366    try std.testing.expectEqual(111, try parseInt(i32, "111", 0));
 367    try std.testing.expectEqual(111, try parseInt(i32, "1_1_1", 0));
 368    try std.testing.expectEqual(111, try parseInt(i32, "1_1_1", 0));
 369    try std.testing.expectEqual(7, try parseInt(i32, "+0b111", 0));
 370    try std.testing.expectEqual(7, try parseInt(i32, "+0B111", 0));
 371    try std.testing.expectEqual(7, try parseInt(i32, "+0b1_11", 0));
 372    try std.testing.expectEqual(73, try parseInt(i32, "+0o111", 0));
 373    try std.testing.expectEqual(73, try parseInt(i32, "+0O111", 0));
 374    try std.testing.expectEqual(73, try parseInt(i32, "+0o11_1", 0));
 375    try std.testing.expectEqual(273, try parseInt(i32, "+0x111", 0));
 376    try std.testing.expectEqual(-7, try parseInt(i32, "-0b111", 0));
 377    try std.testing.expectEqual(-7, try parseInt(i32, "-0b11_1", 0));
 378    try std.testing.expectEqual(-73, try parseInt(i32, "-0o111", 0));
 379    try std.testing.expectEqual(-273, try parseInt(i32, "-0x111", 0));
 380    try std.testing.expectEqual(-273, try parseInt(i32, "-0X111", 0));
 381    try std.testing.expectEqual(-273, try parseInt(i32, "-0x1_11", 0));
 382
 383    // bare binary/octal/decimal prefix is invalid
 384    try std.testing.expectError(error.InvalidCharacter, parseInt(u32, "0b", 0));
 385    try std.testing.expectError(error.InvalidCharacter, parseInt(u32, "0o", 0));
 386    try std.testing.expectError(error.InvalidCharacter, parseInt(u32, "0x", 0));
 387
 388    // edge cases which previously errored due to base overflowing T
 389    try std.testing.expectEqual(@as(i2, -2), try std.fmt.parseInt(i2, "-10", 2));
 390    try std.testing.expectEqual(@as(i4, -8), try std.fmt.parseInt(i4, "-10", 8));
 391    try std.testing.expectEqual(@as(i5, -16), try std.fmt.parseInt(i5, "-10", 16));
 392}
 393
 394fn parseIntWithSign(
 395    comptime Result: type,
 396    comptime Character: type,
 397    buf: []const Character,
 398    base: u8,
 399    comptime sign: enum { pos, neg },
 400) ParseIntError!Result {
 401    if (buf.len == 0) return error.InvalidCharacter;
 402
 403    var buf_base = base;
 404    var buf_start = buf;
 405    if (base == 0) {
 406        // Treat is as a decimal number by default.
 407        buf_base = 10;
 408        // Detect the base by looking at buf prefix.
 409        if (buf.len > 2 and buf[0] == '0') {
 410            if (math.cast(u8, buf[1])) |c| switch (std.ascii.toLower(c)) {
 411                'b' => {
 412                    buf_base = 2;
 413                    buf_start = buf[2..];
 414                },
 415                'o' => {
 416                    buf_base = 8;
 417                    buf_start = buf[2..];
 418                },
 419                'x' => {
 420                    buf_base = 16;
 421                    buf_start = buf[2..];
 422                },
 423                else => {},
 424            };
 425        }
 426    }
 427
 428    const add = switch (sign) {
 429        .pos => math.add,
 430        .neg => math.sub,
 431    };
 432
 433    // accumulate into Accumulate which is always 8 bits or larger.  this prevents
 434    // `buf_base` from overflowing Result.
 435    const info = @typeInfo(Result);
 436    const Accumulate = std.meta.Int(info.int.signedness, @max(8, info.int.bits));
 437    var accumulate: Accumulate = 0;
 438
 439    if (buf_start[0] == '_' or buf_start[buf_start.len - 1] == '_') return error.InvalidCharacter;
 440
 441    for (buf_start) |c| {
 442        if (c == '_') continue;
 443        const digit = try charToDigit(math.cast(u8, c) orelse return error.InvalidCharacter, buf_base);
 444        if (accumulate != 0) {
 445            accumulate = try math.mul(Accumulate, accumulate, math.cast(Accumulate, buf_base) orelse return error.Overflow);
 446        } else if (sign == .neg) {
 447            // The first digit of a negative number.
 448            // Consider parsing "-4" as an i3.
 449            // This should work, but positive 4 overflows i3, so we can't cast the digit to T and subtract.
 450            accumulate = math.cast(Accumulate, -@as(i8, @intCast(digit))) orelse return error.Overflow;
 451            continue;
 452        }
 453        accumulate = try add(Accumulate, accumulate, math.cast(Accumulate, digit) orelse return error.Overflow);
 454    }
 455
 456    return if (Result == Accumulate)
 457        accumulate
 458    else
 459        math.cast(Result, accumulate) orelse return error.Overflow;
 460}
 461
 462/// Parses the string `buf` as unsigned representation in the specified base
 463/// of an integral value of type `T`.
 464///
 465/// When `base` is zero the string prefix is examined to detect the true base:
 466///  * A prefix of "0b" implies base=2,
 467///  * A prefix of "0o" implies base=8,
 468///  * A prefix of "0x" implies base=16,
 469///  * Otherwise base=10 is assumed.
 470///
 471/// Ignores '_' character in `buf`.
 472/// See also `parseInt`.
 473pub fn parseUnsigned(comptime T: type, buf: []const u8, base: u8) ParseIntError!T {
 474    return parseIntWithSign(T, u8, buf, base, .pos);
 475}
 476
 477test parseUnsigned {
 478    try std.testing.expectEqual(50124, try parseUnsigned(u16, "050124", 10));
 479    try std.testing.expectEqual(65535, try parseUnsigned(u16, "65535", 10));
 480    try std.testing.expectEqual(65535, try parseUnsigned(u16, "65_535", 10));
 481    try std.testing.expectError(error.Overflow, parseUnsigned(u16, "65536", 10));
 482
 483    try std.testing.expectEqual(0xffffffffffffffff, try parseUnsigned(u64, "0ffffffffffffffff", 16));
 484    try std.testing.expectEqual(0xffffffffffffffff, try parseUnsigned(u64, "0f_fff_fff_fff_fff_fff", 16));
 485    try std.testing.expectError(error.Overflow, parseUnsigned(u64, "10000000000000000", 16));
 486
 487    try std.testing.expectEqual(0xDEADBEEF, try parseUnsigned(u32, "DeadBeef", 16));
 488
 489    try std.testing.expectEqual(1, try parseUnsigned(u7, "1", 10));
 490    try std.testing.expectEqual(8, try parseUnsigned(u7, "1000", 2));
 491
 492    try std.testing.expectError(error.InvalidCharacter, parseUnsigned(u32, "f", 10));
 493    try std.testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "109", 8));
 494
 495    try std.testing.expectEqual(1442151747, try parseUnsigned(u32, "NUMBER", 36));
 496
 497    // these numbers should fit even though the base itself doesn't fit in the destination type
 498    try std.testing.expectEqual(0, try parseUnsigned(u1, "0", 10));
 499    try std.testing.expectEqual(1, try parseUnsigned(u1, "1", 10));
 500    try std.testing.expectError(error.Overflow, parseUnsigned(u1, "2", 10));
 501    try std.testing.expectEqual(1, try parseUnsigned(u1, "001", 16));
 502    try std.testing.expectEqual(3, try parseUnsigned(u2, "3", 16));
 503    try std.testing.expectError(error.Overflow, parseUnsigned(u2, "4", 16));
 504
 505    // parseUnsigned does not expect a sign
 506    try std.testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "+0", 10));
 507    try std.testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "-0", 10));
 508
 509    // test empty string error
 510    try std.testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "", 10));
 511}
 512
 513/// Parses a number like '2G', '2Gi', or '2GiB'.
 514pub fn parseIntSizeSuffix(buf: []const u8, digit_base: u8) ParseIntError!usize {
 515    var without_B = buf;
 516    if (mem.endsWith(u8, buf, "B")) without_B.len -= 1;
 517    var without_i = without_B;
 518    var magnitude_base: usize = 1000;
 519    if (mem.endsWith(u8, without_B, "i")) {
 520        without_i.len -= 1;
 521        magnitude_base = 1024;
 522    }
 523    if (without_i.len == 0) return error.InvalidCharacter;
 524    const orders_of_magnitude: usize = switch (without_i[without_i.len - 1]) {
 525        'k', 'K' => 1,
 526        'M' => 2,
 527        'G' => 3,
 528        'T' => 4,
 529        'P' => 5,
 530        'E' => 6,
 531        'Z' => 7,
 532        'Y' => 8,
 533        'R' => 9,
 534        'Q' => 10,
 535        else => 0,
 536    };
 537    var without_suffix = without_i;
 538    if (orders_of_magnitude > 0) {
 539        without_suffix.len -= 1;
 540    } else if (without_i.len != without_B.len) {
 541        return error.InvalidCharacter;
 542    }
 543    const multiplier = math.powi(usize, magnitude_base, orders_of_magnitude) catch |err| switch (err) {
 544        error.Underflow => unreachable,
 545        error.Overflow => return error.Overflow,
 546    };
 547    const number = try std.fmt.parseInt(usize, without_suffix, digit_base);
 548    return math.mul(usize, number, multiplier);
 549}
 550
 551test parseIntSizeSuffix {
 552    try std.testing.expectEqual(2, try parseIntSizeSuffix("2", 10));
 553    try std.testing.expectEqual(2, try parseIntSizeSuffix("2B", 10));
 554    try std.testing.expectEqual(2000, try parseIntSizeSuffix("2kB", 10));
 555    try std.testing.expectEqual(2000, try parseIntSizeSuffix("2k", 10));
 556    try std.testing.expectEqual(2048, try parseIntSizeSuffix("2KiB", 10));
 557    try std.testing.expectEqual(2048, try parseIntSizeSuffix("2Ki", 10));
 558    try std.testing.expectEqual(10240, try parseIntSizeSuffix("aKiB", 16));
 559    try std.testing.expectError(error.InvalidCharacter, parseIntSizeSuffix("", 10));
 560    try std.testing.expectError(error.InvalidCharacter, parseIntSizeSuffix("2iB", 10));
 561}
 562
 563pub const parseFloat = @import("fmt/parse_float.zig").parseFloat;
 564pub const ParseFloatError = @import("fmt/parse_float.zig").ParseFloatError;
 565
 566test {
 567    _ = &parseFloat;
 568}
 569
 570pub fn charToDigit(c: u8, base: u8) (error{InvalidCharacter}!u8) {
 571    const value = switch (c) {
 572        '0'...'9' => c - '0',
 573        'A'...'Z' => c - 'A' + 10,
 574        'a'...'z' => c - 'a' + 10,
 575        else => return error.InvalidCharacter,
 576    };
 577
 578    if (value >= base) return error.InvalidCharacter;
 579
 580    return value;
 581}
 582
 583pub fn digitToChar(digit: u8, case: Case) u8 {
 584    return switch (digit) {
 585        0...9 => digit + '0',
 586        10...35 => digit + ((if (case == .upper) @as(u8, 'A') else @as(u8, 'a')) - 10),
 587        else => unreachable,
 588    };
 589}
 590
 591pub const BufPrintError = error{
 592    /// As much as possible was written to the buffer, but it was too small to fit all the printed bytes.
 593    NoSpaceLeft,
 594};
 595
 596/// Print a format string into `buf`. Returns a slice of the bytes printed.
 597pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintError![]u8 {
 598    var w: Writer = .fixed(buf);
 599    w.print(fmt, args) catch |err| switch (err) {
 600        error.WriteFailed => return error.NoSpaceLeft,
 601    };
 602    return w.buffered();
 603}
 604
 605/// Deprecated in favor of `bufPrintSentinel`
 606pub fn bufPrintZ(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintError![:0]u8 {
 607    return try bufPrintSentinel(buf, fmt, args, 0);
 608}
 609
 610pub fn bufPrintSentinel(
 611    buf: []u8,
 612    comptime fmt: []const u8,
 613    args: anytype,
 614    comptime sentinel: u8,
 615) BufPrintError![:sentinel]u8 {
 616    const result = try bufPrint(buf, fmt ++ [_]u8{sentinel}, args);
 617    return result[0 .. result.len - 1 :sentinel];
 618}
 619
 620/// Count the characters needed for format.
 621pub fn count(comptime fmt: []const u8, args: anytype) usize {
 622    var trash_buffer: [64]u8 = undefined;
 623    var dw: Writer.Discarding = .init(&trash_buffer);
 624    dw.writer.print(fmt, args) catch |err| switch (err) {
 625        error.WriteFailed => unreachable,
 626    };
 627    return @intCast(dw.count + dw.writer.end);
 628}
 629
 630pub fn allocPrint(gpa: Allocator, comptime fmt: []const u8, args: anytype) Allocator.Error![]u8 {
 631    var aw = try Writer.Allocating.initCapacity(gpa, fmt.len);
 632    defer aw.deinit();
 633    aw.writer.print(fmt, args) catch |err| switch (err) {
 634        error.WriteFailed => return error.OutOfMemory,
 635    };
 636    return aw.toOwnedSlice();
 637}
 638
 639pub fn allocPrintSentinel(
 640    gpa: Allocator,
 641    comptime fmt: []const u8,
 642    args: anytype,
 643    comptime sentinel: u8,
 644) Allocator.Error![:sentinel]u8 {
 645    var aw = try Writer.Allocating.initCapacity(gpa, fmt.len);
 646    defer aw.deinit();
 647    aw.writer.print(fmt, args) catch |err| switch (err) {
 648        error.WriteFailed => return error.OutOfMemory,
 649    };
 650    return aw.toOwnedSliceSentinel(sentinel);
 651}
 652
 653pub inline fn comptimePrint(comptime fmt: []const u8, args: anytype) *const [count(fmt, args):0]u8 {
 654    comptime {
 655        var buf: [count(fmt, args):0]u8 = undefined;
 656        _ = bufPrint(&buf, fmt, args) catch unreachable;
 657        buf[buf.len] = 0;
 658        const final = buf;
 659        return &final;
 660    }
 661}
 662
 663test comptimePrint {
 664    @setEvalBranchQuota(2000);
 665    try std.testing.expectEqual(*const [3:0]u8, @TypeOf(comptimePrint("{}", .{100})));
 666    try std.testing.expectEqualSlices(u8, "100", comptimePrint("{}", .{100}));
 667    try std.testing.expectEqualStrings("30", comptimePrint("{d}", .{30.0}));
 668    try std.testing.expectEqualStrings("30.0", comptimePrint("{d:3.1}", .{30.0}));
 669    try std.testing.expectEqualStrings("0.05", comptimePrint("{d}", .{0.05}));
 670    try std.testing.expectEqualStrings("5e-2", comptimePrint("{e}", .{0.05}));
 671}
 672
 673test "parse u64 digit too big" {
 674    _ = parseUnsigned(u64, "123a", 10) catch |err| {
 675        if (err == error.InvalidCharacter) return;
 676        unreachable;
 677    };
 678    unreachable;
 679}
 680
 681test "parse unsigned comptime" {
 682    comptime {
 683        try std.testing.expectEqual(2, try parseUnsigned(usize, "2", 10));
 684    }
 685}
 686
 687test "escaped braces" {
 688    try expectFmt("escaped: {{foo}}\n", "escaped: {{{{foo}}}}\n", .{});
 689    try expectFmt("escaped: {foo}\n", "escaped: {{foo}}\n", .{});
 690}
 691
 692test "optional" {
 693    {
 694        const value: ?i32 = 1234;
 695        try expectFmt("optional: 1234\n", "optional: {?}\n", .{value});
 696        try expectFmt("optional: 1234\n", "optional: {?d}\n", .{value});
 697        try expectFmt("optional: 4d2\n", "optional: {?x}\n", .{value});
 698    }
 699    {
 700        const value: ?[]const u8 = "string";
 701        try expectFmt("optional: string\n", "optional: {?s}\n", .{value});
 702    }
 703    {
 704        const value: ?i32 = null;
 705        try expectFmt("optional: null\n", "optional: {?}\n", .{value});
 706    }
 707    {
 708        const value = @as(?*i32, @ptrFromInt(0xf000d000));
 709        try expectFmt("optional: *i32@f000d000\n", "optional: {*}\n", .{value});
 710    }
 711}
 712
 713test "error" {
 714    {
 715        const value: anyerror!i32 = 1234;
 716        try expectFmt("error union: 1234\n", "error union: {!}\n", .{value});
 717        try expectFmt("error union: 1234\n", "error union: {!d}\n", .{value});
 718        try expectFmt("error union: 4d2\n", "error union: {!x}\n", .{value});
 719    }
 720    {
 721        const value: anyerror![]const u8 = "string";
 722        try expectFmt("error union: string\n", "error union: {!s}\n", .{value});
 723    }
 724    {
 725        const value: anyerror!i32 = error.InvalidChar;
 726        try expectFmt("error union: error.InvalidChar\n", "error union: {!}\n", .{value});
 727    }
 728}
 729
 730test "int.small" {
 731    {
 732        const value: u3 = 0b101;
 733        try expectFmt("u3: 5\n", "u3: {}\n", .{value});
 734    }
 735}
 736
 737test "int.specifier" {
 738    {
 739        const value: u8 = 'a';
 740        try expectFmt("u8: a\n", "u8: {c}\n", .{value});
 741    }
 742    {
 743        const value: u8 = 0b1100;
 744        try expectFmt("u8: 0b1100\n", "u8: 0b{b}\n", .{value});
 745    }
 746    {
 747        const value: u16 = 0o1234;
 748        try expectFmt("u16: 0o1234\n", "u16: 0o{o}\n", .{value});
 749    }
 750    {
 751        const value: u8 = 'a';
 752        try expectFmt("UTF-8: a\n", "UTF-8: {u}\n", .{value});
 753    }
 754    {
 755        const value: u21 = 0x1F310;
 756        try expectFmt("UTF-8: 🌐\n", "UTF-8: {u}\n", .{value});
 757    }
 758    {
 759        const value: u21 = 0xD800;
 760        try expectFmt("UTF-8: �\n", "UTF-8: {u}\n", .{value});
 761    }
 762    {
 763        const value: u21 = 0x110001;
 764        try expectFmt("UTF-8: �\n", "UTF-8: {u}\n", .{value});
 765    }
 766}
 767
 768test "int.padded" {
 769    try expectFmt("u8: '   1'", "u8: '{:4}'", .{@as(u8, 1)});
 770    try expectFmt("u8: '1000'", "u8: '{:0<4}'", .{@as(u8, 1)});
 771    try expectFmt("u8: '0001'", "u8: '{:0>4}'", .{@as(u8, 1)});
 772    try expectFmt("u8: '0100'", "u8: '{:0^4}'", .{@as(u8, 1)});
 773    try expectFmt("i8: '-1  '", "i8: '{:<4}'", .{@as(i8, -1)});
 774    try expectFmt("i8: '  -1'", "i8: '{:>4}'", .{@as(i8, -1)});
 775    try expectFmt("i8: ' -1 '", "i8: '{:^4}'", .{@as(i8, -1)});
 776    try expectFmt("i16: '-1234'", "i16: '{:4}'", .{@as(i16, -1234)});
 777    try expectFmt("i16: '+1234'", "i16: '{:4}'", .{@as(i16, 1234)});
 778    try expectFmt("i16: '-12345'", "i16: '{:4}'", .{@as(i16, -12345)});
 779    try expectFmt("i16: '+12345'", "i16: '{:4}'", .{@as(i16, 12345)});
 780    try expectFmt("u16: '12345'", "u16: '{:4}'", .{@as(u16, 12345)});
 781}
 782
 783test "buffer" {
 784    {
 785        var buf1: [32]u8 = undefined;
 786        var w: Writer = .fixed(&buf1);
 787        try w.printValue("", .{}, 1234, std.options.fmt_max_depth);
 788        try std.testing.expectEqualStrings("1234", w.buffered());
 789
 790        w = .fixed(&buf1);
 791        try w.printValue("c", .{}, 'a', std.options.fmt_max_depth);
 792        try std.testing.expectEqualStrings("a", w.buffered());
 793
 794        w = .fixed(&buf1);
 795        try w.printValue("b", .{}, 0b1100, std.options.fmt_max_depth);
 796        try std.testing.expectEqualStrings("1100", w.buffered());
 797    }
 798}
 799
 800// Test formatting of arrays by value, by single-item pointer, and as a slice
 801fn expectArrayFmt(expected: []const u8, comptime template: []const u8, comptime array_value: anytype) !void {
 802    try expectFmt(expected, template, .{array_value});
 803    try expectFmt(expected, template, .{&array_value});
 804    var runtime_zero: usize = 0;
 805    _ = &runtime_zero;
 806    try expectFmt(expected, template, .{array_value[runtime_zero..]});
 807}
 808
 809test "array" {
 810    const value: [3]u8 = "abc".*;
 811    try expectArrayFmt("array: abc\n", "array: {s}\n", value);
 812    try expectArrayFmt("array: 616263\n", "array: {x}\n", value);
 813    try expectArrayFmt("array: { 97, 98, 99 }\n", "array: {any}\n", value);
 814
 815    var buf: [100]u8 = undefined;
 816    try expectFmt(
 817        try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@intFromPtr(&value)}),
 818        "array: {*}\n",
 819        .{&value},
 820    );
 821}
 822
 823test "slice" {
 824    {
 825        const value: []const u8 = "abc";
 826        try expectFmt("slice: abc\n", "slice: {s}\n", .{value});
 827        try expectFmt("slice: 616263\n", "slice: {x}\n", .{value});
 828        try expectFmt("slice: { 97, 98, 99 }\n", "slice: {any}\n", .{value});
 829    }
 830    {
 831        var runtime_zero: usize = 0;
 832        _ = &runtime_zero;
 833        const value = @as([*]align(1) const []const u8, @ptrFromInt(0xdeadbeef))[runtime_zero..runtime_zero];
 834        try expectFmt("slice: []const u8@deadbeef\n", "slice: {*}\n", .{value});
 835    }
 836    {
 837        const null_term_slice: [:0]const u8 = "\x00hello\x00";
 838        try expectFmt("buf: \x00hello\x00\n", "buf: {s}\n", .{null_term_slice});
 839    }
 840
 841    try expectFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"});
 842
 843    {
 844        var int_slice = [_]u32{ 1, 4096, 391891, 1111111111 };
 845        const input: []const u32 = &int_slice;
 846        try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {any}", .{input});
 847    }
 848    {
 849        const S1 = struct {
 850            x: u8,
 851        };
 852        const struct_slice: []const S1 = &[_]S1{ S1{ .x = 8 }, S1{ .x = 42 } };
 853        try expectFmt("slice: { .{ .x = 8 }, .{ .x = 42 } }", "slice: {any}", .{struct_slice});
 854    }
 855    {
 856        const S2 = struct {
 857            x: u8,
 858
 859            pub fn format(s: @This(), writer: *Writer) Writer.Error!void {
 860                try writer.print("S2({})", .{s.x});
 861            }
 862        };
 863        const struct_slice: []const S2 = &[_]S2{ S2{ .x = 8 }, S2{ .x = 42 } };
 864        try expectFmt("slice: { .{ .x = 8 }, .{ .x = 42 } }", "slice: {any}", .{struct_slice});
 865    }
 866}
 867
 868test "pointer" {
 869    {
 870        const value = @as(*align(1) i32, @ptrFromInt(0xdeadbeef));
 871        try expectFmt("pointer: i32@deadbeef\n", "pointer: {}\n", .{value});
 872        try expectFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", .{value});
 873    }
 874    const FnPtr = *align(1) const fn () void;
 875    {
 876        const value = @as(FnPtr, @ptrFromInt(0xdeadbeef));
 877        try expectFmt("pointer: fn () void@deadbeef\n", "pointer: {}\n", .{value});
 878    }
 879    {
 880        const value = @as(FnPtr, @ptrFromInt(0xdeadbeef));
 881        try expectFmt("pointer: fn () void@deadbeef\n", "pointer: {}\n", .{value});
 882    }
 883}
 884
 885test "cstr" {
 886    try expectFmt(
 887        "cstr: Test C\n",
 888        "cstr: {s}\n",
 889        .{@as([*c]const u8, @ptrCast("Test C"))},
 890    );
 891}
 892
 893test "struct" {
 894    {
 895        const Struct = struct {
 896            field: u8,
 897        };
 898        const value = Struct{ .field = 42 };
 899        try expectFmt("struct: .{ .field = 42 }\n", "struct: {}\n", .{value});
 900        try expectFmt("struct: .{ .field = 42 }\n", "struct: {}\n", .{&value});
 901    }
 902    {
 903        const Struct = struct {
 904            a: u0,
 905            b: u1,
 906        };
 907        const value = Struct{ .a = 0, .b = 1 };
 908        try expectFmt("struct: .{ .a = 0, .b = 1 }\n", "struct: {}\n", .{value});
 909    }
 910
 911    const S = struct {
 912        a: u32,
 913        b: anyerror,
 914    };
 915
 916    const inst = S{
 917        .a = 456,
 918        .b = error.Unused,
 919    };
 920
 921    try expectFmt(".{ .a = 456, .b = error.Unused }", "{}", .{inst});
 922    // Tuples
 923    try expectFmt(".{ }", "{}", .{.{}});
 924    try expectFmt(".{ -1 }", "{}", .{.{-1}});
 925    try expectFmt(".{ -1, 42, 25000 }", "{}", .{.{ -1, 42, 0.25e5 }});
 926}
 927
 928test "enum" {
 929    const Enum = enum {
 930        One,
 931        Two,
 932    };
 933    const value = Enum.Two;
 934    try expectFmt("enum: .Two\n", "enum: {}\n", .{value});
 935    try expectFmt("enum: .Two\n", "enum: {}\n", .{&value});
 936    try expectFmt("enum: .One\n", "enum: {}\n", .{Enum.One});
 937    try expectFmt("enum: .Two\n", "enum: {}\n", .{Enum.Two});
 938
 939    // test very large enum to verify ct branch quota is large enough
 940    // TODO: https://github.com/ziglang/zig/issues/15609
 941    if (!((builtin.cpu.arch == .wasm32) and builtin.mode == .Debug)) {
 942        try expectFmt("enum: .INVALID_FUNCTION\n", "enum: {}\n", .{std.os.windows.Win32Error.INVALID_FUNCTION});
 943    }
 944
 945    const E = enum {
 946        One,
 947        Two,
 948        Three,
 949    };
 950
 951    const inst = E.Two;
 952
 953    try expectFmt(".Two", "{}", .{inst});
 954}
 955
 956test "non-exhaustive enum" {
 957    const Enum = enum(u16) {
 958        One = 0x000f,
 959        Two = 0xbeef,
 960        _,
 961    };
 962    try expectFmt("enum: .One\n", "enum: {}\n", .{Enum.One});
 963    try expectFmt("enum: .Two\n", "enum: {}\n", .{Enum.Two});
 964    try expectFmt("enum: @enumFromInt(4660)\n", "enum: {}\n", .{@as(Enum, @enumFromInt(0x1234))});
 965    try expectFmt("enum: f\n", "enum: {x}\n", .{Enum.One});
 966    try expectFmt("enum: beef\n", "enum: {x}\n", .{Enum.Two});
 967    try expectFmt("enum: BEEF\n", "enum: {X}\n", .{Enum.Two});
 968    try expectFmt("enum: 1234\n", "enum: {x}\n", .{@as(Enum, @enumFromInt(0x1234))});
 969
 970    try expectFmt("enum: 15\n", "enum: {d}\n", .{Enum.One});
 971    try expectFmt("enum: 48879\n", "enum: {d}\n", .{Enum.Two});
 972    try expectFmt("enum: 4660\n", "enum: {d}\n", .{@as(Enum, @enumFromInt(0x1234))});
 973}
 974
 975test "float.scientific" {
 976    try expectFmt("f32: 1.34e0", "f32: {e}", .{@as(f32, 1.34)});
 977    try expectFmt("f32: 1.234e1", "f32: {e}", .{@as(f32, 12.34)});
 978    try expectFmt("f64: -1.234e11", "f64: {e}", .{@as(f64, -12.34e10)});
 979    try expectFmt("f64: 9.99996e-40", "f64: {e}", .{@as(f64, 9.999960e-40)});
 980}
 981
 982test "float.scientific.precision" {
 983    try expectFmt("f64: 1.40971e-42", "f64: {e:.5}", .{@as(f64, 1.409706e-42)});
 984    try expectFmt("f64: 1.00000e-9", "f64: {e:.5}", .{@as(f64, @as(f32, @bitCast(@as(u32, 814313563))))});
 985    try expectFmt("f64: 7.81250e-3", "f64: {e:.5}", .{@as(f64, @as(f32, @bitCast(@as(u32, 1006632960))))});
 986    // libc rounds 1.000005e5 to 1.00000e5 but zig does 1.00001e5.
 987    // In fact, libc doesn't round a lot of 5 cases up when one past the precision point.
 988    try expectFmt("f64: 1.00001e5", "f64: {e:.5}", .{@as(f64, @as(f32, @bitCast(@as(u32, 1203982400))))});
 989}
 990
 991test "float.special" {
 992    try expectFmt("f64: nan", "f64: {}", .{math.nan(f64)});
 993    // negative nan is not defined by IEE 754,
 994    // and ARM thus normalizes it to positive nan
 995    if (builtin.target.cpu.arch != .arm) {
 996        try expectFmt("f64: -nan", "f64: {}", .{-math.nan(f64)});
 997    }
 998    try expectFmt("f64: inf", "f64: {}", .{math.inf(f64)});
 999    try expectFmt("f64: -inf", "f64: {}", .{-math.inf(f64)});
1000}
1001
1002test "float.hexadecimal.special" {
1003    try expectFmt("f64: nan", "f64: {x}", .{math.nan(f64)});
1004    // negative nan is not defined by IEE 754,
1005    // and ARM thus normalizes it to positive nan
1006    if (builtin.target.cpu.arch != .arm) {
1007        try expectFmt("f64: -nan", "f64: {x}", .{-math.nan(f64)});
1008    }
1009    try expectFmt("f64: inf", "f64: {x}", .{math.inf(f64)});
1010    try expectFmt("f64: -inf", "f64: {x}", .{-math.inf(f64)});
1011
1012    try expectFmt("f64: 0x0.0p0", "f64: {x}", .{@as(f64, 0)});
1013    try expectFmt("f64: -0x0.0p0", "f64: {x}", .{-@as(f64, 0)});
1014}
1015
1016test "float.hexadecimal" {
1017    try expectFmt("f16: 0x1.554p-2", "f16: {x}", .{@as(f16, 1.0 / 3.0)});
1018    try expectFmt("f32: 0x1.555556p-2", "f32: {x}", .{@as(f32, 1.0 / 3.0)});
1019    try expectFmt("f64: 0x1.5555555555555p-2", "f64: {x}", .{@as(f64, 1.0 / 3.0)});
1020    try expectFmt("f80: 0x1.5555555555555556p-2", "f80: {x}", .{@as(f80, 1.0 / 3.0)});
1021    try expectFmt("f128: 0x1.5555555555555555555555555555p-2", "f128: {x}", .{@as(f128, 1.0 / 3.0)});
1022
1023    try expectFmt("f16: 0x1p-14", "f16: {x}", .{math.floatMin(f16)});
1024    try expectFmt("f32: 0x1p-126", "f32: {x}", .{math.floatMin(f32)});
1025    try expectFmt("f64: 0x1p-1022", "f64: {x}", .{math.floatMin(f64)});
1026    try expectFmt("f80: 0x1p-16382", "f80: {x}", .{math.floatMin(f80)});
1027    try expectFmt("f128: 0x1p-16382", "f128: {x}", .{math.floatMin(f128)});
1028
1029    try expectFmt("f16: 0x0.004p-14", "f16: {x}", .{math.floatTrueMin(f16)});
1030    try expectFmt("f32: 0x0.000002p-126", "f32: {x}", .{math.floatTrueMin(f32)});
1031    try expectFmt("f64: 0x0.0000000000001p-1022", "f64: {x}", .{math.floatTrueMin(f64)});
1032    try expectFmt("f80: 0x0.0000000000000002p-16382", "f80: {x}", .{math.floatTrueMin(f80)});
1033    try expectFmt("f128: 0x0.0000000000000000000000000001p-16382", "f128: {x}", .{math.floatTrueMin(f128)});
1034
1035    try expectFmt("f16: 0x1.ffcp15", "f16: {x}", .{math.floatMax(f16)});
1036    try expectFmt("f32: 0x1.fffffep127", "f32: {x}", .{math.floatMax(f32)});
1037    try expectFmt("f64: 0x1.fffffffffffffp1023", "f64: {x}", .{math.floatMax(f64)});
1038    try expectFmt("f80: 0x1.fffffffffffffffep16383", "f80: {x}", .{math.floatMax(f80)});
1039    try expectFmt("f128: 0x1.ffffffffffffffffffffffffffffp16383", "f128: {x}", .{math.floatMax(f128)});
1040}
1041
1042test "float.hexadecimal.precision" {
1043    try expectFmt("f16: 0x1.5p-2", "f16: {x:.1}", .{@as(f16, 1.0 / 3.0)});
1044    try expectFmt("f32: 0x1.555p-2", "f32: {x:.3}", .{@as(f32, 1.0 / 3.0)});
1045    try expectFmt("f64: 0x1.55555p-2", "f64: {x:.5}", .{@as(f64, 1.0 / 3.0)});
1046    try expectFmt("f80: 0x1.5555555p-2", "f80: {x:.7}", .{@as(f80, 1.0 / 3.0)});
1047    try expectFmt("f128: 0x1.555555555p-2", "f128: {x:.9}", .{@as(f128, 1.0 / 3.0)});
1048
1049    try expectFmt("f16: 0x1.00000p0", "f16: {x:.5}", .{@as(f16, 1.0)});
1050    try expectFmt("f32: 0x1.00000p0", "f32: {x:.5}", .{@as(f32, 1.0)});
1051    try expectFmt("f64: 0x1.00000p0", "f64: {x:.5}", .{@as(f64, 1.0)});
1052    try expectFmt("f80: 0x1.00000p0", "f80: {x:.5}", .{@as(f80, 1.0)});
1053    try expectFmt("f128: 0x1.00000p0", "f128: {x:.5}", .{@as(f128, 1.0)});
1054}
1055
1056test "float.decimal" {
1057    try expectFmt("f64: 152314000000000000000000000000", "f64: {d}", .{@as(f64, 1.52314e29)});
1058    try expectFmt("f32: 0", "f32: {d}", .{@as(f32, 0.0)});
1059    try expectFmt("f32: 0", "f32: {d:.0}", .{@as(f32, 0.0)});
1060    try expectFmt("f32: 1.1", "f32: {d:.1}", .{@as(f32, 1.1234)});
1061    try expectFmt("f32: 1234.57", "f32: {d:.2}", .{@as(f32, 1234.567)});
1062    // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64).
1063    // -11.12339... is rounded back up to -11.1234
1064    try expectFmt("f32: -11.1234", "f32: {d:.4}", .{@as(f32, -11.1234)});
1065    try expectFmt("f32: 91.12345", "f32: {d:.5}", .{@as(f32, 91.12345)});
1066    try expectFmt("f64: 91.1234567890", "f64: {d:.10}", .{@as(f64, 91.12345678901235)});
1067    try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 0.0)});
1068    try expectFmt("f64: 6", "f64: {d:.0}", .{@as(f64, 5.700)});
1069    try expectFmt("f64: 10.0", "f64: {d:.1}", .{@as(f64, 9.999)});
1070    try expectFmt("f64: 1.000", "f64: {d:.3}", .{@as(f64, 1.0)});
1071    try expectFmt("f64: 0.00030000", "f64: {d:.8}", .{@as(f64, 0.0003)});
1072    try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 1.40130e-45)});
1073    try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 9.999960e-40)});
1074    try expectFmt("f64: 10000000000000.00", "f64: {d:.2}", .{@as(f64, 9999999999999.999)});
1075    try expectFmt("f64: 10000000000000000000000000000000000000", "f64: {d}", .{@as(f64, 1e37)});
1076    try expectFmt("f64: 100000000000000000000000000000000000000", "f64: {d}", .{@as(f64, 1e38)});
1077}
1078
1079test "float.libc.sanity" {
1080    try expectFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @as(f32, @bitCast(@as(u32, 916964781))))});
1081    try expectFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @as(f32, @bitCast(@as(u32, 925353389))))});
1082    try expectFmt("f64: 0.10000", "f64: {d:.5}", .{@as(f64, @as(f32, @bitCast(@as(u32, 1036831278))))});
1083    try expectFmt("f64: 1.00000", "f64: {d:.5}", .{@as(f64, @as(f32, @bitCast(@as(u32, 1065353133))))});
1084    try expectFmt("f64: 10.00000", "f64: {d:.5}", .{@as(f64, @as(f32, @bitCast(@as(u32, 1092616192))))});
1085
1086    // libc differences
1087    //
1088    // This is 0.015625 exactly according to gdb. We thus round down,
1089    // however glibc rounds up for some reason. This occurs for all
1090    // floats of the form x.yyyy25 on a precision point.
1091    try expectFmt("f64: 0.01563", "f64: {d:.5}", .{@as(f64, @as(f32, @bitCast(@as(u32, 1015021568))))});
1092    // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3
1093    // also rounds to 630 so I'm inclined to believe libc is not
1094    // optimal here.
1095    try expectFmt("f64: 18014400656965630.00000", "f64: {d:.5}", .{@as(f64, @as(f32, @bitCast(@as(u32, 1518338049))))});
1096}
1097
1098test "union" {
1099    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
1100
1101    const TU = union(enum) {
1102        float: f32,
1103        int: u32,
1104    };
1105
1106    const UU = union {
1107        float: f32,
1108        int: u32,
1109    };
1110
1111    const EU = extern union {
1112        float: f32,
1113        int: u32,
1114    };
1115
1116    const tu_inst: TU = .{ .int = 123 };
1117    const uu_inst: UU = .{ .int = 456 };
1118    const eu_inst: EU = .{ .float = 321.123 };
1119
1120    try expectFmt(".{ .int = 123 }", "{}", .{tu_inst});
1121    try expectFmt(".{ ... }", "{}", .{uu_inst});
1122    try expectFmt(".{ .float = 321.123, .int = 1134596030 }", "{}", .{eu_inst});
1123}
1124
1125test "struct.self-referential" {
1126    const S = struct {
1127        const SelfType = @This();
1128        a: ?*SelfType,
1129    };
1130
1131    var inst = S{
1132        .a = null,
1133    };
1134    inst.a = &inst;
1135
1136    try expectFmt(".{ .a = .{ .a = .{ .a = .{ ... } } } }", "{}", .{inst});
1137}
1138
1139test "struct.zero-size" {
1140    const A = struct {
1141        fn foo() void {}
1142    };
1143    const B = struct {
1144        a: A,
1145        c: i32,
1146    };
1147
1148    const a = A{};
1149    const b = B{ .a = a, .c = 0 };
1150
1151    try expectFmt(".{ .a = .{ }, .c = 0 }", "{}", .{b});
1152}
1153
1154/// Encodes a sequence of bytes as hexadecimal digits.
1155/// Returns an array containing the encoded bytes.
1156pub fn bytesToHex(input: anytype, case: Case) [input.len * 2]u8 {
1157    if (input.len == 0) return [_]u8{};
1158    comptime assert(@TypeOf(input[0]) == u8); // elements to encode must be unsigned bytes
1159
1160    const charset = "0123456789" ++ if (case == .upper) "ABCDEF" else "abcdef";
1161    var result: [input.len * 2]u8 = undefined;
1162    for (input, 0..) |b, i| {
1163        result[i * 2 + 0] = charset[b >> 4];
1164        result[i * 2 + 1] = charset[b & 15];
1165    }
1166    return result;
1167}
1168
1169/// Decodes the sequence of bytes represented by the specified string of
1170/// hexadecimal characters.
1171/// Returns a slice of the output buffer containing the decoded bytes.
1172pub fn hexToBytes(out: []u8, input: []const u8) ![]u8 {
1173    // Expect 0 or n pairs of hexadecimal digits.
1174    if (input.len & 1 != 0)
1175        return error.InvalidLength;
1176    if (out.len * 2 < input.len)
1177        return error.NoSpaceLeft;
1178
1179    var in_i: usize = 0;
1180    while (in_i < input.len) : (in_i += 2) {
1181        const hi = try charToDigit(input[in_i], 16);
1182        const lo = try charToDigit(input[in_i + 1], 16);
1183        out[in_i / 2] = (hi << 4) | lo;
1184    }
1185
1186    return out[0 .. in_i / 2];
1187}
1188
1189test bytesToHex {
1190    const input = "input slice";
1191    const encoded = bytesToHex(input, .lower);
1192    var decoded: [input.len]u8 = undefined;
1193    try std.testing.expectEqualSlices(u8, input, try hexToBytes(&decoded, &encoded));
1194}
1195
1196test hexToBytes {
1197    var buf: [32]u8 = undefined;
1198    try expectFmt("90" ** 32, "{X}", .{try hexToBytes(&buf, "90" ** 32)});
1199    try expectFmt("ABCD", "{X}", .{try hexToBytes(&buf, "ABCD")});
1200    try expectFmt("", "{X}", .{try hexToBytes(&buf, "")});
1201    try std.testing.expectError(error.InvalidCharacter, hexToBytes(&buf, "012Z"));
1202    try std.testing.expectError(error.InvalidLength, hexToBytes(&buf, "AAA"));
1203    try std.testing.expectError(error.NoSpaceLeft, hexToBytes(buf[0..1], "ABAB"));
1204}
1205
1206test "positional" {
1207    try expectFmt("2 1 0", "{2} {1} {0}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) });
1208    try expectFmt("2 1 0", "{2} {1} {}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) });
1209    try expectFmt("0 0", "{0} {0}", .{@as(usize, 0)});
1210    try expectFmt("0 1", "{} {1}", .{ @as(usize, 0), @as(usize, 1) });
1211    try expectFmt("1 0 0 1", "{1} {} {0} {}", .{ @as(usize, 0), @as(usize, 1) });
1212}
1213
1214test "positional with specifier" {
1215    try expectFmt("10.0", "{0d:.1}", .{@as(f64, 9.999)});
1216}
1217
1218test "positional/alignment/width/precision" {
1219    try expectFmt("10.0", "{0d: >3.1}", .{@as(f64, 9.999)});
1220}
1221
1222test "vector" {
1223    const vbool: @Vector(4, bool) = [_]bool{ true, false, true, false };
1224    const vi64: @Vector(4, i64) = [_]i64{ -2, -1, 0, 1 };
1225    const vu64: @Vector(4, u64) = [_]u64{ 1000, 2000, 3000, 4000 };
1226
1227    try expectFmt("{ true, false, true, false }", "{}", .{vbool});
1228    try expectFmt("{ -2, -1, 0, 1 }", "{}", .{vi64});
1229    try expectFmt("{    -2,    -1,    +0,    +1 }", "{d:5}", .{vi64});
1230    try expectFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64});
1231    try expectFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64});
1232
1233    const x: [4]u64 = undefined;
1234    const vp: @Vector(4, *const u64) = [_]*const u64{ &x[0], &x[1], &x[2], &x[3] };
1235    const vop: @Vector(4, ?*const u64) = [_]?*const u64{ &x[0], null, null, &x[3] };
1236
1237    var expect_buffer: [@sizeOf(usize) * 2 * 4 + 64]u8 = undefined;
1238    try expectFmt(try bufPrint(
1239        &expect_buffer,
1240        "{{ {}, {}, {}, {} }}",
1241        .{ &x[0], &x[1], &x[2], &x[3] },
1242    ), "{}", .{vp});
1243    try expectFmt(try bufPrint(
1244        &expect_buffer,
1245        "{{ {?}, null, null, {?} }}",
1246        .{ &x[0], &x[3] },
1247    ), "{any}", .{vop});
1248}
1249
1250test "enum-literal" {
1251    try expectFmt(".hello_world", "{}", .{.hello_world});
1252    try expectFmt("hello_world", "{t}", .{.hello_world});
1253}
1254
1255test "padding" {
1256    try expectFmt("Simple", "{s}", .{"Simple"});
1257    try expectFmt("      1234", "{:10}", .{1234});
1258    try expectFmt("      1234", "{:>10}", .{1234});
1259    try expectFmt("======1234", "{:=>10}", .{1234});
1260    try expectFmt("1234======", "{:=<10}", .{1234});
1261    try expectFmt("   1234   ", "{:^10}", .{1234});
1262    try expectFmt("===1234===", "{:=^10}", .{1234});
1263    try expectFmt("====a", "{c:=>5}", .{'a'});
1264    try expectFmt("==a==", "{c:=^5}", .{'a'});
1265    try expectFmt("a====", "{c:=<5}", .{'a'});
1266}
1267
1268test "decimal float padding" {
1269    const number: f32 = 3.1415;
1270    try expectFmt("left-pad:   **3.142\n", "left-pad:   {d:*>7.3}\n", .{number});
1271    try expectFmt("center-pad: *3.142*\n", "center-pad: {d:*^7.3}\n", .{number});
1272    try expectFmt("right-pad:  3.142**\n", "right-pad:  {d:*<7.3}\n", .{number});
1273}
1274
1275test "sci float padding" {
1276    const number: f32 = 3.1415;
1277    try expectFmt("left-pad:   ****3.142e0\n", "left-pad:   {e:*>11.3}\n", .{number});
1278    try expectFmt("center-pad: **3.142e0**\n", "center-pad: {e:*^11.3}\n", .{number});
1279    try expectFmt("right-pad:  3.142e0****\n", "right-pad:  {e:*<11.3}\n", .{number});
1280}
1281
1282test "padding.zero" {
1283    try expectFmt("zero-pad: '0042'", "zero-pad: '{:04}'", .{42});
1284    try expectFmt("std-pad: '        42'", "std-pad: '{:10}'", .{42});
1285    try expectFmt("std-pad-1: '001'", "std-pad-1: '{:0>3}'", .{1});
1286    try expectFmt("std-pad-2: '911'", "std-pad-2: '{:1<03}'", .{9});
1287    try expectFmt("std-pad-3: '  1'", "std-pad-3: '{:>03}'", .{1});
1288    try expectFmt("center-pad: '515'", "center-pad: '{:5^03}'", .{1});
1289}
1290
1291test "null" {
1292    const inst = null;
1293    try expectFmt("null", "{}", .{inst});
1294}
1295
1296test "type" {
1297    try expectFmt("u8", "{}", .{u8});
1298    try expectFmt("?f32", "{}", .{?f32});
1299    try expectFmt("[]const u8", "{}", .{[]const u8});
1300}
1301
1302test "named arguments" {
1303    try expectFmt("hello world!", "{s} world{c}", .{ "hello", '!' });
1304    try expectFmt("hello world!", "{[greeting]s} world{[punctuation]c}", .{ .punctuation = '!', .greeting = "hello" });
1305    try expectFmt("hello world!", "{[1]s} world{[0]c}", .{ '!', "hello" });
1306}
1307
1308test "runtime width specifier" {
1309    const width: usize = 9;
1310    try expectFmt("~~12345~~", "{d:~^[1]}", .{ 12345, width });
1311    try expectFmt("~~12345~~", "{d:~^[width]}", .{ .string = 12345, .width = width });
1312    try expectFmt("    12345", "{d:[1]}", .{ 12345, width });
1313    try expectFmt("42     12345", "{d} {d:[2]}", .{ 42, 12345, width });
1314}
1315
1316test "runtime precision specifier" {
1317    const number: f32 = 3.1415;
1318    const precision: usize = 2;
1319    try expectFmt("3.14e0", "{e:1.[1]}", .{ number, precision });
1320    try expectFmt("3.14e0", "{e:1.[precision]}", .{ .number = number, .precision = precision });
1321}
1322
1323test "recursive format function" {
1324    const R = union(enum) {
1325        const R = @This();
1326        Leaf: i32,
1327        Branch: struct { left: *const R, right: *const R },
1328
1329        pub fn format(self: R, writer: *Writer) Writer.Error!void {
1330            return switch (self) {
1331                .Leaf => |n| writer.print("Leaf({})", .{n}),
1332                .Branch => |b| writer.print("Branch({f}, {f})", .{ b.left, b.right }),
1333            };
1334        }
1335    };
1336
1337    var r: R = .{ .Leaf = 1 };
1338    try expectFmt("Leaf(1)\n", "{f}\n", .{&r});
1339}
1340
1341pub const hex_charset = "0123456789abcdef";
1342
1343/// Converts an unsigned integer of any multiple of u8 to an array of lowercase
1344/// hex bytes, little endian.
1345pub fn hex(x: anytype) [@sizeOf(@TypeOf(x)) * 2]u8 {
1346    comptime assert(@typeInfo(@TypeOf(x)).int.signedness == .unsigned);
1347    var result: [@sizeOf(@TypeOf(x)) * 2]u8 = undefined;
1348    var i: usize = 0;
1349    while (i < result.len / 2) : (i += 1) {
1350        const byte: u8 = @truncate(x >> @intCast(8 * i));
1351        result[i * 2 + 0] = hex_charset[byte >> 4];
1352        result[i * 2 + 1] = hex_charset[byte & 15];
1353    }
1354    return result;
1355}
1356
1357test hex {
1358    {
1359        const x = hex(@as(u32, 0xdeadbeef));
1360        try std.testing.expect(x.len == 8);
1361        try std.testing.expectEqualStrings("efbeadde", &x);
1362    }
1363    {
1364        const s = "[" ++ hex(@as(u64, 0x12345678_abcdef00)) ++ "]";
1365        try std.testing.expect(s.len == 18);
1366        try std.testing.expectEqualStrings("[00efcdab78563412]", s);
1367    }
1368}
1369
1370test "parser until" {
1371    { // return substring till ':'
1372        var parser: Parser = .{ .bytes = "abc:1234", .i = 0 };
1373        try testing.expectEqualStrings("abc", parser.until(':'));
1374    }
1375
1376    { // return the entire string - `ch` not found
1377        var parser: Parser = .{ .bytes = "abc1234", .i = 0 };
1378        try testing.expectEqualStrings("abc1234", parser.until(':'));
1379    }
1380
1381    { // substring is empty - `ch` is the only character
1382        var parser: Parser = .{ .bytes = ":", .i = 0 };
1383        try testing.expectEqualStrings("", parser.until(':'));
1384    }
1385
1386    { // empty string and `ch` not found
1387        var parser: Parser = .{ .bytes = "", .i = 0 };
1388        try testing.expectEqualStrings("", parser.until(':'));
1389    }
1390
1391    { // substring starts at index 2 and goes upto `ch`
1392        var parser: Parser = .{ .bytes = "abc:1234", .i = 2 };
1393        try testing.expectEqualStrings("c", parser.until(':'));
1394    }
1395
1396    { // substring starts at index 4 and goes upto the end - `ch` not found
1397        var parser: Parser = .{ .bytes = "abc1234", .i = 4 };
1398        try testing.expectEqualStrings("234", parser.until(':'));
1399    }
1400}
1401
1402test "parser peek" {
1403    { // start iteration from the first index
1404        var parser: Parser = .{ .bytes = "hello world", .i = 0 };
1405        try testing.expectEqual('h', parser.peek(0));
1406        try testing.expectEqual('e', parser.peek(1));
1407        try testing.expectEqual(' ', parser.peek(5));
1408        try testing.expectEqual('d', parser.peek(10));
1409        try testing.expectEqual(null, parser.peek(11));
1410    }
1411
1412    { // start iteration from the second last index
1413        var parser: Parser = .{ .bytes = "hello world!", .i = 10 };
1414
1415        try testing.expectEqual('d', parser.peek(0));
1416        try testing.expectEqual('!', parser.peek(1));
1417        try testing.expectEqual(null, parser.peek(5));
1418    }
1419
1420    { // start iteration beyond the length of the string
1421        var parser: Parser = .{ .bytes = "hello", .i = 5 };
1422
1423        try testing.expectEqual(null, parser.peek(0));
1424        try testing.expectEqual(null, parser.peek(1));
1425    }
1426
1427    { // empty string
1428        var parser: Parser = .{ .bytes = "", .i = 0 };
1429
1430        try testing.expectEqual(null, parser.peek(0));
1431        try testing.expectEqual(null, parser.peek(2));
1432    }
1433}
1434
1435test "parser char" {
1436    // character exists - iterator at 0
1437    var parser: Parser = .{ .bytes = "~~hello", .i = 0 };
1438    try testing.expectEqual('~', parser.char());
1439
1440    // character exists - iterator in the middle
1441    parser = .{ .bytes = "~~hello", .i = 3 };
1442    try testing.expectEqual('e', parser.char());
1443
1444    // character exists - iterator at the end
1445    parser = .{ .bytes = "~~hello", .i = 6 };
1446    try testing.expectEqual('o', parser.char());
1447
1448    // character doesn't exist - iterator beyond the length of the string
1449    parser = .{ .bytes = "~~hello", .i = 7 };
1450    try testing.expectEqual(null, parser.char());
1451}
1452
1453test "parser maybe" {
1454    // character exists - iterator at 0
1455    var parser: Parser = .{ .bytes = "hello world", .i = 0 };
1456    try testing.expect(parser.maybe('h'));
1457
1458    // character exists - iterator at space
1459    parser = .{ .bytes = "hello world", .i = 5 };
1460    try testing.expect(parser.maybe(' '));
1461
1462    // character exists - iterator at the end
1463    parser = .{ .bytes = "hello world", .i = 10 };
1464    try testing.expect(parser.maybe('d'));
1465
1466    // character doesn't exist - iterator beyond the length of the string
1467    parser = .{ .bytes = "hello world", .i = 11 };
1468    try testing.expect(!parser.maybe('e'));
1469}
1470
1471test "parser number" {
1472    // input is a single digit natural number - iterator at 0
1473    var parser: Parser = .{ .bytes = "7", .i = 0 };
1474    try testing.expect(7 == parser.number());
1475
1476    // input is a two digit natural number - iterator at 1
1477    parser = .{ .bytes = "29", .i = 1 };
1478    try testing.expect(9 == parser.number());
1479
1480    // input is a two digit natural number - iterator beyond the length of the string
1481    parser = .{ .bytes = "32", .i = 2 };
1482    try testing.expectEqual(null, parser.number());
1483
1484    // input is an integer
1485    parser = .{ .bytes = "0", .i = 0 };
1486    try testing.expect(0 == parser.number());
1487
1488    // input is a negative integer
1489    parser = .{ .bytes = "-2", .i = 0 };
1490    try testing.expectEqual(null, parser.number());
1491
1492    // input is a string
1493    parser = .{ .bytes = "no_number", .i = 2 };
1494    try testing.expectEqual(null, parser.number());
1495
1496    // input is a single character string
1497    parser = .{ .bytes = "n", .i = 0 };
1498    try testing.expectEqual(null, parser.number());
1499
1500    // input is an empty string
1501    parser = .{ .bytes = "", .i = 0 };
1502    try testing.expectEqual(null, parser.number());
1503}
1504
1505test "parser specifier" {
1506    { // input string is a digit; iterator at 0
1507        const expected: Specifier = Specifier{ .number = 1 };
1508        var parser: Parser = .{ .bytes = "1", .i = 0 };
1509
1510        const result = try parser.specifier();
1511        try testing.expect(expected.number == result.number);
1512    }
1513
1514    { // input string is a two digit number; iterator at 0
1515        const digit: Specifier = Specifier{ .number = 42 };
1516        var parser: Parser = .{ .bytes = "42", .i = 0 };
1517
1518        const result = try parser.specifier();
1519        try testing.expect(digit.number == result.number);
1520    }
1521
1522    { // input string is a two digit number digit; iterator at 1
1523        const digit: Specifier = Specifier{ .number = 8 };
1524        var parser: Parser = .{ .bytes = "28", .i = 1 };
1525
1526        const result = try parser.specifier();
1527        try testing.expect(digit.number == result.number);
1528    }
1529
1530    { // input string is a two digit number with square brackets; iterator at 0
1531        const digit: Specifier = Specifier{ .named = "15" };
1532        var parser: Parser = .{ .bytes = "[15]", .i = 0 };
1533
1534        const result = try parser.specifier();
1535        try testing.expectEqualStrings(digit.named, result.named);
1536    }
1537
1538    { // input string is not a number and contains square brackets; iterator at 0
1539        const digit: Specifier = Specifier{ .named = "hello" };
1540        var parser: Parser = .{ .bytes = "[hello]", .i = 0 };
1541
1542        const result = try parser.specifier();
1543        try testing.expectEqualStrings(digit.named, result.named);
1544    }
1545
1546    { // input string is not a number and doesn't contain closing square bracket; iterator at 0
1547        var parser: Parser = .{ .bytes = "[hello", .i = 0 };
1548
1549        const result = parser.specifier();
1550        try testing.expectError(@field(anyerror, "Expected closing ]"), result);
1551    }
1552
1553    { // input string is not a number and doesn't contain closing square bracket; iterator at 2
1554        var parser: Parser = .{ .bytes = "[[[[hello", .i = 2 };
1555
1556        const result = parser.specifier();
1557        try testing.expectError(@field(anyerror, "Expected closing ]"), result);
1558    }
1559
1560    { // input string is not a number and contains unbalanced square brackets; iterator at 0
1561        const digit: Specifier = Specifier{ .named = "[[hello" };
1562        var parser: Parser = .{ .bytes = "[[[hello]", .i = 0 };
1563
1564        const result = try parser.specifier();
1565        try testing.expectEqualStrings(digit.named, result.named);
1566    }
1567
1568    { // input string is not a number and contains unbalanced square brackets; iterator at 1
1569        const digit: Specifier = Specifier{ .named = "[[hello" };
1570        var parser: Parser = .{ .bytes = "[[[[hello]]]]]", .i = 1 };
1571
1572        const result = try parser.specifier();
1573        try testing.expectEqualStrings(digit.named, result.named);
1574    }
1575
1576    { // input string is neither a digit nor a named argument
1577        const char: Specifier = Specifier{ .none = {} };
1578        var parser: Parser = .{ .bytes = "hello", .i = 0 };
1579
1580        const result = try parser.specifier();
1581        try testing.expectEqual(char.none, result.none);
1582    }
1583}
1584
1585test {
1586    _ = float;
1587}