| ... | ... | @@ -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 | 171 | pub fn validationWrap(allocator: anytype) ValidationAllocator(@TypeOf(allocator)) { |
| 170 | 172 | return ValidationAllocator(@TypeOf(allocator)).init(allocator); |
| 171 | 173 | } |
| ... | ... | @@ -597,6 +599,12 @@ test zeroInit { |
| 597 | 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 | 608 | pub fn sort( |
| 601 | 609 | comptime T: type, |
| 602 | 610 | items: []T, |
| ... | ... | @@ -606,6 +614,12 @@ pub fn sort( |
| 606 | 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 | 623 | pub fn sortUnstable( |
| 610 | 624 | comptime T: type, |
| 611 | 625 | items: []T, |
| ... | ... | @@ -621,6 +635,12 @@ pub fn sortContext(a: usize, b: usize, context: anytype) void { |
| 621 | 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 | 644 | pub fn sortUnstableContext(a: usize, b: usize, context: anytype) void { |
| 625 | 645 | std.sort.pdqContext(a, b, context); |
| 626 | 646 | } |
| ... | ... | @@ -1089,6 +1109,8 @@ test len { |
| 1089 | 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 | 1114 | pub fn indexOfSentinel(comptime T: type, comptime sentinel: T, p: [*:sentinel]const T) usize { |
| 1093 | 1115 | var i: usize = 0; |
| 1094 | 1116 | |
| ... | ... | @@ -1255,6 +1277,8 @@ pub fn lastIndexOfScalar(comptime T: type, slice: []const T, value: T) ?usize { |
| 1255 | 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 | 1282 | pub fn indexOfScalarPos(comptime T: type, slice: []const T, start_index: usize, value: T) ?usize { |
| 1259 | 1283 | if (start_index >= slice.len) return null; |
| 1260 | 1284 | |
| ... | ... | @@ -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 | 1360 | pub fn indexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize { |
| 1335 | 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 | 1366 | pub fn lastIndexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize { |
| 1339 | 1367 | var i: usize = slice.len; |
| 1340 | 1368 | while (i != 0) { |
| ... | ... | @@ -1346,6 +1374,8 @@ pub fn lastIndexOfAny(comptime T: type, slice: []const T, values: []const T) ?us |
| 1346 | 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 | 1379 | pub fn indexOfAnyPos(comptime T: type, slice: []const T, start_index: usize, values: []const T) ?usize { |
| 1350 | 1380 | if (start_index >= slice.len) return null; |
| 1351 | 1381 | for (slice[start_index..], start_index..) |c, i| { |
| ... | ... | @@ -1404,6 +1434,9 @@ test indexOfNone { |
| 1404 | 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 | 1440 | pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize { |
| 1408 | 1441 | return indexOfPos(T, haystack, 0, needle); |
| 1409 | 1442 | } |
| ... | ... | @@ -2241,6 +2274,9 @@ test byteSwapAllFields { |
| 2241 | 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 | 2280 | pub fn byteSwapAllElements(comptime Elem: type, slice: []Elem) void { |
| 2245 | 2281 | for (slice) |*elem| { |
| 2246 | 2282 | switch (@typeInfo(@TypeOf(elem.*))) { |
| ... | ... | @@ -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 | 3020 | pub fn WindowIterator(comptime T: type) type { |
| 2984 | 3021 | return struct { |
| 2985 | 3022 | buffer: []const T, |
| ... | ... | @@ -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 | 3062 | pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool { |
| 3024 | 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 | 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 | 3073 | pub fn endsWith(comptime T: type, haystack: []const T, needle: []const T) bool { |
| 3033 | 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 | 3079 | try testing.expect(!endsWith(u8, "Bob", "Bo")); |
| 3039 | 3080 | } |
| 3040 | 3081 | |
| 3082 | /// Delimiter type for tokenization and splitting operations. |
| 3041 | 3083 | pub const DelimiterType = enum { sequence, any, scalar }; |
| 3042 | 3084 | |
| 3085 | /// Iterator type for tokenization operations, skipping empty sequences and delimiter sequences. |
| 3043 | 3086 | pub fn TokenIterator(comptime T: type, comptime delimiter_type: DelimiterType) type { |
| 3044 | 3087 | return struct { |
| 3045 | 3088 | buffer: []const 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 | 3160 | pub fn SplitIterator(comptime T: type, comptime delimiter_type: DelimiterType) type { |
| 3117 | 3161 | return struct { |
| 3118 | 3162 | buffer: []const 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 | 3226 | pub fn SplitBackwardsIterator(comptime T: type, comptime delimiter_type: DelimiterType) type { |
| 3182 | 3227 | return struct { |
| 3183 | 3228 | buffer: []const T, |
| ... | ... | @@ -3587,6 +3632,7 @@ test indexOfMinMax { |
| 3587 | 3632 | try testing.expectEqual(.{ 0, 0 }, indexOfMinMax(u8, "a")); |
| 3588 | 3633 | } |
| 3589 | 3634 | |
| 3635 | /// Exchanges contents of two memory locations. |
| 3590 | 3636 | pub fn swap(comptime T: type, a: *T, b: *T) void { |
| 3591 | 3637 | const tmp = a.*; |
| 3592 | 3638 | a.* = b.*; |
| ... | ... | @@ -4452,6 +4498,9 @@ pub fn alignForward(comptime T: type, addr: T, alignment: T) T { |
| 4452 | 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 | 4504 | pub fn alignForwardLog2(addr: usize, log2_alignment: u8) usize { |
| 4456 | 4505 | const alignment = @as(usize, 1) << @as(math.Log2Int(usize), @intCast(log2_alignment)); |
| 4457 | 4506 | return alignForward(usize, addr, alignment); |
| ... | ... | @@ -4591,6 +4640,9 @@ pub fn isValidAlignGeneric(comptime T: type, alignment: T) bool { |
| 4591 | 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 | 4646 | pub fn isAlignedAnyAlign(i: usize, alignment: usize) bool { |
| 4595 | 4647 | if (isValidAlign(alignment)) |
| 4596 | 4648 | return isAligned(i, alignment); |
| ... | ... | @@ -4598,6 +4650,9 @@ pub fn isAlignedAnyAlign(i: usize, alignment: usize) bool { |
| 4598 | 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 | 4656 | pub fn isAlignedLog2(addr: usize, log2_alignment: u8) bool { |
| 4602 | 4657 | return @ctz(addr) >= log2_alignment; |
| 4603 | 4658 | } |
| ... | ... | @@ -4608,6 +4663,9 @@ pub fn isAligned(addr: usize, alignment: usize) bool { |
| 4608 | 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 | 4669 | pub fn isAlignedGeneric(comptime T: type, addr: T, alignment: T) bool { |
| 4612 | 4670 | return alignBackward(T, addr, alignment) == addr; |
| 4613 | 4671 | } |