authorgravatar for johnnymarler@gmail.comJonathan Marler <johnnymarler@gmail.com> 2020-11-25 13:23:43-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-11-29 10:36:02-08:00
log48660371a2f66b3859831abb276180c557a12f93
treee1342698d7e8464197759c0103731a377b953484
parente701ac1a51e6f1d39b698b9b258c349b902dd848

std.meta: add assumeSentinel


7 files changed, 112 insertions(+), 16 deletions(-)

lib/std/debug.zig+2-2
...@@ -743,7 +743,7 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf...@@ -743,7 +743,7 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf
743 for (present) |_| {743 for (present) |_| {
744 const name_offset = try pdb_stream.inStream().readIntLittle(u32);744 const name_offset = try pdb_stream.inStream().readIntLittle(u32);
745 const name_index = try pdb_stream.inStream().readIntLittle(u32);745 const name_index = try pdb_stream.inStream().readIntLittle(u32);
746 const name = mem.spanZ(@ptrCast([*:0]u8, name_bytes.ptr + name_offset));746 const name = mem.spanZ(std.meta.assumeSentinel(name_bytes.ptr + name_offset, 0));
747 if (mem.eql(u8, name, "/names")) {747 if (mem.eql(u8, name, "/names")) {
748 break :str_tab_index name_index;748 break :str_tab_index name_index;
749 }749 }
...@@ -891,7 +891,7 @@ pub fn readElfDebugInfo(allocator: *mem.Allocator, elf_file: File) !ModuleDebugI...@@ -891,7 +891,7 @@ pub fn readElfDebugInfo(allocator: *mem.Allocator, elf_file: File) !ModuleDebugI
891 for (shdrs) |*shdr| {891 for (shdrs) |*shdr| {
892 if (shdr.sh_type == elf.SHT_NULL) continue;892 if (shdr.sh_type == elf.SHT_NULL) continue;
893893
894 const name = std.mem.span(@ptrCast([*:0]const u8, header_strings[shdr.sh_name..].ptr));894 const name = std.mem.span(std.meta.assumeSentinel(header_strings[shdr.sh_name..].ptr, 0));
895 if (mem.eql(u8, name, ".debug_info")) {895 if (mem.eql(u8, name, ".debug_info")) {
896 opt_debug_info = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);896 opt_debug_info = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
897 } else if (mem.eql(u8, name, ".debug_abbrev")) {897 } else if (mem.eql(u8, name, ".debug_abbrev")) {
lib/std/fs.zig+3-3
...@@ -2330,14 +2330,14 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {...@@ -2330,14 +2330,14 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
2330 var out_len: usize = out_buffer.len;2330 var out_len: usize = out_buffer.len;
2331 try os.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);2331 try os.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);
2332 // TODO could this slice from 0 to out_len instead?2332 // TODO could this slice from 0 to out_len instead?
2333 return mem.spanZ(@ptrCast([*:0]u8, out_buffer));2333 return mem.spanZ(std.meta.assumeSentinel(out_buffer.ptr, 0));
2334 },2334 },
2335 .netbsd => {2335 .netbsd => {
2336 var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC_ARGS, -1, os.KERN_PROC_PATHNAME };2336 var mib = [4]c_int{ os.CTL_KERN, os.KERN_PROC_ARGS, -1, os.KERN_PROC_PATHNAME };
2337 var out_len: usize = out_buffer.len;2337 var out_len: usize = out_buffer.len;
2338 try os.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);2338 try os.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);
2339 // TODO could this slice from 0 to out_len instead?2339 // TODO could this slice from 0 to out_len instead?
2340 return mem.spanZ(@ptrCast([*:0]u8, out_buffer));2340 return mem.spanZ(std.meta.assumeSentinel(out_buffer.ptr, 0));
2341 },2341 },
2342 .openbsd => {2342 .openbsd => {
2343 // OpenBSD doesn't support getting the path of a running process, so try to guess it2343 // 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 {...@@ -2389,7 +2389,7 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
2389/// The result is UTF16LE-encoded.2389/// The result is UTF16LE-encoded.
2390pub fn selfExePathW() [:0]const u16 {2390pub fn selfExePathW() [:0]const u16 {
2391 const image_path_name = &os.windows.peb().ProcessParameters.ImagePathName;2391 const image_path_name = &os.windows.peb().ProcessParameters.ImagePathName;
2392 return mem.spanZ(@ptrCast([*:0]const u16, image_path_name.Buffer));2392 return mem.spanZ(std.meta.assumeSentinel(image_path_name.Buffer, 0));
2393}2393}
23942394
2395/// `selfExeDirPath` except allocates the result on the heap.2395/// `selfExeDirPath` except allocates the result on the heap.
lib/std/meta.zig+96
...@@ -161,6 +161,13 @@ pub fn Elem(comptime T: type) type {...@@ -161,6 +161,13 @@ pub fn Elem(comptime T: type) type {
161 },161 },
162 .Many, .C, .Slice => return info.child,162 .Many, .C, .Slice => return info.child,
163 },163 },
164 .Optional => |info| switch (@typeInfo(info.child)) {
165 .Pointer => |ptr_info| switch (ptr_info.size) {
166 .Many => return ptr_info.child,
167 else => {},
168 },
169 else => {},
170 },
164 else => {},171 else => {},
165 }172 }
166 @compileError("Expected pointer, slice, array or vector type, found '" ++ @typeName(T) ++ "'");173 @compileError("Expected pointer, slice, array or vector type, found '" ++ @typeName(T) ++ "'");
...@@ -173,6 +180,7 @@ test "std.meta.Elem" {...@@ -173,6 +180,7 @@ test "std.meta.Elem" {
173 testing.expect(Elem(*[10]u8) == u8);180 testing.expect(Elem(*[10]u8) == u8);
174 testing.expect(Elem(Vector(2, u8)) == u8);181 testing.expect(Elem(Vector(2, u8)) == u8);
175 testing.expect(Elem(*Vector(2, u8)) == u8);182 testing.expect(Elem(*Vector(2, u8)) == u8);
183 testing.expect(Elem(?[*]u8) == u8);
176}184}
177185
178/// Given a type which can have a sentinel e.g. `[:0]u8`, returns the sentinel value,186/// Given a type which can have a sentinel e.g. `[:0]u8`, returns the sentinel value,
...@@ -213,6 +221,94 @@ fn testSentinel() void {...@@ -213,6 +221,94 @@ fn testSentinel() void {
213 testing.expect(sentinel(*const [5]u8) == null);221 testing.expect(sentinel(*const [5]u8) == null);
214}222}
215223
224/// Given a "memory span" type, returns the same type except with the given sentinel value.
225pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {
226 switch (@typeInfo(T)) {
227 .Pointer => |info| switch (info.size) {
228 .One => switch (@typeInfo(info.child)) {
229 .Array => |array_info| return @Type(.{ .Pointer = .{
230 .size = info.size,
231 .is_const = info.is_const,
232 .is_volatile = info.is_volatile,
233 .alignment = info.alignment,
234 .child = @Type(.{ .Array = .{
235 .len = array_info.len,
236 .child = array_info.child,
237 .sentinel = sentinel_val,
238 }}),
239 .is_allowzero = info.is_allowzero,
240 .sentinel = info.sentinel,
241 }}),
242 else => {},
243 },
244 .Many, .Slice => return @Type(.{ .Pointer = .{
245 .size = info.size,
246 .is_const = info.is_const,
247 .is_volatile = info.is_volatile,
248 .alignment = info.alignment,
249 .child = info.child,
250 .is_allowzero = info.is_allowzero,
251 .sentinel = sentinel_val,
252 }}),
253 else => {},
254 },
255 .Optional => |info| switch (@typeInfo(info.child)) {
256 .Pointer => |ptr_info| switch (ptr_info.size) {
257 .Many => return @Type(.{ .Optional = .{ .child = @Type(.{ .Pointer = .{
258 .size = ptr_info.size,
259 .is_const = ptr_info.is_const,
260 .is_volatile = ptr_info.is_volatile,
261 .alignment = ptr_info.alignment,
262 .child = ptr_info.child,
263 .is_allowzero = ptr_info.is_allowzero,
264 .sentinel = sentinel_val,
265 }})}}),
266 else => {},
267 },
268 else => {},
269 },
270 else => {},
271 }
272 @compileError("Unable to derive a sentinel pointer type from " ++ @typeName(T));
273}
274
275/// Takes a Slice or Many Pointer and returns it with the Type modified to have the given sentinel value.
276/// This function assumes the caller has verified the memory contains the sentinel value.
277pub fn assumeSentinel(p: anytype, comptime sentinel_val: Elem(@TypeOf(p))) Sentinel(@TypeOf(p), sentinel_val) {
278 const T = @TypeOf(p);
279 const ReturnType = Sentinel(T, sentinel_val);
280 switch (@typeInfo(T)) {
281 .Pointer => |info| switch (info.size) {
282 .Slice => return @bitCast(ReturnType, p),
283 .Many, .One => return @ptrCast(ReturnType, p),
284 .C => {},
285 },
286 .Optional => |info| switch (@typeInfo(info.child)) {
287 .Pointer => |ptr_info| switch (ptr_info.size) {
288 .Many => return @ptrCast(ReturnType, p),
289 else => {},
290 },
291 else => {},
292 },
293 else => {},
294 }
295 @compileError("Unable to derive a sentinel pointer type from " ++ @typeName(T));
296}
297
298test "std.meta.assumeSentinel" {
299 testing.expect([*:0]u8 == @TypeOf(assumeSentinel(@as([*]u8 , undefined), 0)));
300 testing.expect([:0]u8 == @TypeOf(assumeSentinel(@as([]u8 , undefined), 0)));
301 testing.expect([*:0]const u8 == @TypeOf(assumeSentinel(@as([*]const u8, undefined), 0)));
302 testing.expect([:0]const u8 == @TypeOf(assumeSentinel(@as([]const u8 , undefined), 0)));
303 testing.expect([*:0]u16 == @TypeOf(assumeSentinel(@as([*]u16 , undefined), 0)));
304 testing.expect([:0]const u16 == @TypeOf(assumeSentinel(@as([]const u16, undefined), 0)));
305 testing.expect([*:3]u8 == @TypeOf(assumeSentinel(@as([*:1]u8 , undefined), 3)));
306 testing.expect([:null]?[*]u8 == @TypeOf(assumeSentinel(@as([]?[*]u8 , undefined), null)));
307 testing.expect([*:null]?[*]u8 == @TypeOf(assumeSentinel(@as([*]?[*]u8 , undefined), null)));
308 testing.expect(*[10:0]u8 == @TypeOf(assumeSentinel(@as(*[10]u8 , undefined), 0)));
309 testing.expect(?[*:0]u8 == @TypeOf(assumeSentinel(@as(?[*]u8 , undefined), 0)));
310}
311
216pub fn containerLayout(comptime T: type) TypeInfo.ContainerLayout {312pub fn containerLayout(comptime T: type) TypeInfo.ContainerLayout {
217 return switch (@typeInfo(T)) {313 return switch (@typeInfo(T)) {
218 .Struct => |info| info.layout,314 .Struct => |info| info.layout,
lib/std/net.zig+3-3
...@@ -178,7 +178,7 @@ pub const Address = extern union {...@@ -178,7 +178,7 @@ pub const Address = extern union {
178 unreachable;178 unreachable;
179 }179 }
180180
181 const path_len = std.mem.len(@ptrCast([*:0]const u8, &self.un.path));181 const path_len = std.mem.len(std.meta.assumeSentinel(&self.un.path, 0));
182 return @intCast(os.socklen_t, @sizeOf(os.sockaddr_un) - self.un.path.len + path_len);182 return @intCast(os.socklen_t, @sizeOf(os.sockaddr_un) - self.un.path.len + path_len);
183 },183 },
184 else => unreachable,184 else => unreachable,
...@@ -721,7 +721,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*...@@ -721,7 +721,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*
721 .next = null,721 .next = null,
722 };722 };
723 var res: *os.addrinfo = undefined;723 var res: *os.addrinfo = undefined;
724 const rc = sys.getaddrinfo(name_c.ptr, @ptrCast([*:0]const u8, port_c.ptr), &hints, &res);724 const rc = sys.getaddrinfo(name_c.ptr, std.meta.assumeSentinel(port_c.ptr, 0), &hints, &res);
725 if (builtin.os.tag == .windows) switch (@intToEnum(os.windows.ws2_32.WinsockError, @intCast(u16, rc))) {725 if (builtin.os.tag == .windows) switch (@intToEnum(os.windows.ws2_32.WinsockError, @intCast(u16, rc))) {
726 @intToEnum(os.windows.ws2_32.WinsockError, 0) => {},726 @intToEnum(os.windows.ws2_32.WinsockError, 0) => {},
727 .WSATRY_AGAIN => return error.TemporaryNameServerFailure,727 .WSATRY_AGAIN => return error.TemporaryNameServerFailure,
...@@ -1556,7 +1556,7 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8)...@@ -1556,7 +1556,7 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8)
1556 var tmp: [256]u8 = undefined;1556 var tmp: [256]u8 = undefined;
1557 // Returns len of compressed name. strlen to get canon name.1557 // Returns len of compressed name. strlen to get canon name.
1558 _ = try os.dn_expand(packet, data, &tmp);1558 _ = try os.dn_expand(packet, data, &tmp);
1559 const canon_name = mem.spanZ(@ptrCast([*:0]const u8, &tmp));1559 const canon_name = mem.spanZ(std.meta.assumeSentinel(&tmp, 0));
1560 if (isValidHostName(canon_name)) {1560 if (isValidHostName(canon_name)) {
1561 try ctx.canon.replaceContents(canon_name);1561 try ctx.canon.replaceContents(canon_name);
1562 }1562 }
lib/std/os.zig+4-4
...@@ -1557,7 +1557,7 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {...@@ -1557,7 +1557,7 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
1557 break :blk errno(system.getcwd(out_buffer.ptr, out_buffer.len));1557 break :blk errno(system.getcwd(out_buffer.ptr, out_buffer.len));
1558 };1558 };
1559 switch (err) {1559 switch (err) {
1560 0 => return mem.spanZ(@ptrCast([*:0]u8, out_buffer.ptr)),1560 0 => return mem.spanZ(std.meta.assumeSentinel(out_buffer.ptr, 0)),
1561 EFAULT => unreachable,1561 EFAULT => unreachable,
1562 EINVAL => unreachable,1562 EINVAL => unreachable,
1563 ENOENT => return error.CurrentWorkingDirectoryUnlinked,1563 ENOENT => return error.CurrentWorkingDirectoryUnlinked,
...@@ -4282,7 +4282,7 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {...@@ -4282,7 +4282,7 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
4282 var procfs_buf: ["/proc/self/fd/-2147483648".len:0]u8 = undefined;4282 var procfs_buf: ["/proc/self/fd/-2147483648".len:0]u8 = undefined;
4283 const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", .{fd}) catch unreachable;4283 const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", .{fd}) catch unreachable;
42844284
4285 const target = readlinkZ(@ptrCast([*:0]const u8, proc_path.ptr), out_buffer) catch |err| {4285 const target = readlinkZ(std.meta.assumeSentinel(proc_path.ptr, 0), out_buffer) catch |err| {
4286 switch (err) {4286 switch (err) {
4287 error.UnsupportedReparsePointType => unreachable, // Windows only,4287 error.UnsupportedReparsePointType => unreachable, // Windows only,
4288 else => |e| return e,4288 else => |e| return e,
...@@ -4606,7 +4606,7 @@ pub const GetHostNameError = error{PermissionDenied} || UnexpectedError;...@@ -4606,7 +4606,7 @@ pub const GetHostNameError = error{PermissionDenied} || UnexpectedError;
4606pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 {4606pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 {
4607 if (builtin.link_libc) {4607 if (builtin.link_libc) {
4608 switch (errno(system.gethostname(name_buffer, name_buffer.len))) {4608 switch (errno(system.gethostname(name_buffer, name_buffer.len))) {
4609 0 => return mem.spanZ(@ptrCast([*:0]u8, name_buffer)),4609 0 => return mem.spanZ(std.meta.assumeSentinel(name_buffer, 0)),
4610 EFAULT => unreachable,4610 EFAULT => unreachable,
4611 ENAMETOOLONG => unreachable, // HOST_NAME_MAX prevents this4611 ENAMETOOLONG => unreachable, // HOST_NAME_MAX prevents this
4612 EPERM => return error.PermissionDenied,4612 EPERM => return error.PermissionDenied,
...@@ -4615,7 +4615,7 @@ pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 {...@@ -4615,7 +4615,7 @@ pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 {
4615 }4615 }
4616 if (builtin.os.tag == .linux) {4616 if (builtin.os.tag == .linux) {
4617 const uts = uname();4617 const uts = uname();
4618 const hostname = mem.spanZ(@ptrCast([*:0]const u8, &uts.nodename));4618 const hostname = mem.spanZ(std.meta.assumeSentinel(&uts.nodename, 0));
4619 mem.copy(u8, name_buffer, hostname);4619 mem.copy(u8, name_buffer, hostname);
4620 return name_buffer[0..hostname.len];4620 return name_buffer[0..hostname.len];
4621 }4621 }
lib/std/os/linux/vdso.zig+2-2
...@@ -74,7 +74,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize {...@@ -74,7 +74,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize {
74 if (0 == (@as(u32, 1) << @intCast(u5, syms[i].st_info & 0xf) & OK_TYPES)) continue;74 if (0 == (@as(u32, 1) << @intCast(u5, syms[i].st_info & 0xf) & OK_TYPES)) continue;
75 if (0 == (@as(u32, 1) << @intCast(u5, syms[i].st_info >> 4) & OK_BINDS)) continue;75 if (0 == (@as(u32, 1) << @intCast(u5, syms[i].st_info >> 4) & OK_BINDS)) continue;
76 if (0 == syms[i].st_shndx) continue;76 if (0 == syms[i].st_shndx) continue;
77 const sym_name = @ptrCast([*:0]const u8, strings + syms[i].st_name);77 const sym_name = std.meta.assumeSentinel(strings + syms[i].st_name, 0);
78 if (!mem.eql(u8, name, mem.spanZ(sym_name))) continue;78 if (!mem.eql(u8, name, mem.spanZ(sym_name))) continue;
79 if (maybe_versym) |versym| {79 if (maybe_versym) |versym| {
80 if (!checkver(maybe_verdef.?, versym[i], vername, strings))80 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: [...@@ -97,6 +97,6 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [
97 def = @intToPtr(*elf.Verdef, @ptrToInt(def) + def.vd_next);97 def = @intToPtr(*elf.Verdef, @ptrToInt(def) + def.vd_next);
98 }98 }
99 const aux = @intToPtr(*elf.Verdaux, @ptrToInt(def) + def.vd_aux);99 const aux = @intToPtr(*elf.Verdaux, @ptrToInt(def) + def.vd_aux);
100 const vda_name = @ptrCast([*:0]const u8, strings + aux.vda_name);100 const vda_name = std.meta.assumeSentinel(strings + aux.vda_name, 0);
101 return mem.eql(u8, vername, mem.spanZ(vda_name));101 return mem.eql(u8, vername, mem.spanZ(vda_name));
102}102}
lib/std/zig/system.zig+2-2
...@@ -780,7 +780,7 @@ pub const NativeTargetInfo = struct {...@@ -780,7 +780,7 @@ pub const NativeTargetInfo = struct {
780 );780 );
781 const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name);781 const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name);
782 // TODO this pointer cast should not be necessary782 // TODO this pointer cast should not be necessary
783 const sh_name = mem.spanZ(@ptrCast([*:0]u8, shstrtab[sh_name_off..].ptr));783 const sh_name = mem.spanZ(std.meta.assumeSentinel(shstrtab[sh_name_off..].ptr, 0));
784 if (mem.eql(u8, sh_name, ".dynstr")) {784 if (mem.eql(u8, sh_name, ".dynstr")) {
785 break :find_dyn_str .{785 break :find_dyn_str .{
786 .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset),786 .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset),
...@@ -798,7 +798,7 @@ pub const NativeTargetInfo = struct {...@@ -798,7 +798,7 @@ pub const NativeTargetInfo = struct {
798 const rpoff_usize = std.math.cast(usize, rpoff) catch |err| switch (err) {798 const rpoff_usize = std.math.cast(usize, rpoff) catch |err| switch (err) {
799 error.Overflow => return error.InvalidElfFile,799 error.Overflow => return error.InvalidElfFile,
800 };800 };
801 const rpath_list = mem.spanZ(@ptrCast([*:0]u8, strtab[rpoff_usize..].ptr));801 const rpath_list = mem.spanZ(std.meta.assumeSentinel(strtab[rpoff_usize..].ptr, 0));
802 var it = mem.tokenize(rpath_list, ":");802 var it = mem.tokenize(rpath_list, ":");
803 while (it.next()) |rpath| {803 while (it.next()) |rpath| {
804 var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) {804 var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) {