Commit 0b0de22fd1

daurnimator <quae@daurnimator.com>
2019-12-27 04:31:20
std: format contents of sentinel terminated many pointers
std: add std.meta.Sentinel to get sentinel of a type
1 parent b99c6d5
Changed files (6)
lib
src-self-hosted
test
stage1
behavior
lib/std/cstr.zig
@@ -28,7 +28,7 @@ test "cstr fns" {
 
 fn testCStrFnsImpl() void {
     testing.expect(cmp("aoeu", "aoez") == -1);
-    testing.expect(mem.len(u8, "123456789") == 9);
+    testing.expect(mem.len(u8, "123456789".*) == 9);
 }
 
 /// Returns a mutable, null-terminated slice with the same length as `slice`.
lib/std/fmt.zig
@@ -441,10 +441,14 @@ pub fn formatType(
                 else => return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }),
             },
             .Many, .C => {
+                if (ptr_info.sentinel) |sentinel| {
+                    const slice = mem.pointerToSlice([:sentinel]const ptr_info.child, value);
+                    return formatType(slice, fmt, options, context, Errors, output, max_depth);
+                }
                 if (ptr_info.child == u8) {
                     if (fmt.len > 0 and fmt[0] == 's') {
-                        const len = mem.len(u8, value);
-                        return formatText(value[0..len], fmt, options, context, Errors, output);
+                        const slice = mem.pointerToSlice([:0]const u8, @as([*:0]const u8, value));
+                        return formatText(slice, fmt, options, context, Errors, output);
                     }
                 }
                 return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });
lib/std/mem.zig
@@ -470,18 +470,31 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
     return true;
 }
 
-pub fn len(comptime T: type, ptr: [*:0]const T) usize {
+pub fn len(comptime T: type, ptr: var) usize {
+    const sentinel: T = comptime meta.Sentinel(@TypeOf(ptr));
     var count: usize = 0;
-    while (ptr[count] != 0) : (count += 1) {}
+    while (ptr[count] != sentinel) : (count += 1) {}
     return count;
 }
 
+/// Given a sentintel-terminated pointer-to-many, find the sentintel and return a slice.
+pub fn pointerToSlice(comptime T: type, ptr: blk: {
+    var info = @typeInfo(T).Pointer;
+    info.size = .Many;
+    break :blk @Type(std.builtin.TypeInfo{ .Pointer = info });
+}) T {
+    const sentinel = comptime meta.Sentinel(T);
+    return ptr[0..len(meta.Child(T), ptr) :sentinel];
+}
+
+/// Deprecated; use pointerToSlice instead
 pub fn toSliceConst(comptime T: type, ptr: [*:0]const T) [:0]const T {
-    return ptr[0..len(T, ptr) :0];
+    return pointerToSlice([:0]const T, ptr);
 }
 
+/// Deprecated; use pointerToSlice instead
 pub fn toSlice(comptime T: type, ptr: [*:0]T) [:0]T {
-    return ptr[0..len(T, ptr) :0];
+    return pointerToSlice([:0]T, ptr);
 }
 
 /// Returns true if all elements in a slice are equal to the scalar value provided
lib/std/meta.zig
@@ -115,6 +115,32 @@ test "std.meta.Child" {
     testing.expect(Child(?u8) == u8);
 }
 
+/// Given a type with a sentinel e.g. `[:0]u8`, returns the sentinel
+pub fn Sentinel(comptime T: type) Child(T) {
+    // comptime asserts that ptr has a sentinel
+    switch (@typeInfo(T)) {
+        .Array => |arrayInfo| {
+            return comptime arrayInfo.sentinel.?;
+        },
+        .Pointer => |ptrInfo| {
+            switch (ptrInfo.size) {
+                .Many, .Slice => {
+                    return comptime ptrInfo.sentinel.?;
+                },
+                else => {},
+            }
+        },
+        else => {},
+    }
+    @compileError("not a sentinel type, found '" ++ @typeName(T) ++ "'");
+}
+
+test "std.meta.Sentinel" {
+    testing.expectEqual(@as(u8, 0), Sentinel([:0]u8));
+    testing.expectEqual(@as(u8, 0), Sentinel([*:0]u8));
+    testing.expectEqual(@as(u8, 0), Sentinel([5:0]u8));
+}
+
 pub fn containerLayout(comptime T: type) TypeInfo.ContainerLayout {
     return switch (@typeInfo(T)) {
         .Struct => |info| info.layout,
src-self-hosted/main.zig
@@ -792,7 +792,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
 }
 
 fn cmdVersion(allocator: *Allocator, args: []const []const u8) !void {
-    try stdout.print("{}\n", .{std.mem.toSliceConst(u8, c.ZIG_VERSION_STRING)});
+    try stdout.print("{}\n", .{c.ZIG_VERSION_STRING});
 }
 
 fn cmdHelp(allocator: *Allocator, args: []const []const u8) !void {
@@ -863,12 +863,12 @@ fn cmdInternalBuildInfo(allocator: *Allocator, args: []const []const u8) !void {
         \\ZIG_DIA_GUIDS_LIB    {}
         \\
     , .{
-        std.mem.toSliceConst(u8, c.ZIG_CMAKE_BINARY_DIR),
-        std.mem.toSliceConst(u8, c.ZIG_CXX_COMPILER),
-        std.mem.toSliceConst(u8, c.ZIG_LLD_INCLUDE_PATH),
-        std.mem.toSliceConst(u8, c.ZIG_LLD_LIBRARIES),
-        std.mem.toSliceConst(u8, c.ZIG_LLVM_CONFIG_EXE),
-        std.mem.toSliceConst(u8, c.ZIG_DIA_GUIDS_LIB),
+        c.ZIG_CMAKE_BINARY_DIR,
+        c.ZIG_CXX_COMPILER,
+        c.ZIG_LLD_INCLUDE_PATH,
+        c.ZIG_LLD_LIBRARIES,
+        c.ZIG_LLVM_CONFIG_EXE,
+        c.ZIG_DIA_GUIDS_LIB,
     });
 }
 
test/stage1/behavior/misc.zig
@@ -335,7 +335,7 @@ test "string concatenation" {
     comptime expect(@TypeOf(a) == *const [12:0]u8);
     comptime expect(@TypeOf(b) == *const [12:0]u8);
 
-    const len = mem.len(u8, b);
+    const len = b.len;
     const len_with_null = len + 1;
     {
         var i: u32 = 0;