From 02396f8d5c5ca32c1d2ab2e2f04afba4588f5944 Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Sun, 7 Sep 2025 03:55:57 +0200 Subject: [PATCH] Document std.mem.* functions (#25168) * Document std.mem.* functions Functions in std.mem are essential for virtually all applications, yet many of them lacked documentation. Co-authored-by: Andrew Kelley --- lib/std/mem.zig | 58 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index f688bafcae..e31e93aae1 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -166,6 +166,8 @@ pub fn ValidationAllocator(comptime T: type) type { }; } +/// Wraps an allocator with basic validation checks. +/// Asserts that allocation sizes are greater than zero and returned pointers have correct alignment. pub fn validationWrap(allocator: anytype) ValidationAllocator(@TypeOf(allocator)) { return ValidationAllocator(@TypeOf(allocator)).init(allocator); } @@ -597,6 +599,12 @@ test zeroInit { }, nested_baz); } +/// Sorts a slice in-place using a stable algorithm (maintains relative order of equal elements). +/// Average time complexity: O(n log n), worst case: O(n log n) +/// Space complexity: O(log n) for recursive calls +/// +/// For slice of primitives with default ordering, consider using `std.sort.block` directly. +/// For unstable but potentially faster sorting, see `sortUnstable`. pub fn sort( comptime T: type, items: []T, @@ -606,6 +614,12 @@ pub fn sort( std.sort.block(T, items, context, lessThanFn); } +/// Sorts a slice in-place using an unstable algorithm (does not preserve relative order of equal elements). +/// Time complexity: O(n) best case, O(n log n) worst case and average case. +/// Generally faster than stable sort but order of equal elements is undefined. +/// +/// Uses pattern-defeating quicksort (PDQ) algorithm which performs well on many data patterns. +/// For stable sorting that preserves equal element order, use `sort`. pub fn sortUnstable( comptime T: type, items: []T, @@ -621,6 +635,12 @@ pub fn sortContext(a: usize, b: usize, context: anytype) void { std.sort.insertionContext(a, b, context); } +/// Sorts a range [a, b) using an unstable algorithm with custom context. +/// This is a lower-level interface for sorting that works with indices instead of slices. +/// Does not preserve relative order of equal elements. +/// +/// The context must provide lessThan(a_idx, b_idx) and swap(a_idx, b_idx) methods. +/// Uses pattern-defeating quicksort (PDQ) algorithm. pub fn sortUnstableContext(a: usize, b: usize, context: anytype) void { std.sort.pdqContext(a, b, context); } @@ -1089,6 +1109,8 @@ test len { try testing.expect(len(c_ptr) == 2); } +/// Returns the index of the sentinel value in a sentinel-terminated pointer. +/// Linear search through memory until the sentinel is found. pub fn indexOfSentinel(comptime T: type, comptime sentinel: T, p: [*:sentinel]const T) usize { var i: usize = 0; @@ -1255,6 +1277,8 @@ pub fn lastIndexOfScalar(comptime T: type, slice: []const T, value: T) ?usize { return null; } +/// Linear search for the index of a scalar value inside a slice, starting from a given position. +/// Returns null if the value is not found. pub fn indexOfScalarPos(comptime T: type, slice: []const T, start_index: usize, value: T) ?usize { if (start_index >= slice.len) return null; @@ -1331,10 +1355,14 @@ test indexOfScalarPos { } } +/// Linear search for the index of any value in the provided list inside a slice. +/// Returns null if no values are found. pub fn indexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize { return indexOfAnyPos(T, slice, 0, values); } +/// Linear search for the last index of any value in the provided list inside a slice. +/// Returns null if no values are found. pub fn lastIndexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize { var i: usize = slice.len; while (i != 0) { @@ -1346,6 +1374,8 @@ pub fn lastIndexOfAny(comptime T: type, slice: []const T, values: []const T) ?us return null; } +/// Linear search for the index of any value in the provided list inside a slice, starting from a given position. +/// Returns null if no values are found. pub fn indexOfAnyPos(comptime T: type, slice: []const T, start_index: usize, values: []const T) ?usize { if (start_index >= slice.len) return null; for (slice[start_index..], start_index..) |c, i| { @@ -1404,6 +1434,9 @@ test indexOfNone { try testing.expect(indexOfNonePos(u8, "abc123", 3, "321") == null); } +/// Search for needle in haystack and return the index of the first occurrence. +/// Uses Boyer-Moore-Horspool algorithm on large inputs; linear search on small inputs. +/// Returns null if needle is not found. pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize { return indexOfPos(T, haystack, 0, needle); } @@ -2241,6 +2274,9 @@ test byteSwapAllFields { }, k); } +/// Reverses the byte order of all elements in a slice. +/// Handles structs, unions, arrays, enums, floats, and integers recursively. +/// Useful for converting between little-endian and big-endian representations. pub fn byteSwapAllElements(comptime Elem: type, slice: []Elem) void { for (slice) |*elem| { switch (@typeInfo(@TypeOf(elem.*))) { @@ -2980,6 +3016,7 @@ test window { } } +/// Iterator type returned by the `window` function for sliding window operations. pub fn WindowIterator(comptime T: type) type { return struct { buffer: []const T, @@ -3020,6 +3057,8 @@ pub fn WindowIterator(comptime T: type) type { }; } +/// Returns true if haystack starts with needle. +/// Time complexity: O(needle.len) pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool { return if (needle.len > haystack.len) false else eql(T, haystack[0..needle.len], needle); } @@ -3029,6 +3068,8 @@ test startsWith { try testing.expect(!startsWith(u8, "Needle in haystack", "haystack")); } +/// Returns true if haystack ends with needle. +/// Time complexity: O(needle.len) pub fn endsWith(comptime T: type, haystack: []const T, needle: []const T) bool { return if (needle.len > haystack.len) false else eql(T, haystack[haystack.len - needle.len ..], needle); } @@ -3038,8 +3079,10 @@ test endsWith { try testing.expect(!endsWith(u8, "Bob", "Bo")); } +/// Delimiter type for tokenization and splitting operations. pub const DelimiterType = enum { sequence, any, scalar }; +/// Iterator type for tokenization operations, skipping empty sequences and delimiter sequences. pub fn TokenIterator(comptime T: type, comptime delimiter_type: DelimiterType) type { return struct { buffer: []const T, @@ -3113,6 +3156,7 @@ pub fn TokenIterator(comptime T: type, comptime delimiter_type: DelimiterType) t }; } +/// Iterator type for splitting operations, including empty sequences between delimiters. pub fn SplitIterator(comptime T: type, comptime delimiter_type: DelimiterType) type { return struct { buffer: []const T, @@ -3178,6 +3222,7 @@ pub fn SplitIterator(comptime T: type, comptime delimiter_type: DelimiterType) t }; } +/// Iterator type for splitting operations from the end backwards, including empty sequences. pub fn SplitBackwardsIterator(comptime T: type, comptime delimiter_type: DelimiterType) type { return struct { buffer: []const T, @@ -3587,6 +3632,7 @@ test indexOfMinMax { try testing.expectEqual(.{ 0, 0 }, indexOfMinMax(u8, "a")); } +/// Exchanges contents of two memory locations. pub fn swap(comptime T: type, a: *T, b: *T) void { const tmp = a.*; a.* = b.*; @@ -4452,6 +4498,9 @@ pub fn alignForward(comptime T: type, addr: T, alignment: T) T { return alignBackward(T, addr + (alignment - 1), alignment); } +/// Rounds an address up to the next alignment boundary using log2 representation. +/// Equivalent to alignForward with alignment = 1 << log2_alignment. +/// More efficient when alignment is known to be a power of 2. pub fn alignForwardLog2(addr: usize, log2_alignment: u8) usize { const alignment = @as(usize, 1) << @as(math.Log2Int(usize), @intCast(log2_alignment)); return alignForward(usize, addr, alignment); @@ -4591,6 +4640,9 @@ pub fn isValidAlignGeneric(comptime T: type, alignment: T) bool { return alignment > 0 and std.math.isPowerOfTwo(alignment); } +/// Returns true if i is aligned to the given alignment. +/// Works with any positive alignment value, not just powers of 2. +/// For power-of-2 alignments, `isAligned` is more efficient. pub fn isAlignedAnyAlign(i: usize, alignment: usize) bool { if (isValidAlign(alignment)) return isAligned(i, alignment); @@ -4598,6 +4650,9 @@ pub fn isAlignedAnyAlign(i: usize, alignment: usize) bool { return 0 == @mod(i, alignment); } +/// Returns true if addr is aligned to 2^log2_alignment. +/// More efficient than `isAligned` when alignment is known to be a power of 2. +/// log2_alignment must be < @bitSizeOf(usize). pub fn isAlignedLog2(addr: usize, log2_alignment: u8) bool { return @ctz(addr) >= log2_alignment; } @@ -4608,6 +4663,9 @@ pub fn isAligned(addr: usize, alignment: usize) bool { return isAlignedGeneric(u64, addr, alignment); } +/// Generic version of `isAligned` that works with any integer type. +/// Returns true if addr is aligned to the given alignment. +/// Alignment must be a power of 2 and greater than 0. pub fn isAlignedGeneric(comptime T: type, addr: T, alignment: T) bool { return alignBackward(T, addr, alignment) == addr; }