1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
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", ""));
}
|