| ... | ... | @@ -1417,10 +1417,23 @@ pub fn formatIntBuf(out_buf: []u8, value: anytype, base: u8, case: Case, options |
| 1417 | 1417 | return fbs.pos; |
| 1418 | 1418 | } |
| 1419 | 1419 | |
| 1420 | | fn formatDuration(ns: u64, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { |
| 1420 | const FormatDurationData = struct { |
| 1421 | ns: u64, |
| 1422 | negative: bool = false, |
| 1423 | }; |
| 1424 | |
| 1425 | fn formatDuration(data: FormatDurationData, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { |
| 1421 | 1426 | _ = fmt; |
| 1422 | | _ = options; |
| 1423 | | var ns_remaining = ns; |
| 1427 | |
| 1428 | // worst case: "-XXXyXXwXXdXXhXXmXX.XXXs".len = 24 |
| 1429 | var buf: [24]u8 = undefined; |
| 1430 | var fbs = std.io.fixedBufferStream(&buf); |
| 1431 | var buf_writer = fbs.writer(); |
| 1432 | if (data.negative) { |
| 1433 | try buf_writer.writeByte('-'); |
| 1434 | } |
| 1435 | |
| 1436 | var ns_remaining = data.ns; |
| 1424 | 1437 | inline for (.{ |
| 1425 | 1438 | .{ .ns = 365 * std.time.ns_per_day, .sep = 'y' }, |
| 1426 | 1439 | .{ .ns = std.time.ns_per_week, .sep = 'w' }, |
| ... | ... | @@ -1430,10 +1443,11 @@ fn formatDuration(ns: u64, comptime fmt: []const u8, options: std.fmt.FormatOpti |
| 1430 | 1443 | }) |unit| { |
| 1431 | 1444 | if (ns_remaining >= unit.ns) { |
| 1432 | 1445 | const units = ns_remaining / unit.ns; |
| 1433 | | try formatInt(units, 10, .lower, .{}, writer); |
| 1434 | | try writer.writeByte(unit.sep); |
| 1446 | try formatInt(units, 10, .lower, .{}, buf_writer); |
| 1447 | try buf_writer.writeByte(unit.sep); |
| 1435 | 1448 | ns_remaining -= units * unit.ns; |
| 1436 | | if (ns_remaining == 0) return; |
| 1449 | if (ns_remaining == 0) |
| 1450 | return formatBuf(fbs.getWritten(), options, writer); |
| 1437 | 1451 | } |
| 1438 | 1452 | } |
| 1439 | 1453 | |
| ... | ... | @@ -1444,32 +1458,33 @@ fn formatDuration(ns: u64, comptime fmt: []const u8, options: std.fmt.FormatOpti |
| 1444 | 1458 | }) |unit| { |
| 1445 | 1459 | const kunits = ns_remaining * 1000 / unit.ns; |
| 1446 | 1460 | if (kunits >= 1000) { |
| 1447 | | try formatInt(kunits / 1000, 10, .lower, .{}, writer); |
| 1461 | try formatInt(kunits / 1000, 10, .lower, .{}, buf_writer); |
| 1448 | 1462 | const frac = kunits % 1000; |
| 1449 | 1463 | if (frac > 0) { |
| 1450 | 1464 | // Write up to 3 decimal places |
| 1451 | | var buf = [_]u8{ '.', 0, 0, 0 }; |
| 1452 | | _ = formatIntBuf(buf[1..], frac, 10, .lower, .{ .fill = '0', .width = 3 }); |
| 1465 | var decimal_buf = [_]u8{ '.', 0, 0, 0 }; |
| 1466 | _ = formatIntBuf(decimal_buf[1..], frac, 10, .lower, .{ .fill = '0', .width = 3 }); |
| 1453 | 1467 | var end: usize = 4; |
| 1454 | 1468 | while (end > 1) : (end -= 1) { |
| 1455 | | if (buf[end - 1] != '0') break; |
| 1469 | if (decimal_buf[end - 1] != '0') break; |
| 1456 | 1470 | } |
| 1457 | | try writer.writeAll(buf[0..end]); |
| 1471 | try buf_writer.writeAll(decimal_buf[0..end]); |
| 1458 | 1472 | } |
| 1459 | | try writer.writeAll(unit.sep); |
| 1460 | | return; |
| 1473 | try buf_writer.writeAll(unit.sep); |
| 1474 | return formatBuf(fbs.getWritten(), options, writer); |
| 1461 | 1475 | } |
| 1462 | 1476 | } |
| 1463 | 1477 | |
| 1464 | | try formatInt(ns_remaining, 10, .lower, .{}, writer); |
| 1465 | | try writer.writeAll("ns"); |
| 1466 | | return; |
| 1478 | try formatInt(ns_remaining, 10, .lower, .{}, buf_writer); |
| 1479 | try buf_writer.writeAll("ns"); |
| 1480 | return formatBuf(fbs.getWritten(), options, writer); |
| 1467 | 1481 | } |
| 1468 | 1482 | |
| 1469 | 1483 | /// Return a Formatter for number of nanoseconds according to its magnitude: |
| 1470 | 1484 | /// [#y][#w][#d][#h][#m]#[.###][n|u|m]s |
| 1471 | 1485 | pub fn fmtDuration(ns: u64) Formatter(formatDuration) { |
| 1472 | | return .{ .data = ns }; |
| 1486 | const data = FormatDurationData{ .ns = ns }; |
| 1487 | return .{ .data = data }; |
| 1473 | 1488 | } |
| 1474 | 1489 | |
| 1475 | 1490 | test "fmtDuration" { |
| ... | ... | @@ -1504,18 +1519,29 @@ test "fmtDuration" { |
| 1504 | 1519 | .{ .s = "1y1h1ms", .d = 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms }, |
| 1505 | 1520 | .{ .s = "1y1h1ms", .d = 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1 }, |
| 1506 | 1521 | .{ .s = "1y1m999ns", .d = 365 * std.time.ns_per_day + std.time.ns_per_min + 999 }, |
| 1522 | .{ .s = "584y49w23h34m33.709s", .d = math.maxInt(u64) }, |
| 1507 | 1523 | }) |tc| { |
| 1508 | 1524 | const slice = try bufPrint(&buf, "{}", .{fmtDuration(tc.d)}); |
| 1509 | 1525 | try std.testing.expectEqualStrings(tc.s, slice); |
| 1510 | 1526 | } |
| 1527 | |
| 1528 | inline for (.{ |
| 1529 | .{ .s = "=======0ns", .f = "{s:=>10}", .d = 0 }, |
| 1530 | .{ .s = "1ns=======", .f = "{s:=<10}", .d = 1 }, |
| 1531 | .{ .s = " 999ns ", .f = "{s:^10}", .d = std.time.ns_per_us - 1 }, |
| 1532 | }) |tc| { |
| 1533 | const slice = try bufPrint(&buf, tc.f, .{fmtDuration(tc.d)}); |
| 1534 | try std.testing.expectEqualStrings(tc.s, slice); |
| 1535 | } |
| 1511 | 1536 | } |
| 1512 | 1537 | |
| 1513 | 1538 | fn formatDurationSigned(ns: i64, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { |
| 1514 | 1539 | if (ns < 0) { |
| 1515 | | try writer.writeByte('-'); |
| 1516 | | try formatDuration(@intCast(u64, -ns), fmt, options, writer); |
| 1540 | const data = FormatDurationData{ .ns = @intCast(u64, -ns), .negative = true }; |
| 1541 | try formatDuration(data, fmt, options, writer); |
| 1517 | 1542 | } else { |
| 1518 | | try formatDuration(@intCast(u64, ns), fmt, options, writer); |
| 1543 | const data = FormatDurationData{ .ns = @intCast(u64, ns) }; |
| 1544 | try formatDuration(data, fmt, options, writer); |
| 1519 | 1545 | } |
| 1520 | 1546 | } |
| 1521 | 1547 | |
| ... | ... | @@ -1585,10 +1611,22 @@ test "fmtDurationSigned" { |
| 1585 | 1611 | .{ .s = "-1y1h1ms", .d = -(365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1) }, |
| 1586 | 1612 | .{ .s = "1y1m999ns", .d = 365 * std.time.ns_per_day + std.time.ns_per_min + 999 }, |
| 1587 | 1613 | .{ .s = "-1y1m999ns", .d = -(365 * std.time.ns_per_day + std.time.ns_per_min + 999) }, |
| 1614 | .{ .s = "292y24w3d23h47m16.854s", .d = math.maxInt(i64) }, |
| 1615 | .{ .s = "-292y24w3d23h47m16.854s", .d = math.minInt(i64) + 1 }, |
| 1588 | 1616 | }) |tc| { |
| 1589 | 1617 | const slice = try bufPrint(&buf, "{}", .{fmtDurationSigned(tc.d)}); |
| 1590 | 1618 | try std.testing.expectEqualStrings(tc.s, slice); |
| 1591 | 1619 | } |
| 1620 | |
| 1621 | inline for (.{ |
| 1622 | .{ .s = "=======0ns", .f = "{s:=>10}", .d = 0 }, |
| 1623 | .{ .s = "1ns=======", .f = "{s:=<10}", .d = 1 }, |
| 1624 | .{ .s = "-1ns======", .f = "{s:=<10}", .d = -(1) }, |
| 1625 | .{ .s = " -999ns ", .f = "{s:^10}", .d = -(std.time.ns_per_us - 1) }, |
| 1626 | }) |tc| { |
| 1627 | const slice = try bufPrint(&buf, tc.f, .{fmtDurationSigned(tc.d)}); |
| 1628 | try std.testing.expectEqualStrings(tc.s, slice); |
| 1629 | } |
| 1592 | 1630 | } |
| 1593 | 1631 | |
| 1594 | 1632 | pub const ParseIntError = error{ |