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}