master
   1const std = @import("std.zig");
   2const builtin = @import("builtin");
   3const debug = std.debug;
   4const assert = debug.assert;
   5const math = std.math;
   6const mem = @This();
   7const testing = std.testing;
   8const Endian = std.builtin.Endian;
   9const native_endian = builtin.cpu.arch.endian();
  10
  11/// The standard library currently thoroughly depends on byte size
  12/// being 8 bits.  (see the use of u8 throughout allocation code as
  13/// the "byte" type.)  Code which depends on this can reference this
  14/// declaration.  If we ever try to port the standard library to a
  15/// non-8-bit-byte platform, this will allow us to search for things
  16/// which need to be updated.
  17pub const byte_size_in_bits = 8;
  18
  19pub const Allocator = @import("mem/Allocator.zig");
  20
  21/// Stored as a power-of-two.
  22pub const Alignment = enum(math.Log2Int(usize)) {
  23    @"1" = 0,
  24    @"2" = 1,
  25    @"4" = 2,
  26    @"8" = 3,
  27    @"16" = 4,
  28    @"32" = 5,
  29    @"64" = 6,
  30    _,
  31
  32    pub fn toByteUnits(a: Alignment) usize {
  33        return @as(usize, 1) << @intFromEnum(a);
  34    }
  35
  36    pub fn fromByteUnits(n: usize) Alignment {
  37        assert(std.math.isPowerOfTwo(n));
  38        return @enumFromInt(@ctz(n));
  39    }
  40
  41    pub inline fn of(comptime T: type) Alignment {
  42        return comptime fromByteUnits(@alignOf(T));
  43    }
  44
  45    pub fn order(lhs: Alignment, rhs: Alignment) std.math.Order {
  46        return std.math.order(@intFromEnum(lhs), @intFromEnum(rhs));
  47    }
  48
  49    pub fn compare(lhs: Alignment, op: std.math.CompareOperator, rhs: Alignment) bool {
  50        return std.math.compare(@intFromEnum(lhs), op, @intFromEnum(rhs));
  51    }
  52
  53    pub fn max(lhs: Alignment, rhs: Alignment) Alignment {
  54        return @enumFromInt(@max(@intFromEnum(lhs), @intFromEnum(rhs)));
  55    }
  56
  57    pub fn min(lhs: Alignment, rhs: Alignment) Alignment {
  58        return @enumFromInt(@min(@intFromEnum(lhs), @intFromEnum(rhs)));
  59    }
  60
  61    /// Return next address with this alignment.
  62    pub fn forward(a: Alignment, address: usize) usize {
  63        const x = (@as(usize, 1) << @intFromEnum(a)) - 1;
  64        return (address + x) & ~x;
  65    }
  66
  67    /// Return previous address with this alignment.
  68    pub fn backward(a: Alignment, address: usize) usize {
  69        const x = (@as(usize, 1) << @intFromEnum(a)) - 1;
  70        return address & ~x;
  71    }
  72
  73    /// Return whether address is aligned to this amount.
  74    pub fn check(a: Alignment, address: usize) bool {
  75        return @ctz(address) >= @intFromEnum(a);
  76    }
  77};
  78
  79/// Detects and asserts if the std.mem.Allocator interface is violated by the caller
  80/// or the allocator.
  81pub fn ValidationAllocator(comptime T: type) type {
  82    return struct {
  83        const Self = @This();
  84
  85        underlying_allocator: T,
  86
  87        pub fn init(underlying_allocator: T) @This() {
  88            return .{
  89                .underlying_allocator = underlying_allocator,
  90            };
  91        }
  92
  93        pub fn allocator(self: *Self) Allocator {
  94            return .{
  95                .ptr = self,
  96                .vtable = &.{
  97                    .alloc = alloc,
  98                    .resize = resize,
  99                    .remap = remap,
 100                    .free = free,
 101                },
 102            };
 103        }
 104
 105        fn getUnderlyingAllocatorPtr(self: *Self) Allocator {
 106            if (T == Allocator) return self.underlying_allocator;
 107            return self.underlying_allocator.allocator();
 108        }
 109
 110        pub fn alloc(
 111            ctx: *anyopaque,
 112            n: usize,
 113            alignment: mem.Alignment,
 114            ret_addr: usize,
 115        ) ?[*]u8 {
 116            assert(n > 0);
 117            const self: *Self = @ptrCast(@alignCast(ctx));
 118            const underlying = self.getUnderlyingAllocatorPtr();
 119            const result = underlying.rawAlloc(n, alignment, ret_addr) orelse
 120                return null;
 121            assert(alignment.check(@intFromPtr(result)));
 122            return result;
 123        }
 124
 125        pub fn resize(
 126            ctx: *anyopaque,
 127            buf: []u8,
 128            alignment: Alignment,
 129            new_len: usize,
 130            ret_addr: usize,
 131        ) bool {
 132            const self: *Self = @ptrCast(@alignCast(ctx));
 133            assert(buf.len > 0);
 134            const underlying = self.getUnderlyingAllocatorPtr();
 135            return underlying.rawResize(buf, alignment, new_len, ret_addr);
 136        }
 137
 138        pub fn remap(
 139            ctx: *anyopaque,
 140            buf: []u8,
 141            alignment: Alignment,
 142            new_len: usize,
 143            ret_addr: usize,
 144        ) ?[*]u8 {
 145            const self: *Self = @ptrCast(@alignCast(ctx));
 146            assert(buf.len > 0);
 147            const underlying = self.getUnderlyingAllocatorPtr();
 148            return underlying.rawRemap(buf, alignment, new_len, ret_addr);
 149        }
 150
 151        pub fn free(
 152            ctx: *anyopaque,
 153            buf: []u8,
 154            alignment: Alignment,
 155            ret_addr: usize,
 156        ) void {
 157            const self: *Self = @ptrCast(@alignCast(ctx));
 158            assert(buf.len > 0);
 159            const underlying = self.getUnderlyingAllocatorPtr();
 160            underlying.rawFree(buf, alignment, ret_addr);
 161        }
 162
 163        pub fn reset(self: *Self) void {
 164            self.underlying_allocator.reset();
 165        }
 166    };
 167}
 168
 169/// Wraps an allocator with basic validation checks.
 170/// Asserts that allocation sizes are greater than zero and returned pointers have correct alignment.
 171pub fn validationWrap(allocator: anytype) ValidationAllocator(@TypeOf(allocator)) {
 172    return ValidationAllocator(@TypeOf(allocator)).init(allocator);
 173}
 174
 175test "Allocator basics" {
 176    try testing.expectError(error.OutOfMemory, testing.failing_allocator.alloc(u8, 1));
 177    try testing.expectError(error.OutOfMemory, testing.failing_allocator.allocSentinel(u8, 1, 0));
 178}
 179
 180test "Allocator.resize" {
 181    const primitiveIntTypes = .{
 182        i8,
 183        u8,
 184        i16,
 185        u16,
 186        i32,
 187        u32,
 188        i64,
 189        u64,
 190        i128,
 191        u128,
 192        isize,
 193        usize,
 194    };
 195    inline for (primitiveIntTypes) |T| {
 196        var values = try testing.allocator.alloc(T, 100);
 197        defer testing.allocator.free(values);
 198
 199        for (values, 0..) |*v, i| v.* = @as(T, @intCast(i));
 200        if (!testing.allocator.resize(values, values.len + 10)) return error.OutOfMemory;
 201        values = values.ptr[0 .. values.len + 10];
 202        try testing.expect(values.len == 110);
 203    }
 204
 205    const primitiveFloatTypes = .{
 206        f16,
 207        f32,
 208        f64,
 209        f128,
 210    };
 211    inline for (primitiveFloatTypes) |T| {
 212        var values = try testing.allocator.alloc(T, 100);
 213        defer testing.allocator.free(values);
 214
 215        for (values, 0..) |*v, i| v.* = @as(T, @floatFromInt(i));
 216        if (!testing.allocator.resize(values, values.len + 10)) return error.OutOfMemory;
 217        values = values.ptr[0 .. values.len + 10];
 218        try testing.expect(values.len == 110);
 219    }
 220}
 221
 222test "Allocator alloc and remap with zero-bit type" {
 223    var values = try testing.allocator.alloc(void, 10);
 224    defer testing.allocator.free(values);
 225
 226    try testing.expectEqual(10, values.len);
 227    const remaped = testing.allocator.remap(values, 200);
 228    try testing.expect(remaped != null);
 229
 230    values = remaped.?;
 231    try testing.expectEqual(200, values.len);
 232}
 233
 234/// Copy all of source into dest at position 0.
 235/// dest.len must be >= source.len.
 236/// If the slices overlap, dest.ptr must be <= src.ptr.
 237/// This function is deprecated; use @memmove instead.
 238pub fn copyForwards(comptime T: type, dest: []T, source: []const T) void {
 239    for (dest[0..source.len], source) |*d, s| d.* = s;
 240}
 241
 242/// Copy all of source into dest at position 0.
 243/// dest.len must be >= source.len.
 244/// If the slices overlap, dest.ptr must be >= src.ptr.
 245/// This function is deprecated; use @memmove instead.
 246pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void {
 247    // TODO instead of manually doing this check for the whole array
 248    // and turning off runtime safety, the compiler should detect loops like
 249    // this and automatically omit safety checks for loops
 250    @setRuntimeSafety(false);
 251    assert(dest.len >= source.len);
 252    var i = source.len;
 253    while (i > 0) {
 254        i -= 1;
 255        dest[i] = source[i];
 256    }
 257}
 258
 259/// Generally, Zig users are encouraged to explicitly initialize all fields of a struct explicitly rather than using this function.
 260/// However, it is recognized that there are sometimes use cases for initializing all fields to a "zero" value. For example, when
 261/// interfacing with a C API where this practice is more common and relied upon. If you are performing code review and see this
 262/// function used, examine closely - it may be a code smell.
 263/// Zero initializes the type.
 264/// This can be used to zero-initialize any type for which it makes sense. Structs will be initialized recursively.
 265pub fn zeroes(comptime T: type) T {
 266    switch (@typeInfo(T)) {
 267        .comptime_int, .int, .comptime_float, .float => {
 268            return @as(T, 0);
 269        },
 270        .@"enum" => {
 271            return @as(T, @enumFromInt(0));
 272        },
 273        .void => {
 274            return {};
 275        },
 276        .bool => {
 277            return false;
 278        },
 279        .optional, .null => {
 280            return null;
 281        },
 282        .@"struct" => |struct_info| {
 283            if (@sizeOf(T) == 0) return undefined;
 284            if (struct_info.layout == .@"extern") {
 285                var item: T = undefined;
 286                @memset(asBytes(&item), 0);
 287                return item;
 288            } else {
 289                var structure: T = undefined;
 290                inline for (struct_info.fields) |field| {
 291                    if (!field.is_comptime) {
 292                        @field(structure, field.name) = zeroes(field.type);
 293                    }
 294                }
 295                return structure;
 296            }
 297        },
 298        .pointer => |ptr_info| {
 299            switch (ptr_info.size) {
 300                .slice => {
 301                    if (ptr_info.sentinel()) |sentinel| {
 302                        if (ptr_info.child == u8 and sentinel == 0) {
 303                            return ""; // A special case for the most common use-case: null-terminated strings.
 304                        }
 305                        @compileError("Can't set a sentinel slice to zero. This would require allocating memory.");
 306                    } else {
 307                        return &[_]ptr_info.child{};
 308                    }
 309                },
 310                .c => {
 311                    return null;
 312                },
 313                .one, .many => {
 314                    if (ptr_info.is_allowzero) return @ptrFromInt(0);
 315                    @compileError("Only nullable and allowzero pointers can be set to zero.");
 316                },
 317            }
 318        },
 319        .array => |info| {
 320            return @splat(zeroes(info.child));
 321        },
 322        .vector => |info| {
 323            return @splat(zeroes(info.child));
 324        },
 325        .@"union" => |info| {
 326            if (info.layout == .@"extern") {
 327                var item: T = undefined;
 328                @memset(asBytes(&item), 0);
 329                return item;
 330            }
 331            @compileError("Can't set a " ++ @typeName(T) ++ " to zero.");
 332        },
 333        .enum_literal,
 334        .error_union,
 335        .error_set,
 336        .@"fn",
 337        .type,
 338        .noreturn,
 339        .undefined,
 340        .@"opaque",
 341        .frame,
 342        .@"anyframe",
 343        => {
 344            @compileError("Can't set a " ++ @typeName(T) ++ " to zero.");
 345        },
 346    }
 347}
 348
 349test zeroes {
 350    const C_struct = extern struct {
 351        x: u32,
 352        y: u32 align(128),
 353    };
 354
 355    var a = zeroes(C_struct);
 356
 357    // Extern structs should have padding zeroed out.
 358    try testing.expectEqualSlices(u8, &[_]u8{0} ** @sizeOf(@TypeOf(a)), asBytes(&a));
 359
 360    a.y += 10;
 361
 362    try testing.expect(a.x == 0);
 363    try testing.expect(a.y == 10);
 364
 365    const ZigStruct = struct {
 366        comptime comptime_field: u8 = 5,
 367
 368        integral_types: struct {
 369            integer_0: i0,
 370            integer_8: i8,
 371            integer_16: i16,
 372            integer_32: i32,
 373            integer_64: i64,
 374            integer_128: i128,
 375            unsigned_0: u0,
 376            unsigned_8: u8,
 377            unsigned_16: u16,
 378            unsigned_32: u32,
 379            unsigned_64: u64,
 380            unsigned_128: u128,
 381
 382            float_32: f32,
 383            float_64: f64,
 384        },
 385
 386        pointers: struct {
 387            optional: ?*u8,
 388            c_pointer: [*c]u8,
 389            slice: []u8,
 390            nullTerminatedString: [:0]const u8,
 391        },
 392
 393        array: [2]u32,
 394        vector_u32: @Vector(2, u32),
 395        vector_f32: @Vector(2, f32),
 396        vector_bool: @Vector(2, bool),
 397        optional_int: ?u8,
 398        empty: void,
 399        sentinel: [3:0]u8,
 400    };
 401
 402    const b = zeroes(ZigStruct);
 403    try testing.expectEqual(@as(u8, 5), b.comptime_field);
 404    try testing.expectEqual(@as(i8, 0), b.integral_types.integer_0);
 405    try testing.expectEqual(@as(i8, 0), b.integral_types.integer_8);
 406    try testing.expectEqual(@as(i16, 0), b.integral_types.integer_16);
 407    try testing.expectEqual(@as(i32, 0), b.integral_types.integer_32);
 408    try testing.expectEqual(@as(i64, 0), b.integral_types.integer_64);
 409    try testing.expectEqual(@as(i128, 0), b.integral_types.integer_128);
 410    try testing.expectEqual(@as(u8, 0), b.integral_types.unsigned_0);
 411    try testing.expectEqual(@as(u8, 0), b.integral_types.unsigned_8);
 412    try testing.expectEqual(@as(u16, 0), b.integral_types.unsigned_16);
 413    try testing.expectEqual(@as(u32, 0), b.integral_types.unsigned_32);
 414    try testing.expectEqual(@as(u64, 0), b.integral_types.unsigned_64);
 415    try testing.expectEqual(@as(u128, 0), b.integral_types.unsigned_128);
 416    try testing.expectEqual(@as(f32, 0), b.integral_types.float_32);
 417    try testing.expectEqual(@as(f64, 0), b.integral_types.float_64);
 418    try testing.expectEqual(@as(?*u8, null), b.pointers.optional);
 419    try testing.expectEqual(@as([*c]u8, null), b.pointers.c_pointer);
 420    try testing.expectEqual(@as([]u8, &[_]u8{}), b.pointers.slice);
 421    try testing.expectEqual(@as([:0]const u8, ""), b.pointers.nullTerminatedString);
 422    for (b.array) |e| {
 423        try testing.expectEqual(@as(u32, 0), e);
 424    }
 425    try testing.expectEqual(@as(@TypeOf(b.vector_u32), @splat(0)), b.vector_u32);
 426    try testing.expectEqual(@as(@TypeOf(b.vector_f32), @splat(0.0)), b.vector_f32);
 427    if (!(builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .hexagon)) {
 428        try testing.expectEqual(@as(@TypeOf(b.vector_bool), @splat(false)), b.vector_bool);
 429    }
 430    try testing.expectEqual(@as(?u8, null), b.optional_int);
 431    for (b.sentinel) |e| {
 432        try testing.expectEqual(@as(u8, 0), e);
 433    }
 434
 435    const C_union = extern union {
 436        a: u8,
 437        b: u32,
 438    };
 439
 440    const c = zeroes(C_union);
 441    try testing.expectEqual(@as(u8, 0), c.a);
 442    try testing.expectEqual(@as(u32, 0), c.b);
 443
 444    const comptime_union = comptime zeroes(C_union);
 445    try testing.expectEqual(@as(u8, 0), comptime_union.a);
 446    try testing.expectEqual(@as(u32, 0), comptime_union.b);
 447
 448    // Ensure zero sized struct with fields is initialized correctly.
 449    _ = zeroes(struct { handle: void });
 450}
 451
 452/// Initializes all fields of the struct with their default value, or zero values if no default value is present.
 453/// If the field is present in the provided initial values, it will have that value instead.
 454/// Structs are initialized recursively.
 455pub fn zeroInit(comptime T: type, init: anytype) T {
 456    const Init = @TypeOf(init);
 457
 458    switch (@typeInfo(T)) {
 459        .@"struct" => |struct_info| {
 460            switch (@typeInfo(Init)) {
 461                .@"struct" => |init_info| {
 462                    if (init_info.is_tuple) {
 463                        if (init_info.fields.len > struct_info.fields.len) {
 464                            @compileError("Tuple initializer has more elements than there are fields in `" ++ @typeName(T) ++ "`");
 465                        }
 466                    } else {
 467                        inline for (init_info.fields) |field| {
 468                            if (!@hasField(T, field.name)) {
 469                                @compileError("Encountered an initializer for `" ++ field.name ++ "`, but it is not a field of " ++ @typeName(T));
 470                            }
 471                        }
 472                    }
 473
 474                    var value: T = if (struct_info.layout == .@"extern") zeroes(T) else undefined;
 475
 476                    inline for (struct_info.fields, 0..) |field, i| {
 477                        if (field.is_comptime) {
 478                            continue;
 479                        }
 480
 481                        if (init_info.is_tuple and init_info.fields.len > i) {
 482                            @field(value, field.name) = @field(init, init_info.fields[i].name);
 483                        } else if (@hasField(@TypeOf(init), field.name)) {
 484                            switch (@typeInfo(field.type)) {
 485                                .@"struct" => {
 486                                    @field(value, field.name) = zeroInit(field.type, @field(init, field.name));
 487                                },
 488                                else => {
 489                                    @field(value, field.name) = @field(init, field.name);
 490                                },
 491                            }
 492                        } else if (field.defaultValue()) |val| {
 493                            @field(value, field.name) = val;
 494                        } else {
 495                            switch (@typeInfo(field.type)) {
 496                                .@"struct" => {
 497                                    @field(value, field.name) = std.mem.zeroInit(field.type, .{});
 498                                },
 499                                else => {
 500                                    @field(value, field.name) = std.mem.zeroes(@TypeOf(@field(value, field.name)));
 501                                },
 502                            }
 503                        }
 504                    }
 505
 506                    return value;
 507                },
 508                else => {
 509                    @compileError("The initializer must be a struct");
 510                },
 511            }
 512        },
 513        else => {
 514            @compileError("Can't default init a " ++ @typeName(T));
 515        },
 516    }
 517}
 518
 519test zeroInit {
 520    const I = struct {
 521        d: f64,
 522    };
 523
 524    const S = struct {
 525        a: u32,
 526        b: ?bool,
 527        c: I,
 528        e: [3]u8,
 529        f: i64 = -1,
 530    };
 531
 532    const s = zeroInit(S, .{
 533        .a = 42,
 534    });
 535
 536    try testing.expectEqual(S{
 537        .a = 42,
 538        .b = null,
 539        .c = .{
 540            .d = 0,
 541        },
 542        .e = [3]u8{ 0, 0, 0 },
 543        .f = -1,
 544    }, s);
 545
 546    const Color = struct {
 547        r: u8,
 548        g: u8,
 549        b: u8,
 550        a: u8,
 551    };
 552
 553    const c = zeroInit(Color, .{ 255, 255 });
 554    try testing.expectEqual(Color{
 555        .r = 255,
 556        .g = 255,
 557        .b = 0,
 558        .a = 0,
 559    }, c);
 560
 561    const Foo = struct {
 562        foo: u8 = 69,
 563        bar: u8,
 564    };
 565
 566    const f = zeroInit(Foo, .{});
 567    try testing.expectEqual(Foo{
 568        .foo = 69,
 569        .bar = 0,
 570    }, f);
 571
 572    const Bar = struct {
 573        foo: u32 = 666,
 574        bar: u32 = 420,
 575    };
 576
 577    const b = zeroInit(Bar, .{69});
 578    try testing.expectEqual(Bar{
 579        .foo = 69,
 580        .bar = 420,
 581    }, b);
 582
 583    const Baz = struct {
 584        foo: [:0]const u8 = "bar",
 585    };
 586
 587    const baz1 = zeroInit(Baz, .{});
 588    try testing.expectEqual(Baz{}, baz1);
 589
 590    const baz2 = zeroInit(Baz, .{ .foo = "zab" });
 591    try testing.expectEqualSlices(u8, "zab", baz2.foo);
 592
 593    const NestedBaz = struct {
 594        bbb: Baz,
 595    };
 596    const nested_baz = zeroInit(NestedBaz, .{});
 597    try testing.expectEqual(NestedBaz{
 598        .bbb = Baz{},
 599    }, nested_baz);
 600}
 601
 602/// Sorts a slice in-place using a stable algorithm (maintains relative order of equal elements).
 603/// Average time complexity: O(n log n), worst case: O(n log n)
 604/// Space complexity: O(log n) for recursive calls
 605///
 606/// For slice of primitives with default ordering, consider using `std.sort.block` directly.
 607/// For unstable but potentially faster sorting, see `sortUnstable`.
 608pub fn sort(
 609    comptime T: type,
 610    items: []T,
 611    context: anytype,
 612    comptime lessThanFn: fn (@TypeOf(context), lhs: T, rhs: T) bool,
 613) void {
 614    std.sort.block(T, items, context, lessThanFn);
 615}
 616
 617/// Sorts a slice in-place using an unstable algorithm (does not preserve relative order of equal elements).
 618/// Time complexity: O(n) best case, O(n log n) worst case and average case.
 619/// Generally faster than stable sort but order of equal elements is undefined.
 620///
 621/// Uses pattern-defeating quicksort (PDQ) algorithm which performs well on many data patterns.
 622/// For stable sorting that preserves equal element order, use `sort`.
 623pub fn sortUnstable(
 624    comptime T: type,
 625    items: []T,
 626    context: anytype,
 627    comptime lessThanFn: fn (@TypeOf(context), lhs: T, rhs: T) bool,
 628) void {
 629    std.sort.pdq(T, items, context, lessThanFn);
 630}
 631
 632/// TODO: currently this just calls `insertionSortContext`. The block sort implementation
 633/// in this file needs to be adapted to use the sort context.
 634pub fn sortContext(a: usize, b: usize, context: anytype) void {
 635    std.sort.insertionContext(a, b, context);
 636}
 637
 638/// Sorts a range [a, b) using an unstable algorithm with custom context.
 639/// This is a lower-level interface for sorting that works with indices instead of slices.
 640/// Does not preserve relative order of equal elements.
 641///
 642/// The context must provide lessThan(a_idx, b_idx) and swap(a_idx, b_idx) methods.
 643/// Uses pattern-defeating quicksort (PDQ) algorithm.
 644pub fn sortUnstableContext(a: usize, b: usize, context: anytype) void {
 645    std.sort.pdqContext(a, b, context);
 646}
 647
 648/// Compares two slices of numbers lexicographically. O(n).
 649pub fn order(comptime T: type, lhs: []const T, rhs: []const T) math.Order {
 650    if (lhs.ptr != rhs.ptr) {
 651        const n = @min(lhs.len, rhs.len);
 652        for (lhs[0..n], rhs[0..n]) |lhs_elem, rhs_elem| {
 653            switch (math.order(lhs_elem, rhs_elem)) {
 654                .eq => continue,
 655                .lt => return .lt,
 656                .gt => return .gt,
 657            }
 658        }
 659    }
 660    return math.order(lhs.len, rhs.len);
 661}
 662
 663/// Compares two many-item pointers with NUL-termination lexicographically.
 664pub fn orderZ(comptime T: type, lhs: [*:0]const T, rhs: [*:0]const T) math.Order {
 665    if (lhs == rhs) return .eq;
 666    var i: usize = 0;
 667    while (lhs[i] == rhs[i] and lhs[i] != 0) : (i += 1) {}
 668    return math.order(lhs[i], rhs[i]);
 669}
 670
 671test order {
 672    try testing.expect(order(u8, "abcd", "bee") == .lt);
 673    try testing.expect(order(u8, "abc", "abc") == .eq);
 674    try testing.expect(order(u8, "abc", "abc0") == .lt);
 675    try testing.expect(order(u8, "", "") == .eq);
 676    try testing.expect(order(u8, "", "a") == .lt);
 677
 678    const s: []const u8 = "abc";
 679    try testing.expect(order(u8, s, s) == .eq);
 680    try testing.expect(order(u8, s[0..2], s) == .lt);
 681}
 682
 683test orderZ {
 684    try testing.expect(orderZ(u8, "abcd", "bee") == .lt);
 685    try testing.expect(orderZ(u8, "abc", "abc") == .eq);
 686    try testing.expect(orderZ(u8, "abc", "abc0") == .lt);
 687    try testing.expect(orderZ(u8, "", "") == .eq);
 688    try testing.expect(orderZ(u8, "", "a") == .lt);
 689
 690    const s: [*:0]const u8 = "abc";
 691    try testing.expect(orderZ(u8, s, s) == .eq);
 692}
 693
 694/// Returns true if lhs < rhs, false otherwise
 695pub fn lessThan(comptime T: type, lhs: []const T, rhs: []const T) bool {
 696    return order(T, lhs, rhs) == .lt;
 697}
 698
 699test lessThan {
 700    try testing.expect(lessThan(u8, "abcd", "bee"));
 701    try testing.expect(!lessThan(u8, "abc", "abc"));
 702    try testing.expect(lessThan(u8, "abc", "abc0"));
 703    try testing.expect(!lessThan(u8, "", ""));
 704    try testing.expect(lessThan(u8, "", "a"));
 705}
 706
 707const use_vectors = switch (builtin.zig_backend) {
 708    // These backends don't support vectors yet.
 709    .stage2_aarch64,
 710    .stage2_powerpc,
 711    .stage2_riscv64,
 712    => false,
 713    // The SPIR-V backend does not support the optimized path yet.
 714    .stage2_spirv => false,
 715    else => true,
 716};
 717
 718// The naive memory comparison implementation is more useful for fuzzers to find interesting inputs.
 719const use_vectors_for_comparison = use_vectors and !builtin.fuzz;
 720
 721/// Returns true if and only if the slices have the same length and all elements
 722/// compare true using equality operator.
 723pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
 724    if (!@inComptime() and @sizeOf(T) != 0 and std.meta.hasUniqueRepresentation(T) and
 725        use_vectors_for_comparison)
 726    {
 727        return eqlBytes(sliceAsBytes(a), sliceAsBytes(b));
 728    }
 729
 730    if (a.len != b.len) return false;
 731    if (a.len == 0 or a.ptr == b.ptr) return true;
 732
 733    for (a, b) |a_elem, b_elem| {
 734        if (a_elem != b_elem) return false;
 735    }
 736    return true;
 737}
 738
 739test eql {
 740    try testing.expect(eql(u8, "abcd", "abcd"));
 741    try testing.expect(!eql(u8, "abcdef", "abZdef"));
 742    try testing.expect(!eql(u8, "abcdefg", "abcdef"));
 743
 744    comptime {
 745        try testing.expect(eql(type, &.{ bool, f32 }, &.{ bool, f32 }));
 746        try testing.expect(!eql(type, &.{ bool, f32 }, &.{ f32, bool }));
 747        try testing.expect(!eql(type, &.{ bool, f32 }, &.{bool}));
 748
 749        try testing.expect(eql(comptime_int, &.{ 1, 2, 3 }, &.{ 1, 2, 3 }));
 750        try testing.expect(!eql(comptime_int, &.{ 1, 2, 3 }, &.{ 3, 2, 1 }));
 751        try testing.expect(!eql(comptime_int, &.{1}, &.{ 1, 2 }));
 752    }
 753
 754    try testing.expect(eql(void, &.{ {}, {} }, &.{ {}, {} }));
 755    try testing.expect(!eql(void, &.{{}}, &.{ {}, {} }));
 756}
 757
 758/// std.mem.eql heavily optimized for slices of bytes.
 759fn eqlBytes(a: []const u8, b: []const u8) bool {
 760    comptime assert(use_vectors_for_comparison);
 761
 762    if (a.len != b.len) return false;
 763    if (a.len == 0 or a.ptr == b.ptr) return true;
 764
 765    if (a.len <= 16) {
 766        if (a.len < 4) {
 767            const x = (a[0] ^ b[0]) | (a[a.len - 1] ^ b[a.len - 1]) | (a[a.len / 2] ^ b[a.len / 2]);
 768            return x == 0;
 769        }
 770        var x: u32 = 0;
 771        for ([_]usize{ 0, a.len - 4, (a.len / 8) * 4, a.len - 4 - ((a.len / 8) * 4) }) |n| {
 772            x |= @as(u32, @bitCast(a[n..][0..4].*)) ^ @as(u32, @bitCast(b[n..][0..4].*));
 773        }
 774        return x == 0;
 775    }
 776
 777    // Figure out the fastest way to scan through the input in chunks.
 778    // Uses vectors when supported and falls back to usize/words when not.
 779    const Scan = if (std.simd.suggestVectorLength(u8)) |vec_size|
 780        struct {
 781            pub const size = vec_size;
 782            pub const Chunk = @Vector(size, u8);
 783            pub inline fn isNotEqual(chunk_a: Chunk, chunk_b: Chunk) bool {
 784                return @reduce(.Or, chunk_a != chunk_b);
 785            }
 786        }
 787    else
 788        struct {
 789            pub const size = @sizeOf(usize);
 790            pub const Chunk = usize;
 791            pub inline fn isNotEqual(chunk_a: Chunk, chunk_b: Chunk) bool {
 792                return chunk_a != chunk_b;
 793            }
 794        };
 795
 796    inline for (1..6) |s| {
 797        const n = 16 << s;
 798        if (n <= Scan.size and a.len <= n) {
 799            const V = @Vector(n / 2, u8);
 800            var x = @as(V, a[0 .. n / 2].*) ^ @as(V, b[0 .. n / 2].*);
 801            x |= @as(V, a[a.len - n / 2 ..][0 .. n / 2].*) ^ @as(V, b[a.len - n / 2 ..][0 .. n / 2].*);
 802            const zero: V = @splat(0);
 803            return !@reduce(.Or, x != zero);
 804        }
 805    }
 806    // Compare inputs in chunks at a time (excluding the last chunk).
 807    for (0..(a.len - 1) / Scan.size) |i| {
 808        const a_chunk: Scan.Chunk = @bitCast(a[i * Scan.size ..][0..Scan.size].*);
 809        const b_chunk: Scan.Chunk = @bitCast(b[i * Scan.size ..][0..Scan.size].*);
 810        if (Scan.isNotEqual(a_chunk, b_chunk)) return false;
 811    }
 812
 813    // Compare the last chunk using an overlapping read (similar to the previous size strategies).
 814    const last_a_chunk: Scan.Chunk = @bitCast(a[a.len - Scan.size ..][0..Scan.size].*);
 815    const last_b_chunk: Scan.Chunk = @bitCast(b[a.len - Scan.size ..][0..Scan.size].*);
 816    return !Scan.isNotEqual(last_a_chunk, last_b_chunk);
 817}
 818
 819/// Deprecated in favor of `findDiff`.
 820pub const indexOfDiff = findDiff;
 821
 822/// Compares two slices and returns the index of the first inequality.
 823/// Returns null if the slices are equal.
 824pub fn findDiff(comptime T: type, a: []const T, b: []const T) ?usize {
 825    const shortest = @min(a.len, b.len);
 826    if (a.ptr == b.ptr)
 827        return if (a.len == b.len) null else shortest;
 828    var index: usize = 0;
 829    while (index < shortest) : (index += 1) if (a[index] != b[index]) return index;
 830    return if (a.len == b.len) null else shortest;
 831}
 832
 833test findDiff {
 834    try testing.expectEqual(findDiff(u8, "one", "one"), null);
 835    try testing.expectEqual(findDiff(u8, "one two", "one"), 3);
 836    try testing.expectEqual(findDiff(u8, "one", "one two"), 3);
 837    try testing.expectEqual(findDiff(u8, "one twx", "one two"), 6);
 838    try testing.expectEqual(findDiff(u8, "xne", "one"), 0);
 839}
 840
 841/// Takes a sentinel-terminated pointer and returns a slice preserving pointer attributes.
 842/// `[*c]` pointers are assumed to be 0-terminated and assumed to not be allowzero.
 843fn Span(comptime T: type) type {
 844    switch (@typeInfo(T)) {
 845        .optional => |optional_info| {
 846            return ?Span(optional_info.child);
 847        },
 848        .pointer => |ptr_info| {
 849            const new_sentinel: ?ptr_info.child = switch (ptr_info.size) {
 850                .one, .slice => @compileError("invalid type given to std.mem.span: " ++ @typeName(T)),
 851                .many => ptr_info.sentinel() orelse @compileError("invalid type given to std.mem.span: " ++ @typeName(T)),
 852                .c => 0,
 853            };
 854            return @Pointer(.slice, .{
 855                .@"const" = ptr_info.is_const,
 856                .@"volatile" = ptr_info.is_volatile,
 857                .@"allowzero" = ptr_info.is_allowzero and ptr_info.size != .c,
 858                .@"align" = ptr_info.alignment,
 859                .@"addrspace" = ptr_info.address_space,
 860            }, ptr_info.child, new_sentinel);
 861        },
 862        else => {},
 863    }
 864    @compileError("invalid type given to std.mem.span: " ++ @typeName(T));
 865}
 866
 867test Span {
 868    try testing.expect(Span([*:1]u16) == [:1]u16);
 869    try testing.expect(Span(?[*:1]u16) == ?[:1]u16);
 870    try testing.expect(Span([*:1]const u8) == [:1]const u8);
 871    try testing.expect(Span(?[*:1]const u8) == ?[:1]const u8);
 872    try testing.expect(Span([*c]u16) == [:0]u16);
 873    try testing.expect(Span(?[*c]u16) == ?[:0]u16);
 874    try testing.expect(Span([*c]const u8) == [:0]const u8);
 875    try testing.expect(Span(?[*c]const u8) == ?[:0]const u8);
 876}
 877
 878/// Takes a sentinel-terminated pointer and returns a slice, iterating over the
 879/// memory to find the sentinel and determine the length.
 880/// Pointer attributes such as const are preserved.
 881/// `[*c]` pointers are assumed to be non-null and 0-terminated.
 882pub fn span(ptr: anytype) Span(@TypeOf(ptr)) {
 883    if (@typeInfo(@TypeOf(ptr)) == .optional) {
 884        if (ptr) |non_null| {
 885            return span(non_null);
 886        } else {
 887            return null;
 888        }
 889    }
 890    const Result = Span(@TypeOf(ptr));
 891    const l = len(ptr);
 892    const ptr_info = @typeInfo(Result).pointer;
 893    if (ptr_info.sentinel()) |s| {
 894        return ptr[0..l :s];
 895    } else {
 896        return ptr[0..l];
 897    }
 898}
 899
 900test span {
 901    var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
 902    const ptr = @as([*:3]u16, array[0..2 :3]);
 903    try testing.expect(eql(u16, span(ptr), &[_]u16{ 1, 2 }));
 904    try testing.expectEqual(@as(?[:0]u16, null), span(@as(?[*:0]u16, null)));
 905}
 906
 907/// Helper for the return type of sliceTo()
 908fn SliceTo(comptime T: type, comptime end: std.meta.Elem(T)) type {
 909    switch (@typeInfo(T)) {
 910        .optional => |optional_info| {
 911            return ?SliceTo(optional_info.child, end);
 912        },
 913        .pointer => |ptr_info| {
 914            const Elem = std.meta.Elem(T);
 915            const have_sentinel: bool = switch (ptr_info.size) {
 916                .one, .slice, .many => if (std.meta.sentinel(T)) |s| s == end else false,
 917                .c => false,
 918            };
 919            return @Pointer(.slice, .{
 920                .@"const" = ptr_info.is_const,
 921                .@"volatile" = ptr_info.is_volatile,
 922                .@"allowzero" = ptr_info.is_allowzero and ptr_info.size != .c,
 923                .@"align" = ptr_info.alignment,
 924                .@"addrspace" = ptr_info.address_space,
 925            }, Elem, if (have_sentinel) end else null);
 926        },
 927        else => {},
 928    }
 929    @compileError("invalid type given to std.mem.sliceTo: " ++ @typeName(T));
 930}
 931
 932/// Takes a pointer to an array, a sentinel-terminated pointer, or a slice and iterates searching for
 933/// the first occurrence of `end`, returning the scanned slice.
 934/// If `end` is not found, the full length of the array/slice/sentinel terminated pointer is returned.
 935/// If the pointer type is sentinel terminated and `end` matches that terminator, the
 936/// resulting slice is also sentinel terminated.
 937/// Pointer properties such as mutability and alignment are preserved.
 938/// C pointers are assumed to be non-null.
 939pub fn sliceTo(ptr: anytype, comptime end: std.meta.Elem(@TypeOf(ptr))) SliceTo(@TypeOf(ptr), end) {
 940    if (@typeInfo(@TypeOf(ptr)) == .optional) {
 941        const non_null = ptr orelse return null;
 942        return sliceTo(non_null, end);
 943    }
 944    const Result = SliceTo(@TypeOf(ptr), end);
 945    const length = lenSliceTo(ptr, end);
 946    const ptr_info = @typeInfo(Result).pointer;
 947    if (ptr_info.sentinel()) |s| {
 948        return ptr[0..length :s];
 949    } else {
 950        return ptr[0..length];
 951    }
 952}
 953
 954test sliceTo {
 955    try testing.expectEqualSlices(u8, "aoeu", sliceTo("aoeu", 0));
 956
 957    {
 958        var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
 959        try testing.expectEqualSlices(u16, &array, sliceTo(&array, 0));
 960        try testing.expectEqualSlices(u16, array[0..3], sliceTo(array[0..3], 0));
 961        try testing.expectEqualSlices(u16, array[0..2], sliceTo(&array, 3));
 962        try testing.expectEqualSlices(u16, array[0..2], sliceTo(array[0..3], 3));
 963
 964        const sentinel_ptr = @as([*:5]u16, @ptrCast(&array));
 965        try testing.expectEqualSlices(u16, array[0..2], sliceTo(sentinel_ptr, 3));
 966        try testing.expectEqualSlices(u16, array[0..4], sliceTo(sentinel_ptr, 99));
 967
 968        const optional_sentinel_ptr = @as(?[*:5]u16, @ptrCast(&array));
 969        try testing.expectEqualSlices(u16, array[0..2], sliceTo(optional_sentinel_ptr, 3).?);
 970        try testing.expectEqualSlices(u16, array[0..4], sliceTo(optional_sentinel_ptr, 99).?);
 971
 972        const c_ptr = @as([*c]u16, &array);
 973        try testing.expectEqualSlices(u16, array[0..2], sliceTo(c_ptr, 3));
 974
 975        const slice: []u16 = &array;
 976        try testing.expectEqualSlices(u16, array[0..2], sliceTo(slice, 3));
 977        try testing.expectEqualSlices(u16, &array, sliceTo(slice, 99));
 978
 979        const sentinel_slice: [:5]u16 = array[0..4 :5];
 980        try testing.expectEqualSlices(u16, array[0..2], sliceTo(sentinel_slice, 3));
 981        try testing.expectEqualSlices(u16, array[0..4], sliceTo(sentinel_slice, 99));
 982    }
 983    {
 984        var sentinel_array: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 };
 985        try testing.expectEqualSlices(u16, sentinel_array[0..2], sliceTo(&sentinel_array, 3));
 986        try testing.expectEqualSlices(u16, &sentinel_array, sliceTo(&sentinel_array, 0));
 987        try testing.expectEqualSlices(u16, &sentinel_array, sliceTo(&sentinel_array, 99));
 988    }
 989
 990    try testing.expectEqual(@as(?[]u8, null), sliceTo(@as(?[]u8, null), 0));
 991}
 992
 993/// Private helper for sliceTo(). If you want the length, use sliceTo(foo, x).len
 994fn lenSliceTo(ptr: anytype, comptime end: std.meta.Elem(@TypeOf(ptr))) usize {
 995    switch (@typeInfo(@TypeOf(ptr))) {
 996        .pointer => |ptr_info| switch (ptr_info.size) {
 997            .one => switch (@typeInfo(ptr_info.child)) {
 998                .array => |array_info| {
 999                    if (array_info.sentinel()) |s| {
1000                        if (s == end) {
1001                            return indexOfSentinel(array_info.child, end, ptr);
1002                        }
1003                    }
1004                    return findScalar(array_info.child, ptr, end) orelse array_info.len;
1005                },
1006                else => {},
1007            },
1008            .many => if (ptr_info.sentinel()) |s| {
1009                if (s == end) {
1010                    return indexOfSentinel(ptr_info.child, end, ptr);
1011                }
1012                // We're looking for something other than the sentinel,
1013                // but iterating past the sentinel would be a bug so we need
1014                // to check for both.
1015                var i: usize = 0;
1016                while (ptr[i] != end and ptr[i] != s) i += 1;
1017                return i;
1018            },
1019            .c => {
1020                assert(ptr != null);
1021                return indexOfSentinel(ptr_info.child, end, ptr);
1022            },
1023            .slice => {
1024                if (ptr_info.sentinel()) |s| {
1025                    if (s == end) {
1026                        return indexOfSentinel(ptr_info.child, s, ptr);
1027                    }
1028                }
1029                return findScalar(ptr_info.child, ptr, end) orelse ptr.len;
1030            },
1031        },
1032        else => {},
1033    }
1034    @compileError("invalid type given to std.mem.sliceTo: " ++ @typeName(@TypeOf(ptr)));
1035}
1036
1037test lenSliceTo {
1038    try testing.expect(lenSliceTo("aoeu", 0) == 4);
1039
1040    {
1041        var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
1042        try testing.expectEqual(@as(usize, 5), lenSliceTo(&array, 0));
1043        try testing.expectEqual(@as(usize, 3), lenSliceTo(array[0..3], 0));
1044        try testing.expectEqual(@as(usize, 2), lenSliceTo(&array, 3));
1045        try testing.expectEqual(@as(usize, 2), lenSliceTo(array[0..3], 3));
1046
1047        const sentinel_ptr = @as([*:5]u16, @ptrCast(&array));
1048        try testing.expectEqual(@as(usize, 2), lenSliceTo(sentinel_ptr, 3));
1049        try testing.expectEqual(@as(usize, 4), lenSliceTo(sentinel_ptr, 99));
1050
1051        const c_ptr = @as([*c]u16, &array);
1052        try testing.expectEqual(@as(usize, 2), lenSliceTo(c_ptr, 3));
1053
1054        const slice: []u16 = &array;
1055        try testing.expectEqual(@as(usize, 2), lenSliceTo(slice, 3));
1056        try testing.expectEqual(@as(usize, 5), lenSliceTo(slice, 99));
1057
1058        const sentinel_slice: [:5]u16 = array[0..4 :5];
1059        try testing.expectEqual(@as(usize, 2), lenSliceTo(sentinel_slice, 3));
1060        try testing.expectEqual(@as(usize, 4), lenSliceTo(sentinel_slice, 99));
1061    }
1062    {
1063        var sentinel_array: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 };
1064        try testing.expectEqual(@as(usize, 2), lenSliceTo(&sentinel_array, 3));
1065        try testing.expectEqual(@as(usize, 5), lenSliceTo(&sentinel_array, 0));
1066        try testing.expectEqual(@as(usize, 5), lenSliceTo(&sentinel_array, 99));
1067    }
1068}
1069
1070/// Takes a sentinel-terminated pointer and iterates over the memory to find the
1071/// sentinel and determine the length.
1072/// `[*c]` pointers are assumed to be non-null and 0-terminated.
1073pub fn len(value: anytype) usize {
1074    switch (@typeInfo(@TypeOf(value))) {
1075        .pointer => |info| switch (info.size) {
1076            .many => {
1077                const sentinel = info.sentinel() orelse
1078                    @compileError("invalid type given to std.mem.len: " ++ @typeName(@TypeOf(value)));
1079                return indexOfSentinel(info.child, sentinel, value);
1080            },
1081            .c => {
1082                assert(value != null);
1083                return indexOfSentinel(info.child, 0, value);
1084            },
1085            else => @compileError("invalid type given to std.mem.len: " ++ @typeName(@TypeOf(value))),
1086        },
1087        else => @compileError("invalid type given to std.mem.len: " ++ @typeName(@TypeOf(value))),
1088    }
1089}
1090
1091test len {
1092    var array: [5]u16 = [_]u16{ 1, 2, 0, 4, 5 };
1093    const ptr = @as([*:4]u16, array[0..3 :4]);
1094    try testing.expect(len(ptr) == 3);
1095    const c_ptr = @as([*c]u16, ptr);
1096    try testing.expect(len(c_ptr) == 2);
1097}
1098
1099/// Deprecated in favor of `findSentinel`.
1100pub const indexOfSentinel = findSentinel;
1101
1102/// Returns the index of the sentinel value in a sentinel-terminated pointer.
1103/// Linear search through memory until the sentinel is found.
1104pub fn findSentinel(comptime T: type, comptime sentinel: T, p: [*:sentinel]const T) usize {
1105    var i: usize = 0;
1106
1107    if (use_vectors_for_comparison and
1108        !std.debug.inValgrind() and // https://github.com/ziglang/zig/issues/17717
1109        !@inComptime() and
1110        (@typeInfo(T) == .int or @typeInfo(T) == .float) and std.math.isPowerOfTwo(@bitSizeOf(T)))
1111    {
1112        switch (@import("builtin").cpu.arch) {
1113            // The below branch assumes that reading past the end of the buffer is valid, as long
1114            // as we don't read into a new page. This should be the case for most architectures
1115            // which use paged memory, however should be confirmed before adding a new arch below.
1116            .aarch64, .x86, .x86_64 => if (std.simd.suggestVectorLength(T)) |block_len| {
1117                const page_size = std.heap.page_size_min;
1118                const block_size = @sizeOf(T) * block_len;
1119                const Block = @Vector(block_len, T);
1120                const mask: Block = @splat(sentinel);
1121
1122                comptime assert(std.heap.page_size_min % @sizeOf(Block) == 0);
1123                assert(page_size % @sizeOf(Block) == 0);
1124
1125                // First block may be unaligned
1126                const start_addr = @intFromPtr(&p[i]);
1127                const offset_in_page = start_addr & (page_size - 1);
1128                if (offset_in_page <= page_size - @sizeOf(Block)) {
1129                    // Will not read past the end of a page, full block.
1130                    const block: Block = p[i..][0..block_len].*;
1131                    const matches = block == mask;
1132                    if (@reduce(.Or, matches)) {
1133                        return i + std.simd.firstTrue(matches).?;
1134                    }
1135
1136                    i += @divExact(std.mem.alignForward(usize, start_addr, block_size) - start_addr, @sizeOf(T));
1137                } else {
1138                    @branchHint(.unlikely);
1139                    // Would read over a page boundary. Per-byte at a time until aligned or found.
1140                    // 0.39% chance this branch is taken for 4K pages at 16b block length.
1141                    //
1142                    // An alternate strategy is to do read a full block (the last in the page) and
1143                    // mask the entries before the pointer.
1144                    while ((@intFromPtr(&p[i]) & (block_size - 1)) != 0) : (i += 1) {
1145                        if (p[i] == sentinel) return i;
1146                    }
1147                }
1148
1149                std.debug.assertAligned(&p[i], .fromByteUnits(block_size));
1150                while (true) {
1151                    const block: Block = p[i..][0..block_len].*;
1152                    const matches = block == mask;
1153                    if (@reduce(.Or, matches)) {
1154                        return i + std.simd.firstTrue(matches).?;
1155                    }
1156                    i += block_len;
1157                }
1158            },
1159            else => {},
1160        }
1161    }
1162
1163    while (p[i] != sentinel) {
1164        i += 1;
1165    }
1166    return i;
1167}
1168
1169test "indexOfSentinel vector paths" {
1170    const Types = [_]type{ u8, u16, u32, u64 };
1171    const allocator = std.testing.allocator;
1172    const page_size = std.heap.page_size_min;
1173
1174    inline for (Types) |T| {
1175        const block_len = std.simd.suggestVectorLength(T) orelse continue;
1176
1177        // Allocate three pages so we guarantee a page-crossing address with a full page after
1178        const memory = try allocator.alloc(T, 3 * page_size / @sizeOf(T));
1179        defer allocator.free(memory);
1180        @memset(memory, 0xaa);
1181
1182        // Find starting page-alignment = 0
1183        var start: usize = 0;
1184        const start_addr = @intFromPtr(&memory);
1185        start += (std.mem.alignForward(usize, start_addr, page_size) - start_addr) / @sizeOf(T);
1186        try testing.expect(start < page_size / @sizeOf(T));
1187
1188        // Validate all sub-block alignments
1189        const search_len = page_size / @sizeOf(T);
1190        memory[start + search_len] = 0;
1191        for (0..block_len) |offset| {
1192            try testing.expectEqual(search_len - offset, indexOfSentinel(T, 0, @ptrCast(&memory[start + offset])));
1193        }
1194        memory[start + search_len] = 0xaa;
1195
1196        // Validate page boundary crossing
1197        const start_page_boundary = start + (page_size / @sizeOf(T));
1198        memory[start_page_boundary + block_len] = 0;
1199        for (0..block_len) |offset| {
1200            try testing.expectEqual(2 * block_len - offset, indexOfSentinel(T, 0, @ptrCast(&memory[start_page_boundary - block_len + offset])));
1201        }
1202    }
1203}
1204
1205/// Returns true if all elements in a slice are equal to the scalar value provided
1206pub fn allEqual(comptime T: type, slice: []const T, scalar: T) bool {
1207    for (slice) |item| {
1208        if (item != scalar) return false;
1209    }
1210    return true;
1211}
1212
1213/// Remove a set of values from the beginning of a slice.
1214pub fn trimStart(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
1215    var begin: usize = 0;
1216    while (begin < slice.len and findScalar(T, values_to_strip, slice[begin]) != null) : (begin += 1) {}
1217    return slice[begin..];
1218}
1219
1220test trimStart {
1221    try testing.expectEqualSlices(u8, "foo\n ", trimStart(u8, " foo\n ", " \n"));
1222}
1223
1224/// Deprecated: use `trimStart` instead.
1225pub const trimLeft = trimStart;
1226
1227/// Remove a set of values from the end of a slice.
1228pub fn trimEnd(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
1229    var end: usize = slice.len;
1230    while (end > 0 and findScalar(T, values_to_strip, slice[end - 1]) != null) : (end -= 1) {}
1231    return slice[0..end];
1232}
1233
1234test trimEnd {
1235    try testing.expectEqualSlices(u8, " foo", trimEnd(u8, " foo\n ", " \n"));
1236}
1237
1238/// Deprecated: use `trimEnd` instead.
1239pub const trimRight = trimEnd;
1240
1241/// Remove a set of values from the beginning and end of a slice.
1242pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
1243    var begin: usize = 0;
1244    var end: usize = slice.len;
1245    while (begin < end and findScalar(T, values_to_strip, slice[begin]) != null) : (begin += 1) {}
1246    while (end > begin and findScalar(T, values_to_strip, slice[end - 1]) != null) : (end -= 1) {}
1247    return slice[begin..end];
1248}
1249
1250test trim {
1251    try testing.expectEqualSlices(u8, "foo", trim(u8, " foo\n ", " \n"));
1252    try testing.expectEqualSlices(u8, "foo", trim(u8, "foo", " \n"));
1253}
1254
1255/// Deprecated in favor of `findScalar`.
1256pub const indexOfScalar = findScalar;
1257
1258/// Linear search for the index of a scalar value inside a slice.
1259pub fn findScalar(comptime T: type, slice: []const T, value: T) ?usize {
1260    return indexOfScalarPos(T, slice, 0, value);
1261}
1262
1263/// Deprecated in favor of `findScalarLast`.
1264pub const lastIndexOfScalar = findScalarLast;
1265
1266/// Linear search for the last index of a scalar value inside a slice.
1267pub fn findScalarLast(comptime T: type, slice: []const T, value: T) ?usize {
1268    var i: usize = slice.len;
1269    while (i != 0) {
1270        i -= 1;
1271        if (slice[i] == value) return i;
1272    }
1273    return null;
1274}
1275
1276/// Deprecated in favor of `findScalarPos`.
1277pub const indexOfScalarPos = findScalarPos;
1278
1279/// Linear search for the index of a scalar value inside a slice, starting from a given position.
1280/// Returns null if the value is not found.
1281pub fn findScalarPos(comptime T: type, slice: []const T, start_index: usize, value: T) ?usize {
1282    if (start_index >= slice.len) return null;
1283
1284    var i: usize = start_index;
1285    if (use_vectors_for_comparison and
1286        !std.debug.inValgrind() and // https://github.com/ziglang/zig/issues/17717
1287        !@inComptime() and
1288        (@typeInfo(T) == .int or @typeInfo(T) == .float) and std.math.isPowerOfTwo(@bitSizeOf(T)))
1289    {
1290        if (std.simd.suggestVectorLength(T)) |block_len| {
1291            // For Intel Nehalem (2009) and AMD Bulldozer (2012) or later, unaligned loads on aligned data result
1292            // in the same execution as aligned loads. We ignore older arch's here and don't bother pre-aligning.
1293            //
1294            // Use `std.simd.suggestVectorLength(T)` to get the same alignment as used in this function
1295            // however this usually isn't necessary unless your arch has a performance penalty due to this.
1296            //
1297            // This may differ for other arch's. Arm for example costs a cycle when loading across a cache
1298            // line so explicit alignment prologues may be worth exploration.
1299
1300            // Unrolling here is ~10% improvement. We can then do one bounds check every 2 blocks
1301            // instead of one which adds up.
1302            const Block = @Vector(block_len, T);
1303            if (i + 2 * block_len < slice.len) {
1304                const mask: Block = @splat(value);
1305                while (true) {
1306                    inline for (0..2) |_| {
1307                        const block: Block = slice[i..][0..block_len].*;
1308                        const matches = block == mask;
1309                        if (@reduce(.Or, matches)) {
1310                            return i + std.simd.firstTrue(matches).?;
1311                        }
1312                        i += block_len;
1313                    }
1314                    if (i + 2 * block_len >= slice.len) break;
1315                }
1316            }
1317
1318            // {block_len, block_len / 2} check
1319            inline for (0..2) |j| {
1320                const block_x_len = block_len / (1 << j);
1321                comptime if (block_x_len < 4) break;
1322
1323                const BlockX = @Vector(block_x_len, T);
1324                if (i + block_x_len < slice.len) {
1325                    const mask: BlockX = @splat(value);
1326                    const block: BlockX = slice[i..][0..block_x_len].*;
1327                    const matches = block == mask;
1328                    if (@reduce(.Or, matches)) {
1329                        return i + std.simd.firstTrue(matches).?;
1330                    }
1331                    i += block_x_len;
1332                }
1333            }
1334        }
1335    }
1336
1337    for (slice[i..], i..) |c, j| {
1338        if (c == value) return j;
1339    }
1340    return null;
1341}
1342
1343test indexOfScalarPos {
1344    const Types = [_]type{ u8, u16, u32, u64 };
1345
1346    inline for (Types) |T| {
1347        var memory: [64 / @sizeOf(T)]T = undefined;
1348        @memset(&memory, 0xaa);
1349        memory[memory.len - 1] = 0;
1350
1351        for (0..memory.len) |i| {
1352            try testing.expectEqual(memory.len - i - 1, indexOfScalarPos(T, memory[i..], 0, 0).?);
1353        }
1354    }
1355}
1356
1357/// Deprecated in favor of `findAny`.
1358pub const indexOfAny = findAny;
1359
1360/// Linear search for the index of any value in the provided list inside a slice.
1361/// Returns null if no values are found.
1362pub fn findAny(comptime T: type, slice: []const T, values: []const T) ?usize {
1363    return indexOfAnyPos(T, slice, 0, values);
1364}
1365
1366/// Deprecated in favor of `findLastAny`.
1367pub const lastIndexOfAny = findLastAny;
1368
1369/// Linear search for the last index of any value in the provided list inside a slice.
1370/// Returns null if no values are found.
1371pub fn findLastAny(comptime T: type, slice: []const T, values: []const T) ?usize {
1372    var i: usize = slice.len;
1373    while (i != 0) {
1374        i -= 1;
1375        for (values) |value| {
1376            if (slice[i] == value) return i;
1377        }
1378    }
1379    return null;
1380}
1381
1382/// Deprecated in favor of `findAnyPos`.
1383pub const indexOfAnyPos = findAnyPos;
1384
1385/// Linear search for the index of any value in the provided list inside a slice, starting from a given position.
1386/// Returns null if no values are found.
1387pub fn findAnyPos(comptime T: type, slice: []const T, start_index: usize, values: []const T) ?usize {
1388    if (start_index >= slice.len) return null;
1389    for (slice[start_index..], start_index..) |c, i| {
1390        for (values) |value| {
1391            if (c == value) return i;
1392        }
1393    }
1394    return null;
1395}
1396
1397/// Deprecated in favor of `findNone`.
1398pub const indexOfNone = findNone;
1399
1400/// Find the first item in `slice` which is not contained in `values`.
1401///
1402/// Comparable to `strspn` in the C standard library.
1403pub fn findNone(comptime T: type, slice: []const T, values: []const T) ?usize {
1404    return indexOfNonePos(T, slice, 0, values);
1405}
1406
1407test findNone {
1408    try testing.expect(findNone(u8, "abc123", "123").? == 0);
1409    try testing.expect(findLastNone(u8, "abc123", "123").? == 2);
1410    try testing.expect(findNone(u8, "123abc", "123").? == 3);
1411    try testing.expect(findLastNone(u8, "123abc", "123").? == 5);
1412    try testing.expect(findNone(u8, "123123", "123") == null);
1413    try testing.expect(findNone(u8, "333333", "123") == null);
1414
1415    try testing.expect(indexOfNonePos(u8, "abc123", 3, "321") == null);
1416}
1417
1418/// Deprecated in favor of `findLastNone`.
1419pub const lastIndexOfNone = findLastNone;
1420
1421/// Find the last item in `slice` which is not contained in `values`.
1422///
1423/// Like `strspn` in the C standard library, but searches from the end.
1424pub fn findLastNone(comptime T: type, slice: []const T, values: []const T) ?usize {
1425    var i: usize = slice.len;
1426    outer: while (i != 0) {
1427        i -= 1;
1428        for (values) |value| {
1429            if (slice[i] == value) continue :outer;
1430        }
1431        return i;
1432    }
1433    return null;
1434}
1435
1436pub const indexOfNonePos = findNonePos;
1437
1438/// Find the first item in `slice[start_index..]` which is not contained in `values`.
1439/// The returned index will be relative to the start of `slice`, and never less than `start_index`.
1440///
1441/// Comparable to `strspn` in the C standard library.
1442pub fn findNonePos(comptime T: type, slice: []const T, start_index: usize, values: []const T) ?usize {
1443    if (start_index >= slice.len) return null;
1444    outer: for (slice[start_index..], start_index..) |c, i| {
1445        for (values) |value| {
1446            if (c == value) continue :outer;
1447        }
1448        return i;
1449    }
1450    return null;
1451}
1452
1453/// Deprecated in favor of `find`.
1454pub const indexOf = find;
1455
1456/// Search for needle in haystack and return the index of the first occurrence.
1457/// Uses Boyer-Moore-Horspool algorithm on large inputs; linear search on small inputs.
1458/// Returns null if needle is not found.
1459pub fn find(comptime T: type, haystack: []const T, needle: []const T) ?usize {
1460    return indexOfPos(T, haystack, 0, needle);
1461}
1462
1463/// Deprecated in favor of `findLastLinear`.
1464pub const lastIndexOfLinear = findLastLinear;
1465
1466/// Find the index in a slice of a sub-slice, searching from the end backwards.
1467/// To start looking at a different index, slice the haystack first.
1468/// Consider using `lastIndexOf` instead of this, which will automatically use a
1469/// more sophisticated algorithm on larger inputs.
1470pub fn findLastLinear(comptime T: type, haystack: []const T, needle: []const T) ?usize {
1471    if (needle.len > haystack.len) return null;
1472    var i: usize = haystack.len - needle.len;
1473    while (true) : (i -= 1) {
1474        if (mem.eql(T, haystack[i..][0..needle.len], needle)) return i;
1475        if (i == 0) return null;
1476    }
1477}
1478
1479pub const indexOfPosLinear = findPosLinear;
1480
1481/// Consider using `indexOfPos` instead of this, which will automatically use a
1482/// more sophisticated algorithm on larger inputs.
1483pub fn findPosLinear(comptime T: type, haystack: []const T, start_index: usize, needle: []const T) ?usize {
1484    if (needle.len > haystack.len) return null;
1485    var i: usize = start_index;
1486    const end = haystack.len - needle.len;
1487    while (i <= end) : (i += 1) {
1488        if (eql(T, haystack[i..][0..needle.len], needle)) return i;
1489    }
1490    return null;
1491}
1492
1493test findPosLinear {
1494    try testing.expectEqual(0, findPosLinear(u8, "", 0, ""));
1495    try testing.expectEqual(0, findPosLinear(u8, "123", 0, ""));
1496
1497    try testing.expectEqual(null, findPosLinear(u8, "", 0, "1"));
1498    try testing.expectEqual(0, findPosLinear(u8, "1", 0, "1"));
1499    try testing.expectEqual(null, findPosLinear(u8, "2", 0, "1"));
1500    try testing.expectEqual(1, findPosLinear(u8, "21", 0, "1"));
1501    try testing.expectEqual(null, findPosLinear(u8, "222", 0, "1"));
1502
1503    try testing.expectEqual(null, findPosLinear(u8, "", 0, "12"));
1504    try testing.expectEqual(null, findPosLinear(u8, "1", 0, "12"));
1505    try testing.expectEqual(null, findPosLinear(u8, "2", 0, "12"));
1506    try testing.expectEqual(0, findPosLinear(u8, "12", 0, "12"));
1507    try testing.expectEqual(null, findPosLinear(u8, "21", 0, "12"));
1508    try testing.expectEqual(1, findPosLinear(u8, "212", 0, "12"));
1509    try testing.expectEqual(0, findPosLinear(u8, "122", 0, "12"));
1510    try testing.expectEqual(1, findPosLinear(u8, "212112", 0, "12"));
1511}
1512
1513fn boyerMooreHorspoolPreprocessReverse(pattern: []const u8, table: *[256]usize) void {
1514    for (table) |*c| {
1515        c.* = pattern.len;
1516    }
1517
1518    var i: usize = pattern.len - 1;
1519    // The first item is intentionally ignored and the skip size will be pattern.len.
1520    // This is the standard way Boyer-Moore-Horspool is implemented.
1521    while (i > 0) : (i -= 1) {
1522        table[pattern[i]] = i;
1523    }
1524}
1525
1526fn boyerMooreHorspoolPreprocess(pattern: []const u8, table: *[256]usize) void {
1527    for (table) |*c| {
1528        c.* = pattern.len;
1529    }
1530
1531    var i: usize = 0;
1532    // The last item is intentionally ignored and the skip size will be pattern.len.
1533    // This is the standard way Boyer-Moore-Horspool is implemented.
1534    while (i < pattern.len - 1) : (i += 1) {
1535        table[pattern[i]] = pattern.len - 1 - i;
1536    }
1537}
1538
1539/// Deprecated in favor of `find`.
1540pub const lastIndexOf = findLast;
1541
1542/// Find the index in a slice of a sub-slice, searching from the end backwards.
1543/// To start looking at a different index, slice the haystack first.
1544/// Uses the Reverse Boyer-Moore-Horspool algorithm on large inputs;
1545/// `lastIndexOfLinear` on small inputs.
1546pub fn findLast(comptime T: type, haystack: []const T, needle: []const T) ?usize {
1547    if (needle.len > haystack.len) return null;
1548    if (needle.len == 0) return haystack.len;
1549
1550    if (!std.meta.hasUniqueRepresentation(T) or haystack.len < 52 or needle.len <= 4)
1551        return lastIndexOfLinear(T, haystack, needle);
1552
1553    const haystack_bytes = sliceAsBytes(haystack);
1554    const needle_bytes = sliceAsBytes(needle);
1555
1556    var skip_table: [256]usize = undefined;
1557    boyerMooreHorspoolPreprocessReverse(needle_bytes, skip_table[0..]);
1558
1559    var i: usize = haystack_bytes.len - needle_bytes.len;
1560    while (true) {
1561        if (i % @sizeOf(T) == 0 and mem.eql(u8, haystack_bytes[i .. i + needle_bytes.len], needle_bytes)) {
1562            return @divExact(i, @sizeOf(T));
1563        }
1564        const skip = skip_table[haystack_bytes[i]];
1565        if (skip > i) break;
1566        i -= skip;
1567    }
1568
1569    return null;
1570}
1571
1572/// Deprecated in favor of `findPos`.
1573pub const indexOfPos = findPos;
1574
1575/// Uses Boyer-Moore-Horspool algorithm on large inputs; `indexOfPosLinear` on small inputs.
1576pub fn findPos(comptime T: type, haystack: []const T, start_index: usize, needle: []const T) ?usize {
1577    if (needle.len > haystack.len) return null;
1578    if (needle.len < 2) {
1579        if (needle.len == 0) return start_index;
1580        // indexOfScalarPos is significantly faster than indexOfPosLinear
1581        return indexOfScalarPos(T, haystack, start_index, needle[0]);
1582    }
1583
1584    if (!std.meta.hasUniqueRepresentation(T) or haystack.len < 52 or needle.len <= 4)
1585        return indexOfPosLinear(T, haystack, start_index, needle);
1586
1587    const haystack_bytes = sliceAsBytes(haystack);
1588    const needle_bytes = sliceAsBytes(needle);
1589
1590    var skip_table: [256]usize = undefined;
1591    boyerMooreHorspoolPreprocess(needle_bytes, skip_table[0..]);
1592
1593    var i: usize = start_index * @sizeOf(T);
1594    while (i <= haystack_bytes.len - needle_bytes.len) {
1595        if (i % @sizeOf(T) == 0 and mem.eql(u8, haystack_bytes[i .. i + needle_bytes.len], needle_bytes)) {
1596            return @divExact(i, @sizeOf(T));
1597        }
1598        i += skip_table[haystack_bytes[i + needle_bytes.len - 1]];
1599    }
1600
1601    return null;
1602}
1603
1604test indexOf {
1605    try testing.expect(indexOf(u8, "one two three four five six seven eight nine ten eleven", "three four").? == 8);
1606    try testing.expect(lastIndexOf(u8, "one two three four five six seven eight nine ten eleven", "three four").? == 8);
1607    try testing.expect(indexOf(u8, "one two three four five six seven eight nine ten eleven", "two two") == null);
1608    try testing.expect(lastIndexOf(u8, "one two three four five six seven eight nine ten eleven", "two two") == null);
1609
1610    try testing.expect(indexOf(u8, "one two three four five six seven eight nine ten", "").? == 0);
1611    try testing.expect(lastIndexOf(u8, "one two three four five six seven eight nine ten", "").? == 48);
1612
1613    try testing.expect(indexOf(u8, "one two three four", "four").? == 14);
1614    try testing.expect(lastIndexOf(u8, "one two three two four", "two").? == 14);
1615    try testing.expect(indexOf(u8, "one two three four", "gour") == null);
1616    try testing.expect(lastIndexOf(u8, "one two three four", "gour") == null);
1617    try testing.expect(indexOf(u8, "foo", "foo").? == 0);
1618    try testing.expect(lastIndexOf(u8, "foo", "foo").? == 0);
1619    try testing.expect(indexOf(u8, "foo", "fool") == null);
1620    try testing.expect(lastIndexOf(u8, "foo", "lfoo") == null);
1621    try testing.expect(lastIndexOf(u8, "foo", "fool") == null);
1622
1623    try testing.expect(indexOf(u8, "foo foo", "foo").? == 0);
1624    try testing.expect(lastIndexOf(u8, "foo foo", "foo").? == 4);
1625    try testing.expect(lastIndexOfAny(u8, "boo, cat", "abo").? == 6);
1626    try testing.expect(findScalarLast(u8, "boo", 'o').? == 2);
1627}
1628
1629test "indexOf multibyte" {
1630    {
1631        // make haystack and needle long enough to trigger Boyer-Moore-Horspool algorithm
1632        const haystack = [1]u16{0} ** 100 ++ [_]u16{ 0xbbaa, 0xccbb, 0xddcc, 0xeedd, 0xffee, 0x00ff };
1633        const needle = [_]u16{ 0xbbaa, 0xccbb, 0xddcc, 0xeedd, 0xffee };
1634        try testing.expectEqual(indexOfPos(u16, &haystack, 0, &needle), 100);
1635
1636        // check for misaligned false positives (little and big endian)
1637        const needleLE = [_]u16{ 0xbbbb, 0xcccc, 0xdddd, 0xeeee, 0xffff };
1638        try testing.expectEqual(indexOfPos(u16, &haystack, 0, &needleLE), null);
1639        const needleBE = [_]u16{ 0xaacc, 0xbbdd, 0xccee, 0xddff, 0xee00 };
1640        try testing.expectEqual(indexOfPos(u16, &haystack, 0, &needleBE), null);
1641    }
1642
1643    {
1644        // make haystack and needle long enough to trigger Boyer-Moore-Horspool algorithm
1645        const haystack = [_]u16{ 0xbbaa, 0xccbb, 0xddcc, 0xeedd, 0xffee, 0x00ff } ++ [1]u16{0} ** 100;
1646        const needle = [_]u16{ 0xbbaa, 0xccbb, 0xddcc, 0xeedd, 0xffee };
1647        try testing.expectEqual(lastIndexOf(u16, &haystack, &needle), 0);
1648
1649        // check for misaligned false positives (little and big endian)
1650        const needleLE = [_]u16{ 0xbbbb, 0xcccc, 0xdddd, 0xeeee, 0xffff };
1651        try testing.expectEqual(lastIndexOf(u16, &haystack, &needleLE), null);
1652        const needleBE = [_]u16{ 0xaacc, 0xbbdd, 0xccee, 0xddff, 0xee00 };
1653        try testing.expectEqual(lastIndexOf(u16, &haystack, &needleBE), null);
1654    }
1655}
1656
1657test "indexOfPos empty needle" {
1658    try testing.expectEqual(indexOfPos(u8, "abracadabra", 5, ""), 5);
1659}
1660
1661/// Returns the number of needles inside the haystack
1662/// needle.len must be > 0
1663/// does not count overlapping needles
1664pub fn count(comptime T: type, haystack: []const T, needle: []const T) usize {
1665    if (needle.len == 1) return countScalar(T, haystack, needle[0]);
1666    assert(needle.len > 0);
1667    var i: usize = 0;
1668    var found: usize = 0;
1669
1670    while (indexOfPos(T, haystack, i, needle)) |idx| {
1671        i = idx + needle.len;
1672        found += 1;
1673    }
1674
1675    return found;
1676}
1677
1678test count {
1679    try testing.expect(count(u8, "", "h") == 0);
1680    try testing.expect(count(u8, "h", "h") == 1);
1681    try testing.expect(count(u8, "hh", "h") == 2);
1682    try testing.expect(count(u8, "world!", "hello") == 0);
1683    try testing.expect(count(u8, "hello world!", "hello") == 1);
1684    try testing.expect(count(u8, "   abcabc   abc", "abc") == 3);
1685    try testing.expect(count(u8, "udexdcbvbruhasdrw", "bruh") == 1);
1686    try testing.expect(count(u8, "foo bar", "o bar") == 1);
1687    try testing.expect(count(u8, "foofoofoo", "foo") == 3);
1688    try testing.expect(count(u8, "fffffff", "ff") == 3);
1689    try testing.expect(count(u8, "owowowu", "owowu") == 1);
1690}
1691
1692/// Returns the number of times `element` appears in a slice of memory.
1693pub fn countScalar(comptime T: type, list: []const T, element: T) usize {
1694    const n = list.len;
1695    var i: usize = 0;
1696    var found: usize = 0;
1697
1698    if (use_vectors_for_comparison and
1699        (@typeInfo(T) == .int or @typeInfo(T) == .float) and std.math.isPowerOfTwo(@bitSizeOf(T)))
1700    {
1701        if (std.simd.suggestVectorLength(T)) |block_size| {
1702            const Block = @Vector(block_size, T);
1703
1704            const letter_mask: Block = @splat(element);
1705            while (n - i >= block_size) : (i += block_size) {
1706                const haystack_block: Block = list[i..][0..block_size].*;
1707                found += std.simd.countTrues(letter_mask == haystack_block);
1708            }
1709        }
1710    }
1711
1712    for (list[i..n]) |item| {
1713        found += @intFromBool(item == element);
1714    }
1715
1716    return found;
1717}
1718
1719test countScalar {
1720    try testing.expectEqual(0, countScalar(u8, "", 'h'));
1721    try testing.expectEqual(1, countScalar(u8, "h", 'h'));
1722    try testing.expectEqual(2, countScalar(u8, "hh", 'h'));
1723    try testing.expectEqual(2, countScalar(u8, "ahhb", 'h'));
1724    try testing.expectEqual(3, countScalar(u8, "   abcabc   abc", 'b'));
1725}
1726
1727/// Returns true if the haystack contains expected_count or more needles
1728/// needle.len must be > 0
1729/// does not count overlapping needles
1730//
1731/// See also: `containsAtLeastScalar`
1732pub fn containsAtLeast(comptime T: type, haystack: []const T, expected_count: usize, needle: []const T) bool {
1733    if (needle.len == 1) return containsAtLeastScalar(T, haystack, expected_count, needle[0]);
1734    assert(needle.len > 0);
1735    if (expected_count == 0) return true;
1736
1737    var i: usize = 0;
1738    var found: usize = 0;
1739
1740    while (indexOfPos(T, haystack, i, needle)) |idx| {
1741        i = idx + needle.len;
1742        found += 1;
1743        if (found == expected_count) return true;
1744    }
1745    return false;
1746}
1747
1748test containsAtLeast {
1749    try testing.expect(containsAtLeast(u8, "aa", 0, "a"));
1750    try testing.expect(containsAtLeast(u8, "aa", 1, "a"));
1751    try testing.expect(containsAtLeast(u8, "aa", 2, "a"));
1752    try testing.expect(!containsAtLeast(u8, "aa", 3, "a"));
1753
1754    try testing.expect(containsAtLeast(u8, "radaradar", 1, "radar"));
1755    try testing.expect(!containsAtLeast(u8, "radaradar", 2, "radar"));
1756
1757    try testing.expect(containsAtLeast(u8, "radarradaradarradar", 3, "radar"));
1758    try testing.expect(!containsAtLeast(u8, "radarradaradarradar", 4, "radar"));
1759
1760    try testing.expect(containsAtLeast(u8, "   radar      radar   ", 2, "radar"));
1761    try testing.expect(!containsAtLeast(u8, "   radar      radar   ", 3, "radar"));
1762}
1763
1764/// Deprecated in favor of `containsAtLeastScalar2`.
1765pub fn containsAtLeastScalar(comptime T: type, list: []const T, minimum: usize, element: T) bool {
1766    return containsAtLeastScalar2(T, list, element, minimum);
1767}
1768
1769/// Returns true if `element` appears at least `minimum` number of times in `list`.
1770//
1771/// Related:
1772/// * `containsAtLeast`
1773/// * `countScalar`
1774pub fn containsAtLeastScalar2(comptime T: type, list: []const T, element: T, minimum: usize) bool {
1775    const n = list.len;
1776    var i: usize = 0;
1777    var found: usize = 0;
1778
1779    if (use_vectors_for_comparison and
1780        (@typeInfo(T) == .int or @typeInfo(T) == .float) and std.math.isPowerOfTwo(@bitSizeOf(T)))
1781    {
1782        if (std.simd.suggestVectorLength(T)) |block_size| {
1783            const Block = @Vector(block_size, T);
1784
1785            const letter_mask: Block = @splat(element);
1786            while (n - i >= block_size) : (i += block_size) {
1787                const haystack_block: Block = list[i..][0..block_size].*;
1788                found += std.simd.countTrues(letter_mask == haystack_block);
1789                if (found >= minimum) return true;
1790            }
1791        }
1792    }
1793
1794    for (list[i..n]) |item| {
1795        found += @intFromBool(item == element);
1796        if (found >= minimum) return true;
1797    }
1798
1799    return false;
1800}
1801
1802test containsAtLeastScalar2 {
1803    try testing.expect(containsAtLeastScalar2(u8, "aa", 'a', 0));
1804    try testing.expect(containsAtLeastScalar2(u8, "aa", 'a', 1));
1805    try testing.expect(containsAtLeastScalar2(u8, "aa", 'a', 2));
1806    try testing.expect(!containsAtLeastScalar2(u8, "aa", 'a', 3));
1807
1808    try testing.expect(containsAtLeastScalar2(u8, "adadda", 'd', 3));
1809    try testing.expect(!containsAtLeastScalar2(u8, "adadda", 'd', 4));
1810}
1811
1812/// Reads an integer from memory with size equal to bytes.len.
1813/// T specifies the return type, which must be large enough to store
1814/// the result.
1815pub fn readVarInt(comptime ReturnType: type, bytes: []const u8, endian: Endian) ReturnType {
1816    assert(@typeInfo(ReturnType).int.bits >= bytes.len * 8);
1817    const bits = @typeInfo(ReturnType).int.bits;
1818    const signedness = @typeInfo(ReturnType).int.signedness;
1819    const WorkType = std.meta.Int(signedness, @max(16, bits));
1820    var result: WorkType = 0;
1821    switch (endian) {
1822        .big => {
1823            for (bytes) |b| {
1824                result = (result << 8) | b;
1825            }
1826        },
1827        .little => {
1828            const ShiftType = math.Log2Int(WorkType);
1829            for (bytes, 0..) |b, index| {
1830                result = result | (@as(WorkType, b) << @as(ShiftType, @intCast(index * 8)));
1831            }
1832        },
1833    }
1834    return @truncate(result);
1835}
1836
1837test readVarInt {
1838    try testing.expect(readVarInt(u0, &[_]u8{}, .big) == 0x0);
1839    try testing.expect(readVarInt(u0, &[_]u8{}, .little) == 0x0);
1840    try testing.expect(readVarInt(u8, &[_]u8{0x12}, .big) == 0x12);
1841    try testing.expect(readVarInt(u8, &[_]u8{0xde}, .little) == 0xde);
1842    try testing.expect(readVarInt(u16, &[_]u8{ 0x12, 0x34 }, .big) == 0x1234);
1843    try testing.expect(readVarInt(u16, &[_]u8{ 0x12, 0x34 }, .little) == 0x3412);
1844
1845    try testing.expect(readVarInt(i8, &[_]u8{0xff}, .big) == -1);
1846    try testing.expect(readVarInt(i8, &[_]u8{0xfe}, .little) == -2);
1847    try testing.expect(readVarInt(i16, &[_]u8{ 0xff, 0xfd }, .big) == -3);
1848    try testing.expect(readVarInt(i16, &[_]u8{ 0xfc, 0xff }, .little) == -4);
1849
1850    // Return type can be oversized (bytes.len * 8 < @typeInfo(ReturnType).int.bits)
1851    try testing.expect(readVarInt(u9, &[_]u8{0x12}, .little) == 0x12);
1852    try testing.expect(readVarInt(u9, &[_]u8{0xde}, .big) == 0xde);
1853    try testing.expect(readVarInt(u80, &[_]u8{ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x24 }, .big) == 0x123456789abcdef024);
1854    try testing.expect(readVarInt(u80, &[_]u8{ 0xec, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe }, .little) == 0xfedcba9876543210ec);
1855
1856    try testing.expect(readVarInt(i9, &[_]u8{0xff}, .big) == 0xff);
1857    try testing.expect(readVarInt(i9, &[_]u8{0xfe}, .little) == 0xfe);
1858}
1859
1860/// Loads an integer from packed memory with provided bit_count, bit_offset, and signedness.
1861/// Asserts that T is large enough to store the read value.
1862pub fn readVarPackedInt(
1863    comptime T: type,
1864    bytes: []const u8,
1865    bit_offset: usize,
1866    bit_count: usize,
1867    endian: std.builtin.Endian,
1868    signedness: std.builtin.Signedness,
1869) T {
1870    const uN = std.meta.Int(.unsigned, @bitSizeOf(T));
1871    const iN = std.meta.Int(.signed, @bitSizeOf(T));
1872    const Log2N = std.math.Log2Int(T);
1873
1874    const read_size = (bit_count + (bit_offset % 8) + 7) / 8;
1875    const bit_shift = @as(u3, @intCast(bit_offset % 8));
1876    const pad = @as(Log2N, @intCast(@bitSizeOf(T) - bit_count));
1877
1878    const lowest_byte = switch (endian) {
1879        .big => bytes.len - (bit_offset / 8) - read_size,
1880        .little => bit_offset / 8,
1881    };
1882    const read_bytes = bytes[lowest_byte..][0..read_size];
1883
1884    if (@bitSizeOf(T) <= 8) {
1885        // These are the same shifts/masks we perform below, but adds `@truncate`/`@intCast`
1886        // where needed since int is smaller than a byte.
1887        const value = if (read_size == 1) b: {
1888            break :b @as(uN, @truncate(read_bytes[0] >> bit_shift));
1889        } else b: {
1890            const i: u1 = @intFromBool(endian == .big);
1891            const head = @as(uN, @truncate(read_bytes[i] >> bit_shift));
1892            const tail_shift = @as(Log2N, @intCast(@as(u4, 8) - bit_shift));
1893            const tail = @as(uN, @truncate(read_bytes[1 - i]));
1894            break :b (tail << tail_shift) | head;
1895        };
1896        switch (signedness) {
1897            .signed => return @as(T, @intCast((@as(iN, @bitCast(value)) << pad) >> pad)),
1898            .unsigned => return @as(T, @intCast((@as(uN, @bitCast(value)) << pad) >> pad)),
1899        }
1900    }
1901
1902    // Copy the value out (respecting endianness), accounting for bit_shift
1903    var int: uN = 0;
1904    switch (endian) {
1905        .big => {
1906            for (read_bytes[0 .. read_size - 1]) |elem| {
1907                int = elem | (int << 8);
1908            }
1909            int = (read_bytes[read_size - 1] >> bit_shift) | (int << (@as(u4, 8) - bit_shift));
1910        },
1911        .little => {
1912            int = read_bytes[0] >> bit_shift;
1913            for (read_bytes[1..], 0..) |elem, i| {
1914                int |= (@as(uN, elem) << @as(Log2N, @intCast((8 * (i + 1) - bit_shift))));
1915            }
1916        },
1917    }
1918    switch (signedness) {
1919        .signed => return @as(T, @intCast((@as(iN, @bitCast(int)) << pad) >> pad)),
1920        .unsigned => return @as(T, @intCast((@as(uN, @bitCast(int)) << pad) >> pad)),
1921    }
1922}
1923
1924test readVarPackedInt {
1925    const T = packed struct(u16) { a: u3, b: u7, c: u6 };
1926    var st = T{ .a = 1, .b = 2, .c = 4 };
1927    const b_field = readVarPackedInt(u64, std.mem.asBytes(&st), @bitOffsetOf(T, "b"), 7, builtin.cpu.arch.endian(), .unsigned);
1928    try std.testing.expectEqual(st.b, b_field);
1929}
1930
1931/// Reads an integer from memory with bit count specified by T.
1932/// The bit count of T must be evenly divisible by 8.
1933/// This function cannot fail and cannot cause undefined behavior.
1934pub inline fn readInt(comptime T: type, buffer: *const [@divExact(@typeInfo(T).int.bits, 8)]u8, endian: Endian) T {
1935    const value: T = @bitCast(buffer.*);
1936    return if (endian == native_endian) value else @byteSwap(value);
1937}
1938
1939test readInt {
1940    try testing.expect(readInt(u0, &[_]u8{}, .big) == 0x0);
1941    try testing.expect(readInt(u0, &[_]u8{}, .little) == 0x0);
1942
1943    try testing.expect(readInt(u8, &[_]u8{0x32}, .big) == 0x32);
1944    try testing.expect(readInt(u8, &[_]u8{0x12}, .little) == 0x12);
1945
1946    try testing.expect(readInt(u16, &[_]u8{ 0x12, 0x34 }, .big) == 0x1234);
1947    try testing.expect(readInt(u16, &[_]u8{ 0x12, 0x34 }, .little) == 0x3412);
1948
1949    try testing.expect(readInt(u72, &[_]u8{ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x24 }, .big) == 0x123456789abcdef024);
1950    try testing.expect(readInt(u72, &[_]u8{ 0xec, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe }, .little) == 0xfedcba9876543210ec);
1951
1952    try testing.expect(readInt(i8, &[_]u8{0xff}, .big) == -1);
1953    try testing.expect(readInt(i8, &[_]u8{0xfe}, .little) == -2);
1954
1955    try testing.expect(readInt(i16, &[_]u8{ 0xff, 0xfd }, .big) == -3);
1956    try testing.expect(readInt(i16, &[_]u8{ 0xfc, 0xff }, .little) == -4);
1957
1958    try moreReadIntTests();
1959    try comptime moreReadIntTests();
1960}
1961
1962fn readPackedIntLittle(comptime T: type, bytes: []const u8, bit_offset: usize) T {
1963    const uN = std.meta.Int(.unsigned, @bitSizeOf(T));
1964    const Log2N = std.math.Log2Int(T);
1965
1966    const bit_count = @as(usize, @bitSizeOf(T));
1967    const bit_shift = @as(u3, @intCast(bit_offset % 8));
1968
1969    const load_size = (bit_count + 7) / 8;
1970    const load_tail_bits = @as(u3, @intCast((load_size * 8) - bit_count));
1971    const LoadInt = std.meta.Int(.unsigned, load_size * 8);
1972
1973    if (bit_count == 0)
1974        return 0;
1975
1976    // Read by loading a LoadInt, and then follow it up with a 1-byte read
1977    // of the tail if bit_offset pushed us over a byte boundary.
1978    const read_bytes = bytes[bit_offset / 8 ..];
1979    const val = @as(uN, @truncate(readInt(LoadInt, read_bytes[0..load_size], .little) >> bit_shift));
1980    if (bit_shift > load_tail_bits) {
1981        const tail_bits = @as(Log2N, @intCast(bit_shift - load_tail_bits));
1982        const tail_byte = read_bytes[load_size];
1983        const tail_truncated = if (bit_count < 8) @as(uN, @truncate(tail_byte)) else @as(uN, tail_byte);
1984        return @as(T, @bitCast(val | (tail_truncated << (@as(Log2N, @truncate(bit_count)) -% tail_bits))));
1985    } else return @as(T, @bitCast(val));
1986}
1987
1988fn readPackedIntBig(comptime T: type, bytes: []const u8, bit_offset: usize) T {
1989    const uN = std.meta.Int(.unsigned, @bitSizeOf(T));
1990    const Log2N = std.math.Log2Int(T);
1991
1992    const bit_count = @as(usize, @bitSizeOf(T));
1993    const bit_shift = @as(u3, @intCast(bit_offset % 8));
1994    const byte_count = (@as(usize, bit_shift) + bit_count + 7) / 8;
1995
1996    const load_size = (bit_count + 7) / 8;
1997    const load_tail_bits = @as(u3, @intCast((load_size * 8) - bit_count));
1998    const LoadInt = std.meta.Int(.unsigned, load_size * 8);
1999
2000    if (bit_count == 0)
2001        return 0;
2002
2003    // Read by loading a LoadInt, and then follow it up with a 1-byte read
2004    // of the tail if bit_offset pushed us over a byte boundary.
2005    const end = bytes.len - (bit_offset / 8);
2006    const read_bytes = bytes[(end - byte_count)..end];
2007    const val = @as(uN, @truncate(readInt(LoadInt, bytes[(end - load_size)..end][0..load_size], .big) >> bit_shift));
2008    if (bit_shift > load_tail_bits) {
2009        const tail_bits = @as(Log2N, @intCast(bit_shift - load_tail_bits));
2010        const tail_byte = if (bit_count < 8) @as(uN, @truncate(read_bytes[0])) else @as(uN, read_bytes[0]);
2011        return @as(T, @bitCast(val | (tail_byte << (@as(Log2N, @truncate(bit_count)) -% tail_bits))));
2012    } else return @as(T, @bitCast(val));
2013}
2014
2015pub const readPackedIntNative = switch (native_endian) {
2016    .little => readPackedIntLittle,
2017    .big => readPackedIntBig,
2018};
2019
2020pub const readPackedIntForeign = switch (native_endian) {
2021    .little => readPackedIntBig,
2022    .big => readPackedIntLittle,
2023};
2024
2025/// Loads an integer from packed memory.
2026/// Asserts that buffer contains at least bit_offset + @bitSizeOf(T) bits.
2027pub fn readPackedInt(comptime T: type, bytes: []const u8, bit_offset: usize, endian: Endian) T {
2028    switch (endian) {
2029        .little => return readPackedIntLittle(T, bytes, bit_offset),
2030        .big => return readPackedIntBig(T, bytes, bit_offset),
2031    }
2032}
2033
2034test readPackedInt {
2035    const T = packed struct(u16) { a: u3, b: u7, c: u6 };
2036    var st = T{ .a = 1, .b = 2, .c = 4 };
2037    const b_field = readPackedInt(u7, std.mem.asBytes(&st), @bitOffsetOf(T, "b"), builtin.cpu.arch.endian());
2038    try std.testing.expectEqual(st.b, b_field);
2039}
2040
2041test "comptime read/write int" {
2042    comptime {
2043        var bytes: [2]u8 = undefined;
2044        writeInt(u16, &bytes, 0x1234, .little);
2045        const result = readInt(u16, &bytes, .big);
2046        try testing.expect(result == 0x3412);
2047    }
2048    comptime {
2049        var bytes: [2]u8 = undefined;
2050        writeInt(u16, &bytes, 0x1234, .big);
2051        const result = readInt(u16, &bytes, .little);
2052        try testing.expect(result == 0x3412);
2053    }
2054}
2055
2056/// Writes an integer to memory, storing it in twos-complement.
2057/// This function always succeeds, has defined behavior for all inputs, but
2058/// the integer bit width must be divisible by 8.
2059pub inline fn writeInt(comptime T: type, buffer: *[@divExact(@typeInfo(T).int.bits, 8)]u8, value: T, endian: Endian) void {
2060    buffer.* = @bitCast(if (endian == native_endian) value else @byteSwap(value));
2061}
2062
2063test writeInt {
2064    var buf0: [0]u8 = undefined;
2065    var buf1: [1]u8 = undefined;
2066    var buf2: [2]u8 = undefined;
2067    var buf9: [9]u8 = undefined;
2068
2069    writeInt(u0, &buf0, 0x0, .big);
2070    try testing.expect(eql(u8, buf0[0..], &[_]u8{}));
2071    writeInt(u0, &buf0, 0x0, .little);
2072    try testing.expect(eql(u8, buf0[0..], &[_]u8{}));
2073
2074    writeInt(u8, &buf1, 0x12, .big);
2075    try testing.expect(eql(u8, buf1[0..], &[_]u8{0x12}));
2076    writeInt(u8, &buf1, 0x34, .little);
2077    try testing.expect(eql(u8, buf1[0..], &[_]u8{0x34}));
2078
2079    writeInt(u16, &buf2, 0x1234, .big);
2080    try testing.expect(eql(u8, buf2[0..], &[_]u8{ 0x12, 0x34 }));
2081    writeInt(u16, &buf2, 0x5678, .little);
2082    try testing.expect(eql(u8, buf2[0..], &[_]u8{ 0x78, 0x56 }));
2083
2084    writeInt(u72, &buf9, 0x123456789abcdef024, .big);
2085    try testing.expect(eql(u8, buf9[0..], &[_]u8{ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x24 }));
2086    writeInt(u72, &buf9, 0xfedcba9876543210ec, .little);
2087    try testing.expect(eql(u8, buf9[0..], &[_]u8{ 0xec, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe }));
2088
2089    writeInt(i8, &buf1, -1, .big);
2090    try testing.expect(eql(u8, buf1[0..], &[_]u8{0xff}));
2091    writeInt(i8, &buf1, -2, .little);
2092    try testing.expect(eql(u8, buf1[0..], &[_]u8{0xfe}));
2093
2094    writeInt(i16, &buf2, -3, .big);
2095    try testing.expect(eql(u8, buf2[0..], &[_]u8{ 0xff, 0xfd }));
2096    writeInt(i16, &buf2, -4, .little);
2097    try testing.expect(eql(u8, buf2[0..], &[_]u8{ 0xfc, 0xff }));
2098}
2099
2100fn writePackedIntLittle(comptime T: type, bytes: []u8, bit_offset: usize, value: T) void {
2101    const uN = std.meta.Int(.unsigned, @bitSizeOf(T));
2102    const Log2N = std.math.Log2Int(T);
2103
2104    const bit_count = @as(usize, @bitSizeOf(T));
2105    const bit_shift = @as(u3, @intCast(bit_offset % 8));
2106
2107    const store_size = (@bitSizeOf(T) + 7) / 8;
2108    const store_tail_bits = @as(u3, @intCast((store_size * 8) - bit_count));
2109    const StoreInt = std.meta.Int(.unsigned, store_size * 8);
2110
2111    if (bit_count == 0)
2112        return;
2113
2114    // Write by storing a StoreInt, and then follow it up with a 1-byte tail
2115    // if bit_offset pushed us over a byte boundary.
2116    const write_bytes = bytes[bit_offset / 8 ..];
2117    const head = write_bytes[0] & ((@as(u8, 1) << bit_shift) - 1);
2118
2119    var write_value = (@as(StoreInt, @as(uN, @bitCast(value))) << bit_shift) | @as(StoreInt, @intCast(head));
2120    if (bit_shift > store_tail_bits) {
2121        const tail_len = @as(Log2N, @intCast(bit_shift - store_tail_bits));
2122        write_bytes[store_size] &= ~((@as(u8, 1) << @as(u3, @intCast(tail_len))) - 1);
2123        write_bytes[store_size] |= @as(u8, @intCast((@as(uN, @bitCast(value)) >> (@as(Log2N, @truncate(bit_count)) -% tail_len))));
2124    } else if (bit_shift < store_tail_bits) {
2125        const tail_len = store_tail_bits - bit_shift;
2126        const tail = write_bytes[store_size - 1] & (@as(u8, 0xfe) << (7 - tail_len));
2127        write_value |= @as(StoreInt, tail) << (8 * (store_size - 1));
2128    }
2129
2130    writeInt(StoreInt, write_bytes[0..store_size], write_value, .little);
2131}
2132
2133fn writePackedIntBig(comptime T: type, bytes: []u8, bit_offset: usize, value: T) void {
2134    const uN = std.meta.Int(.unsigned, @bitSizeOf(T));
2135    const Log2N = std.math.Log2Int(T);
2136
2137    const bit_count = @as(usize, @bitSizeOf(T));
2138    const bit_shift = @as(u3, @intCast(bit_offset % 8));
2139    const byte_count = (bit_shift + bit_count + 7) / 8;
2140
2141    const store_size = (@bitSizeOf(T) + 7) / 8;
2142    const store_tail_bits = @as(u3, @intCast((store_size * 8) - bit_count));
2143    const StoreInt = std.meta.Int(.unsigned, store_size * 8);
2144
2145    if (bit_count == 0)
2146        return;
2147
2148    // Write by storing a StoreInt, and then follow it up with a 1-byte tail
2149    // if bit_offset pushed us over a byte boundary.
2150    const end = bytes.len - (bit_offset / 8);
2151    const write_bytes = bytes[(end - byte_count)..end];
2152    const head = write_bytes[byte_count - 1] & ((@as(u8, 1) << bit_shift) - 1);
2153
2154    var write_value = (@as(StoreInt, @as(uN, @bitCast(value))) << bit_shift) | @as(StoreInt, @intCast(head));
2155    if (bit_shift > store_tail_bits) {
2156        const tail_len = @as(Log2N, @intCast(bit_shift - store_tail_bits));
2157        write_bytes[0] &= ~((@as(u8, 1) << @as(u3, @intCast(tail_len))) - 1);
2158        write_bytes[0] |= @as(u8, @intCast((@as(uN, @bitCast(value)) >> (@as(Log2N, @truncate(bit_count)) -% tail_len))));
2159    } else if (bit_shift < store_tail_bits) {
2160        const tail_len = store_tail_bits - bit_shift;
2161        const tail = write_bytes[0] & (@as(u8, 0xfe) << (7 - tail_len));
2162        write_value |= @as(StoreInt, tail) << (8 * (store_size - 1));
2163    }
2164
2165    writeInt(StoreInt, write_bytes[(byte_count - store_size)..][0..store_size], write_value, .big);
2166}
2167
2168pub const writePackedIntNative = switch (native_endian) {
2169    .little => writePackedIntLittle,
2170    .big => writePackedIntBig,
2171};
2172
2173pub const writePackedIntForeign = switch (native_endian) {
2174    .little => writePackedIntBig,
2175    .big => writePackedIntLittle,
2176};
2177
2178/// Stores an integer to packed memory.
2179/// Asserts that buffer contains at least bit_offset + @bitSizeOf(T) bits.
2180pub fn writePackedInt(comptime T: type, bytes: []u8, bit_offset: usize, value: T, endian: Endian) void {
2181    switch (endian) {
2182        .little => writePackedIntLittle(T, bytes, bit_offset, value),
2183        .big => writePackedIntBig(T, bytes, bit_offset, value),
2184    }
2185}
2186
2187test writePackedInt {
2188    const T = packed struct(u16) { a: u3, b: u7, c: u6 };
2189    var st = T{ .a = 1, .b = 2, .c = 4 };
2190    writePackedInt(u7, std.mem.asBytes(&st), @bitOffsetOf(T, "b"), 0x7f, builtin.cpu.arch.endian());
2191    try std.testing.expectEqual(T{ .a = 1, .b = 0x7f, .c = 4 }, st);
2192}
2193
2194/// Stores an integer to packed memory with provided bit_offset, bit_count, and signedness.
2195/// If negative, the written value is sign-extended.
2196pub fn writeVarPackedInt(bytes: []u8, bit_offset: usize, bit_count: usize, value: anytype, endian: std.builtin.Endian) void {
2197    const T = @TypeOf(value);
2198    const uN = std.meta.Int(.unsigned, @bitSizeOf(T));
2199
2200    const bit_shift = @as(u3, @intCast(bit_offset % 8));
2201    const write_size = (bit_count + bit_shift + 7) / 8;
2202    const lowest_byte = switch (endian) {
2203        .big => bytes.len - (bit_offset / 8) - write_size,
2204        .little => bit_offset / 8,
2205    };
2206    const write_bytes = bytes[lowest_byte..][0..write_size];
2207
2208    if (write_size == 0) {
2209        return;
2210    } else if (write_size == 1) {
2211        // Single byte writes are handled specially, since we need to mask bits
2212        // on both ends of the byte.
2213        const mask = (@as(u8, 0xff) >> @as(u3, @intCast(8 - bit_count)));
2214        const new_bits = @as(u8, @intCast(@as(uN, @bitCast(value)) & mask)) << bit_shift;
2215        write_bytes[0] = (write_bytes[0] & ~(mask << bit_shift)) | new_bits;
2216        return;
2217    }
2218
2219    var remaining: T = value;
2220
2221    // Iterate bytes forward for Little-endian, backward for Big-endian
2222    const delta: i2 = if (endian == .big) -1 else 1;
2223    const start = if (endian == .big) @as(isize, @intCast(write_bytes.len - 1)) else 0;
2224
2225    var i: isize = start; // isize for signed index arithmetic
2226
2227    // Write first byte, using a mask to protects bits preceding bit_offset
2228    const head_mask = @as(u8, 0xff) >> bit_shift;
2229    write_bytes[@intCast(i)] &= ~(head_mask << bit_shift);
2230    write_bytes[@intCast(i)] |= @as(u8, @intCast(@as(uN, @bitCast(remaining)) & head_mask)) << bit_shift;
2231    remaining = math.shr(T, remaining, @as(u4, 8) - bit_shift);
2232    i += delta;
2233
2234    // Write bytes[1..bytes.len - 1]
2235    if (@bitSizeOf(T) > 8) {
2236        const loop_end = start + delta * (@as(isize, @intCast(write_size)) - 1);
2237        while (i != loop_end) : (i += delta) {
2238            write_bytes[@as(usize, @intCast(i))] = @as(u8, @truncate(@as(uN, @bitCast(remaining))));
2239            remaining >>= 8;
2240        }
2241    }
2242
2243    // Write last byte, using a mask to protect bits following bit_offset + bit_count
2244    const following_bits = -%@as(u3, @truncate(bit_shift + bit_count));
2245    const tail_mask = (@as(u8, 0xff) << following_bits) >> following_bits;
2246    write_bytes[@as(usize, @intCast(i))] &= ~tail_mask;
2247    write_bytes[@as(usize, @intCast(i))] |= @as(u8, @intCast(@as(uN, @bitCast(remaining)) & tail_mask));
2248}
2249
2250test writeVarPackedInt {
2251    const T = packed struct(u16) { a: u3, b: u7, c: u6 };
2252    var st = T{ .a = 1, .b = 2, .c = 4 };
2253    const value: u64 = 0x7f;
2254    writeVarPackedInt(std.mem.asBytes(&st), @bitOffsetOf(T, "b"), 7, value, builtin.cpu.arch.endian());
2255    try testing.expectEqual(T{ .a = 1, .b = value, .c = 4 }, st);
2256}
2257
2258/// Swap the byte order of all the members of the fields of a struct
2259/// (Changing their endianness)
2260pub fn byteSwapAllFields(comptime S: type, ptr: *S) void {
2261    switch (@typeInfo(S)) {
2262        .@"struct" => {
2263            inline for (std.meta.fields(S)) |f| {
2264                switch (@typeInfo(f.type)) {
2265                    .@"struct" => |struct_info| if (struct_info.backing_integer) |Int| {
2266                        @field(ptr, f.name) = @bitCast(@byteSwap(@as(Int, @bitCast(@field(ptr, f.name)))));
2267                    } else {
2268                        byteSwapAllFields(f.type, &@field(ptr, f.name));
2269                    },
2270                    .@"union", .array => byteSwapAllFields(f.type, &@field(ptr, f.name)),
2271                    .@"enum" => {
2272                        @field(ptr, f.name) = @enumFromInt(@byteSwap(@intFromEnum(@field(ptr, f.name))));
2273                    },
2274                    .bool => {},
2275                    .float => |float_info| {
2276                        @field(ptr, f.name) = @bitCast(@byteSwap(@as(std.meta.Int(.unsigned, float_info.bits), @bitCast(@field(ptr, f.name)))));
2277                    },
2278                    else => {
2279                        @field(ptr, f.name) = @byteSwap(@field(ptr, f.name));
2280                    },
2281                }
2282            }
2283        },
2284        .@"union" => |union_info| {
2285            if (union_info.tag_type != null) {
2286                @compileError("byteSwapAllFields expects an untagged union");
2287            }
2288
2289            const first_size = @bitSizeOf(union_info.fields[0].type);
2290            inline for (union_info.fields) |field| {
2291                if (@bitSizeOf(field.type) != first_size) {
2292                    @compileError("Unable to byte-swap unions with varying field sizes");
2293                }
2294            }
2295
2296            const BackingInt = std.meta.Int(.unsigned, @bitSizeOf(S));
2297            ptr.* = @bitCast(@byteSwap(@as(BackingInt, @bitCast(ptr.*))));
2298        },
2299        .array => |info| {
2300            byteSwapAllElements(info.child, ptr);
2301        },
2302        else => {
2303            ptr.* = @byteSwap(ptr.*);
2304        },
2305    }
2306}
2307
2308test byteSwapAllFields {
2309    const T = extern struct {
2310        f0: u8,
2311        f1: u16,
2312        f2: u32,
2313        f3: [1]u8,
2314        f4: bool,
2315        f5: f32,
2316        f6: extern union { f0: u16, f1: u16 },
2317    };
2318    const K = extern struct {
2319        f0: u8,
2320        f1: T,
2321        f2: u16,
2322        f3: [1]u8,
2323        f4: bool,
2324        f5: f32,
2325    };
2326    var s = T{
2327        .f0 = 0x12,
2328        .f1 = 0x1234,
2329        .f2 = 0x12345678,
2330        .f3 = .{0x12},
2331        .f4 = true,
2332        .f5 = @as(f32, @bitCast(@as(u32, 0x4640e400))),
2333        .f6 = .{ .f0 = 0x1234 },
2334    };
2335    var k = K{
2336        .f0 = 0x12,
2337        .f1 = s,
2338        .f2 = 0x1234,
2339        .f3 = .{0x12},
2340        .f4 = false,
2341        .f5 = @as(f32, @bitCast(@as(u32, 0x45d42800))),
2342    };
2343    byteSwapAllFields(T, &s);
2344    byteSwapAllFields(K, &k);
2345    try std.testing.expectEqual(T{
2346        .f0 = 0x12,
2347        .f1 = 0x3412,
2348        .f2 = 0x78563412,
2349        .f3 = .{0x12},
2350        .f4 = true,
2351        .f5 = @as(f32, @bitCast(@as(u32, 0x00e44046))),
2352        .f6 = .{ .f0 = 0x3412 },
2353    }, s);
2354    try std.testing.expectEqual(K{
2355        .f0 = 0x12,
2356        .f1 = s,
2357        .f2 = 0x3412,
2358        .f3 = .{0x12},
2359        .f4 = false,
2360        .f5 = @as(f32, @bitCast(@as(u32, 0x0028d445))),
2361    }, k);
2362}
2363
2364/// Reverses the byte order of all elements in a slice.
2365/// Handles structs, unions, arrays, enums, floats, and integers recursively.
2366/// Useful for converting between little-endian and big-endian representations.
2367pub fn byteSwapAllElements(comptime Elem: type, slice: []Elem) void {
2368    for (slice) |*elem| {
2369        switch (@typeInfo(@TypeOf(elem.*))) {
2370            .@"struct", .@"union", .array => byteSwapAllFields(@TypeOf(elem.*), elem),
2371            .@"enum" => {
2372                elem.* = @enumFromInt(@byteSwap(@intFromEnum(elem.*)));
2373            },
2374            .bool => {},
2375            .float => |float_info| {
2376                elem.* = @bitCast(@byteSwap(@as(std.meta.Int(.unsigned, float_info.bits), @bitCast(elem.*))));
2377            },
2378            else => {
2379                elem.* = @byteSwap(elem.*);
2380            },
2381        }
2382    }
2383}
2384
2385/// Returns an iterator that iterates over the slices of `buffer` that are not
2386/// any of the items in `delimiters`.
2387///
2388/// `tokenizeAny(u8, "   abc|def ||  ghi  ", " |")` will return slices
2389/// for "abc", "def", "ghi", null, in that order.
2390///
2391/// If `buffer` is empty, the iterator will return null.
2392/// If none of `delimiters` exist in buffer,
2393/// the iterator will return `buffer`, null, in that order.
2394///
2395/// See also: `tokenizeSequence`, `tokenizeScalar`,
2396///           `splitSequence`,`splitAny`, `splitScalar`,
2397///           `splitBackwardsSequence`, `splitBackwardsAny`, and `splitBackwardsScalar`
2398pub fn tokenizeAny(comptime T: type, buffer: []const T, delimiters: []const T) TokenIterator(T, .any) {
2399    return .{
2400        .index = 0,
2401        .buffer = buffer,
2402        .delimiter = delimiters,
2403    };
2404}
2405
2406/// Returns an iterator that iterates over the slices of `buffer` that are not
2407/// the sequence in `delimiter`.
2408///
2409/// `tokenizeSequence(u8, "<>abc><def<><>ghi", "<>")` will return slices
2410/// for "abc><def", "ghi", null, in that order.
2411///
2412/// If `buffer` is empty, the iterator will return null.
2413/// If `delimiter` does not exist in buffer,
2414/// the iterator will return `buffer`, null, in that order.
2415/// The delimiter length must not be zero.
2416///
2417/// See also: `tokenizeAny`, `tokenizeScalar`,
2418///           `splitSequence`,`splitAny`, and `splitScalar`
2419///           `splitBackwardsSequence`, `splitBackwardsAny`, and `splitBackwardsScalar`
2420pub fn tokenizeSequence(comptime T: type, buffer: []const T, delimiter: []const T) TokenIterator(T, .sequence) {
2421    assert(delimiter.len != 0);
2422    return .{
2423        .index = 0,
2424        .buffer = buffer,
2425        .delimiter = delimiter,
2426    };
2427}
2428
2429/// Returns an iterator that iterates over the slices of `buffer` that are not
2430/// `delimiter`.
2431///
2432/// `tokenizeScalar(u8, "   abc def     ghi  ", ' ')` will return slices
2433/// for "abc", "def", "ghi", null, in that order.
2434///
2435/// If `buffer` is empty, the iterator will return null.
2436/// If `delimiter` does not exist in buffer,
2437/// the iterator will return `buffer`, null, in that order.
2438///
2439/// See also: `tokenizeAny`, `tokenizeSequence`,
2440///           `splitSequence`,`splitAny`, and `splitScalar`
2441///           `splitBackwardsSequence`, `splitBackwardsAny`, and `splitBackwardsScalar`
2442pub fn tokenizeScalar(comptime T: type, buffer: []const T, delimiter: T) TokenIterator(T, .scalar) {
2443    return .{
2444        .index = 0,
2445        .buffer = buffer,
2446        .delimiter = delimiter,
2447    };
2448}
2449
2450test tokenizeScalar {
2451    var it = tokenizeScalar(u8, "   abc def   ghi  ", ' ');
2452    try testing.expect(eql(u8, it.next().?, "abc"));
2453    try testing.expect(eql(u8, it.peek().?, "def"));
2454    try testing.expect(eql(u8, it.next().?, "def"));
2455    try testing.expect(eql(u8, it.next().?, "ghi"));
2456    try testing.expect(it.next() == null);
2457
2458    it = tokenizeScalar(u8, "..\\bob", '\\');
2459    try testing.expect(eql(u8, it.next().?, ".."));
2460    try testing.expect(eql(u8, "..", "..\\bob"[0..it.index]));
2461    try testing.expect(eql(u8, it.next().?, "bob"));
2462    try testing.expect(it.next() == null);
2463
2464    it = tokenizeScalar(u8, "//a/b", '/');
2465    try testing.expect(eql(u8, it.next().?, "a"));
2466    try testing.expect(eql(u8, it.next().?, "b"));
2467    try testing.expect(eql(u8, "//a/b", "//a/b"[0..it.index]));
2468    try testing.expect(it.next() == null);
2469
2470    it = tokenizeScalar(u8, "|", '|');
2471    try testing.expect(it.next() == null);
2472    try testing.expect(it.peek() == null);
2473
2474    it = tokenizeScalar(u8, "", '|');
2475    try testing.expect(it.next() == null);
2476    try testing.expect(it.peek() == null);
2477
2478    it = tokenizeScalar(u8, "hello", ' ');
2479    try testing.expect(eql(u8, it.next().?, "hello"));
2480    try testing.expect(it.next() == null);
2481
2482    var it16 = tokenizeScalar(
2483        u16,
2484        std.unicode.utf8ToUtf16LeStringLiteral("hello"),
2485        ' ',
2486    );
2487    try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("hello")));
2488    try testing.expect(it16.next() == null);
2489}
2490
2491test tokenizeAny {
2492    var it = tokenizeAny(u8, "a|b,c/d e", " /,|");
2493    try testing.expect(eql(u8, it.next().?, "a"));
2494    try testing.expect(eql(u8, it.peek().?, "b"));
2495    try testing.expect(eql(u8, it.next().?, "b"));
2496    try testing.expect(eql(u8, it.next().?, "c"));
2497    try testing.expect(eql(u8, it.next().?, "d"));
2498    try testing.expect(eql(u8, it.next().?, "e"));
2499    try testing.expect(it.next() == null);
2500    try testing.expect(it.peek() == null);
2501
2502    it = tokenizeAny(u8, "hello", "");
2503    try testing.expect(eql(u8, it.next().?, "hello"));
2504    try testing.expect(it.next() == null);
2505
2506    var it16 = tokenizeAny(
2507        u16,
2508        std.unicode.utf8ToUtf16LeStringLiteral("a|b,c/d e"),
2509        std.unicode.utf8ToUtf16LeStringLiteral(" /,|"),
2510    );
2511    try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("a")));
2512    try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("b")));
2513    try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("c")));
2514    try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("d")));
2515    try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("e")));
2516    try testing.expect(it16.next() == null);
2517}
2518
2519test tokenizeSequence {
2520    var it = tokenizeSequence(u8, "a<>b<><>c><>d><", "<>");
2521    try testing.expectEqualStrings("a", it.next().?);
2522    try testing.expectEqualStrings("b", it.peek().?);
2523    try testing.expectEqualStrings("b", it.next().?);
2524    try testing.expectEqualStrings("c>", it.next().?);
2525    try testing.expectEqualStrings("d><", it.next().?);
2526    try testing.expect(it.next() == null);
2527    try testing.expect(it.peek() == null);
2528
2529    var it16 = tokenizeSequence(
2530        u16,
2531        std.unicode.utf8ToUtf16LeStringLiteral("a<>b<><>c><>d><"),
2532        std.unicode.utf8ToUtf16LeStringLiteral("<>"),
2533    );
2534    try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("a")));
2535    try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("b")));
2536    try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("c>")));
2537    try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("d><")));
2538    try testing.expect(it16.next() == null);
2539}
2540
2541test "tokenize (reset)" {
2542    {
2543        var it = tokenizeAny(u8, "   abc def   ghi  ", " ");
2544        try testing.expect(eql(u8, it.next().?, "abc"));
2545        try testing.expect(eql(u8, it.next().?, "def"));
2546        try testing.expect(eql(u8, it.next().?, "ghi"));
2547
2548        it.reset();
2549
2550        try testing.expect(eql(u8, it.next().?, "abc"));
2551        try testing.expect(eql(u8, it.next().?, "def"));
2552        try testing.expect(eql(u8, it.next().?, "ghi"));
2553        try testing.expect(it.next() == null);
2554    }
2555    {
2556        var it = tokenizeSequence(u8, "<><>abc<>def<><>ghi<>", "<>");
2557        try testing.expect(eql(u8, it.next().?, "abc"));
2558        try testing.expect(eql(u8, it.next().?, "def"));
2559        try testing.expect(eql(u8, it.next().?, "ghi"));
2560
2561        it.reset();
2562
2563        try testing.expect(eql(u8, it.next().?, "abc"));
2564        try testing.expect(eql(u8, it.next().?, "def"));
2565        try testing.expect(eql(u8, it.next().?, "ghi"));
2566        try testing.expect(it.next() == null);
2567    }
2568    {
2569        var it = tokenizeScalar(u8, "   abc def   ghi  ", ' ');
2570        try testing.expect(eql(u8, it.next().?, "abc"));
2571        try testing.expect(eql(u8, it.next().?, "def"));
2572        try testing.expect(eql(u8, it.next().?, "ghi"));
2573
2574        it.reset();
2575
2576        try testing.expect(eql(u8, it.next().?, "abc"));
2577        try testing.expect(eql(u8, it.next().?, "def"));
2578        try testing.expect(eql(u8, it.next().?, "ghi"));
2579        try testing.expect(it.next() == null);
2580    }
2581}
2582
2583/// Returns an iterator that iterates over the slices of `buffer` that
2584/// are separated by the byte sequence in `delimiter`.
2585///
2586/// `splitSequence(u8, "abc||def||||ghi", "||")` will return slices
2587/// for "abc", "def", "", "ghi", null, in that order.
2588///
2589/// If `delimiter` does not exist in buffer,
2590/// the iterator will return `buffer`, null, in that order.
2591/// The delimiter length must not be zero.
2592///
2593/// See also: `splitAny`, `splitScalar`, `splitBackwardsSequence`,
2594///           `splitBackwardsAny`,`splitBackwardsScalar`,
2595///           `tokenizeAny`, `tokenizeSequence`, and `tokenizeScalar`.
2596pub fn splitSequence(comptime T: type, buffer: []const T, delimiter: []const T) SplitIterator(T, .sequence) {
2597    assert(delimiter.len != 0);
2598    return .{
2599        .index = 0,
2600        .buffer = buffer,
2601        .delimiter = delimiter,
2602    };
2603}
2604
2605/// Returns an iterator that iterates over the slices of `buffer` that
2606/// are separated by any item in `delimiters`.
2607///
2608/// `splitAny(u8, "abc,def||ghi", "|,")` will return slices
2609/// for "abc", "def", "", "ghi", null, in that order.
2610///
2611/// If none of `delimiters` exist in buffer,
2612/// the iterator will return `buffer`, null, in that order.
2613///
2614/// See also: `splitSequence`, `splitScalar`, `splitBackwardsSequence`,
2615///           `splitBackwardsAny`,`splitBackwardsScalar`,
2616///           `tokenizeAny`, `tokenizeSequence`, and `tokenizeScalar`.
2617pub fn splitAny(comptime T: type, buffer: []const T, delimiters: []const T) SplitIterator(T, .any) {
2618    return .{
2619        .index = 0,
2620        .buffer = buffer,
2621        .delimiter = delimiters,
2622    };
2623}
2624
2625/// Returns an iterator that iterates over the slices of `buffer` that
2626/// are separated by `delimiter`.
2627///
2628/// `splitScalar(u8, "abc|def||ghi", '|')` will return slices
2629/// for "abc", "def", "", "ghi", null, in that order.
2630///
2631/// If `delimiter` does not exist in buffer,
2632/// the iterator will return `buffer`, null, in that order.
2633///
2634/// See also: `splitSequence`, `splitAny`, `splitBackwardsSequence`,
2635///           `splitBackwardsAny`,`splitBackwardsScalar`,
2636///           `tokenizeAny`, `tokenizeSequence`, and `tokenizeScalar`.
2637pub fn splitScalar(comptime T: type, buffer: []const T, delimiter: T) SplitIterator(T, .scalar) {
2638    return .{
2639        .index = 0,
2640        .buffer = buffer,
2641        .delimiter = delimiter,
2642    };
2643}
2644
2645test splitScalar {
2646    var it = splitScalar(u8, "abc|def||ghi", '|');
2647    try testing.expectEqualSlices(u8, it.rest(), "abc|def||ghi");
2648    try testing.expectEqualSlices(u8, it.first(), "abc");
2649
2650    try testing.expectEqualSlices(u8, it.rest(), "def||ghi");
2651    try testing.expectEqualSlices(u8, it.peek().?, "def");
2652    try testing.expectEqualSlices(u8, it.next().?, "def");
2653
2654    try testing.expectEqualSlices(u8, it.rest(), "|ghi");
2655    try testing.expectEqualSlices(u8, it.next().?, "");
2656
2657    try testing.expectEqualSlices(u8, it.rest(), "ghi");
2658    try testing.expectEqualSlices(u8, it.peek().?, "ghi");
2659    try testing.expectEqualSlices(u8, it.next().?, "ghi");
2660
2661    try testing.expectEqualSlices(u8, it.rest(), "");
2662    try testing.expect(it.peek() == null);
2663    try testing.expect(it.next() == null);
2664
2665    it = splitScalar(u8, "", '|');
2666    try testing.expectEqualSlices(u8, it.first(), "");
2667    try testing.expect(it.next() == null);
2668
2669    it = splitScalar(u8, "|", '|');
2670    try testing.expectEqualSlices(u8, it.first(), "");
2671    try testing.expectEqualSlices(u8, it.next().?, "");
2672    try testing.expect(it.peek() == null);
2673    try testing.expect(it.next() == null);
2674
2675    it = splitScalar(u8, "hello", ' ');
2676    try testing.expectEqualSlices(u8, it.first(), "hello");
2677    try testing.expect(it.next() == null);
2678
2679    var it16 = splitScalar(
2680        u16,
2681        std.unicode.utf8ToUtf16LeStringLiteral("hello"),
2682        ' ',
2683    );
2684    try testing.expectEqualSlices(u16, it16.first(), std.unicode.utf8ToUtf16LeStringLiteral("hello"));
2685    try testing.expect(it16.next() == null);
2686}
2687
2688test splitSequence {
2689    var it = splitSequence(u8, "a, b ,, c, d, e", ", ");
2690    try testing.expectEqualSlices(u8, it.first(), "a");
2691    try testing.expectEqualSlices(u8, it.rest(), "b ,, c, d, e");
2692    try testing.expectEqualSlices(u8, it.next().?, "b ,");
2693    try testing.expectEqualSlices(u8, it.next().?, "c");
2694    try testing.expectEqualSlices(u8, it.next().?, "d");
2695    try testing.expectEqualSlices(u8, it.next().?, "e");
2696    try testing.expect(it.next() == null);
2697
2698    var it16 = splitSequence(
2699        u16,
2700        std.unicode.utf8ToUtf16LeStringLiteral("a, b ,, c, d, e"),
2701        std.unicode.utf8ToUtf16LeStringLiteral(", "),
2702    );
2703    try testing.expectEqualSlices(u16, it16.first(), std.unicode.utf8ToUtf16LeStringLiteral("a"));
2704    try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("b ,"));
2705    try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("c"));
2706    try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("d"));
2707    try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("e"));
2708    try testing.expect(it16.next() == null);
2709}
2710
2711test splitAny {
2712    var it = splitAny(u8, "a,b, c d e", ", ");
2713    try testing.expectEqualSlices(u8, it.first(), "a");
2714    try testing.expectEqualSlices(u8, it.rest(), "b, c d e");
2715    try testing.expectEqualSlices(u8, it.next().?, "b");
2716    try testing.expectEqualSlices(u8, it.next().?, "");
2717    try testing.expectEqualSlices(u8, it.next().?, "c");
2718    try testing.expectEqualSlices(u8, it.next().?, "d");
2719    try testing.expectEqualSlices(u8, it.next().?, "e");
2720    try testing.expect(it.next() == null);
2721
2722    it = splitAny(u8, "hello", "");
2723    try testing.expect(eql(u8, it.next().?, "hello"));
2724    try testing.expect(it.next() == null);
2725
2726    var it16 = splitAny(
2727        u16,
2728        std.unicode.utf8ToUtf16LeStringLiteral("a,b, c d e"),
2729        std.unicode.utf8ToUtf16LeStringLiteral(", "),
2730    );
2731    try testing.expectEqualSlices(u16, it16.first(), std.unicode.utf8ToUtf16LeStringLiteral("a"));
2732    try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("b"));
2733    try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral(""));
2734    try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("c"));
2735    try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("d"));
2736    try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("e"));
2737    try testing.expect(it16.next() == null);
2738}
2739
2740test "split (reset)" {
2741    {
2742        var it = splitSequence(u8, "abc def ghi", " ");
2743        try testing.expect(eql(u8, it.first(), "abc"));
2744        try testing.expect(eql(u8, it.next().?, "def"));
2745        try testing.expect(eql(u8, it.next().?, "ghi"));
2746
2747        it.reset();
2748
2749        try testing.expect(eql(u8, it.first(), "abc"));
2750        try testing.expect(eql(u8, it.next().?, "def"));
2751        try testing.expect(eql(u8, it.next().?, "ghi"));
2752        try testing.expect(it.next() == null);
2753    }
2754    {
2755        var it = splitAny(u8, "abc def,ghi", " ,");
2756        try testing.expect(eql(u8, it.first(), "abc"));
2757        try testing.expect(eql(u8, it.next().?, "def"));
2758        try testing.expect(eql(u8, it.next().?, "ghi"));
2759
2760        it.reset();
2761
2762        try testing.expect(eql(u8, it.first(), "abc"));
2763        try testing.expect(eql(u8, it.next().?, "def"));
2764        try testing.expect(eql(u8, it.next().?, "ghi"));
2765        try testing.expect(it.next() == null);
2766    }
2767    {
2768        var it = splitScalar(u8, "abc def ghi", ' ');
2769        try testing.expect(eql(u8, it.first(), "abc"));
2770        try testing.expect(eql(u8, it.next().?, "def"));
2771        try testing.expect(eql(u8, it.next().?, "ghi"));
2772
2773        it.reset();
2774
2775        try testing.expect(eql(u8, it.first(), "abc"));
2776        try testing.expect(eql(u8, it.next().?, "def"));
2777        try testing.expect(eql(u8, it.next().?, "ghi"));
2778        try testing.expect(it.next() == null);
2779    }
2780}
2781
2782/// Returns an iterator that iterates backwards over the slices of `buffer` that
2783/// are separated by the sequence in `delimiter`.
2784///
2785/// `splitBackwardsSequence(u8, "abc||def||||ghi", "||")` will return slices
2786/// for "ghi", "", "def", "abc", null, in that order.
2787///
2788/// If `delimiter` does not exist in buffer,
2789/// the iterator will return `buffer`, null, in that order.
2790/// The delimiter length must not be zero.
2791///
2792/// See also: `splitBackwardsAny`, `splitBackwardsScalar`,
2793///           `splitSequence`, `splitAny`,`splitScalar`,
2794///           `tokenizeAny`, `tokenizeSequence`, and `tokenizeScalar`.
2795pub fn splitBackwardsSequence(comptime T: type, buffer: []const T, delimiter: []const T) SplitBackwardsIterator(T, .sequence) {
2796    assert(delimiter.len != 0);
2797    return .{
2798        .index = buffer.len,
2799        .buffer = buffer,
2800        .delimiter = delimiter,
2801    };
2802}
2803
2804/// Returns an iterator that iterates backwards over the slices of `buffer` that
2805/// are separated by any item in `delimiters`.
2806///
2807/// `splitBackwardsAny(u8, "abc,def||ghi", "|,")` will return slices
2808/// for "ghi", "", "def", "abc", null, in that order.
2809///
2810/// If none of `delimiters` exist in buffer,
2811/// the iterator will return `buffer`, null, in that order.
2812///
2813/// See also: `splitBackwardsSequence`, `splitBackwardsScalar`,
2814///           `splitSequence`, `splitAny`,`splitScalar`,
2815///           `tokenizeAny`, `tokenizeSequence`, and `tokenizeScalar`.
2816pub fn splitBackwardsAny(comptime T: type, buffer: []const T, delimiters: []const T) SplitBackwardsIterator(T, .any) {
2817    return .{
2818        .index = buffer.len,
2819        .buffer = buffer,
2820        .delimiter = delimiters,
2821    };
2822}
2823
2824/// Returns an iterator that iterates backwards over the slices of `buffer` that
2825/// are separated by `delimiter`.
2826///
2827/// `splitBackwardsScalar(u8, "abc|def||ghi", '|')` will return slices
2828/// for "ghi", "", "def", "abc", null, in that order.
2829///
2830/// If `delimiter` does not exist in buffer,
2831/// the iterator will return `buffer`, null, in that order.
2832///
2833/// See also: `splitBackwardsSequence`, `splitBackwardsAny`,
2834///           `splitSequence`, `splitAny`,`splitScalar`,
2835///           `tokenizeAny`, `tokenizeSequence`, and `tokenizeScalar`.
2836pub fn splitBackwardsScalar(comptime T: type, buffer: []const T, delimiter: T) SplitBackwardsIterator(T, .scalar) {
2837    return .{
2838        .index = buffer.len,
2839        .buffer = buffer,
2840        .delimiter = delimiter,
2841    };
2842}
2843
2844test splitBackwardsScalar {
2845    var it = splitBackwardsScalar(u8, "abc|def||ghi", '|');
2846    try testing.expectEqualSlices(u8, it.rest(), "abc|def||ghi");
2847    try testing.expectEqualSlices(u8, it.first(), "ghi");
2848
2849    try testing.expectEqualSlices(u8, it.rest(), "abc|def|");
2850    try testing.expectEqualSlices(u8, it.next().?, "");
2851
2852    try testing.expectEqualSlices(u8, it.rest(), "abc|def");
2853    try testing.expectEqualSlices(u8, it.next().?, "def");
2854
2855    try testing.expectEqualSlices(u8, it.rest(), "abc");
2856    try testing.expectEqualSlices(u8, it.next().?, "abc");
2857
2858    try testing.expectEqualSlices(u8, it.rest(), "");
2859    try testing.expect(it.next() == null);
2860
2861    it = splitBackwardsScalar(u8, "", '|');
2862    try testing.expectEqualSlices(u8, it.first(), "");
2863    try testing.expect(it.next() == null);
2864
2865    it = splitBackwardsScalar(u8, "|", '|');
2866    try testing.expectEqualSlices(u8, it.first(), "");
2867    try testing.expectEqualSlices(u8, it.next().?, "");
2868    try testing.expect(it.next() == null);
2869
2870    it = splitBackwardsScalar(u8, "hello", ' ');
2871    try testing.expectEqualSlices(u8, it.first(), "hello");
2872    try testing.expect(it.next() == null);
2873
2874    var it16 = splitBackwardsScalar(
2875        u16,
2876        std.unicode.utf8ToUtf16LeStringLiteral("hello"),
2877        ' ',
2878    );
2879    try testing.expectEqualSlices(u16, it16.first(), std.unicode.utf8ToUtf16LeStringLiteral("hello"));
2880    try testing.expect(it16.next() == null);
2881}
2882
2883test splitBackwardsSequence {
2884    var it = splitBackwardsSequence(u8, "a, b ,, c, d, e", ", ");
2885    try testing.expectEqualSlices(u8, it.rest(), "a, b ,, c, d, e");
2886    try testing.expectEqualSlices(u8, it.first(), "e");
2887
2888    try testing.expectEqualSlices(u8, it.rest(), "a, b ,, c, d");
2889    try testing.expectEqualSlices(u8, it.next().?, "d");
2890
2891    try testing.expectEqualSlices(u8, it.rest(), "a, b ,, c");
2892    try testing.expectEqualSlices(u8, it.next().?, "c");
2893
2894    try testing.expectEqualSlices(u8, it.rest(), "a, b ,");
2895    try testing.expectEqualSlices(u8, it.next().?, "b ,");
2896
2897    try testing.expectEqualSlices(u8, it.rest(), "a");
2898    try testing.expectEqualSlices(u8, it.next().?, "a");
2899
2900    try testing.expectEqualSlices(u8, it.rest(), "");
2901    try testing.expect(it.next() == null);
2902
2903    var it16 = splitBackwardsSequence(
2904        u16,
2905        std.unicode.utf8ToUtf16LeStringLiteral("a, b ,, c, d, e"),
2906        std.unicode.utf8ToUtf16LeStringLiteral(", "),
2907    );
2908    try testing.expectEqualSlices(u16, it16.first(), std.unicode.utf8ToUtf16LeStringLiteral("e"));
2909    try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("d"));
2910    try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("c"));
2911    try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("b ,"));
2912    try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("a"));
2913    try testing.expect(it16.next() == null);
2914}
2915
2916test splitBackwardsAny {
2917    var it = splitBackwardsAny(u8, "a,b, c d e", ", ");
2918    try testing.expectEqualSlices(u8, it.rest(), "a,b, c d e");
2919    try testing.expectEqualSlices(u8, it.first(), "e");
2920
2921    try testing.expectEqualSlices(u8, it.rest(), "a,b, c d");
2922    try testing.expectEqualSlices(u8, it.next().?, "d");
2923
2924    try testing.expectEqualSlices(u8, it.rest(), "a,b, c");
2925    try testing.expectEqualSlices(u8, it.next().?, "c");
2926
2927    try testing.expectEqualSlices(u8, it.rest(), "a,b,");
2928    try testing.expectEqualSlices(u8, it.next().?, "");
2929
2930    try testing.expectEqualSlices(u8, it.rest(), "a,b");
2931    try testing.expectEqualSlices(u8, it.next().?, "b");
2932
2933    try testing.expectEqualSlices(u8, it.rest(), "a");
2934    try testing.expectEqualSlices(u8, it.next().?, "a");
2935
2936    try testing.expectEqualSlices(u8, it.rest(), "");
2937    try testing.expect(it.next() == null);
2938
2939    var it16 = splitBackwardsAny(
2940        u16,
2941        std.unicode.utf8ToUtf16LeStringLiteral("a,b, c d e"),
2942        std.unicode.utf8ToUtf16LeStringLiteral(", "),
2943    );
2944    try testing.expectEqualSlices(u16, it16.first(), std.unicode.utf8ToUtf16LeStringLiteral("e"));
2945    try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("d"));
2946    try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("c"));
2947    try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral(""));
2948    try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("b"));
2949    try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("a"));
2950    try testing.expect(it16.next() == null);
2951}
2952
2953test "splitBackwards (reset)" {
2954    {
2955        var it = splitBackwardsSequence(u8, "abc def ghi", " ");
2956        try testing.expect(eql(u8, it.first(), "ghi"));
2957        try testing.expect(eql(u8, it.next().?, "def"));
2958        try testing.expect(eql(u8, it.next().?, "abc"));
2959
2960        it.reset();
2961
2962        try testing.expect(eql(u8, it.first(), "ghi"));
2963        try testing.expect(eql(u8, it.next().?, "def"));
2964        try testing.expect(eql(u8, it.next().?, "abc"));
2965        try testing.expect(it.next() == null);
2966    }
2967    {
2968        var it = splitBackwardsAny(u8, "abc def,ghi", " ,");
2969        try testing.expect(eql(u8, it.first(), "ghi"));
2970        try testing.expect(eql(u8, it.next().?, "def"));
2971        try testing.expect(eql(u8, it.next().?, "abc"));
2972
2973        it.reset();
2974
2975        try testing.expect(eql(u8, it.first(), "ghi"));
2976        try testing.expect(eql(u8, it.next().?, "def"));
2977        try testing.expect(eql(u8, it.next().?, "abc"));
2978        try testing.expect(it.next() == null);
2979    }
2980    {
2981        var it = splitBackwardsScalar(u8, "abc def ghi", ' ');
2982        try testing.expect(eql(u8, it.first(), "ghi"));
2983        try testing.expect(eql(u8, it.next().?, "def"));
2984        try testing.expect(eql(u8, it.next().?, "abc"));
2985
2986        it.reset();
2987
2988        try testing.expect(eql(u8, it.first(), "ghi"));
2989        try testing.expect(eql(u8, it.next().?, "def"));
2990        try testing.expect(eql(u8, it.next().?, "abc"));
2991        try testing.expect(it.next() == null);
2992    }
2993}
2994
2995/// Returns an iterator with a sliding window of slices for `buffer`.
2996/// The sliding window has length `size` and on every iteration moves
2997/// forward by `advance`.
2998///
2999/// Extract data for moving average with:
3000/// `window(u8, "abcdefg", 3, 1)` will return slices
3001/// "abc", "bcd", "cde", "def", "efg", null, in that order.
3002///
3003/// Chunk or split every N items with:
3004/// `window(u8, "abcdefg", 3, 3)` will return slices
3005/// "abc", "def", "g", null, in that order.
3006///
3007/// Pick every even index with:
3008/// `window(u8, "abcdefg", 1, 2)` will return slices
3009/// "a", "c", "e", "g" null, in that order.
3010///
3011/// The `size` and `advance` must be not be zero.
3012pub fn window(comptime T: type, buffer: []const T, size: usize, advance: usize) WindowIterator(T) {
3013    assert(size != 0);
3014    assert(advance != 0);
3015    return .{
3016        .index = 0,
3017        .buffer = buffer,
3018        .size = size,
3019        .advance = advance,
3020    };
3021}
3022
3023test window {
3024    {
3025        // moving average size 3
3026        var it = window(u8, "abcdefg", 3, 1);
3027        try testing.expectEqualSlices(u8, it.next().?, "abc");
3028        try testing.expectEqualSlices(u8, it.next().?, "bcd");
3029        try testing.expectEqualSlices(u8, it.next().?, "cde");
3030        try testing.expectEqualSlices(u8, it.next().?, "def");
3031        try testing.expectEqualSlices(u8, it.next().?, "efg");
3032        try testing.expectEqual(it.next(), null);
3033
3034        // multibyte
3035        var it16 = window(u16, std.unicode.utf8ToUtf16LeStringLiteral("abcdefg"), 3, 1);
3036        try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("abc"));
3037        try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("bcd"));
3038        try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("cde"));
3039        try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("def"));
3040        try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("efg"));
3041        try testing.expectEqual(it16.next(), null);
3042    }
3043
3044    {
3045        // chunk/split every 3
3046        var it = window(u8, "abcdefg", 3, 3);
3047        try testing.expectEqualSlices(u8, it.next().?, "abc");
3048        try testing.expectEqualSlices(u8, it.next().?, "def");
3049        try testing.expectEqualSlices(u8, it.next().?, "g");
3050        try testing.expectEqual(it.next(), null);
3051    }
3052
3053    {
3054        // pick even
3055        var it = window(u8, "abcdefg", 1, 2);
3056        try testing.expectEqualSlices(u8, it.next().?, "a");
3057        try testing.expectEqualSlices(u8, it.next().?, "c");
3058        try testing.expectEqualSlices(u8, it.next().?, "e");
3059        try testing.expectEqualSlices(u8, it.next().?, "g");
3060        try testing.expectEqual(it.next(), null);
3061    }
3062
3063    {
3064        // empty
3065        var it = window(u8, "", 1, 1);
3066        try testing.expectEqualSlices(u8, it.next().?, "");
3067        try testing.expectEqual(it.next(), null);
3068
3069        it = window(u8, "", 10, 1);
3070        try testing.expectEqualSlices(u8, it.next().?, "");
3071        try testing.expectEqual(it.next(), null);
3072
3073        it = window(u8, "", 1, 10);
3074        try testing.expectEqualSlices(u8, it.next().?, "");
3075        try testing.expectEqual(it.next(), null);
3076
3077        it = window(u8, "", 10, 10);
3078        try testing.expectEqualSlices(u8, it.next().?, "");
3079        try testing.expectEqual(it.next(), null);
3080    }
3081
3082    {
3083        // first
3084        var it = window(u8, "abcdefg", 3, 3);
3085        try testing.expectEqualSlices(u8, it.first(), "abc");
3086        it.reset();
3087        try testing.expectEqualSlices(u8, it.next().?, "abc");
3088    }
3089
3090    {
3091        // reset
3092        var it = window(u8, "abcdefg", 3, 3);
3093        try testing.expectEqualSlices(u8, it.next().?, "abc");
3094        try testing.expectEqualSlices(u8, it.next().?, "def");
3095        try testing.expectEqualSlices(u8, it.next().?, "g");
3096        try testing.expectEqual(it.next(), null);
3097
3098        it.reset();
3099        try testing.expectEqualSlices(u8, it.next().?, "abc");
3100        try testing.expectEqualSlices(u8, it.next().?, "def");
3101        try testing.expectEqualSlices(u8, it.next().?, "g");
3102        try testing.expectEqual(it.next(), null);
3103    }
3104}
3105
3106/// Iterator type returned by the `window` function for sliding window operations.
3107pub fn WindowIterator(comptime T: type) type {
3108    return struct {
3109        buffer: []const T,
3110        index: ?usize,
3111        size: usize,
3112        advance: usize,
3113
3114        const Self = @This();
3115
3116        /// Returns a slice of the first window.
3117        /// Call this only to get the first window and then use `next` to get
3118        /// all subsequent windows.
3119        /// Asserts that iteration has not begun.
3120        pub fn first(self: *Self) []const T {
3121            assert(self.index.? == 0);
3122            return self.next().?;
3123        }
3124
3125        /// Returns a slice of the next window, or null if window is at end.
3126        pub fn next(self: *Self) ?[]const T {
3127            const start = self.index orelse return null;
3128            const next_index = start + self.advance;
3129            const end = if (start + self.size < self.buffer.len and next_index < self.buffer.len) blk: {
3130                self.index = next_index;
3131                break :blk start + self.size;
3132            } else blk: {
3133                self.index = null;
3134                break :blk self.buffer.len;
3135            };
3136
3137            return self.buffer[start..end];
3138        }
3139
3140        /// Resets the iterator to the initial window.
3141        pub fn reset(self: *Self) void {
3142            self.index = 0;
3143        }
3144    };
3145}
3146
3147/// Returns true if haystack starts with needle.
3148/// Time complexity: O(needle.len)
3149pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool {
3150    return if (needle.len > haystack.len) false else eql(T, haystack[0..needle.len], needle);
3151}
3152
3153test startsWith {
3154    try testing.expect(startsWith(u8, "Bob", "Bo"));
3155    try testing.expect(!startsWith(u8, "Needle in haystack", "haystack"));
3156}
3157
3158/// Returns true if haystack ends with needle.
3159/// Time complexity: O(needle.len)
3160pub fn endsWith(comptime T: type, haystack: []const T, needle: []const T) bool {
3161    return if (needle.len > haystack.len) false else eql(T, haystack[haystack.len - needle.len ..], needle);
3162}
3163
3164test endsWith {
3165    try testing.expect(endsWith(u8, "Needle in haystack", "haystack"));
3166    try testing.expect(!endsWith(u8, "Bob", "Bo"));
3167}
3168
3169/// If `slice` starts with `prefix`, returns the rest of `slice` starting at `prefix.len`.
3170pub fn cutPrefix(comptime T: type, slice: []const T, prefix: []const T) ?[]const T {
3171    return if (startsWith(T, slice, prefix)) slice[prefix.len..] else null;
3172}
3173
3174test cutPrefix {
3175    try testing.expectEqualStrings("foo", cutPrefix(u8, "--example=foo", "--example=").?);
3176    try testing.expectEqual(null, cutPrefix(u8, "--example=foo", "-example="));
3177}
3178
3179/// If `slice` ends with `suffix`, returns `slice` from beginning to start of `suffix`.
3180pub fn cutSuffix(comptime T: type, slice: []const T, suffix: []const T) ?[]const T {
3181    return if (endsWith(T, slice, suffix)) slice[0 .. slice.len - suffix.len] else null;
3182}
3183
3184test cutSuffix {
3185    try testing.expectEqualStrings("foo", cutSuffix(u8, "foobar", "bar").?);
3186    try testing.expectEqual(null, cutSuffix(u8, "foobar", "baz"));
3187}
3188
3189/// Returns slice of `haystack` before and after first occurrence of `needle`,
3190/// or `null` if not found.
3191///
3192/// See also:
3193/// * `cutScalar`
3194/// * `split`
3195/// * `tokenizeAny`
3196pub fn cut(comptime T: type, haystack: []const T, needle: []const T) ?struct { []const T, []const T } {
3197    const index = find(T, haystack, needle) orelse return null;
3198    return .{ haystack[0..index], haystack[index + needle.len ..] };
3199}
3200
3201test cut {
3202    try testing.expectEqual(null, cut(u8, "a b c", "B"));
3203    const before, const after = cut(u8, "a be c", "be") orelse return error.TestFailed;
3204    try testing.expectEqualStrings("a ", before);
3205    try testing.expectEqualStrings(" c", after);
3206}
3207
3208/// Returns slice of `haystack` before and after last occurrence of `needle`,
3209/// or `null` if not found.
3210///
3211/// See also:
3212/// * `cut`
3213/// * `cutScalarLast`
3214pub fn cutLast(comptime T: type, haystack: []const T, needle: []const T) ?struct { []const T, []const T } {
3215    const index = findLast(T, haystack, needle) orelse return null;
3216    return .{ haystack[0..index], haystack[index + needle.len ..] };
3217}
3218
3219test cutLast {
3220    try testing.expectEqual(null, cutLast(u8, "a b c", "B"));
3221    const before, const after = cutLast(u8, "a be c be d", "be") orelse return error.TestFailed;
3222    try testing.expectEqualStrings("a be c ", before);
3223    try testing.expectEqualStrings(" d", after);
3224}
3225
3226/// Returns slice of `haystack` before and after first occurrence `needle`, or
3227/// `null` if not found.
3228///
3229/// See also:
3230/// * `cut`
3231/// * `splitScalar`
3232/// * `tokenizeScalar`
3233pub fn cutScalar(comptime T: type, haystack: []const T, needle: T) ?struct { []const T, []const T } {
3234    const index = findScalar(T, haystack, needle) orelse return null;
3235    return .{ haystack[0..index], haystack[index + 1 ..] };
3236}
3237
3238test cutScalar {
3239    try testing.expectEqual(null, cutScalar(u8, "a b c", 'B'));
3240    const before, const after = cutScalar(u8, "a b c", 'b') orelse return error.TestFailed;
3241    try testing.expectEqualStrings("a ", before);
3242    try testing.expectEqualStrings(" c", after);
3243}
3244
3245/// Returns slice of `haystack` before and after last occurrence of `needle`,
3246/// or `null` if not found.
3247///
3248/// See also:
3249/// * `cut`
3250/// * `splitScalar`
3251/// * `tokenizeScalar`
3252pub fn cutScalarLast(comptime T: type, haystack: []const T, needle: T) ?struct { []const T, []const T } {
3253    const index = findScalarLast(T, haystack, needle) orelse return null;
3254    return .{ haystack[0..index], haystack[index + 1 ..] };
3255}
3256
3257test cutScalarLast {
3258    try testing.expectEqual(null, cutScalarLast(u8, "a b c", 'B'));
3259    const before, const after = cutScalarLast(u8, "a b c b d", 'b') orelse return error.TestFailed;
3260    try testing.expectEqualStrings("a b c ", before);
3261    try testing.expectEqualStrings(" d", after);
3262}
3263
3264/// Delimiter type for tokenization and splitting operations.
3265pub const DelimiterType = enum { sequence, any, scalar };
3266
3267/// Iterator type for tokenization operations, skipping empty sequences and delimiter sequences.
3268pub fn TokenIterator(comptime T: type, comptime delimiter_type: DelimiterType) type {
3269    return struct {
3270        buffer: []const T,
3271        delimiter: switch (delimiter_type) {
3272            .sequence, .any => []const T,
3273            .scalar => T,
3274        },
3275        index: usize,
3276
3277        const Self = @This();
3278
3279        /// Returns a slice of the current token, or null if tokenization is
3280        /// complete, and advances to the next token.
3281        pub fn next(self: *Self) ?[]const T {
3282            const result = self.peek() orelse return null;
3283            self.index += result.len;
3284            return result;
3285        }
3286
3287        /// Returns a slice of the current token, or null if tokenization is
3288        /// complete. Does not advance to the next token.
3289        pub fn peek(self: *Self) ?[]const T {
3290            // move to beginning of token
3291            while (self.index < self.buffer.len and self.isDelimiter(self.index)) : (self.index += switch (delimiter_type) {
3292                .sequence => self.delimiter.len,
3293                .any, .scalar => 1,
3294            }) {}
3295            const start = self.index;
3296            if (start == self.buffer.len) {
3297                return null;
3298            }
3299
3300            // move to end of token
3301            var end = start;
3302            while (end < self.buffer.len and !self.isDelimiter(end)) : (end += 1) {}
3303
3304            return self.buffer[start..end];
3305        }
3306
3307        /// Returns a slice of the remaining bytes. Does not affect iterator state.
3308        pub fn rest(self: Self) []const T {
3309            // move to beginning of token
3310            var index: usize = self.index;
3311            while (index < self.buffer.len and self.isDelimiter(index)) : (index += switch (delimiter_type) {
3312                .sequence => self.delimiter.len,
3313                .any, .scalar => 1,
3314            }) {}
3315            return self.buffer[index..];
3316        }
3317
3318        /// Resets the iterator to the initial token.
3319        pub fn reset(self: *Self) void {
3320            self.index = 0;
3321        }
3322
3323        fn isDelimiter(self: Self, index: usize) bool {
3324            switch (delimiter_type) {
3325                .sequence => return startsWith(T, self.buffer[index..], self.delimiter),
3326                .any => {
3327                    const item = self.buffer[index];
3328                    for (self.delimiter) |delimiter_item| {
3329                        if (item == delimiter_item) {
3330                            return true;
3331                        }
3332                    }
3333                    return false;
3334                },
3335                .scalar => return self.buffer[index] == self.delimiter,
3336            }
3337        }
3338    };
3339}
3340
3341/// Iterator type for splitting operations, including empty sequences between delimiters.
3342pub fn SplitIterator(comptime T: type, comptime delimiter_type: DelimiterType) type {
3343    return struct {
3344        buffer: []const T,
3345        index: ?usize,
3346        delimiter: switch (delimiter_type) {
3347            .sequence, .any => []const T,
3348            .scalar => T,
3349        },
3350
3351        const Self = @This();
3352
3353        /// Returns a slice of the first field.
3354        /// Call this only to get the first field and then use `next` to get all subsequent fields.
3355        /// Asserts that iteration has not begun.
3356        pub fn first(self: *Self) []const T {
3357            assert(self.index.? == 0);
3358            return self.next().?;
3359        }
3360
3361        /// Returns a slice of the next field, or null if splitting is complete.
3362        pub fn next(self: *Self) ?[]const T {
3363            const start = self.index orelse return null;
3364            const end = if (switch (delimiter_type) {
3365                .sequence => indexOfPos(T, self.buffer, start, self.delimiter),
3366                .any => indexOfAnyPos(T, self.buffer, start, self.delimiter),
3367                .scalar => indexOfScalarPos(T, self.buffer, start, self.delimiter),
3368            }) |delim_start| blk: {
3369                self.index = delim_start + switch (delimiter_type) {
3370                    .sequence => self.delimiter.len,
3371                    .any, .scalar => 1,
3372                };
3373                break :blk delim_start;
3374            } else blk: {
3375                self.index = null;
3376                break :blk self.buffer.len;
3377            };
3378            return self.buffer[start..end];
3379        }
3380
3381        /// Returns a slice of the next field, or null if splitting is complete.
3382        /// This method does not alter self.index.
3383        pub fn peek(self: *Self) ?[]const T {
3384            const start = self.index orelse return null;
3385            const end = if (switch (delimiter_type) {
3386                .sequence => indexOfPos(T, self.buffer, start, self.delimiter),
3387                .any => indexOfAnyPos(T, self.buffer, start, self.delimiter),
3388                .scalar => indexOfScalarPos(T, self.buffer, start, self.delimiter),
3389            }) |delim_start| delim_start else self.buffer.len;
3390            return self.buffer[start..end];
3391        }
3392
3393        /// Returns a slice of the remaining bytes. Does not affect iterator state.
3394        pub fn rest(self: Self) []const T {
3395            const end = self.buffer.len;
3396            const start = self.index orelse end;
3397            return self.buffer[start..end];
3398        }
3399
3400        /// Resets the iterator to the initial slice.
3401        pub fn reset(self: *Self) void {
3402            self.index = 0;
3403        }
3404    };
3405}
3406
3407/// Iterator type for splitting operations from the end backwards, including empty sequences.
3408pub fn SplitBackwardsIterator(comptime T: type, comptime delimiter_type: DelimiterType) type {
3409    return struct {
3410        buffer: []const T,
3411        index: ?usize,
3412        delimiter: switch (delimiter_type) {
3413            .sequence, .any => []const T,
3414            .scalar => T,
3415        },
3416
3417        const Self = @This();
3418
3419        /// Returns a slice of the first field.
3420        /// Call this only to get the first field and then use `next` to get all subsequent fields.
3421        /// Asserts that iteration has not begun.
3422        pub fn first(self: *Self) []const T {
3423            assert(self.index.? == self.buffer.len);
3424            return self.next().?;
3425        }
3426
3427        /// Returns a slice of the next field, or null if splitting is complete.
3428        pub fn next(self: *Self) ?[]const T {
3429            const end = self.index orelse return null;
3430            const start = if (switch (delimiter_type) {
3431                .sequence => lastIndexOf(T, self.buffer[0..end], self.delimiter),
3432                .any => lastIndexOfAny(T, self.buffer[0..end], self.delimiter),
3433                .scalar => findScalarLast(T, self.buffer[0..end], self.delimiter),
3434            }) |delim_start| blk: {
3435                self.index = delim_start;
3436                break :blk delim_start + switch (delimiter_type) {
3437                    .sequence => self.delimiter.len,
3438                    .any, .scalar => 1,
3439                };
3440            } else blk: {
3441                self.index = null;
3442                break :blk 0;
3443            };
3444            return self.buffer[start..end];
3445        }
3446
3447        /// Returns a slice of the remaining bytes. Does not affect iterator state.
3448        pub fn rest(self: Self) []const T {
3449            const end = self.index orelse 0;
3450            return self.buffer[0..end];
3451        }
3452
3453        /// Resets the iterator to the initial slice.
3454        pub fn reset(self: *Self) void {
3455            self.index = self.buffer.len;
3456        }
3457    };
3458}
3459
3460/// Naively combines a series of slices with a separator.
3461/// Allocates memory for the result, which must be freed by the caller.
3462pub fn join(allocator: Allocator, separator: []const u8, slices: []const []const u8) Allocator.Error![]u8 {
3463    return joinMaybeZ(allocator, separator, slices, false);
3464}
3465
3466/// Naively combines a series of slices with a separator and null terminator.
3467/// Allocates memory for the result, which must be freed by the caller.
3468pub fn joinZ(allocator: Allocator, separator: []const u8, slices: []const []const u8) Allocator.Error![:0]u8 {
3469    const out = try joinMaybeZ(allocator, separator, slices, true);
3470    return out[0 .. out.len - 1 :0];
3471}
3472
3473fn joinMaybeZ(allocator: Allocator, separator: []const u8, slices: []const []const u8, zero: bool) Allocator.Error![]u8 {
3474    if (slices.len == 0) return if (zero) try allocator.dupe(u8, &[1]u8{0}) else &[0]u8{};
3475
3476    const total_len = blk: {
3477        var sum: usize = separator.len * (slices.len - 1);
3478        for (slices) |slice| sum += slice.len;
3479        if (zero) sum += 1;
3480        break :blk sum;
3481    };
3482
3483    const buf = try allocator.alloc(u8, total_len);
3484    errdefer allocator.free(buf);
3485
3486    @memcpy(buf[0..slices[0].len], slices[0]);
3487    var buf_index: usize = slices[0].len;
3488    for (slices[1..]) |slice| {
3489        @memcpy(buf[buf_index .. buf_index + separator.len], separator);
3490        buf_index += separator.len;
3491        @memcpy(buf[buf_index .. buf_index + slice.len], slice);
3492        buf_index += slice.len;
3493    }
3494
3495    if (zero) buf[buf.len - 1] = 0;
3496
3497    // No need for shrink since buf is exactly the correct size.
3498    return buf;
3499}
3500
3501test join {
3502    {
3503        const str = try join(testing.allocator, ",", &[_][]const u8{});
3504        defer testing.allocator.free(str);
3505        try testing.expect(eql(u8, str, ""));
3506    }
3507    {
3508        const str = try join(testing.allocator, ",", &[_][]const u8{ "a", "b", "c" });
3509        defer testing.allocator.free(str);
3510        try testing.expect(eql(u8, str, "a,b,c"));
3511    }
3512    {
3513        const str = try join(testing.allocator, ",", &[_][]const u8{"a"});
3514        defer testing.allocator.free(str);
3515        try testing.expect(eql(u8, str, "a"));
3516    }
3517    {
3518        const str = try join(testing.allocator, ",", &[_][]const u8{ "a", "", "b", "", "c" });
3519        defer testing.allocator.free(str);
3520        try testing.expect(eql(u8, str, "a,,b,,c"));
3521    }
3522}
3523
3524test joinZ {
3525    {
3526        const str = try joinZ(testing.allocator, ",", &[_][]const u8{});
3527        defer testing.allocator.free(str);
3528        try testing.expect(eql(u8, str, ""));
3529        try testing.expectEqual(str[str.len], 0);
3530    }
3531    {
3532        const str = try joinZ(testing.allocator, ",", &[_][]const u8{ "a", "b", "c" });
3533        defer testing.allocator.free(str);
3534        try testing.expect(eql(u8, str, "a,b,c"));
3535        try testing.expectEqual(str[str.len], 0);
3536    }
3537    {
3538        const str = try joinZ(testing.allocator, ",", &[_][]const u8{"a"});
3539        defer testing.allocator.free(str);
3540        try testing.expect(eql(u8, str, "a"));
3541        try testing.expectEqual(str[str.len], 0);
3542    }
3543    {
3544        const str = try joinZ(testing.allocator, ",", &[_][]const u8{ "a", "", "b", "", "c" });
3545        defer testing.allocator.free(str);
3546        try testing.expect(eql(u8, str, "a,,b,,c"));
3547        try testing.expectEqual(str[str.len], 0);
3548    }
3549}
3550
3551/// Copies each T from slices into a new slice that exactly holds all the elements.
3552pub fn concat(allocator: Allocator, comptime T: type, slices: []const []const T) Allocator.Error![]T {
3553    return concatMaybeSentinel(allocator, T, slices, null);
3554}
3555
3556/// Copies each T from slices into a new slice that exactly holds all the elements.
3557pub fn concatWithSentinel(allocator: Allocator, comptime T: type, slices: []const []const T, comptime s: T) Allocator.Error![:s]T {
3558    const ret = try concatMaybeSentinel(allocator, T, slices, s);
3559    return ret[0 .. ret.len - 1 :s];
3560}
3561
3562/// Copies each T from slices into a new slice that exactly holds all the elements as well as the sentinel.
3563pub fn concatMaybeSentinel(allocator: Allocator, comptime T: type, slices: []const []const T, comptime s: ?T) Allocator.Error![]T {
3564    if (slices.len == 0) return if (s) |sentinel| try allocator.dupe(T, &[1]T{sentinel}) else &[0]T{};
3565
3566    const total_len = blk: {
3567        var sum: usize = 0;
3568        for (slices) |slice| {
3569            sum += slice.len;
3570        }
3571
3572        if (s) |_| {
3573            sum += 1;
3574        }
3575
3576        break :blk sum;
3577    };
3578
3579    const buf = try allocator.alloc(T, total_len);
3580    errdefer allocator.free(buf);
3581
3582    var buf_index: usize = 0;
3583    for (slices) |slice| {
3584        @memcpy(buf[buf_index .. buf_index + slice.len], slice);
3585        buf_index += slice.len;
3586    }
3587
3588    if (s) |sentinel| {
3589        buf[buf.len - 1] = sentinel;
3590    }
3591
3592    // No need for shrink since buf is exactly the correct size.
3593    return buf;
3594}
3595
3596test concat {
3597    {
3598        const str = try concat(testing.allocator, u8, &[_][]const u8{ "abc", "def", "ghi" });
3599        defer testing.allocator.free(str);
3600        try testing.expect(eql(u8, str, "abcdefghi"));
3601    }
3602    {
3603        const str = try concat(testing.allocator, u32, &[_][]const u32{
3604            &[_]u32{ 0, 1 },
3605            &[_]u32{ 2, 3, 4 },
3606            &[_]u32{},
3607            &[_]u32{5},
3608        });
3609        defer testing.allocator.free(str);
3610        try testing.expect(eql(u32, str, &[_]u32{ 0, 1, 2, 3, 4, 5 }));
3611    }
3612    {
3613        const str = try concatWithSentinel(testing.allocator, u8, &[_][]const u8{ "abc", "def", "ghi" }, 0);
3614        defer testing.allocator.free(str);
3615        try testing.expectEqualSentinel(u8, 0, str, "abcdefghi");
3616    }
3617    {
3618        const slice = try concatWithSentinel(testing.allocator, u8, &[_][]const u8{}, 0);
3619        defer testing.allocator.free(slice);
3620        try testing.expectEqualSentinel(u8, 0, slice, &[_:0]u8{});
3621    }
3622    {
3623        const slice = try concatWithSentinel(testing.allocator, u32, &[_][]const u32{
3624            &[_]u32{ 0, 1 },
3625            &[_]u32{ 2, 3, 4 },
3626            &[_]u32{},
3627            &[_]u32{5},
3628        }, 2);
3629        defer testing.allocator.free(slice);
3630        try testing.expectEqualSentinel(u32, 2, slice, &[_:2]u32{ 0, 1, 2, 3, 4, 5 });
3631    }
3632}
3633
3634fn moreReadIntTests() !void {
3635    {
3636        const bytes = [_]u8{
3637            0x12,
3638            0x34,
3639            0x56,
3640            0x78,
3641        };
3642        try testing.expect(readInt(u32, &bytes, .big) == 0x12345678);
3643        try testing.expect(readInt(u32, &bytes, .big) == 0x12345678);
3644        try testing.expect(readInt(i32, &bytes, .big) == 0x12345678);
3645        try testing.expect(readInt(u32, &bytes, .little) == 0x78563412);
3646        try testing.expect(readInt(u32, &bytes, .little) == 0x78563412);
3647        try testing.expect(readInt(i32, &bytes, .little) == 0x78563412);
3648    }
3649    {
3650        const buf = [_]u8{
3651            0x00,
3652            0x00,
3653            0x12,
3654            0x34,
3655        };
3656        const answer = readInt(u32, &buf, .big);
3657        try testing.expect(answer == 0x00001234);
3658    }
3659    {
3660        const buf = [_]u8{
3661            0x12,
3662            0x34,
3663            0x00,
3664            0x00,
3665        };
3666        const answer = readInt(u32, &buf, .little);
3667        try testing.expect(answer == 0x00003412);
3668    }
3669    {
3670        const bytes = [_]u8{
3671            0xff,
3672            0xfe,
3673        };
3674        try testing.expect(readInt(u16, &bytes, .big) == 0xfffe);
3675        try testing.expect(readInt(i16, &bytes, .big) == -0x0002);
3676        try testing.expect(readInt(u16, &bytes, .little) == 0xfeff);
3677        try testing.expect(readInt(i16, &bytes, .little) == -0x0101);
3678    }
3679}
3680
3681/// Returns the smallest number in a slice. O(n).
3682/// `slice` must not be empty.
3683pub fn min(comptime T: type, slice: []const T) T {
3684    assert(slice.len > 0);
3685    var best = slice[0];
3686    for (slice[1..]) |item| {
3687        best = @min(best, item);
3688    }
3689    return best;
3690}
3691
3692test min {
3693    try testing.expectEqual(min(u8, "abcdefg"), 'a');
3694    try testing.expectEqual(min(u8, "bcdefga"), 'a');
3695    try testing.expectEqual(min(u8, "a"), 'a');
3696}
3697
3698/// Returns the largest number in a slice. O(n).
3699/// `slice` must not be empty.
3700pub fn max(comptime T: type, slice: []const T) T {
3701    assert(slice.len > 0);
3702    var best = slice[0];
3703    for (slice[1..]) |item| {
3704        best = @max(best, item);
3705    }
3706    return best;
3707}
3708
3709test max {
3710    try testing.expectEqual(max(u8, "abcdefg"), 'g');
3711    try testing.expectEqual(max(u8, "gabcdef"), 'g');
3712    try testing.expectEqual(max(u8, "g"), 'g');
3713}
3714
3715/// Finds the smallest and largest number in a slice. O(n).
3716/// Returns an anonymous struct with the fields `min` and `max`.
3717/// `slice` must not be empty.
3718pub fn minMax(comptime T: type, slice: []const T) struct { T, T } {
3719    assert(slice.len > 0);
3720    var running_minimum = slice[0];
3721    var running_maximum = slice[0];
3722    for (slice[1..]) |item| {
3723        running_minimum = @min(running_minimum, item);
3724        running_maximum = @max(running_maximum, item);
3725    }
3726    return .{ running_minimum, running_maximum };
3727}
3728
3729test minMax {
3730    {
3731        const actual_min, const actual_max = minMax(u8, "abcdefg");
3732        try testing.expectEqual(@as(u8, 'a'), actual_min);
3733        try testing.expectEqual(@as(u8, 'g'), actual_max);
3734    }
3735    {
3736        const actual_min, const actual_max = minMax(u8, "bcdefga");
3737        try testing.expectEqual(@as(u8, 'a'), actual_min);
3738        try testing.expectEqual(@as(u8, 'g'), actual_max);
3739    }
3740    {
3741        const actual_min, const actual_max = minMax(u8, "a");
3742        try testing.expectEqual(@as(u8, 'a'), actual_min);
3743        try testing.expectEqual(@as(u8, 'a'), actual_max);
3744    }
3745}
3746
3747/// Deprecated in favor of `findMin`.
3748pub const indexOfMin = findMin;
3749
3750/// Returns the index of the smallest number in a slice. O(n).
3751/// `slice` must not be empty.
3752pub fn findMin(comptime T: type, slice: []const T) usize {
3753    assert(slice.len > 0);
3754    var best = slice[0];
3755    var index: usize = 0;
3756    for (slice[1..], 0..) |item, i| {
3757        if (item < best) {
3758            best = item;
3759            index = i + 1;
3760        }
3761    }
3762    return index;
3763}
3764
3765test findMin {
3766    try testing.expectEqual(findMin(u8, "abcdefg"), 0);
3767    try testing.expectEqual(findMin(u8, "bcdefga"), 6);
3768    try testing.expectEqual(findMin(u8, "a"), 0);
3769}
3770
3771pub const indexOfMax = findMax;
3772
3773/// Returns the index of the largest number in a slice. O(n).
3774/// `slice` must not be empty.
3775pub fn findMax(comptime T: type, slice: []const T) usize {
3776    assert(slice.len > 0);
3777    var best = slice[0];
3778    var index: usize = 0;
3779    for (slice[1..], 0..) |item, i| {
3780        if (item > best) {
3781            best = item;
3782            index = i + 1;
3783        }
3784    }
3785    return index;
3786}
3787
3788test findMax {
3789    try testing.expectEqual(findMax(u8, "abcdefg"), 6);
3790    try testing.expectEqual(findMax(u8, "gabcdef"), 0);
3791    try testing.expectEqual(findMax(u8, "a"), 0);
3792}
3793
3794/// Deprecated in favor of `findMinMax`.
3795pub const indexOfMinMax = findMinMax;
3796
3797/// Finds the indices of the smallest and largest number in a slice. O(n).
3798/// Returns the indices of the smallest and largest numbers in that order.
3799/// `slice` must not be empty.
3800pub fn findMinMax(comptime T: type, slice: []const T) struct { usize, usize } {
3801    assert(slice.len > 0);
3802    var minVal = slice[0];
3803    var maxVal = slice[0];
3804    var minIdx: usize = 0;
3805    var maxIdx: usize = 0;
3806    for (slice[1..], 0..) |item, i| {
3807        if (item < minVal) {
3808            minVal = item;
3809            minIdx = i + 1;
3810        }
3811        if (item > maxVal) {
3812            maxVal = item;
3813            maxIdx = i + 1;
3814        }
3815    }
3816    return .{ minIdx, maxIdx };
3817}
3818
3819test findMinMax {
3820    try testing.expectEqual(.{ 0, 6 }, findMinMax(u8, "abcdefg"));
3821    try testing.expectEqual(.{ 1, 0 }, findMinMax(u8, "gabcdef"));
3822    try testing.expectEqual(.{ 0, 0 }, findMinMax(u8, "a"));
3823}
3824
3825/// Exchanges contents of two memory locations.
3826pub fn swap(comptime T: type, noalias a: *T, noalias b: *T) void {
3827    if (@inComptime()) {
3828        // In comptime, accessing bytes of values with no defined layout is a compile error.
3829        const tmp = a.*;
3830        a.* = b.*;
3831        b.* = tmp;
3832    } else {
3833        // Swapping in streaming nature from start to end instead of swapping
3834        // everything in one step allows easier optimizations and less stack usage.
3835        const a_bytes: []align(@alignOf(T)) u8 = @ptrCast(a);
3836        const b_bytes: []align(@alignOf(T)) u8 = @ptrCast(b);
3837        for (a_bytes, b_bytes) |*ab, *bb| {
3838            const tmp = ab.*;
3839            ab.* = bb.*;
3840            bb.* = tmp;
3841        }
3842    }
3843}
3844
3845test "swap works at comptime with types with no defined layout" {
3846    comptime {
3847        const T = struct { val: u64 };
3848        var a: T = .{ .val = 0 };
3849        var b: T = .{ .val = 1 };
3850        swap(T, &a, &b);
3851        try testing.expectEqual(T{ .val = 1 }, a);
3852        try testing.expectEqual(T{ .val = 0 }, b);
3853    }
3854}
3855
3856inline fn reverseVector(comptime N: usize, comptime T: type, a: []T) [N]T {
3857    var res: [N]T = undefined;
3858    inline for (0..N) |i| {
3859        res[i] = a[N - i - 1];
3860    }
3861    return res;
3862}
3863
3864/// In-place order reversal of a slice
3865pub fn reverse(comptime T: type, items: []T) void {
3866    var i: usize = 0;
3867    const end = items.len / 2;
3868    if (use_vectors and
3869        !@inComptime() and
3870        @bitSizeOf(T) > 0 and
3871        std.math.isPowerOfTwo(@bitSizeOf(T)))
3872    {
3873        if (std.simd.suggestVectorLength(T)) |simd_size| {
3874            if (simd_size <= end) {
3875                const simd_end = end - (simd_size - 1);
3876                while (i < simd_end) : (i += simd_size) {
3877                    const left_slice = items[i .. i + simd_size];
3878                    const right_slice = items[items.len - i - simd_size .. items.len - i];
3879
3880                    const left_shuffled: [simd_size]T = reverseVector(simd_size, T, left_slice);
3881                    const right_shuffled: [simd_size]T = reverseVector(simd_size, T, right_slice);
3882
3883                    @memcpy(right_slice, &left_shuffled);
3884                    @memcpy(left_slice, &right_shuffled);
3885                }
3886            }
3887        }
3888    }
3889
3890    while (i < end) : (i += 1) {
3891        swap(T, &items[i], &items[items.len - i - 1]);
3892    }
3893}
3894
3895test reverse {
3896    {
3897        var arr = [_]i32{ 5, 3, 1, 2, 4 };
3898        reverse(i32, arr[0..]);
3899        try testing.expectEqualSlices(i32, &arr, &.{ 4, 2, 1, 3, 5 });
3900    }
3901    {
3902        var arr = [_]u0{};
3903        reverse(u0, arr[0..]);
3904        try testing.expectEqualSlices(u0, &arr, &.{});
3905    }
3906    {
3907        var arr = [_]i64{ 19, 17, 15, 13, 11, 9, 7, 5, 3, 1, 2, 4, 6, 8, 10, 12, 14, 16, 18 };
3908        reverse(i64, arr[0..]);
3909        try testing.expectEqualSlices(i64, &arr, &.{ 18, 16, 14, 12, 10, 8, 6, 4, 2, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 });
3910    }
3911    {
3912        var arr = [_][]const u8{ "a", "b", "c", "d" };
3913        reverse([]const u8, arr[0..]);
3914        try testing.expectEqualSlices([]const u8, &arr, &.{ "d", "c", "b", "a" });
3915    }
3916    {
3917        const MyType = union(enum) {
3918            a: [3]u8,
3919            b: u24,
3920            c,
3921        };
3922        var arr = [_]MyType{ .{ .a = .{ 0, 0, 0 } }, .{ .b = 0 }, .c };
3923        reverse(MyType, arr[0..]);
3924        try testing.expectEqualSlices(MyType, &arr, &([_]MyType{ .c, .{ .b = 0 }, .{ .a = .{ 0, 0, 0 } } }));
3925    }
3926}
3927fn ReverseIterator(comptime T: type) type {
3928    const ptr = switch (@typeInfo(T)) {
3929        .pointer => |ptr| ptr,
3930        else => @compileError("expected slice or pointer to array, found '" ++ @typeName(T) ++ "'"),
3931    };
3932    switch (ptr.size) {
3933        .slice => {},
3934        .one => if (@typeInfo(ptr.child) != .array) @compileError("expected slice or pointer to array, found '" ++ @typeName(T) ++ "'"),
3935        .many, .c => @compileError("expected slice or pointer to array, found '" ++ @typeName(T) ++ "'"),
3936    }
3937    const Element = std.meta.Elem(T);
3938    const attrs: std.builtin.Type.Pointer.Attributes = .{
3939        .@"const" = ptr.is_const,
3940        .@"volatile" = ptr.is_volatile,
3941        .@"allowzero" = ptr.is_allowzero,
3942        .@"align" = ptr.alignment,
3943        .@"addrspace" = ptr.address_space,
3944    };
3945    const Pointer = @Pointer(.many, attrs, Element, std.meta.sentinel(T));
3946    const ElementPointer = @Pointer(.one, attrs, Element, null);
3947    return struct {
3948        ptr: Pointer,
3949        index: usize,
3950        pub fn next(self: *@This()) ?Element {
3951            if (self.index == 0) return null;
3952            self.index -= 1;
3953            return self.ptr[self.index];
3954        }
3955        pub fn nextPtr(self: *@This()) ?ElementPointer {
3956            if (self.index == 0) return null;
3957            self.index -= 1;
3958            return &self.ptr[self.index];
3959        }
3960    };
3961}
3962
3963/// Iterates over a slice in reverse.
3964pub fn reverseIterator(slice: anytype) ReverseIterator(@TypeOf(slice)) {
3965    return .{ .ptr = slice.ptr, .index = slice.len };
3966}
3967
3968test reverseIterator {
3969    {
3970        var it = reverseIterator("abc");
3971        try testing.expectEqual(@as(?u8, 'c'), it.next());
3972        try testing.expectEqual(@as(?u8, 'b'), it.next());
3973        try testing.expectEqual(@as(?u8, 'a'), it.next());
3974        try testing.expectEqual(@as(?u8, null), it.next());
3975    }
3976    {
3977        var array = [2]i32{ 3, 7 };
3978        const slice: []const i32 = &array;
3979        var it = reverseIterator(slice);
3980        try testing.expectEqual(@as(?i32, 7), it.next());
3981        try testing.expectEqual(@as(?i32, 3), it.next());
3982        try testing.expectEqual(@as(?i32, null), it.next());
3983
3984        it = reverseIterator(slice);
3985        try testing.expect(*const i32 == @TypeOf(it.nextPtr().?));
3986        try testing.expectEqual(@as(?i32, 7), it.nextPtr().?.*);
3987        try testing.expectEqual(@as(?i32, 3), it.nextPtr().?.*);
3988        try testing.expectEqual(@as(?*const i32, null), it.nextPtr());
3989
3990        const mut_slice: []i32 = &array;
3991        var mut_it = reverseIterator(mut_slice);
3992        mut_it.nextPtr().?.* += 1;
3993        mut_it.nextPtr().?.* += 2;
3994        try testing.expectEqual([2]i32{ 5, 8 }, array);
3995    }
3996    {
3997        var array = [2]i32{ 3, 7 };
3998        const ptr_to_array: *const [2]i32 = &array;
3999        var it = reverseIterator(ptr_to_array);
4000        try testing.expectEqual(@as(?i32, 7), it.next());
4001        try testing.expectEqual(@as(?i32, 3), it.next());
4002        try testing.expectEqual(@as(?i32, null), it.next());
4003
4004        it = reverseIterator(ptr_to_array);
4005        try testing.expect(*const i32 == @TypeOf(it.nextPtr().?));
4006        try testing.expectEqual(@as(?i32, 7), it.nextPtr().?.*);
4007        try testing.expectEqual(@as(?i32, 3), it.nextPtr().?.*);
4008        try testing.expectEqual(@as(?*const i32, null), it.nextPtr());
4009
4010        const mut_ptr_to_array: *[2]i32 = &array;
4011        var mut_it = reverseIterator(mut_ptr_to_array);
4012        mut_it.nextPtr().?.* += 1;
4013        mut_it.nextPtr().?.* += 2;
4014        try testing.expectEqual([2]i32{ 5, 8 }, array);
4015    }
4016}
4017
4018/// In-place rotation of the values in an array ([0 1 2 3] becomes [1 2 3 0] if we rotate by 1)
4019/// Assumes 0 <= amount <= items.len
4020pub fn rotate(comptime T: type, items: []T, amount: usize) void {
4021    reverse(T, items[0..amount]);
4022    reverse(T, items[amount..]);
4023    reverse(T, items);
4024}
4025
4026test rotate {
4027    var arr = [_]i32{ 5, 3, 1, 2, 4 };
4028    rotate(i32, arr[0..], 2);
4029
4030    try testing.expect(eql(i32, &arr, &[_]i32{ 1, 2, 4, 5, 3 }));
4031}
4032
4033/// Replace needle with replacement as many times as possible, writing to an output buffer which is assumed to be of
4034/// appropriate size. Use replacementSize to calculate an appropriate buffer size.
4035/// The `input` and `output` slices must not overlap.
4036/// The needle must not be empty.
4037/// Returns the number of replacements made.
4038pub fn replace(comptime T: type, input: []const T, needle: []const T, replacement: []const T, output: []T) usize {
4039    // Empty needle will loop until output buffer overflows.
4040    assert(needle.len > 0);
4041
4042    var i: usize = 0;
4043    var slide: usize = 0;
4044    var replacements: usize = 0;
4045    while (slide < input.len) {
4046        if (mem.startsWith(T, input[slide..], needle)) {
4047            @memcpy(output[i..][0..replacement.len], replacement);
4048            i += replacement.len;
4049            slide += needle.len;
4050            replacements += 1;
4051        } else {
4052            output[i] = input[slide];
4053            i += 1;
4054            slide += 1;
4055        }
4056    }
4057
4058    return replacements;
4059}
4060
4061test replace {
4062    var output: [29]u8 = undefined;
4063    var replacements = replace(u8, "All your base are belong to us", "base", "Zig", output[0..]);
4064    var expected: []const u8 = "All your Zig are belong to us";
4065    try testing.expect(replacements == 1);
4066    try testing.expectEqualStrings(expected, output[0..expected.len]);
4067
4068    replacements = replace(u8, "Favor reading code over writing code.", "code", "", output[0..]);
4069    expected = "Favor reading  over writing .";
4070    try testing.expect(replacements == 2);
4071    try testing.expectEqualStrings(expected, output[0..expected.len]);
4072
4073    // Empty needle is not allowed but input may be empty.
4074    replacements = replace(u8, "", "x", "y", output[0..0]);
4075    expected = "";
4076    try testing.expect(replacements == 0);
4077    try testing.expectEqualStrings(expected, output[0..expected.len]);
4078
4079    // Adjacent replacements.
4080
4081    replacements = replace(u8, "\\n\\n", "\\n", "\n", output[0..]);
4082    expected = "\n\n";
4083    try testing.expect(replacements == 2);
4084    try testing.expectEqualStrings(expected, output[0..expected.len]);
4085
4086    replacements = replace(u8, "abbba", "b", "cd", output[0..]);
4087    expected = "acdcdcda";
4088    try testing.expect(replacements == 3);
4089    try testing.expectEqualStrings(expected, output[0..expected.len]);
4090}
4091
4092/// Replace all occurrences of `match` with `replacement`.
4093pub fn replaceScalar(comptime T: type, slice: []T, match: T, replacement: T) void {
4094    for (slice) |*e| {
4095        if (e.* == match)
4096            e.* = replacement;
4097    }
4098}
4099
4100/// Collapse consecutive duplicate elements into one entry.
4101pub fn collapseRepeatsLen(comptime T: type, slice: []T, elem: T) usize {
4102    if (slice.len == 0) return 0;
4103    var write_idx: usize = 1;
4104    var read_idx: usize = 1;
4105    while (read_idx < slice.len) : (read_idx += 1) {
4106        if (slice[read_idx - 1] != elem or slice[read_idx] != elem) {
4107            slice[write_idx] = slice[read_idx];
4108            write_idx += 1;
4109        }
4110    }
4111    return write_idx;
4112}
4113
4114/// Collapse consecutive duplicate elements into one entry.
4115pub fn collapseRepeats(comptime T: type, slice: []T, elem: T) []T {
4116    return slice[0..collapseRepeatsLen(T, slice, elem)];
4117}
4118
4119fn testCollapseRepeats(str: []const u8, elem: u8, expected: []const u8) !void {
4120    const mutable = try std.testing.allocator.dupe(u8, str);
4121    defer std.testing.allocator.free(mutable);
4122    try testing.expect(std.mem.eql(u8, collapseRepeats(u8, mutable, elem), expected));
4123}
4124test collapseRepeats {
4125    try testCollapseRepeats("", '/', "");
4126    try testCollapseRepeats("a", '/', "a");
4127    try testCollapseRepeats("/", '/', "/");
4128    try testCollapseRepeats("//", '/', "/");
4129    try testCollapseRepeats("/a", '/', "/a");
4130    try testCollapseRepeats("//a", '/', "/a");
4131    try testCollapseRepeats("a/", '/', "a/");
4132    try testCollapseRepeats("a//", '/', "a/");
4133    try testCollapseRepeats("a/a", '/', "a/a");
4134    try testCollapseRepeats("a//a", '/', "a/a");
4135    try testCollapseRepeats("//a///a////", '/', "/a/a/");
4136}
4137
4138/// Calculate the size needed in an output buffer to perform a replacement.
4139/// The needle must not be empty.
4140pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, replacement: []const T) usize {
4141    // Empty needle will loop forever.
4142    assert(needle.len > 0);
4143
4144    var i: usize = 0;
4145    var size: usize = input.len;
4146    while (i < input.len) {
4147        if (mem.startsWith(T, input[i..], needle)) {
4148            size = size - needle.len + replacement.len;
4149            i += needle.len;
4150        } else {
4151            i += 1;
4152        }
4153    }
4154
4155    return size;
4156}
4157
4158test replacementSize {
4159    try testing.expect(replacementSize(u8, "All your base are belong to us", "base", "Zig") == 29);
4160    try testing.expect(replacementSize(u8, "Favor reading code over writing code.", "code", "") == 29);
4161    try testing.expect(replacementSize(u8, "Only one obvious way to do things.", "things.", "things in Zig.") == 41);
4162
4163    // Empty needle is not allowed but input may be empty.
4164    try testing.expect(replacementSize(u8, "", "x", "y") == 0);
4165
4166    // Adjacent replacements.
4167    try testing.expect(replacementSize(u8, "\\n\\n", "\\n", "\n") == 2);
4168    try testing.expect(replacementSize(u8, "abbba", "b", "cd") == 8);
4169}
4170
4171/// Perform a replacement on an allocated buffer of pre-determined size. Caller must free returned memory.
4172pub fn replaceOwned(comptime T: type, allocator: Allocator, input: []const T, needle: []const T, replacement: []const T) Allocator.Error![]T {
4173    const output = try allocator.alloc(T, replacementSize(T, input, needle, replacement));
4174    _ = replace(T, input, needle, replacement, output);
4175    return output;
4176}
4177
4178test replaceOwned {
4179    const gpa = std.testing.allocator;
4180
4181    const base_replace = replaceOwned(u8, gpa, "All your base are belong to us", "base", "Zig") catch @panic("out of memory");
4182    defer gpa.free(base_replace);
4183    try testing.expect(eql(u8, base_replace, "All your Zig are belong to us"));
4184
4185    const zen_replace = replaceOwned(u8, gpa, "Favor reading code over writing code.", " code", "") catch @panic("out of memory");
4186    defer gpa.free(zen_replace);
4187    try testing.expect(eql(u8, zen_replace, "Favor reading over writing."));
4188}
4189
4190/// Converts a little-endian integer to host endianness.
4191pub fn littleToNative(comptime T: type, x: T) T {
4192    return switch (native_endian) {
4193        .little => x,
4194        .big => @byteSwap(x),
4195    };
4196}
4197
4198/// Converts a big-endian integer to host endianness.
4199pub fn bigToNative(comptime T: type, x: T) T {
4200    return switch (native_endian) {
4201        .little => @byteSwap(x),
4202        .big => x,
4203    };
4204}
4205
4206/// Converts an integer from specified endianness to host endianness.
4207pub fn toNative(comptime T: type, x: T, endianness_of_x: Endian) T {
4208    return switch (endianness_of_x) {
4209        .little => littleToNative(T, x),
4210        .big => bigToNative(T, x),
4211    };
4212}
4213
4214/// Converts an integer which has host endianness to the desired endianness.
4215pub fn nativeTo(comptime T: type, x: T, desired_endianness: Endian) T {
4216    return switch (desired_endianness) {
4217        .little => nativeToLittle(T, x),
4218        .big => nativeToBig(T, x),
4219    };
4220}
4221
4222/// Converts an integer which has host endianness to little endian.
4223pub fn nativeToLittle(comptime T: type, x: T) T {
4224    return switch (native_endian) {
4225        .little => x,
4226        .big => @byteSwap(x),
4227    };
4228}
4229
4230/// Converts an integer which has host endianness to big endian.
4231pub fn nativeToBig(comptime T: type, x: T) T {
4232    return switch (native_endian) {
4233        .little => @byteSwap(x),
4234        .big => x,
4235    };
4236}
4237
4238/// Returns the number of elements that, if added to the given pointer, align it
4239/// to a multiple of the given quantity, or `null` if one of the following
4240/// conditions is met:
4241/// - The aligned pointer would not fit the address space,
4242/// - The delta required to align the pointer is not a multiple of the pointee's
4243///   type.
4244pub fn alignPointerOffset(ptr: anytype, align_to: usize) ?usize {
4245    assert(isValidAlign(align_to));
4246
4247    const T = @TypeOf(ptr);
4248    const info = @typeInfo(T);
4249    if (info != .pointer or info.pointer.size != .many)
4250        @compileError("expected many item pointer, got " ++ @typeName(T));
4251
4252    // Do nothing if the pointer is already well-aligned.
4253    if (align_to <= info.pointer.alignment)
4254        return 0;
4255
4256    // Calculate the aligned base address with an eye out for overflow.
4257    const addr = @intFromPtr(ptr);
4258    var ov = @addWithOverflow(addr, align_to - 1);
4259    if (ov[1] != 0) return null;
4260    ov[0] &= ~@as(usize, align_to - 1);
4261
4262    // The delta is expressed in terms of bytes, turn it into a number of child
4263    // type elements.
4264    const delta = ov[0] - addr;
4265    const pointee_size = @sizeOf(info.pointer.child);
4266    if (delta % pointee_size != 0) return null;
4267    return delta / pointee_size;
4268}
4269
4270/// Aligns a given pointer value to a specified alignment factor.
4271/// Returns an aligned pointer or null if one of the following conditions is
4272/// met:
4273/// - The aligned pointer would not fit the address space,
4274/// - The delta required to align the pointer is not a multiple of the pointee's
4275///   type.
4276pub fn alignPointer(ptr: anytype, align_to: usize) ?@TypeOf(ptr) {
4277    const adjust_off = alignPointerOffset(ptr, align_to) orelse return null;
4278    // Avoid the use of ptrFromInt to avoid losing the pointer provenance info.
4279    return @alignCast(ptr + adjust_off);
4280}
4281
4282test alignPointer {
4283    const S = struct {
4284        fn checkAlign(comptime T: type, base: usize, align_to: usize, expected: usize) !void {
4285            const ptr: T = @ptrFromInt(base);
4286            const aligned = alignPointer(ptr, align_to);
4287            try testing.expectEqual(expected, @intFromPtr(aligned));
4288        }
4289    };
4290
4291    try S.checkAlign([*]u8, 0x123, 0x200, 0x200);
4292    try S.checkAlign([*]align(4) u8, 0x10, 2, 0x10);
4293    try S.checkAlign([*]u32, 0x10, 2, 0x10);
4294    try S.checkAlign([*]u32, 0x4, 16, 0x10);
4295    // Misaligned.
4296    try S.checkAlign([*]align(1) u32, 0x3, 2, 0);
4297    // Overflow.
4298    try S.checkAlign([*]u32, math.maxInt(usize) - 3, 8, 0);
4299}
4300
4301fn CopyPtrAttrs(
4302    comptime source: type,
4303    comptime size: std.builtin.Type.Pointer.Size,
4304    comptime child: type,
4305) type {
4306    const ptr = @typeInfo(source).pointer;
4307    return @Pointer(size, .{
4308        .@"const" = ptr.is_const,
4309        .@"volatile" = ptr.is_volatile,
4310        .@"allowzero" = ptr.is_allowzero,
4311        .@"align" = ptr.alignment,
4312        .@"addrspace" = ptr.address_space,
4313    }, child, null);
4314}
4315
4316fn AsBytesReturnType(comptime P: type) type {
4317    const pointer = @typeInfo(P).pointer;
4318    assert(pointer.size == .one);
4319    const size = @sizeOf(pointer.child);
4320    return CopyPtrAttrs(P, .one, [size]u8);
4321}
4322
4323/// Given a pointer to a single item, returns a slice of the underlying bytes, preserving pointer attributes.
4324pub fn asBytes(ptr: anytype) AsBytesReturnType(@TypeOf(ptr)) {
4325    return @ptrCast(@alignCast(ptr));
4326}
4327
4328test asBytes {
4329    const deadbeef = @as(u32, 0xDEADBEEF);
4330    const deadbeef_bytes = switch (native_endian) {
4331        .big => "\xDE\xAD\xBE\xEF",
4332        .little => "\xEF\xBE\xAD\xDE",
4333    };
4334
4335    try testing.expect(eql(u8, asBytes(&deadbeef), deadbeef_bytes));
4336
4337    var codeface = @as(u32, 0xC0DEFACE);
4338    for (asBytes(&codeface)) |*b|
4339        b.* = 0;
4340    try testing.expect(codeface == 0);
4341
4342    const S = packed struct {
4343        a: u8,
4344        b: u8,
4345        c: u8,
4346        d: u8,
4347    };
4348
4349    const inst = S{
4350        .a = 0xBE,
4351        .b = 0xEF,
4352        .c = 0xDE,
4353        .d = 0xA1,
4354    };
4355    switch (native_endian) {
4356        .little => {
4357            try testing.expect(eql(u8, asBytes(&inst), "\xBE\xEF\xDE\xA1"));
4358        },
4359        .big => {
4360            try testing.expect(eql(u8, asBytes(&inst), "\xA1\xDE\xEF\xBE"));
4361        },
4362    }
4363
4364    const ZST = struct {};
4365    const zero = ZST{};
4366    try testing.expect(eql(u8, asBytes(&zero), ""));
4367}
4368
4369test "asBytes preserves pointer attributes" {
4370    const inArr: u32 align(16) = 0xDEADBEEF;
4371    const inPtr = @as(*align(16) const volatile u32, @ptrCast(&inArr));
4372    const outSlice = asBytes(inPtr);
4373
4374    const in = @typeInfo(@TypeOf(inPtr)).pointer;
4375    const out = @typeInfo(@TypeOf(outSlice)).pointer;
4376
4377    try testing.expectEqual(in.is_const, out.is_const);
4378    try testing.expectEqual(in.is_volatile, out.is_volatile);
4379    try testing.expectEqual(in.is_allowzero, out.is_allowzero);
4380    try testing.expectEqual(in.alignment, out.alignment);
4381}
4382
4383/// Given any value, returns a copy of its bytes in an array.
4384pub fn toBytes(value: anytype) [@sizeOf(@TypeOf(value))]u8 {
4385    return asBytes(&value).*;
4386}
4387
4388test toBytes {
4389    var my_bytes = toBytes(@as(u32, 0x12345678));
4390    switch (native_endian) {
4391        .big => try testing.expect(eql(u8, &my_bytes, "\x12\x34\x56\x78")),
4392        .little => try testing.expect(eql(u8, &my_bytes, "\x78\x56\x34\x12")),
4393    }
4394
4395    my_bytes[0] = '\x99';
4396    switch (native_endian) {
4397        .big => try testing.expect(eql(u8, &my_bytes, "\x99\x34\x56\x78")),
4398        .little => try testing.expect(eql(u8, &my_bytes, "\x99\x56\x34\x12")),
4399    }
4400}
4401
4402fn BytesAsValueReturnType(comptime T: type, comptime B: type) type {
4403    return CopyPtrAttrs(B, .one, T);
4404}
4405
4406/// Given a pointer to an array of bytes, returns a pointer to a value of the specified type
4407/// backed by those bytes, preserving pointer attributes.
4408pub fn bytesAsValue(comptime T: type, bytes: anytype) BytesAsValueReturnType(T, @TypeOf(bytes)) {
4409    return @ptrCast(bytes);
4410}
4411
4412test bytesAsValue {
4413    const deadbeef = @as(u32, 0xDEADBEEF);
4414    const deadbeef_bytes = switch (native_endian) {
4415        .big => "\xDE\xAD\xBE\xEF",
4416        .little => "\xEF\xBE\xAD\xDE",
4417    };
4418
4419    try testing.expect(deadbeef == bytesAsValue(u32, deadbeef_bytes).*);
4420
4421    var codeface_bytes: [4]u8 = switch (native_endian) {
4422        .big => "\xC0\xDE\xFA\xCE",
4423        .little => "\xCE\xFA\xDE\xC0",
4424    }.*;
4425    const codeface = bytesAsValue(u32, &codeface_bytes);
4426    try testing.expect(codeface.* == 0xC0DEFACE);
4427    codeface.* = 0;
4428    for (codeface_bytes) |b|
4429        try testing.expect(b == 0);
4430
4431    const S = packed struct {
4432        a: u8,
4433        b: u8,
4434        c: u8,
4435        d: u8,
4436    };
4437
4438    const inst = S{
4439        .a = 0xBE,
4440        .b = 0xEF,
4441        .c = 0xDE,
4442        .d = 0xA1,
4443    };
4444    const inst_bytes = switch (native_endian) {
4445        .little => "\xBE\xEF\xDE\xA1",
4446        .big => "\xA1\xDE\xEF\xBE",
4447    };
4448    const inst2 = bytesAsValue(S, inst_bytes);
4449    try testing.expect(std.meta.eql(inst, inst2.*));
4450}
4451
4452test "bytesAsValue preserves pointer attributes" {
4453    const inArr align(16) = [4]u8{ 0xDE, 0xAD, 0xBE, 0xEF };
4454    const inSlice = @as(*align(16) const volatile [4]u8, @ptrCast(&inArr))[0..];
4455    const outPtr = bytesAsValue(u32, inSlice);
4456
4457    const in = @typeInfo(@TypeOf(inSlice)).pointer;
4458    const out = @typeInfo(@TypeOf(outPtr)).pointer;
4459
4460    try testing.expectEqual(in.is_const, out.is_const);
4461    try testing.expectEqual(in.is_volatile, out.is_volatile);
4462    try testing.expectEqual(in.is_allowzero, out.is_allowzero);
4463    try testing.expectEqual(in.alignment, out.alignment);
4464}
4465
4466/// Given a pointer to an array of bytes, returns a value of the specified type backed by a
4467/// copy of those bytes.
4468pub fn bytesToValue(comptime T: type, bytes: anytype) T {
4469    return bytesAsValue(T, bytes).*;
4470}
4471test bytesToValue {
4472    const deadbeef_bytes = switch (native_endian) {
4473        .big => "\xDE\xAD\xBE\xEF",
4474        .little => "\xEF\xBE\xAD\xDE",
4475    };
4476
4477    const deadbeef = bytesToValue(u32, deadbeef_bytes);
4478    try testing.expect(deadbeef == @as(u32, 0xDEADBEEF));
4479}
4480
4481fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type {
4482    return CopyPtrAttrs(bytesType, .slice, T);
4483}
4484
4485/// Given a slice of bytes, returns a slice of the specified type
4486/// backed by those bytes, preserving pointer attributes.
4487/// If `T` is zero-bytes sized, the returned slice has a len of zero.
4488pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T, @TypeOf(bytes)) {
4489    // let's not give an undefined pointer to @ptrCast
4490    // it may be equal to zero and fail a null check
4491    if (bytes.len == 0 or @sizeOf(T) == 0) {
4492        return &[0]T{};
4493    }
4494
4495    const cast_target = CopyPtrAttrs(@TypeOf(bytes), .many, T);
4496
4497    return @as(cast_target, @ptrCast(bytes))[0..@divExact(bytes.len, @sizeOf(T))];
4498}
4499
4500test bytesAsSlice {
4501    {
4502        const bytes = [_]u8{ 0xDE, 0xAD, 0xBE, 0xEF };
4503        const slice = bytesAsSlice(u16, bytes[0..]);
4504        try testing.expect(slice.len == 2);
4505        try testing.expect(bigToNative(u16, slice[0]) == 0xDEAD);
4506        try testing.expect(bigToNative(u16, slice[1]) == 0xBEEF);
4507    }
4508    {
4509        const bytes = [_]u8{ 0xDE, 0xAD, 0xBE, 0xEF };
4510        var runtime_zero: usize = 0;
4511        _ = &runtime_zero;
4512        const slice = bytesAsSlice(u16, bytes[runtime_zero..]);
4513        try testing.expect(slice.len == 2);
4514        try testing.expect(bigToNative(u16, slice[0]) == 0xDEAD);
4515        try testing.expect(bigToNative(u16, slice[1]) == 0xBEEF);
4516    }
4517}
4518
4519test "bytesAsSlice keeps pointer alignment" {
4520    {
4521        var bytes = [_]u8{ 0x01, 0x02, 0x03, 0x04 };
4522        const numbers = bytesAsSlice(u32, bytes[0..]);
4523        try comptime testing.expect(@TypeOf(numbers) == []align(@alignOf(@TypeOf(bytes))) u32);
4524    }
4525    {
4526        var bytes = [_]u8{ 0x01, 0x02, 0x03, 0x04 };
4527        var runtime_zero: usize = 0;
4528        _ = &runtime_zero;
4529        const numbers = bytesAsSlice(u32, bytes[runtime_zero..]);
4530        try comptime testing.expect(@TypeOf(numbers) == []align(@alignOf(@TypeOf(bytes))) u32);
4531    }
4532}
4533
4534test "bytesAsSlice on a packed struct" {
4535    const F = packed struct {
4536        a: u8,
4537    };
4538
4539    const b: [1]u8 = .{9};
4540    const f = bytesAsSlice(F, &b);
4541    try testing.expect(f[0].a == 9);
4542}
4543
4544test "bytesAsSlice with specified alignment" {
4545    var bytes align(4) = [_]u8{
4546        0x33,
4547        0x33,
4548        0x33,
4549        0x33,
4550    };
4551    const slice: []u32 = std.mem.bytesAsSlice(u32, bytes[0..]);
4552    try testing.expect(slice[0] == 0x33333333);
4553}
4554
4555test "bytesAsSlice preserves pointer attributes" {
4556    const inArr align(16) = [4]u8{ 0xDE, 0xAD, 0xBE, 0xEF };
4557    const inSlice = @as(*align(16) const volatile [4]u8, @ptrCast(&inArr))[0..];
4558    const outSlice = bytesAsSlice(u16, inSlice);
4559
4560    const in = @typeInfo(@TypeOf(inSlice)).pointer;
4561    const out = @typeInfo(@TypeOf(outSlice)).pointer;
4562
4563    try testing.expectEqual(in.is_const, out.is_const);
4564    try testing.expectEqual(in.is_volatile, out.is_volatile);
4565    try testing.expectEqual(in.is_allowzero, out.is_allowzero);
4566    try testing.expectEqual(in.alignment, out.alignment);
4567}
4568
4569test "bytesAsSlice with zero-bit element type" {
4570    {
4571        const bytes = [_]u8{};
4572        const slice = bytesAsSlice(void, &bytes);
4573        try testing.expectEqual(0, slice.len);
4574    }
4575    {
4576        const bytes = [_]u8{ 0x01, 0x02, 0x03, 0x04 };
4577        const slice = bytesAsSlice(u0, &bytes);
4578        try testing.expectEqual(0, slice.len);
4579    }
4580}
4581
4582fn SliceAsBytesReturnType(comptime Slice: type) type {
4583    return CopyPtrAttrs(Slice, .slice, u8);
4584}
4585
4586/// Given a slice, returns a slice of the underlying bytes, preserving pointer attributes.
4587pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) {
4588    const Slice = @TypeOf(slice);
4589
4590    // a slice of zero-bit values always occupies zero bytes
4591    if (@sizeOf(std.meta.Elem(Slice)) == 0) return &[0]u8{};
4592
4593    // let's not give an undefined pointer to @ptrCast
4594    // it may be equal to zero and fail a null check
4595    if (slice.len == 0 and std.meta.sentinel(Slice) == null) return &[0]u8{};
4596
4597    const cast_target = CopyPtrAttrs(Slice, .many, u8);
4598
4599    return @as(cast_target, @ptrCast(slice))[0 .. slice.len * @sizeOf(std.meta.Elem(Slice))];
4600}
4601
4602test sliceAsBytes {
4603    const bytes = [_]u16{ 0xDEAD, 0xBEEF };
4604    const slice = sliceAsBytes(bytes[0..]);
4605    try testing.expect(slice.len == 4);
4606    try testing.expect(eql(u8, slice, switch (native_endian) {
4607        .big => "\xDE\xAD\xBE\xEF",
4608        .little => "\xAD\xDE\xEF\xBE",
4609    }));
4610}
4611
4612test "sliceAsBytes with sentinel slice" {
4613    const empty_string: [:0]const u8 = "";
4614    const bytes = sliceAsBytes(empty_string);
4615    try testing.expect(bytes.len == 0);
4616}
4617
4618test "sliceAsBytes with zero-bit element type" {
4619    const lots_of_nothing = [1]void{{}} ** 10_000;
4620    const bytes = sliceAsBytes(&lots_of_nothing);
4621    try testing.expect(bytes.len == 0);
4622}
4623
4624test "sliceAsBytes packed struct at runtime and comptime" {
4625    const Foo = packed struct {
4626        a: u4,
4627        b: u4,
4628    };
4629    const S = struct {
4630        fn doTheTest() !void {
4631            var foo: Foo = undefined;
4632            var slice = sliceAsBytes(@as(*[1]Foo, &foo)[0..1]);
4633            slice[0] = 0x13;
4634            try testing.expect(foo.a == 0x3);
4635            try testing.expect(foo.b == 0x1);
4636        }
4637    };
4638    try S.doTheTest();
4639    try comptime S.doTheTest();
4640}
4641
4642test "sliceAsBytes and bytesAsSlice back" {
4643    try testing.expect(@sizeOf(i32) == 4);
4644
4645    var big_thing_array = [_]i32{ 1, 2, 3, 4 };
4646    const big_thing_slice: []i32 = big_thing_array[0..];
4647
4648    const bytes = sliceAsBytes(big_thing_slice);
4649    try testing.expect(bytes.len == 4 * 4);
4650
4651    bytes[4] = 0;
4652    bytes[5] = 0;
4653    bytes[6] = 0;
4654    bytes[7] = 0;
4655    try testing.expect(big_thing_slice[1] == 0);
4656
4657    const big_thing_again = bytesAsSlice(i32, bytes);
4658    try testing.expect(big_thing_again[2] == 3);
4659
4660    big_thing_again[2] = -1;
4661    try testing.expect(bytes[8] == math.maxInt(u8));
4662    try testing.expect(bytes[9] == math.maxInt(u8));
4663    try testing.expect(bytes[10] == math.maxInt(u8));
4664    try testing.expect(bytes[11] == math.maxInt(u8));
4665}
4666
4667test "sliceAsBytes preserves pointer attributes" {
4668    const inArr align(16) = [2]u16{ 0xDEAD, 0xBEEF };
4669    const inSlice = @as(*align(16) const volatile [2]u16, @ptrCast(&inArr))[0..];
4670    const outSlice = sliceAsBytes(inSlice);
4671
4672    const in = @typeInfo(@TypeOf(inSlice)).pointer;
4673    const out = @typeInfo(@TypeOf(outSlice)).pointer;
4674
4675    try testing.expectEqual(in.is_const, out.is_const);
4676    try testing.expectEqual(in.is_volatile, out.is_volatile);
4677    try testing.expectEqual(in.is_allowzero, out.is_allowzero);
4678    try testing.expectEqual(in.alignment, out.alignment);
4679}
4680
4681/// Round an address down to the next (or current) aligned address.
4682/// Unlike `alignForward`, `alignment` can be any positive number, not just a power of 2.
4683pub fn alignForwardAnyAlign(comptime T: type, addr: T, alignment: T) T {
4684    if (isValidAlignGeneric(T, alignment))
4685        return alignForward(T, addr, alignment);
4686    assert(alignment != 0);
4687    return alignBackwardAnyAlign(T, addr + (alignment - 1), alignment);
4688}
4689
4690/// Round an address up to the next (or current) aligned address.
4691/// The alignment must be a power of 2 and greater than 0.
4692/// Asserts that rounding up the address does not cause integer overflow.
4693pub fn alignForward(comptime T: type, addr: T, alignment: T) T {
4694    assert(isValidAlignGeneric(T, alignment));
4695    return alignBackward(T, addr + (alignment - 1), alignment);
4696}
4697
4698/// Rounds an address up to the next alignment boundary using log2 representation.
4699/// Equivalent to alignForward with alignment = 1 << log2_alignment.
4700/// More efficient when alignment is known to be a power of 2.
4701pub fn alignForwardLog2(addr: usize, log2_alignment: u8) usize {
4702    const alignment = @as(usize, 1) << @as(math.Log2Int(usize), @intCast(log2_alignment));
4703    return alignForward(usize, addr, alignment);
4704}
4705
4706/// Force an evaluation of the expression; this tries to prevent
4707/// the compiler from optimizing the computation away even if the
4708/// result eventually gets discarded.
4709// TODO: use @declareSideEffect() when it is available - https://github.com/ziglang/zig/issues/6168
4710pub fn doNotOptimizeAway(val: anytype) void {
4711    if (@inComptime()) return;
4712
4713    const max_gp_register_bits = @bitSizeOf(c_long);
4714    const t = @typeInfo(@TypeOf(val));
4715    switch (t) {
4716        .void, .null, .comptime_int, .comptime_float => return,
4717        .@"enum" => doNotOptimizeAway(@intFromEnum(val)),
4718        .bool => doNotOptimizeAway(@intFromBool(val)),
4719        .int => {
4720            const bits = t.int.bits;
4721            if (bits <= max_gp_register_bits and builtin.zig_backend != .stage2_c) {
4722                const val2 = @as(
4723                    std.meta.Int(t.int.signedness, @max(8, std.math.ceilPowerOfTwoAssert(u16, bits))),
4724                    val,
4725                );
4726                asm volatile (""
4727                    :
4728                    : [_] "r" (val2),
4729                );
4730            } else doNotOptimizeAway(&val);
4731        },
4732        .float => {
4733            // https://github.com/llvm/llvm-project/issues/159200
4734            if ((t.float.bits == 32 or t.float.bits == 64) and builtin.zig_backend != .stage2_c and !builtin.cpu.arch.isLoongArch()) {
4735                asm volatile (""
4736                    :
4737                    : [_] "rm" (val),
4738                );
4739            } else doNotOptimizeAway(&val);
4740        },
4741        .pointer => {
4742            if (builtin.zig_backend == .stage2_c) {
4743                doNotOptimizeAwayC(val);
4744            } else {
4745                asm volatile (""
4746                    :
4747                    : [_] "m" (val),
4748                    : .{ .memory = true });
4749            }
4750        },
4751        .array => {
4752            if (t.array.len * @sizeOf(t.array.child) <= 64) {
4753                for (val) |v| doNotOptimizeAway(v);
4754            } else doNotOptimizeAway(&val);
4755        },
4756        else => doNotOptimizeAway(&val),
4757    }
4758}
4759
4760/// .stage2_c doesn't support asm blocks yet, so use volatile stores instead
4761var deopt_target: if (builtin.zig_backend == .stage2_c) u8 else void = undefined;
4762fn doNotOptimizeAwayC(ptr: anytype) void {
4763    const dest = @as(*volatile u8, @ptrCast(&deopt_target));
4764    for (asBytes(ptr)) |b| {
4765        dest.* = b;
4766    }
4767    dest.* = 0;
4768}
4769
4770test doNotOptimizeAway {
4771    comptime doNotOptimizeAway("test");
4772
4773    doNotOptimizeAway(null);
4774    doNotOptimizeAway(true);
4775    doNotOptimizeAway(0);
4776    doNotOptimizeAway(0.0);
4777    doNotOptimizeAway(@as(u1, 0));
4778    doNotOptimizeAway(@as(u3, 0));
4779    doNotOptimizeAway(@as(u8, 0));
4780    doNotOptimizeAway(@as(u16, 0));
4781    doNotOptimizeAway(@as(u32, 0));
4782    doNotOptimizeAway(@as(u64, 0));
4783    doNotOptimizeAway(@as(u128, 0));
4784    doNotOptimizeAway(@as(u13, 0));
4785    doNotOptimizeAway(@as(u37, 0));
4786    doNotOptimizeAway(@as(u96, 0));
4787    doNotOptimizeAway(@as(u200, 0));
4788    doNotOptimizeAway(@as(f32, 0.0));
4789    doNotOptimizeAway(@as(f64, 0.0));
4790    doNotOptimizeAway([_]u8{0} ** 4);
4791    doNotOptimizeAway([_]u8{0} ** 100);
4792    doNotOptimizeAway(@as(std.builtin.Endian, .little));
4793}
4794
4795test alignForward {
4796    try testing.expect(alignForward(usize, 1, 1) == 1);
4797    try testing.expect(alignForward(usize, 2, 1) == 2);
4798    try testing.expect(alignForward(usize, 1, 2) == 2);
4799    try testing.expect(alignForward(usize, 2, 2) == 2);
4800    try testing.expect(alignForward(usize, 3, 2) == 4);
4801    try testing.expect(alignForward(usize, 4, 2) == 4);
4802    try testing.expect(alignForward(usize, 7, 8) == 8);
4803    try testing.expect(alignForward(usize, 8, 8) == 8);
4804    try testing.expect(alignForward(usize, 9, 8) == 16);
4805    try testing.expect(alignForward(usize, 15, 8) == 16);
4806    try testing.expect(alignForward(usize, 16, 8) == 16);
4807    try testing.expect(alignForward(usize, 17, 8) == 24);
4808}
4809
4810/// Round an address down to the previous (or current) aligned address.
4811/// Unlike `alignBackward`, `alignment` can be any positive number, not just a power of 2.
4812pub fn alignBackwardAnyAlign(comptime T: type, addr: T, alignment: T) T {
4813    if (isValidAlignGeneric(T, alignment))
4814        return alignBackward(T, addr, alignment);
4815    assert(alignment != 0);
4816    return addr - @mod(addr, alignment);
4817}
4818
4819/// Round an address down to the previous (or current) aligned address.
4820/// The alignment must be a power of 2 and greater than 0.
4821pub fn alignBackward(comptime T: type, addr: T, alignment: T) T {
4822    assert(isValidAlignGeneric(T, alignment));
4823    // 000010000 // example alignment
4824    // 000001111 // subtract 1
4825    // 111110000 // binary not
4826    return addr & ~(alignment - 1);
4827}
4828
4829/// Returns whether `alignment` is a valid alignment, meaning it is
4830/// a positive power of 2.
4831pub fn isValidAlign(alignment: usize) bool {
4832    return isValidAlignGeneric(usize, alignment);
4833}
4834
4835/// Returns whether `alignment` is a valid alignment, meaning it is
4836/// a positive power of 2.
4837pub fn isValidAlignGeneric(comptime T: type, alignment: T) bool {
4838    return alignment > 0 and std.math.isPowerOfTwo(alignment);
4839}
4840
4841/// Returns true if i is aligned to the given alignment.
4842/// Works with any positive alignment value, not just powers of 2.
4843/// For power-of-2 alignments, `isAligned` is more efficient.
4844pub fn isAlignedAnyAlign(i: usize, alignment: usize) bool {
4845    if (isValidAlign(alignment))
4846        return isAligned(i, alignment);
4847    assert(alignment != 0);
4848    return 0 == @mod(i, alignment);
4849}
4850
4851/// Returns true if addr is aligned to 2^log2_alignment.
4852/// More efficient than `isAligned` when alignment is known to be a power of 2.
4853/// log2_alignment must be < @bitSizeOf(usize).
4854pub fn isAlignedLog2(addr: usize, log2_alignment: u8) bool {
4855    return @ctz(addr) >= log2_alignment;
4856}
4857
4858/// Given an address and an alignment, return true if the address is a multiple of the alignment
4859/// The alignment must be a power of 2 and greater than 0.
4860pub fn isAligned(addr: usize, alignment: usize) bool {
4861    return isAlignedGeneric(u64, addr, alignment);
4862}
4863
4864/// Generic version of `isAligned` that works with any integer type.
4865/// Returns true if addr is aligned to the given alignment.
4866/// Alignment must be a power of 2 and greater than 0.
4867pub fn isAlignedGeneric(comptime T: type, addr: T, alignment: T) bool {
4868    return alignBackward(T, addr, alignment) == addr;
4869}
4870
4871test isAligned {
4872    try testing.expect(isAligned(0, 4));
4873    try testing.expect(isAligned(1, 1));
4874    try testing.expect(isAligned(2, 1));
4875    try testing.expect(isAligned(2, 2));
4876    try testing.expect(!isAligned(2, 4));
4877    try testing.expect(isAligned(3, 1));
4878    try testing.expect(!isAligned(3, 2));
4879    try testing.expect(!isAligned(3, 4));
4880    try testing.expect(isAligned(4, 4));
4881    try testing.expect(isAligned(4, 2));
4882    try testing.expect(isAligned(4, 1));
4883    try testing.expect(!isAligned(4, 8));
4884    try testing.expect(!isAligned(4, 16));
4885}
4886
4887test "freeing empty string with null-terminated sentinel" {
4888    const empty_string = try testing.allocator.dupeZ(u8, "");
4889    testing.allocator.free(empty_string);
4890}
4891
4892/// Returns a slice with the given new alignment,
4893/// all other pointer attributes copied from `AttributeSource`.
4894fn AlignedSlice(comptime AttributeSource: type, comptime new_alignment: usize) type {
4895    const ptr = @typeInfo(AttributeSource).pointer;
4896    return @Pointer(.slice, .{
4897        .@"const" = ptr.is_const,
4898        .@"volatile" = ptr.is_volatile,
4899        .@"allowzero" = ptr.is_allowzero,
4900        .@"align" = new_alignment,
4901        .@"addrspace" = ptr.address_space,
4902    }, ptr.child, null);
4903}
4904
4905/// Returns the largest slice in the given bytes that conforms to the new alignment,
4906/// or `null` if the given bytes contain no conforming address.
4907pub fn alignInBytes(bytes: []u8, comptime new_alignment: usize) ?[]align(new_alignment) u8 {
4908    const begin_address = @intFromPtr(bytes.ptr);
4909    const end_address = begin_address + bytes.len;
4910
4911    const begin_address_aligned = mem.alignForward(usize, begin_address, new_alignment);
4912    const new_length = std.math.sub(usize, end_address, begin_address_aligned) catch |e| switch (e) {
4913        error.Overflow => return null,
4914    };
4915    const alignment_offset = begin_address_aligned - begin_address;
4916    return @alignCast(bytes[alignment_offset .. alignment_offset + new_length]);
4917}
4918
4919/// Returns the largest sub-slice within the given slice that conforms to the new alignment,
4920/// or `null` if the given slice contains no conforming address.
4921pub fn alignInSlice(slice: anytype, comptime new_alignment: usize) ?AlignedSlice(@TypeOf(slice), new_alignment) {
4922    const bytes = sliceAsBytes(slice);
4923    const aligned_bytes = alignInBytes(bytes, new_alignment) orelse return null;
4924
4925    const Element = @TypeOf(slice[0]);
4926    const slice_length_bytes = aligned_bytes.len - (aligned_bytes.len % @sizeOf(Element));
4927    const aligned_slice = bytesAsSlice(Element, aligned_bytes[0..slice_length_bytes]);
4928    return @alignCast(aligned_slice);
4929}
4930
4931test "read/write(Var)PackedInt" {
4932    switch (builtin.cpu.arch) {
4933        // This test generates too much code to execute on WASI.
4934        // LLVM backend fails with "too many locals: locals exceed maximum"
4935        .wasm32, .wasm64 => return error.SkipZigTest,
4936        else => {},
4937    }
4938
4939    const foreign_endian: Endian = if (native_endian == .big) .little else .big;
4940    const expect = std.testing.expect;
4941    var prng = std.Random.DefaultPrng.init(1234);
4942    const random = prng.random();
4943
4944    @setEvalBranchQuota(10_000);
4945    inline for ([_]type{ u8, u16, u32, u128 }) |BackingType| {
4946        for ([_]BackingType{
4947            @as(BackingType, 0), // all zeros
4948            -%@as(BackingType, 1), // all ones
4949            random.int(BackingType), // random
4950            random.int(BackingType), // random
4951            random.int(BackingType), // random
4952        }) |init_value| {
4953            const uTs = [_]type{ u1, u3, u7, u8, u9, u10, u15, u16, u86 };
4954            const iTs = [_]type{ i1, i3, i7, i8, i9, i10, i15, i16, i86 };
4955            inline for (uTs ++ iTs) |PackedType| {
4956                if (@bitSizeOf(PackedType) > @bitSizeOf(BackingType))
4957                    continue;
4958
4959                const iPackedType = std.meta.Int(.signed, @bitSizeOf(PackedType));
4960                const uPackedType = std.meta.Int(.unsigned, @bitSizeOf(PackedType));
4961                const Log2T = std.math.Log2Int(BackingType);
4962
4963                const offset_at_end = @bitSizeOf(BackingType) - @bitSizeOf(PackedType);
4964                for ([_]usize{ 0, 1, 7, 8, 9, 10, 15, 16, 86, offset_at_end }) |offset| {
4965                    if (offset > offset_at_end or offset == @bitSizeOf(BackingType))
4966                        continue;
4967
4968                    for ([_]PackedType{
4969                        ~@as(PackedType, 0), // all ones: -1 iN / maxInt uN
4970                        @as(PackedType, 0), // all zeros: 0 iN / 0 uN
4971                        @as(PackedType, @bitCast(@as(iPackedType, math.maxInt(iPackedType)))), // maxInt iN
4972                        @as(PackedType, @bitCast(@as(iPackedType, math.minInt(iPackedType)))), // maxInt iN
4973                        random.int(PackedType), // random
4974                        random.int(PackedType), // random
4975                    }) |write_value| {
4976                        { // Fixed-size Read/Write (Native-endian)
4977
4978                            // Initialize Value
4979                            var value: BackingType = init_value;
4980
4981                            // Read
4982                            const read_value1 = readPackedInt(PackedType, asBytes(&value), offset, native_endian);
4983                            try expect(read_value1 == @as(PackedType, @bitCast(@as(uPackedType, @truncate(value >> @as(Log2T, @intCast(offset)))))));
4984
4985                            // Write
4986                            writePackedInt(PackedType, asBytes(&value), offset, write_value, native_endian);
4987                            try expect(write_value == @as(PackedType, @bitCast(@as(uPackedType, @truncate(value >> @as(Log2T, @intCast(offset)))))));
4988
4989                            // Read again
4990                            const read_value2 = readPackedInt(PackedType, asBytes(&value), offset, native_endian);
4991                            try expect(read_value2 == write_value);
4992
4993                            // Verify bits outside of the target integer are unmodified
4994                            const diff_bits = init_value ^ value;
4995                            if (offset != offset_at_end)
4996                                try expect(diff_bits >> @as(Log2T, @intCast(offset + @bitSizeOf(PackedType))) == 0);
4997                            if (offset != 0)
4998                                try expect(diff_bits << @as(Log2T, @intCast(@bitSizeOf(BackingType) - offset)) == 0);
4999                        }
5000
5001                        { // Fixed-size Read/Write (Foreign-endian)
5002
5003                            // Initialize Value
5004                            var value: BackingType = @byteSwap(init_value);
5005
5006                            // Read
5007                            const read_value1 = readPackedInt(PackedType, asBytes(&value), offset, foreign_endian);
5008                            try expect(read_value1 == @as(PackedType, @bitCast(@as(uPackedType, @truncate(@byteSwap(value) >> @as(Log2T, @intCast(offset)))))));
5009
5010                            // Write
5011                            writePackedInt(PackedType, asBytes(&value), offset, write_value, foreign_endian);
5012                            try expect(write_value == @as(PackedType, @bitCast(@as(uPackedType, @truncate(@byteSwap(value) >> @as(Log2T, @intCast(offset)))))));
5013
5014                            // Read again
5015                            const read_value2 = readPackedInt(PackedType, asBytes(&value), offset, foreign_endian);
5016                            try expect(read_value2 == write_value);
5017
5018                            // Verify bits outside of the target integer are unmodified
5019                            const diff_bits = init_value ^ @byteSwap(value);
5020                            if (offset != offset_at_end)
5021                                try expect(diff_bits >> @as(Log2T, @intCast(offset + @bitSizeOf(PackedType))) == 0);
5022                            if (offset != 0)
5023                                try expect(diff_bits << @as(Log2T, @intCast(@bitSizeOf(BackingType) - offset)) == 0);
5024                        }
5025
5026                        const signedness = @typeInfo(PackedType).int.signedness;
5027                        const NextPowerOfTwoInt = std.meta.Int(signedness, try comptime std.math.ceilPowerOfTwo(u16, @bitSizeOf(PackedType)));
5028                        const ui64 = std.meta.Int(signedness, 64);
5029                        inline for ([_]type{ PackedType, NextPowerOfTwoInt, ui64 }) |U| {
5030                            { // Variable-size Read/Write (Native-endian)
5031
5032                                if (@bitSizeOf(U) < @bitSizeOf(PackedType))
5033                                    continue;
5034
5035                                // Initialize Value
5036                                var value: BackingType = init_value;
5037
5038                                // Read
5039                                const read_value1 = readVarPackedInt(U, asBytes(&value), offset, @bitSizeOf(PackedType), native_endian, signedness);
5040                                try expect(read_value1 == @as(PackedType, @bitCast(@as(uPackedType, @truncate(value >> @as(Log2T, @intCast(offset)))))));
5041
5042                                // Write
5043                                writeVarPackedInt(asBytes(&value), offset, @bitSizeOf(PackedType), @as(U, write_value), native_endian);
5044                                try expect(write_value == @as(PackedType, @bitCast(@as(uPackedType, @truncate(value >> @as(Log2T, @intCast(offset)))))));
5045
5046                                // Read again
5047                                const read_value2 = readVarPackedInt(U, asBytes(&value), offset, @bitSizeOf(PackedType), native_endian, signedness);
5048                                try expect(read_value2 == write_value);
5049
5050                                // Verify bits outside of the target integer are unmodified
5051                                const diff_bits = init_value ^ value;
5052                                if (offset != offset_at_end)
5053                                    try expect(diff_bits >> @as(Log2T, @intCast(offset + @bitSizeOf(PackedType))) == 0);
5054                                if (offset != 0)
5055                                    try expect(diff_bits << @as(Log2T, @intCast(@bitSizeOf(BackingType) - offset)) == 0);
5056                            }
5057
5058                            { // Variable-size Read/Write (Foreign-endian)
5059
5060                                if (@bitSizeOf(U) < @bitSizeOf(PackedType))
5061                                    continue;
5062
5063                                // Initialize Value
5064                                var value: BackingType = @byteSwap(init_value);
5065
5066                                // Read
5067                                const read_value1 = readVarPackedInt(U, asBytes(&value), offset, @bitSizeOf(PackedType), foreign_endian, signedness);
5068                                try expect(read_value1 == @as(PackedType, @bitCast(@as(uPackedType, @truncate(@byteSwap(value) >> @as(Log2T, @intCast(offset)))))));
5069
5070                                // Write
5071                                writeVarPackedInt(asBytes(&value), offset, @bitSizeOf(PackedType), @as(U, write_value), foreign_endian);
5072                                try expect(write_value == @as(PackedType, @bitCast(@as(uPackedType, @truncate(@byteSwap(value) >> @as(Log2T, @intCast(offset)))))));
5073
5074                                // Read again
5075                                const read_value2 = readVarPackedInt(U, asBytes(&value), offset, @bitSizeOf(PackedType), foreign_endian, signedness);
5076                                try expect(read_value2 == write_value);
5077
5078                                // Verify bits outside of the target integer are unmodified
5079                                const diff_bits = init_value ^ @byteSwap(value);
5080                                if (offset != offset_at_end)
5081                                    try expect(diff_bits >> @as(Log2T, @intCast(offset + @bitSizeOf(PackedType))) == 0);
5082                                if (offset != 0)
5083                                    try expect(diff_bits << @as(Log2T, @intCast(@bitSizeOf(BackingType) - offset)) == 0);
5084                            }
5085                        }
5086                    }
5087                }
5088            }
5089        }
5090    }
5091}