diff options
Diffstat (limited to '')
-rw-r--r-- | src/bun.zig | 130 |
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]; + } +} |