authorgravatar for greg@gpanders.comGreg Anders <greg@gpanders.com> 2020-05-11 19:36:41-06:00
committergravatar for greg@gpanders.comGreg Anders <greg@gpanders.com> 2020-05-11 19:36:41-06:00
log9f496c077766172465a065eafba56dd5280021a6
treef4008a138d1696ff074ed319ff6a9916e43b02fc
parent7456389ef3c29e37d5d9bc7f8365db3217b3f60e

Add helper functions and docstrings to ascii.zig

* Add an upper case variant of `allocLowerString` * Add case-sensitive variants of `eqlIgnoreCase`, `indexOfIgnoreCase`, and `indexOfIgnoreCasePos` * Add and update docstrings on functions

1 files changed, 66 insertions(+), 5 deletions(-)

lib/std/ascii.zig+66-5
...@@ -227,6 +227,8 @@ test "ascii character classes" {...@@ -227,6 +227,8 @@ test "ascii character classes" {
227 testing.expect(isSpace(' '));227 testing.expect(isSpace(' '));
228}228}
229229
230/// Allocates a lower case copy of `ascii_string`.
231/// Caller owns returned string and must free with `allocator`.
230pub fn allocLowerString(allocator: *std.mem.Allocator, ascii_string: []const u8) ![]u8 {232pub fn allocLowerString(allocator: *std.mem.Allocator, ascii_string: []const u8) ![]u8 {
231 const result = try allocator.alloc(u8, ascii_string.len);233 const result = try allocator.alloc(u8, ascii_string.len);
232 for (result) |*c, i| {234 for (result) |*c, i| {
...@@ -241,6 +243,23 @@ test "allocLowerString" {...@@ -241,6 +243,23 @@ test "allocLowerString" {
241 std.testing.expect(std.mem.eql(u8, "abcdefghijklmnopqrst0234+💩!", result));243 std.testing.expect(std.mem.eql(u8, "abcdefghijklmnopqrst0234+💩!", result));
242}244}
243245
246/// Allocates an upper case copy of `ascii_string`.
247/// Caller owns returned string and must free with `allocator`.
248pub fn allocUpperString(allocator: *std.mem.Allocator, ascii_string: []const u8) ![]u8 {
249 const result = try allocator.alloc(u8, ascii_string.len);
250 for (result) |*c, i| {
251 c.* = toUpper(ascii_string[i]);
252 }
253 return result;
254}
255
256test "allocUpperString" {
257 const result = try allocUpperString(std.testing.allocator, "aBcDeFgHiJkLmNOPqrst0234+💩!");
258 defer std.testing.allocator.free(result);
259 std.testing.expect(std.mem.eql(u8, "ABCDEFGHIJKLMNOPQRST0234+💩!", result));
260}
261
262/// Return `true` if `a` and `b` are equal, case insensitive.
244pub fn eqlIgnoreCase(a: []const u8, b: []const u8) bool {263pub fn eqlIgnoreCase(a: []const u8, b: []const u8) bool {
245 if (a.len != b.len) return false;264 if (a.len != b.len) return false;
246 for (a) |a_c, i| {265 for (a) |a_c, i| {
...@@ -255,20 +274,43 @@ test "eqlIgnoreCase" {...@@ -255,20 +274,43 @@ test "eqlIgnoreCase" {
255 std.testing.expect(!eqlIgnoreCase("hElLo!", "helro!"));274 std.testing.expect(!eqlIgnoreCase("hElLo!", "helro!"));
256}275}
257276
258/// Finds `substr` in `container`, starting at `start_index`.277/// Return `true` if `a` and `b` are equal, case sensitive.
259/// TODO boyer-moore algorithm278pub fn eqlMatchCase(a: []const u8, b: []const u8) bool {
260pub fn indexOfIgnoreCasePos(container: []const u8, start_index: usize, substr: []const u8) ?usize {279 if (a.len != b.len) return false;
280 for (a) |a_c, i| {
281 if (a_c != b[i]) return false;
282 }
283 return true;
284}
285
286test "eqlMatchCase" {
287 std.testing.expect(!eqlMatchCase("HEl💩Lo!", "hel💩lo!"));
288 std.testing.expect(eqlMatchCase("Hello! ", "Hello! "));
289 std.testing.expect(eqlMatchCase("hElLo!", "hElLo!"));
290}
291
292fn indexOfPos(container: []const u8, start_index: usize, substr: []const u8, comptime case_sensitive: bool) ?usize {
261 if (substr.len > container.len) return null;293 if (substr.len > container.len) return null;
262294
263 var i: usize = start_index;295 var i: usize = start_index;
264 const end = container.len - substr.len;296 const end = container.len - substr.len;
265 while (i <= end) : (i += 1) {297 while (i <= end) : (i += 1) {
266 if (eqlIgnoreCase(container[i .. i + substr.len], substr)) return i;298 if (case_sensitive) {
299 if (eqlMatchCase(container[i .. i + substr.len], substr)) return i;
300 } else {
301 if (eqlIgnoreCase(container[i .. i + substr.len], substr)) return i;
302 }
267 }303 }
268 return null;304 return null;
269}305}
270306
271/// Finds `substr` in `container`, starting at `start_index`.307/// Finds `substr` in `container`, case insensitive, starting at `start_index`.
308/// TODO boyer-moore algorithm
309pub fn indexOfIgnoreCasePos(container: []const u8, start_index: usize, substr: []const u8) ?usize {
310 return indexOfPos(container, start_index, substr, false);
311}
312
313/// Finds `substr` in `container`, case insensitive, starting at index 0.
272pub fn indexOfIgnoreCase(container: []const u8, substr: []const u8) ?usize {314pub fn indexOfIgnoreCase(container: []const u8, substr: []const u8) ?usize {
273 return indexOfIgnoreCasePos(container, 0, substr);315 return indexOfIgnoreCasePos(container, 0, substr);
274}316}
...@@ -281,3 +323,22 @@ test "indexOfIgnoreCase" {...@@ -281,3 +323,22 @@ test "indexOfIgnoreCase" {
281323
282 std.testing.expect(indexOfIgnoreCase("FOO foo", "fOo").? == 0);324 std.testing.expect(indexOfIgnoreCase("FOO foo", "fOo").? == 0);
283}325}
326
327/// Finds `substr` in `container`, case sensitive, starting at `start_index`.
328pub fn indexOfMatchCasePos(container: []const u8, start_index: usize, substr: []const u8) ?usize {
329 return indexOfPos(container, start_index, substr, true);
330}
331
332/// Finds `substr` in `container`, case sensitive, starting at index 0.
333pub fn indexOfMatchCase(container: []const u8, substr: []const u8) ?usize {
334 return indexOfMatchCasePos(container, 0, substr);
335}
336
337test "indexOfMatchCase" {
338 std.testing.expect(indexOfMatchCase("one Two Three Four", "Four").? == 14);
339 std.testing.expect(indexOfMatchCase("one two three FouR", "fOur") == null);
340 std.testing.expect(indexOfMatchCase("foO", "foO").? == 0);
341 std.testing.expect(indexOfMatchCase("foo", "fool") == null);
342
343 std.testing.expect(indexOfMatchCase("FOO foo", "FOO").? == 0);
344}