diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 6850bb0ae..0f1a1e8ae 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -743,7 +743,7 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf for (present) |_| { const name_offset = try pdb_stream.inStream().readIntLittle(u32); const name_index = try pdb_stream.inStream().readIntLittle(u32); - const name = mem.spanZ(@ptrCast([*:0]u8, name_bytes.ptr + name_offset)); + const name = mem.spanZ(std.meta.assumeSentinel(name_bytes.ptr + name_offset, 0)); if (mem.eql(u8, name, "/names")) { break :str_tab_index name_index; } @@ -891,7 +891,7 @@ pub fn readElfDebugInfo(allocator: *mem.Allocator, elf_file: File) !ModuleDebugI for (shdrs) |*shdr| { if (shdr.sh_type == elf.SHT_NULL) continue; - const name = std.mem.span(@ptrCast([*:0]const u8, header_strings[shdr.sh_name..].ptr)); + const name = std.mem.span(std.meta.assumeSentinel(header_strings[shdr.sh_name..].ptr, 0)); if (mem.eql(u8, name, ".debug_info")) { opt_debug_info = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size); } else if (mem.eql(u8, name, ".debug_abbrev")) { diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 64bd166f3..6f39182d7 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -2330,14 +2330,14 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { var out_len: usize = out_buffer.len; try os.sysctl(&mib, out_buffer.ptr, &out_len, null, 0); // TODO could this slice from 0 to out_len instead? - return mem.spanZ(@ptrCast([*:0]u8, out_buffer)); + return mem.spanZ(std.meta.assumeSentinel(out_buffer.ptr, 0)); }, .netbsd => { var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC_ARGS, -1, os.KERN_PROC_PATHNAME }; var out_len: usize = out_buffer.len; try os.sysctl(&mib, out_buffer.ptr, &out_len, null, 0); // TODO could this slice from 0 to out_len instead? - return mem.spanZ(@ptrCast([*:0]u8, out_buffer)); + return mem.spanZ(std.meta.assumeSentinel(out_buffer.ptr, 0)); }, .openbsd => { // OpenBSD doesn't support getting the path of a running process, so try to guess it @@ -2389,7 +2389,7 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { /// The result is UTF16LE-encoded. pub fn selfExePathW() [:0]const u16 { const image_path_name = &os.windows.peb().ProcessParameters.ImagePathName; - return mem.spanZ(@ptrCast([*:0]const u16, image_path_name.Buffer)); + return mem.spanZ(std.meta.assumeSentinel(image_path_name.Buffer, 0)); } /// `selfExeDirPath` except allocates the result on the heap. diff --git a/lib/std/meta.zig b/lib/std/meta.zig index c95096671..c7b38bbfe 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -161,6 +161,13 @@ pub fn Elem(comptime T: type) type { }, .Many, .C, .Slice => return info.child, }, + .Optional => |info| switch (@typeInfo(info.child)) { + .Pointer => |ptr_info| switch (ptr_info.size) { + .Many => return ptr_info.child, + else => {}, + }, + else => {}, + }, else => {}, } @compileError("Expected pointer, slice, array or vector type, found '" ++ @typeName(T) ++ "'"); @@ -173,6 +180,7 @@ test "std.meta.Elem" { testing.expect(Elem(*[10]u8) == u8); testing.expect(Elem(Vector(2, u8)) == u8); testing.expect(Elem(*Vector(2, u8)) == u8); + testing.expect(Elem(?[*]u8) == u8); } /// Given a type which can have a sentinel e.g. `[:0]u8`, returns the sentinel value, @@ -213,6 +221,94 @@ fn testSentinel() void { testing.expect(sentinel(*const [5]u8) == null); } +/// Given a "memory span" type, returns the same type except with the given sentinel value. +pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type { + switch (@typeInfo(T)) { + .Pointer => |info| switch (info.size) { + .One => switch (@typeInfo(info.child)) { + .Array => |array_info| return @Type(.{ .Pointer = .{ + .size = info.size, + .is_const = info.is_const, + .is_volatile = info.is_volatile, + .alignment = info.alignment, + .child = @Type(.{ .Array = .{ + .len = array_info.len, + .child = array_info.child, + .sentinel = sentinel_val, + }}), + .is_allowzero = info.is_allowzero, + .sentinel = info.sentinel, + }}), + else => {}, + }, + .Many, .Slice => return @Type(.{ .Pointer = .{ + .size = info.size, + .is_const = info.is_const, + .is_volatile = info.is_volatile, + .alignment = info.alignment, + .child = info.child, + .is_allowzero = info.is_allowzero, + .sentinel = sentinel_val, + }}), + else => {}, + }, + .Optional => |info| switch (@typeInfo(info.child)) { + .Pointer => |ptr_info| switch (ptr_info.size) { + .Many => return @Type(.{ .Optional = .{ .child = @Type(.{ .Pointer = .{ + .size = ptr_info.size, + .is_const = ptr_info.is_const, + .is_volatile = ptr_info.is_volatile, + .alignment = ptr_info.alignment, + .child = ptr_info.child, + .is_allowzero = ptr_info.is_allowzero, + .sentinel = sentinel_val, + }})}}), + else => {}, + }, + else => {}, + }, + else => {}, + } + @compileError("Unable to derive a sentinel pointer type from " ++ @typeName(T)); +} + +/// Takes a Slice or Many Pointer and returns it with the Type modified to have the given sentinel value. +/// This function assumes the caller has verified the memory contains the sentinel value. +pub fn assumeSentinel(p: anytype, comptime sentinel_val: Elem(@TypeOf(p))) Sentinel(@TypeOf(p), sentinel_val) { + const T = @TypeOf(p); + const ReturnType = Sentinel(T, sentinel_val); + switch (@typeInfo(T)) { + .Pointer => |info| switch (info.size) { + .Slice => return @bitCast(ReturnType, p), + .Many, .One => return @ptrCast(ReturnType, p), + .C => {}, + }, + .Optional => |info| switch (@typeInfo(info.child)) { + .Pointer => |ptr_info| switch (ptr_info.size) { + .Many => return @ptrCast(ReturnType, p), + else => {}, + }, + else => {}, + }, + else => {}, + } + @compileError("Unable to derive a sentinel pointer type from " ++ @typeName(T)); +} + +test "std.meta.assumeSentinel" { + testing.expect([*:0]u8 == @TypeOf(assumeSentinel(@as([*]u8 , undefined), 0))); + testing.expect([:0]u8 == @TypeOf(assumeSentinel(@as([]u8 , undefined), 0))); + testing.expect([*:0]const u8 == @TypeOf(assumeSentinel(@as([*]const u8, undefined), 0))); + testing.expect([:0]const u8 == @TypeOf(assumeSentinel(@as([]const u8 , undefined), 0))); + testing.expect([*:0]u16 == @TypeOf(assumeSentinel(@as([*]u16 , undefined), 0))); + testing.expect([:0]const u16 == @TypeOf(assumeSentinel(@as([]const u16, undefined), 0))); + testing.expect([*:3]u8 == @TypeOf(assumeSentinel(@as([*:1]u8 , undefined), 3))); + testing.expect([:null]?[*]u8 == @TypeOf(assumeSentinel(@as([]?[*]u8 , undefined), null))); + testing.expect([*:null]?[*]u8 == @TypeOf(assumeSentinel(@as([*]?[*]u8 , undefined), null))); + testing.expect(*[10:0]u8 == @TypeOf(assumeSentinel(@as(*[10]u8 , undefined), 0))); + testing.expect(?[*:0]u8 == @TypeOf(assumeSentinel(@as(?[*]u8 , undefined), 0))); +} + pub fn containerLayout(comptime T: type) TypeInfo.ContainerLayout { return switch (@typeInfo(T)) { .Struct => |info| info.layout, diff --git a/lib/std/net.zig b/lib/std/net.zig index 1ac6e3e6a..1466635ff 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -178,7 +178,7 @@ pub const Address = extern union { unreachable; } - const path_len = std.mem.len(@ptrCast([*:0]const u8, &self.un.path)); + const path_len = std.mem.len(std.meta.assumeSentinel(&self.un.path, 0)); return @intCast(os.socklen_t, @sizeOf(os.sockaddr_un) - self.un.path.len + path_len); }, else => unreachable, @@ -721,7 +721,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !* .next = null, }; var res: *os.addrinfo = undefined; - const rc = sys.getaddrinfo(name_c.ptr, @ptrCast([*:0]const u8, port_c.ptr), &hints, &res); + const rc = sys.getaddrinfo(name_c.ptr, std.meta.assumeSentinel(port_c.ptr, 0), &hints, &res); if (builtin.os.tag == .windows) switch (@intToEnum(os.windows.ws2_32.WinsockError, @intCast(u16, rc))) { @intToEnum(os.windows.ws2_32.WinsockError, 0) => {}, .WSATRY_AGAIN => return error.TemporaryNameServerFailure, @@ -1556,7 +1556,7 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) var tmp: [256]u8 = undefined; // Returns len of compressed name. strlen to get canon name. _ = try os.dn_expand(packet, data, &tmp); - const canon_name = mem.spanZ(@ptrCast([*:0]const u8, &tmp)); + const canon_name = mem.spanZ(std.meta.assumeSentinel(&tmp, 0)); if (isValidHostName(canon_name)) { try ctx.canon.replaceContents(canon_name); } diff --git a/lib/std/os.zig b/lib/std/os.zig index c93f8d72a..ac122af3a 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1557,7 +1557,7 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { break :blk errno(system.getcwd(out_buffer.ptr, out_buffer.len)); }; switch (err) { - 0 => return mem.spanZ(@ptrCast([*:0]u8, out_buffer.ptr)), + 0 => return mem.spanZ(std.meta.assumeSentinel(out_buffer.ptr, 0)), EFAULT => unreachable, EINVAL => unreachable, ENOENT => return error.CurrentWorkingDirectoryUnlinked, @@ -4282,7 +4282,7 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { var procfs_buf: ["/proc/self/fd/-2147483648".len:0]u8 = undefined; const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", .{fd}) catch unreachable; - const target = readlinkZ(@ptrCast([*:0]const u8, proc_path.ptr), out_buffer) catch |err| { + const target = readlinkZ(std.meta.assumeSentinel(proc_path.ptr, 0), out_buffer) catch |err| { switch (err) { error.UnsupportedReparsePointType => unreachable, // Windows only, else => |e| return e, @@ -4606,7 +4606,7 @@ pub const GetHostNameError = error{PermissionDenied} || UnexpectedError; pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 { if (builtin.link_libc) { switch (errno(system.gethostname(name_buffer, name_buffer.len))) { - 0 => return mem.spanZ(@ptrCast([*:0]u8, name_buffer)), + 0 => return mem.spanZ(std.meta.assumeSentinel(name_buffer, 0)), EFAULT => unreachable, ENAMETOOLONG => unreachable, // HOST_NAME_MAX prevents this EPERM => return error.PermissionDenied, @@ -4615,7 +4615,7 @@ pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 { } if (builtin.os.tag == .linux) { const uts = uname(); - const hostname = mem.spanZ(@ptrCast([*:0]const u8, &uts.nodename)); + const hostname = mem.spanZ(std.meta.assumeSentinel(&uts.nodename, 0)); mem.copy(u8, name_buffer, hostname); return name_buffer[0..hostname.len]; } diff --git a/lib/std/os/linux/vdso.zig b/lib/std/os/linux/vdso.zig index 776737d92..eb99c7407 100644 --- a/lib/std/os/linux/vdso.zig +++ b/lib/std/os/linux/vdso.zig @@ -74,7 +74,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { if (0 == (@as(u32, 1) << @intCast(u5, syms[i].st_info & 0xf) & OK_TYPES)) continue; if (0 == (@as(u32, 1) << @intCast(u5, syms[i].st_info >> 4) & OK_BINDS)) continue; if (0 == syms[i].st_shndx) continue; - const sym_name = @ptrCast([*:0]const u8, strings + syms[i].st_name); + const sym_name = std.meta.assumeSentinel(strings + syms[i].st_name, 0); if (!mem.eql(u8, name, mem.spanZ(sym_name))) continue; if (maybe_versym) |versym| { if (!checkver(maybe_verdef.?, versym[i], vername, strings)) @@ -97,6 +97,6 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [ def = @intToPtr(*elf.Verdef, @ptrToInt(def) + def.vd_next); } const aux = @intToPtr(*elf.Verdaux, @ptrToInt(def) + def.vd_aux); - const vda_name = @ptrCast([*:0]const u8, strings + aux.vda_name); + const vda_name = std.meta.assumeSentinel(strings + aux.vda_name, 0); return mem.eql(u8, vername, mem.spanZ(vda_name)); } diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index cfaeb180c..3c9c90eaa 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -780,7 +780,7 @@ pub const NativeTargetInfo = struct { ); const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name); // TODO this pointer cast should not be necessary - const sh_name = mem.spanZ(@ptrCast([*:0]u8, shstrtab[sh_name_off..].ptr)); + const sh_name = mem.spanZ(std.meta.assumeSentinel(shstrtab[sh_name_off..].ptr, 0)); if (mem.eql(u8, sh_name, ".dynstr")) { break :find_dyn_str .{ .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset), @@ -798,7 +798,7 @@ pub const NativeTargetInfo = struct { const rpoff_usize = std.math.cast(usize, rpoff) catch |err| switch (err) { error.Overflow => return error.InvalidElfFile, }; - const rpath_list = mem.spanZ(@ptrCast([*:0]u8, strtab[rpoff_usize..].ptr)); + const rpath_list = mem.spanZ(std.meta.assumeSentinel(strtab[rpoff_usize..].ptr, 0)); var it = mem.tokenize(rpath_list, ":"); while (it.next()) |rpath| { var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) {