aboutsummaryrefslogtreecommitdiff
path: root/src/bun.zig
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/bun.zig130
1 files changed, 128 insertions, 2 deletions
diff --git a/src/bun.zig b/src/bun.zig
index b6c0e2c4a..cf3ea67a5 100644
--- a/src/bun.zig
+++ b/src/bun.zig
@@ -252,8 +252,14 @@ pub fn len(value: anytype) usize {
.Array => |info| info.len,
.Vector => |info| info.len,
.Pointer => |info| switch (info.size) {
- .One => switch (@typeInfo(info.child)) {
- .Array => value.len,
+ .One => switch (@as(@import("builtin").TypeInfo, @typeInfo(info.child))) {
+ .Array => |array| brk: {
+ if (array.sentinel != null) {
+ @compileError("use bun.sliceTo");
+ }
+
+ break :brk array.len;
+ },
else => @compileError("invalid type given to std.mem.len"),
},
.Many => {
@@ -807,3 +813,123 @@ pub fn getFdPath(fd: std.os.fd_t, buf: *[@This().MAX_PATH_BYTES]u8) ![]u8 {
return err;
};
}
+
+fn lenSliceTo(ptr: anytype, comptime end: meta.Elem(@TypeOf(ptr))) usize {
+ switch (@typeInfo(@TypeOf(ptr))) {
+ .Pointer => |ptr_info| switch (ptr_info.size) {
+ .One => switch (@typeInfo(ptr_info.child)) {
+ .Array => |array_info| {
+ if (array_info.sentinel) |sentinel_ptr| {
+ const sentinel = @ptrCast(*align(1) const array_info.child, sentinel_ptr).*;
+ if (sentinel == end) {
+ return indexOfSentinel(array_info.child, end, ptr);
+ }
+ }
+ return std.mem.indexOfScalar(array_info.child, ptr, end) orelse array_info.len;
+ },
+ else => {},
+ },
+ .Many => if (ptr_info.sentinel) |sentinel_ptr| {
+ const sentinel = @ptrCast(*align(1) const ptr_info.child, sentinel_ptr).*;
+ // We may be looking for something other than the sentinel,
+ // but iterating past the sentinel would be a bug so we need
+ // to check for both.
+ var i: usize = 0;
+ while (ptr[i] != end and ptr[i] != sentinel) i += 1;
+ return i;
+ },
+ .C => {
+ std.debug.assert(ptr != null);
+ return indexOfSentinel(ptr_info.child, end, ptr);
+ },
+ .Slice => {
+ if (ptr_info.sentinel) |sentinel_ptr| {
+ const sentinel = @ptrCast(*align(1) const ptr_info.child, sentinel_ptr).*;
+ if (sentinel == end) {
+ return indexOfSentinel(ptr_info.child, sentinel, ptr);
+ }
+ }
+ return std.mem.indexOfScalar(ptr_info.child, ptr, end) orelse ptr.len;
+ },
+ },
+ else => {},
+ }
+ @compileError("invalid type given to std.mem.sliceTo: " ++ @typeName(@TypeOf(ptr)));
+}
+
+/// Helper for the return type of sliceTo()
+fn SliceTo(comptime T: type, comptime end: meta.Elem(T)) type {
+ switch (@typeInfo(T)) {
+ .Optional => |optional_info| {
+ return ?SliceTo(optional_info.child, end);
+ },
+ .Pointer => |ptr_info| {
+ var new_ptr_info = ptr_info;
+ new_ptr_info.size = .Slice;
+ switch (ptr_info.size) {
+ .One => switch (@typeInfo(ptr_info.child)) {
+ .Array => |array_info| {
+ new_ptr_info.child = array_info.child;
+ // The return type must only be sentinel terminated if we are guaranteed
+ // to find the value searched for, which is only the case if it matches
+ // the sentinel of the type passed.
+ if (array_info.sentinel) |sentinel_ptr| {
+ const sentinel = @ptrCast(*align(1) const array_info.child, sentinel_ptr).*;
+ if (end == sentinel) {
+ new_ptr_info.sentinel = &end;
+ } else {
+ new_ptr_info.sentinel = null;
+ }
+ }
+ },
+ else => {},
+ },
+ .Many, .Slice => {
+ // The return type must only be sentinel terminated if we are guaranteed
+ // to find the value searched for, which is only the case if it matches
+ // the sentinel of the type passed.
+ if (ptr_info.sentinel) |sentinel_ptr| {
+ const sentinel = @ptrCast(*align(1) const ptr_info.child, sentinel_ptr).*;
+ if (end == sentinel) {
+ new_ptr_info.sentinel = &end;
+ } else {
+ new_ptr_info.sentinel = null;
+ }
+ }
+ },
+ .C => {
+ new_ptr_info.sentinel = &end;
+ // C pointers are always allowzero, but we don't want the return type to be.
+ std.debug.assert(new_ptr_info.is_allowzero);
+ new_ptr_info.is_allowzero = false;
+ },
+ }
+ return @Type(.{ .Pointer = new_ptr_info });
+ },
+ else => {},
+ }
+ @compileError("invalid type given to std.mem.sliceTo: " ++ @typeName(T));
+}
+
+/// Takes an array, a pointer to an array, a sentinel-terminated pointer, or a slice and
+/// iterates searching for the first occurrence of `end`, returning the scanned slice.
+/// If `end` is not found, the full length of the array/slice/sentinel terminated pointer is returned.
+/// If the pointer type is sentinel terminated and `end` matches that terminator, the
+/// resulting slice is also sentinel terminated.
+/// Pointer properties such as mutability and alignment are preserved.
+/// C pointers are assumed to be non-null.
+pub fn sliceTo(ptr: anytype, comptime end: meta.Elem(@TypeOf(ptr))) SliceTo(@TypeOf(ptr), end) {
+ if (@typeInfo(@TypeOf(ptr)) == .Optional) {
+ const non_null = ptr orelse return null;
+ return sliceTo(non_null, end);
+ }
+ const Result = SliceTo(@TypeOf(ptr), end);
+ const length = lenSliceTo(ptr, end);
+ const ptr_info = @typeInfo(Result).Pointer;
+ if (ptr_info.sentinel) |s_ptr| {
+ const s = @ptrCast(*align(1) const ptr_info.child, s_ptr).*;
+ return ptr[0..length :s];
+ } else {
+ return ptr[0..length];
+ }
+}