const std = @import("std"); /// Trim leading and trailing ASCII whitespace. pub fn trim(s: []const u8) []const u8 { return std.mem.trim(u8, s, " \t\r\n"); } /// Count non-overlapping occurrences of `needle` in `haystack`. pub fn countOccurrences(haystack: []const u8, needle: []const u8) usize { if (needle.len == 0) return 0; var count: usize = 0; var i: usize = 0; while (i + needle.len <= haystack.len) { if (std.mem.eql(u8, haystack[i .. i + needle.len], needle)) { count += 1; i += needle.len; } else { i += 1; } } return count; } /// Reverse a string byte-by-byte (ASCII safe; not grapheme-safe). pub fn reverseAlloc(allocator: std.mem.Allocator, s: []const u8) ![]u8 { const result = try allocator.alloc(u8, s.len); for (s, 0..) |byte, i| result[s.len - 1 - i] = byte; return result; } /// Returns true if `s` starts with `prefix`. pub fn startsWith(s: []const u8, prefix: []const u8) bool { return std.mem.startsWith(u8, s, prefix); } // ── Tests with "strings: " prefix ──────────────────────────────────────────── // Run all: zig build test // Run strings only: zig build test -- --test-filter "strings:" test "strings: trim removes spaces" { try std.testing.expectEqualStrings("hello", trim(" hello ")); } test "strings: trim handles empty and whitespace-only" { try std.testing.expectEqualStrings("", trim("")); try std.testing.expectEqualStrings("", trim(" \t\n")); } test "strings: trim preserves inner whitespace" { try std.testing.expectEqualStrings("hello world", trim(" hello world ")); } test "strings: countOccurrences basic" { try std.testing.expectEqual(@as(usize, 3), countOccurrences("abcabcabc", "abc")); } test "strings: countOccurrences empty needle" { try std.testing.expectEqual(@as(usize, 0), countOccurrences("hello", "")); } test "strings: countOccurrences no match" { try std.testing.expectEqual(@as(usize, 0), countOccurrences("hello", "xyz")); } test "strings: countOccurrences overlapping boundary" { // Non-overlapping: "aa" in "aaaa" = 2 try std.testing.expectEqual(@as(usize, 2), countOccurrences("aaaa", "aa")); } test "strings: reverseAlloc" { const reversed = try reverseAlloc(std.testing.allocator, "hello"); defer std.testing.allocator.free(reversed); try std.testing.expectEqualStrings("olleh", reversed); } test "strings: reverseAlloc empty" { const reversed = try reverseAlloc(std.testing.allocator, ""); defer std.testing.allocator.free(reversed); try std.testing.expectEqualStrings("", reversed); } test "strings: startsWith" { try std.testing.expect(startsWith("hello world", "hello")); try std.testing.expect(!startsWith("hello world", "world")); try std.testing.expect(startsWith("abc", "")); }