| ... | @@ -166,6 +166,8 @@ pub fn ValidationAllocator(comptime T: type) type { | ... | @@ -166,6 +166,8 @@ pub fn ValidationAllocator(comptime T: type) type { |
| 166 | }; | 166 | }; |
| 167 | } | 167 | } |
| 168 | | 168 | |
| | 169 | /// Wraps an allocator with basic validation checks. |
| | 170 | /// Asserts that allocation sizes are greater than zero and returned pointers have correct alignment. |
| 169 | pub fn validationWrap(allocator: anytype) ValidationAllocator(@TypeOf(allocator)) { | 171 | pub fn validationWrap(allocator: anytype) ValidationAllocator(@TypeOf(allocator)) { |
| 170 | return ValidationAllocator(@TypeOf(allocator)).init(allocator); | 172 | return ValidationAllocator(@TypeOf(allocator)).init(allocator); |
| 171 | } | 173 | } |
| ... | @@ -597,6 +599,12 @@ test zeroInit { | ... | @@ -597,6 +599,12 @@ test zeroInit { |
| 597 | }, nested_baz); | 599 | }, nested_baz); |
| 598 | } | 600 | } |
| 599 | | 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`. |
| 600 | pub fn sort( | 608 | pub fn sort( |
| 601 | comptime T: type, | 609 | comptime T: type, |
| 602 | items: []T, | 610 | items: []T, |
| ... | @@ -606,6 +614,12 @@ pub fn sort( | ... | @@ -606,6 +614,12 @@ pub fn sort( |
| 606 | std.sort.block(T, items, context, lessThanFn); | 614 | std.sort.block(T, items, context, lessThanFn); |
| 607 | } | 615 | } |
| 608 | | 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`. |
| 609 | pub fn sortUnstable( | 623 | pub fn sortUnstable( |
| 610 | comptime T: type, | 624 | comptime T: type, |
| 611 | items: []T, | 625 | items: []T, |
| ... | @@ -621,6 +635,12 @@ pub fn sortContext(a: usize, b: usize, context: anytype) void { | ... | @@ -621,6 +635,12 @@ pub fn sortContext(a: usize, b: usize, context: anytype) void { |
| 621 | std.sort.insertionContext(a, b, context); | 635 | std.sort.insertionContext(a, b, context); |
| 622 | } | 636 | } |
| 623 | | 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. |
| 624 | pub fn sortUnstableContext(a: usize, b: usize, context: anytype) void { | 644 | pub fn sortUnstableContext(a: usize, b: usize, context: anytype) void { |
| 625 | std.sort.pdqContext(a, b, context); | 645 | std.sort.pdqContext(a, b, context); |
| 626 | } | 646 | } |
| ... | @@ -1089,6 +1109,8 @@ test len { | ... | @@ -1089,6 +1109,8 @@ test len { |
| 1089 | try testing.expect(len(c_ptr) == 2); | 1109 | try testing.expect(len(c_ptr) == 2); |
| 1090 | } | 1110 | } |
| 1091 | | 1111 | |
| | 1112 | /// Returns the index of the sentinel value in a sentinel-terminated pointer. |
| | 1113 | /// Linear search through memory until the sentinel is found. |
| 1092 | pub fn indexOfSentinel(comptime T: type, comptime sentinel: T, p: [*:sentinel]const T) usize { | 1114 | pub fn indexOfSentinel(comptime T: type, comptime sentinel: T, p: [*:sentinel]const T) usize { |
| 1093 | var i: usize = 0; | 1115 | var i: usize = 0; |
| 1094 | | 1116 | |
| ... | @@ -1255,6 +1277,8 @@ pub fn lastIndexOfScalar(comptime T: type, slice: []const T, value: T) ?usize { | ... | @@ -1255,6 +1277,8 @@ pub fn lastIndexOfScalar(comptime T: type, slice: []const T, value: T) ?usize { |
| 1255 | return null; | 1277 | return null; |
| 1256 | } | 1278 | } |
| 1257 | | 1279 | |
| | 1280 | /// Linear search for the index of a scalar value inside a slice, starting from a given position. |
| | 1281 | /// Returns null if the value is not found. |
| 1258 | pub fn indexOfScalarPos(comptime T: type, slice: []const T, start_index: usize, value: T) ?usize { | 1282 | pub fn indexOfScalarPos(comptime T: type, slice: []const T, start_index: usize, value: T) ?usize { |
| 1259 | if (start_index >= slice.len) return null; | 1283 | if (start_index >= slice.len) return null; |
| 1260 | | 1284 | |
| ... | @@ -1331,10 +1355,14 @@ test indexOfScalarPos { | ... | @@ -1331,10 +1355,14 @@ test indexOfScalarPos { |
| 1331 | } | 1355 | } |
| 1332 | } | 1356 | } |
| 1333 | | 1357 | |
| | 1358 | /// Linear search for the index of any value in the provided list inside a slice. |
| | 1359 | /// Returns null if no values are found. |
| 1334 | pub fn indexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize { | 1360 | pub fn indexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize { |
| 1335 | return indexOfAnyPos(T, slice, 0, values); | 1361 | return indexOfAnyPos(T, slice, 0, values); |
| 1336 | } | 1362 | } |
| 1337 | | 1363 | |
| | 1364 | /// Linear search for the last index of any value in the provided list inside a slice. |
| | 1365 | /// Returns null if no values are found. |
| 1338 | pub fn lastIndexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize { | 1366 | pub fn lastIndexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize { |
| 1339 | var i: usize = slice.len; | 1367 | var i: usize = slice.len; |
| 1340 | while (i != 0) { | 1368 | while (i != 0) { |
| ... | @@ -1346,6 +1374,8 @@ pub fn lastIndexOfAny(comptime T: type, slice: []const T, values: []const T) ?us | ... | @@ -1346,6 +1374,8 @@ pub fn lastIndexOfAny(comptime T: type, slice: []const T, values: []const T) ?us |
| 1346 | return null; | 1374 | return null; |
| 1347 | } | 1375 | } |
| 1348 | | 1376 | |
| | 1377 | /// Linear search for the index of any value in the provided list inside a slice, starting from a given position. |
| | 1378 | /// Returns null if no values are found. |
| 1349 | pub fn indexOfAnyPos(comptime T: type, slice: []const T, start_index: usize, values: []const T) ?usize { | 1379 | pub fn indexOfAnyPos(comptime T: type, slice: []const T, start_index: usize, values: []const T) ?usize { |
| 1350 | if (start_index >= slice.len) return null; | 1380 | if (start_index >= slice.len) return null; |
| 1351 | for (slice[start_index..], start_index..) |c, i| { | 1381 | for (slice[start_index..], start_index..) |c, i| { |
| ... | @@ -1404,6 +1434,9 @@ test indexOfNone { | ... | @@ -1404,6 +1434,9 @@ test indexOfNone { |
| 1404 | try testing.expect(indexOfNonePos(u8, "abc123", 3, "321") == null); | 1434 | try testing.expect(indexOfNonePos(u8, "abc123", 3, "321") == null); |
| 1405 | } | 1435 | } |
| 1406 | | 1436 | |
| | 1437 | /// Search for needle in haystack and return the index of the first occurrence. |
| | 1438 | /// Uses Boyer-Moore-Horspool algorithm on large inputs; linear search on small inputs. |
| | 1439 | /// Returns null if needle is not found. |
| 1407 | pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize { | 1440 | pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize { |
| 1408 | return indexOfPos(T, haystack, 0, needle); | 1441 | return indexOfPos(T, haystack, 0, needle); |
| 1409 | } | 1442 | } |
| ... | @@ -2241,6 +2274,9 @@ test byteSwapAllFields { | ... | @@ -2241,6 +2274,9 @@ test byteSwapAllFields { |
| 2241 | }, k); | 2274 | }, k); |
| 2242 | } | 2275 | } |
| 2243 | | 2276 | |
| | 2277 | /// Reverses the byte order of all elements in a slice. |
| | 2278 | /// Handles structs, unions, arrays, enums, floats, and integers recursively. |
| | 2279 | /// Useful for converting between little-endian and big-endian representations. |
| 2244 | pub fn byteSwapAllElements(comptime Elem: type, slice: []Elem) void { | 2280 | pub fn byteSwapAllElements(comptime Elem: type, slice: []Elem) void { |
| 2245 | for (slice) |*elem| { | 2281 | for (slice) |*elem| { |
| 2246 | switch (@typeInfo(@TypeOf(elem.*))) { | 2282 | switch (@typeInfo(@TypeOf(elem.*))) { |
| ... | @@ -2980,6 +3016,7 @@ test window { | ... | @@ -2980,6 +3016,7 @@ test window { |
| 2980 | } | 3016 | } |
| 2981 | } | 3017 | } |
| 2982 | | 3018 | |
| | 3019 | /// Iterator type returned by the `window` function for sliding window operations. |
| 2983 | pub fn WindowIterator(comptime T: type) type { | 3020 | pub fn WindowIterator(comptime T: type) type { |
| 2984 | return struct { | 3021 | return struct { |
| 2985 | buffer: []const T, | 3022 | buffer: []const T, |
| ... | @@ -3020,6 +3057,8 @@ pub fn WindowIterator(comptime T: type) type { | ... | @@ -3020,6 +3057,8 @@ pub fn WindowIterator(comptime T: type) type { |
| 3020 | }; | 3057 | }; |
| 3021 | } | 3058 | } |
| 3022 | | 3059 | |
| | 3060 | /// Returns true if haystack starts with needle. |
| | 3061 | /// Time complexity: O(needle.len) |
| 3023 | pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool { | 3062 | pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool { |
| 3024 | return if (needle.len > haystack.len) false else eql(T, haystack[0..needle.len], needle); | 3063 | return if (needle.len > haystack.len) false else eql(T, haystack[0..needle.len], needle); |
| 3025 | } | 3064 | } |
| ... | @@ -3029,6 +3068,8 @@ test startsWith { | ... | @@ -3029,6 +3068,8 @@ test startsWith { |
| 3029 | try testing.expect(!startsWith(u8, "Needle in haystack", "haystack")); | 3068 | try testing.expect(!startsWith(u8, "Needle in haystack", "haystack")); |
| 3030 | } | 3069 | } |
| 3031 | | 3070 | |
| | 3071 | /// Returns true if haystack ends with needle. |
| | 3072 | /// Time complexity: O(needle.len) |
| 3032 | pub fn endsWith(comptime T: type, haystack: []const T, needle: []const T) bool { | 3073 | pub fn endsWith(comptime T: type, haystack: []const T, needle: []const T) bool { |
| 3033 | return if (needle.len > haystack.len) false else eql(T, haystack[haystack.len - needle.len ..], needle); | 3074 | return if (needle.len > haystack.len) false else eql(T, haystack[haystack.len - needle.len ..], needle); |
| 3034 | } | 3075 | } |
| ... | @@ -3038,8 +3079,10 @@ test endsWith { | ... | @@ -3038,8 +3079,10 @@ test endsWith { |
| 3038 | try testing.expect(!endsWith(u8, "Bob", "Bo")); | 3079 | try testing.expect(!endsWith(u8, "Bob", "Bo")); |
| 3039 | } | 3080 | } |
| 3040 | | 3081 | |
| | 3082 | /// Delimiter type for tokenization and splitting operations. |
| 3041 | pub const DelimiterType = enum { sequence, any, scalar }; | 3083 | pub const DelimiterType = enum { sequence, any, scalar }; |
| 3042 | | 3084 | |
| | 3085 | /// Iterator type for tokenization operations, skipping empty sequences and delimiter sequences. |
| 3043 | pub fn TokenIterator(comptime T: type, comptime delimiter_type: DelimiterType) type { | 3086 | pub fn TokenIterator(comptime T: type, comptime delimiter_type: DelimiterType) type { |
| 3044 | return struct { | 3087 | return struct { |
| 3045 | buffer: []const T, | 3088 | buffer: []const T, |
| ... | @@ -3113,6 +3156,7 @@ pub fn TokenIterator(comptime T: type, comptime delimiter_type: DelimiterType) t | ... | @@ -3113,6 +3156,7 @@ pub fn TokenIterator(comptime T: type, comptime delimiter_type: DelimiterType) t |
| 3113 | }; | 3156 | }; |
| 3114 | } | 3157 | } |
| 3115 | | 3158 | |
| | 3159 | /// Iterator type for splitting operations, including empty sequences between delimiters. |
| 3116 | pub fn SplitIterator(comptime T: type, comptime delimiter_type: DelimiterType) type { | 3160 | pub fn SplitIterator(comptime T: type, comptime delimiter_type: DelimiterType) type { |
| 3117 | return struct { | 3161 | return struct { |
| 3118 | buffer: []const T, | 3162 | buffer: []const T, |
| ... | @@ -3178,6 +3222,7 @@ pub fn SplitIterator(comptime T: type, comptime delimiter_type: DelimiterType) t | ... | @@ -3178,6 +3222,7 @@ pub fn SplitIterator(comptime T: type, comptime delimiter_type: DelimiterType) t |
| 3178 | }; | 3222 | }; |
| 3179 | } | 3223 | } |
| 3180 | | 3224 | |
| | 3225 | /// Iterator type for splitting operations from the end backwards, including empty sequences. |
| 3181 | pub fn SplitBackwardsIterator(comptime T: type, comptime delimiter_type: DelimiterType) type { | 3226 | pub fn SplitBackwardsIterator(comptime T: type, comptime delimiter_type: DelimiterType) type { |
| 3182 | return struct { | 3227 | return struct { |
| 3183 | buffer: []const T, | 3228 | buffer: []const T, |
| ... | @@ -3587,6 +3632,7 @@ test indexOfMinMax { | ... | @@ -3587,6 +3632,7 @@ test indexOfMinMax { |
| 3587 | try testing.expectEqual(.{ 0, 0 }, indexOfMinMax(u8, "a")); | 3632 | try testing.expectEqual(.{ 0, 0 }, indexOfMinMax(u8, "a")); |
| 3588 | } | 3633 | } |
| 3589 | | 3634 | |
| | 3635 | /// Exchanges contents of two memory locations. |
| 3590 | pub fn swap(comptime T: type, a: *T, b: *T) void { | 3636 | pub fn swap(comptime T: type, a: *T, b: *T) void { |
| 3591 | const tmp = a.*; | 3637 | const tmp = a.*; |
| 3592 | a.* = b.*; | 3638 | a.* = b.*; |
| ... | @@ -4452,6 +4498,9 @@ pub fn alignForward(comptime T: type, addr: T, alignment: T) T { | ... | @@ -4452,6 +4498,9 @@ pub fn alignForward(comptime T: type, addr: T, alignment: T) T { |
| 4452 | return alignBackward(T, addr + (alignment - 1), alignment); | 4498 | return alignBackward(T, addr + (alignment - 1), alignment); |
| 4453 | } | 4499 | } |
| 4454 | | 4500 | |
| | 4501 | /// Rounds an address up to the next alignment boundary using log2 representation. |
| | 4502 | /// Equivalent to alignForward with alignment = 1 << log2_alignment. |
| | 4503 | /// More efficient when alignment is known to be a power of 2. |
| 4455 | pub fn alignForwardLog2(addr: usize, log2_alignment: u8) usize { | 4504 | pub fn alignForwardLog2(addr: usize, log2_alignment: u8) usize { |
| 4456 | const alignment = @as(usize, 1) << @as(math.Log2Int(usize), @intCast(log2_alignment)); | 4505 | const alignment = @as(usize, 1) << @as(math.Log2Int(usize), @intCast(log2_alignment)); |
| 4457 | return alignForward(usize, addr, alignment); | 4506 | return alignForward(usize, addr, alignment); |
| ... | @@ -4591,6 +4640,9 @@ pub fn isValidAlignGeneric(comptime T: type, alignment: T) bool { | ... | @@ -4591,6 +4640,9 @@ pub fn isValidAlignGeneric(comptime T: type, alignment: T) bool { |
| 4591 | return alignment > 0 and std.math.isPowerOfTwo(alignment); | 4640 | return alignment > 0 and std.math.isPowerOfTwo(alignment); |
| 4592 | } | 4641 | } |
| 4593 | | 4642 | |
| | 4643 | /// Returns true if i is aligned to the given alignment. |
| | 4644 | /// Works with any positive alignment value, not just powers of 2. |
| | 4645 | /// For power-of-2 alignments, `isAligned` is more efficient. |
| 4594 | pub fn isAlignedAnyAlign(i: usize, alignment: usize) bool { | 4646 | pub fn isAlignedAnyAlign(i: usize, alignment: usize) bool { |
| 4595 | if (isValidAlign(alignment)) | 4647 | if (isValidAlign(alignment)) |
| 4596 | return isAligned(i, alignment); | 4648 | return isAligned(i, alignment); |
| ... | @@ -4598,6 +4650,9 @@ pub fn isAlignedAnyAlign(i: usize, alignment: usize) bool { | ... | @@ -4598,6 +4650,9 @@ pub fn isAlignedAnyAlign(i: usize, alignment: usize) bool { |
| 4598 | return 0 == @mod(i, alignment); | 4650 | return 0 == @mod(i, alignment); |
| 4599 | } | 4651 | } |
| 4600 | | 4652 | |
| | 4653 | /// Returns true if addr is aligned to 2^log2_alignment. |
| | 4654 | /// More efficient than `isAligned` when alignment is known to be a power of 2. |
| | 4655 | /// log2_alignment must be < @bitSizeOf(usize). |
| 4601 | pub fn isAlignedLog2(addr: usize, log2_alignment: u8) bool { | 4656 | pub fn isAlignedLog2(addr: usize, log2_alignment: u8) bool { |
| 4602 | return @ctz(addr) >= log2_alignment; | 4657 | return @ctz(addr) >= log2_alignment; |
| 4603 | } | 4658 | } |
| ... | @@ -4608,6 +4663,9 @@ pub fn isAligned(addr: usize, alignment: usize) bool { | ... | @@ -4608,6 +4663,9 @@ pub fn isAligned(addr: usize, alignment: usize) bool { |
| 4608 | return isAlignedGeneric(u64, addr, alignment); | 4663 | return isAlignedGeneric(u64, addr, alignment); |
| 4609 | } | 4664 | } |
| 4610 | | 4665 | |
| | 4666 | /// Generic version of `isAligned` that works with any integer type. |
| | 4667 | /// Returns true if addr is aligned to the given alignment. |
| | 4668 | /// Alignment must be a power of 2 and greater than 0. |
| 4611 | pub fn isAlignedGeneric(comptime T: type, addr: T, alignment: T) bool { | 4669 | pub fn isAlignedGeneric(comptime T: type, addr: T, alignment: T) bool { |
| 4612 | return alignBackward(T, addr, alignment) == addr; | 4670 | return alignBackward(T, addr, alignment) == addr; |
| 4613 | } | 4671 | } |