diff --git a/lib/std/cstr.zig b/lib/std/cstr.zig index 5194dc1a13b4..a46699acaeaa 100644 --- a/lib/std/cstr.zig +++ b/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`. diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index e0d58a23e6e8..b85474102319 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -442,10 +442,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) }); diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 46f23c84fe80..685697d58ce3 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -365,18 +365,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 diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 5cb9c6589ccb..c0a7429f50cd 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -116,6 +116,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)) { TypeId.Struct => |info| info.layout, diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index fc0825f3dbcc..4d19ddd5244b 100644 --- a/src-self-hosted/main.zig +++ b/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, }); } diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig index c77b49f03a28..7f9377aa5e8a 100644 --- a/test/stage1/behavior/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -365,7 +365,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;