Commit 5b26128bac
Changed files (2)
lib
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| {