Commit 5b26128bac

Andrew Kelley <andrew@ziglang.org>
2020-03-01 19:07:55
add new functions to std.mem and deprecate others
add std.mem.Span add std.mem.span add std.mem.length add std.mem.indexOfSentinel deprecate std.mem.len deprecate std.mem.toSlice deprecate std.mem.toSliceConst
1 parent 4505857
Changed files (2)
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`.
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,20 +482,122 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
     return true;
 }
 
+/// Deprecated. Use `length` or `indexOfSentinel`.
 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];
 }
 
+/// Deprecated. Use `span`.
 pub fn toSlice(comptime T: type, ptr: [*:0]T) [:0]T {
     return ptr[0..len(T, 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.
+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;
+        },
+        .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 = length(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 length(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 "length" {
+    testing.expect(length("aoeu") == 4);
+
+    {
+        var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
+        testing.expect(length(&array) == 5);
+        testing.expect(length(array[0..3]) == 3);
+        array[2] = 0;
+        const ptr = array[0..2 :0].ptr;
+        testing.expect(length(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
 pub fn allEqual(comptime T: type, slice: []const T, scalar: T) bool {
     for (slice) |item| {