diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2020-03-01 13:53:13 -0500 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2020-03-01 13:53:13 -0500 |
| commit | 3c1432701129bacf95b944f0f86af2d96ae72c69 (patch) | |
| tree | d05e7ddf19d709975f432e60f5a2321309078517 /lib/std | |
| parent | b99c6d56da3d2db995c268fb07f48548ea6d1148 (diff) | |
| parent | ef3d761da545a3a72928ed0e0ba3b749a4cb74d8 (diff) | |
| download | zig-3c1432701129bacf95b944f0f86af2d96ae72c69.tar.gz zig-3c1432701129bacf95b944f0f86af2d96ae72c69.zip | |
Merge branch 'daurnimator-fmt-sentinel-pointers'
closes #3972
Diffstat (limited to 'lib/std')
| -rw-r--r-- | lib/std/builtin.zig | 2 | ||||
| -rw-r--r-- | lib/std/cstr.zig | 2 | ||||
| -rw-r--r-- | lib/std/fmt.zig | 6 | ||||
| -rw-r--r-- | lib/std/mem.zig | 138 | ||||
| -rw-r--r-- | lib/std/meta.zig | 26 | ||||
| -rw-r--r-- | lib/std/net.zig | 2 | ||||
| -rw-r--r-- | lib/std/os.zig | 2 | ||||
| -rw-r--r-- | lib/std/special/c.zig | 2 |
8 files changed, 162 insertions, 18 deletions
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index de37fda903..a4f0ef269f 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -185,6 +185,7 @@ pub const TypeInfo = union(enum) { child: type, is_allowzero: bool, + /// This field is an optional type. /// The type of the sentinel is the element type of the pointer, which is /// the value of the `child` field in this struct. However there is no way /// to refer to that type here, so we use `var`. @@ -206,6 +207,7 @@ pub const TypeInfo = union(enum) { len: comptime_int, child: type, + /// This field is an optional type. /// The type of the sentinel is the element type of the array, which is /// the value of the `child` field in this struct. However there is no way /// to refer to that type here, so we use `var`. diff --git a/lib/std/cstr.zig b/lib/std/cstr.zig index 4057d4b62b..9cb16b0ed3 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("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 d093101646..3729b7dc87 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -441,10 +441,12 @@ pub fn formatType( else => return format(context, Errors, output, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }), }, .Many, .C => { + if (ptr_info.sentinel) |sentinel| { + return formatType(mem.span(value), 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); + return formatText(mem.span(value), 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 a4b48bbc1c..a6c4d0057e 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -333,8 +333,20 @@ pub fn zeroes(comptime T: type) T { } return array; }, - .Vector, .ErrorUnion, .ErrorSet, .Union, .Fn, .BoundFn, .Type, .NoReturn, .Undefined, .Opaque, .Frame, .AnyFrame, => { - @compileError("Can't set a "++ @typeName(T) ++" to zero."); + .Vector, + .ErrorUnion, + .ErrorSet, + .Union, + .Fn, + .BoundFn, + .Type, + .NoReturn, + .Undefined, + .Opaque, + .Frame, + .AnyFrame, + => { + @compileError("Can't set a " ++ @typeName(T) ++ " to zero."); }, } } @@ -470,18 +482,115 @@ 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 { - var count: usize = 0; - while (ptr[count] != 0) : (count += 1) {} - return count; -} - +/// Deprecated. Use `span`. pub fn toSliceConst(comptime T: type, ptr: [*:0]const T) [:0]const T { - return ptr[0..len(T, ptr) :0]; + return ptr[0..len(ptr) :0]; } +/// Deprecated. Use `span`. pub fn toSlice(comptime T: type, ptr: [*:0]T) [:0]T { - return ptr[0..len(T, ptr) :0]; + return ptr[0..len(ptr) :0]; +} + +/// Takes a pointer to an array, a sentinel-terminated pointer, or a slice, and +/// returns a slice. If there is a sentinel on the input type, there will be a +/// sentinel on the output type. The constness of the output type matches +/// the constness of the input type. `[*c]` pointers are assumed to be 0-terminated, +/// and assumed to not allow null. +pub fn Span(comptime T: type) type { + var ptr_info = @typeInfo(T).Pointer; + switch (ptr_info.size) { + .One => switch (@typeInfo(ptr_info.child)) { + .Array => |info| { + ptr_info.child = info.child; + ptr_info.sentinel = info.sentinel; + }, + else => @compileError("invalid type given to std.mem.Span"), + }, + .C => { + ptr_info.sentinel = 0; + ptr_info.is_allowzero = false; + }, + .Many, .Slice => {}, + } + ptr_info.size = .Slice; + return @Type(std.builtin.TypeInfo{ .Pointer = ptr_info }); +} + +test "Span" { + testing.expect(Span(*[5]u16) == []u16); + testing.expect(Span(*const [5]u16) == []const u16); + testing.expect(Span([]u16) == []u16); + testing.expect(Span([]const u8) == []const u8); + testing.expect(Span([:1]u16) == [:1]u16); + testing.expect(Span([:1]const u8) == [:1]const u8); + testing.expect(Span([*:1]u16) == [:1]u16); + testing.expect(Span([*:1]const u8) == [:1]const u8); + testing.expect(Span([*c]u16) == [:0]u16); + testing.expect(Span([*c]const u8) == [:0]const u8); +} + +/// Takes a pointer to an array, a sentinel-terminated pointer, or a slice, and +/// returns a slice. If there is a sentinel on the input type, there will be a +/// sentinel on the output type. The constness of the output type matches +/// the constness of the input type. +pub fn span(ptr: var) Span(@TypeOf(ptr)) { + const Result = Span(@TypeOf(ptr)); + const l = len(ptr); + if (@typeInfo(Result).Pointer.sentinel) |s| { + return ptr[0..l :s]; + } else { + return ptr[0..l]; + } +} + +test "span" { + var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 }; + const ptr = array[0..2 :3].ptr; + testing.expect(eql(u16, span(ptr), &[_]u16{ 1, 2 })); + testing.expect(eql(u16, span(&array), &[_]u16{ 1, 2, 3, 4, 5 })); +} + +/// Takes a pointer to an array, an array, a sentinel-terminated pointer, +/// or a slice, and returns the length. +pub fn len(ptr: var) usize { + return switch (@typeInfo(@TypeOf(ptr))) { + .Array => |info| info.len, + .Pointer => |info| switch (info.size) { + .One => switch (@typeInfo(info.child)) { + .Array => |x| x.len, + else => @compileError("invalid type given to std.mem.length"), + }, + .Many => if (info.sentinel) |sentinel| + indexOfSentinel(info.child, sentinel, ptr) + else + @compileError("length of pointer with no sentinel"), + .C => indexOfSentinel(info.child, 0, ptr), + .Slice => ptr.len, + }, + else => @compileError("invalid type given to std.mem.length"), + }; +} + +test "len" { + testing.expect(len("aoeu") == 4); + + { + var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 }; + testing.expect(len(&array) == 5); + testing.expect(len(array[0..3]) == 3); + array[2] = 0; + const ptr = array[0..2 :0].ptr; + testing.expect(len(ptr) == 2); + } +} + +pub fn indexOfSentinel(comptime Elem: type, comptime sentinel: Elem, ptr: [*:sentinel]const Elem) usize { + var i: usize = 0; + while (ptr[i] != sentinel) { + i += 1; + } + return i; } /// Returns true if all elements in a slice are equal to the scalar value provided @@ -1753,8 +1862,13 @@ fn SubArrayPtrReturnType(comptime T: type, comptime length: usize) type { return *[length]meta.Child(meta.Child(T)); } -///Given a pointer to an array, returns a pointer to a portion of that array, preserving constness. -pub fn subArrayPtr(ptr: var, comptime start: usize, comptime length: usize) SubArrayPtrReturnType(@TypeOf(ptr), length) { +/// Given a pointer to an array, returns a pointer to a portion of that array, preserving constness. +/// TODO this will be obsoleted by https://github.com/ziglang/zig/issues/863 +pub fn subArrayPtr( + ptr: var, + comptime start: usize, + comptime length: usize, +) SubArrayPtrReturnType(@TypeOf(ptr), length) { assert(start + length <= ptr.*.len); const ReturnType = SubArrayPtrReturnType(@TypeOf(ptr), length); diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 58fd6b9da7..65809abb5c 100644 --- a/lib/std/meta.zig +++ b/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, diff --git a/lib/std/net.zig b/lib/std/net.zig index b54803cd39..6d0daefdc0 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -352,7 +352,7 @@ pub const Address = extern union { unreachable; } - const path_len = std.mem.len(u8, @ptrCast([*:0]const u8, &self.un.path)); + const path_len = std.mem.len(@ptrCast([*:0]const u8, &self.un.path)); return @intCast(os.socklen_t, @sizeOf(os.sockaddr_un) - self.un.path.len + path_len); }, else => unreachable, diff --git a/lib/std/os.zig b/lib/std/os.zig index 24d78bec9a..127ada8fe5 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1095,7 +1095,7 @@ pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std. pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8) void { for (envp_buf) |env| { - const env_buf = if (env) |ptr| ptr[0 .. mem.len(u8, ptr) + 1] else break; + const env_buf = if (env) |ptr| ptr[0 .. mem.len(ptr) + 1] else break; allocator.free(env_buf); } allocator.free(envp_buf); diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index 1f11fabca0..e0c2636df1 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -47,7 +47,7 @@ fn strcmp(s1: [*:0]const u8, s2: [*:0]const u8) callconv(.C) c_int { } fn strlen(s: [*:0]const u8) callconv(.C) usize { - return std.mem.len(u8, s); + return std.mem.len(s); } fn strncmp(_l: [*:0]const u8, _r: [*:0]const u8, _n: usize) callconv(.C) c_int { |
