Simple timestamp lib to record timestamp in logs with millisecond precision

Reminds me a bit of this topic:


@miagi Personally I think it would be better to use format functions and write the output directly to a writer, also I don’t like the baked in panic everywhere, I think it would be better to return the error and then let the user/call-site decide whether they want to panic. (And document that those functions could panic)

I think for application code it is fine to decide to panic, but in library code I would try to avoid it or at least keep the number of places that could panic lower, for example by having an error-ing version and another versions that panics and just wraps the error version (if it seems appropriate to provide a panic-ing one at all).

Also it makes sense to statically prove the absense of errors for certain inputs through exhaustive testing, because check_decimal(u6, ...) will never result in error.NoSpaceLeft, because it is impossible to provide a value that would cause that error, so basically I think it would make sense to use more specialized functions here that are proven via tests or assertions to not fail and thus don’t need to return an error. (Which also removes the need for the repeated catch panic)

Additionally there is already a digit2 function in the standard library so we can use that one when size is 2. So here is a sketch of what I am thinking of:

const std = @import("std");

fn safeDecimal(size: comptime_int, comptime T: type, in: T) [size]u8 {
    const fmt = "{[value]d:0>[size]}";
    comptime std.debug.assert(std.fmt.count(fmt, .{ .value = std.math.maxInt(T), .size = size }) <= size);
    if (comptime size == 2) return std.fmt.digits2(in);
    var b: [size]u8 = undefined;
    _ = std.fmt.bufPrint(&b, fmt, .{ .value = in, .size = size }) catch unreachable;
    return b;
}

fn testHelper(size: comptime_int, comptime T: type) void {
    for (0..std.math.maxInt(T)) |i| {
        _ = safeDecimal(size, T, @intCast(i));
    }
}

test {
    testHelper(1, u1);
    testHelper(1, u2);
    testHelper(1, u3);
    testHelper(2, u4);
    testHelper(2, u5);
    testHelper(2, u6);
    testHelper(3, u7);
    testHelper(3, u8);
    testHelper(3, u9);
    testHelper(4, u10);
}

An additional improvement could be to calculate the highest type of all types that fit in a certain size and then use that one’s specialization for all in that size-group (to save on the number of generated type specializations and save on code size).

2 Likes