diff --git a/lib/std/ascii.zig b/lib/std/ascii.zig index 8174361800f5..3fc216abd797 100644 --- a/lib/std/ascii.zig +++ b/lib/std/ascii.zig @@ -1,16 +1,19 @@ -// Does NOT look at the locale the way C89's toupper(3), isspace() et cetera does. -// I could have taken only a u7 to make this clear, but it would be slower -// It is my opinion that encodings other than UTF-8 should not be supported. -// -// (and 128 bytes is not much to pay). -// Also does not handle Unicode character classes. -// -// https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/USASCII_code_chart.png/1200px-USASCII_code_chart.png +//! The 7-bit [ASCII](https://en.wikipedia.org/wiki/ASCII) character encoding standard. +//! +//! This is not to be confused with the 8-bit [extended ASCII](https://en.wikipedia.org/wiki/Extended_ASCII) character encoding. +//! +//! Even though this module concerns itself with 7-bit ASCII, +//! functions use `u8` as the type instead of `u7` for convenience and compatibility. +//! Characters outside of the 7-bit range are gracefully handled (e.g. by returning `false`). +//! +//! See also: https://en.wikipedia.org/wiki/ASCII#Character_set const std = @import("std"); +const testing = std.testing; /// Contains constants for the C0 control codes of the ASCII encoding. -/// https://en.wikipedia.org/wiki/C0_and_C1_control_codes +/// +/// See also: https://en.wikipedia.org/wiki/C0_and_C1_control_codes and `is_control` pub const control_code = struct { pub const NUL = 0x00; pub const SOH = 0x01; @@ -47,190 +50,185 @@ pub const control_code = struct { pub const DEL = 0x7F; + /// An alias to `DC1`. pub const XON = 0x11; + /// An alias to `DC3`. pub const XOFF = 0x13; }; -const tIndex = enum(u3) { - Alpha, - Hex, - Space, - Digit, - Lower, - Upper, - // Ctrl, < 0x20 || == DEL - // Print, = Graph || == ' '. NOT '\t' et cetera - Punct, - Graph, - //ASCII, | ~0b01111111 - //isBlank, == ' ' || == '\x09' -}; +// These naive functions are used to generate the lookup table +// and they're used as fallbacks for if the lookup table isn't available. +// +// Note that even some very simple functions like `isDigit` use a table because it avoids +// branching which is slow. + +fn isAlphaNaive(c: u8) bool { + return isLowerNaive(c) or isUpperNaive(c); +} +fn isXDigitNaive(c: u8) bool { + return isDigitNaive(c) or + (c >= 'a' and c <= 'f') or + (c >= 'A' and c <= 'F'); +} +fn isSpaceNaive(c: u8) bool { + return std.mem.indexOfScalar(u8, &spaces, c) != null; +} +fn isDigitNaive(c: u8) bool { + return c >= '0' and c <= '9'; +} +fn isLowerNaive(c: u8) bool { + return c >= 'a' and c <= 'z'; +} +fn isUpperNaive(c: u8) bool { + return c >= 'A' and c <= 'Z'; +} +fn isPunctNaive(c: u8) bool { + return (c >= '!' and c <= '/') or + (c >= '[' and c <= '`') or + (c >= '{' and c <= '~') or + (c >= ':' and c <= '@'); +} +fn isAlNumNaive(c: u8) bool { + return isDigitNaive(c) or isAlphaNaive(c); +} + +/// A lookup table. +const CombinedTable = struct { + table: [256]u8, + + // We cannot have >8 variants here which means we should choose + // only the most important/common functions to use a table for. + const Index = enum(u3) { + alphabetic, + hexadecimal, + space, + digit, + lower, + upper, + punct, + alnum, + }; -const combinedTable = init: { - comptime var table: [256]u8 = undefined; + /// Generates a table which is filled with the results of the given function for all characters. + fn getBoolTable(comptime condition: fn (u8) bool) [128]bool { + @setEvalBranchQuota(7500); + comptime var table: [128]bool = undefined; + comptime var index = 0; + while (index < 128) : (index += 1) { + table[index] = condition(index); + } + return table; + } - const mem = std.mem; + fn init() CombinedTable { + comptime var table: [256]u8 = undefined; + + const alphabetic_table = comptime getBoolTable(isAlphaNaive); + const hexadecimal_table = comptime getBoolTable(isXDigitNaive); + const space_table = comptime getBoolTable(isSpaceNaive); + const digit_table = comptime getBoolTable(isDigitNaive); + const lower_table = comptime getBoolTable(isLowerNaive); + const upper_table = comptime getBoolTable(isUpperNaive); + const punct_table = comptime getBoolTable(isPunctNaive); + const alnum_table = comptime getBoolTable(isAlNumNaive); + + comptime var i = 0; + inline while (i < 128) : (i += 1) { + table[i] = + @boolToInt(alphabetic_table[i]) << @enumToInt(Index.alphabetic) | + @boolToInt(hexadecimal_table[i]) << @enumToInt(Index.hexadecimal) | + @boolToInt(space_table[i]) << @enumToInt(Index.space) | + @boolToInt(digit_table[i]) << @enumToInt(Index.digit) | + @boolToInt(lower_table[i]) << @enumToInt(Index.lower) | + @boolToInt(upper_table[i]) << @enumToInt(Index.upper) | + @boolToInt(punct_table[i]) << @enumToInt(Index.punct) | + @boolToInt(alnum_table[i]) << @enumToInt(Index.alnum); + } - const alpha = [_]u1{ - // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + std.mem.set(u8, table[128..256], 0); - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, - }; - const lower = [_]u1{ - // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, - }; - const upper = [_]u1{ - // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - }; - const digit = [_]u1{ - // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - }; - const hex = [_]u1{ - // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, - - 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - }; - const space = [_]u1{ - // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - }; - const punct = [_]u1{ - // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, - - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, - }; - const graph = [_]u1{ - // 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9,10,11,12,13,14,15 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, - }; + return .{ .table = table }; + } - comptime var i = 0; - inline while (i < 128) : (i += 1) { - table[i] = - @as(u8, alpha[i]) << @enumToInt(tIndex.Alpha) | - @as(u8, hex[i]) << @enumToInt(tIndex.Hex) | - @as(u8, space[i]) << @enumToInt(tIndex.Space) | - @as(u8, digit[i]) << @enumToInt(tIndex.Digit) | - @as(u8, lower[i]) << @enumToInt(tIndex.Lower) | - @as(u8, upper[i]) << @enumToInt(tIndex.Upper) | - @as(u8, punct[i]) << @enumToInt(tIndex.Punct) | - @as(u8, graph[i]) << @enumToInt(tIndex.Graph); + fn contains(self: CombinedTable, c: u8, index: Index) bool { + return (self.table[c] & (@as(u8, 1) << @enumToInt(index))) != 0; } - mem.set(u8, table[128..256], 0); - break :init table; }; -fn inTable(c: u8, t: tIndex) bool { - return (combinedTable[c] & (@as(u8, 1) << @enumToInt(t))) != 0; -} +/// The combined table for fast lookup. +/// +/// This is not used in `ReleaseSmall` to save 256 bytes at the cost of +/// a small decrease in performance. +const combined_table: ?CombinedTable = if (@import("builtin").mode == .ReleaseSmall) + null +else + CombinedTable.init(); +/// Returns whether the character is alphanumeric. This is case-insensitive. pub fn isAlNum(c: u8) bool { - return (combinedTable[c] & ((@as(u8, 1) << @enumToInt(tIndex.Alpha)) | - @as(u8, 1) << @enumToInt(tIndex.Digit))) != 0; + if (combined_table) |table| + return table.contains(c, .alnum) + else + return isAlNumNaive(c); } +/// Returns whether the character is alphabetic. This is case-insensitive. pub fn isAlpha(c: u8) bool { - return inTable(c, tIndex.Alpha); + if (combined_table) |table| + return table.contains(c, .alphabetic) + else + return isAlphaNaive(c); } +/// Returns whether the character is a control character. +/// +/// See also: `control` pub fn isCntrl(c: u8) bool { - return c < 0x20 or c == 127; //DEL + return c <= control_code.US or c == control_code.DEL; } pub fn isDigit(c: u8) bool { - return inTable(c, tIndex.Digit); + if (combined_table) |table| + return table.contains(c, .digit) + else + return isDigitNaive(c); } pub fn isGraph(c: u8) bool { - return inTable(c, tIndex.Graph); + return isPrint(c) and c != ' '; } pub fn isLower(c: u8) bool { - return inTable(c, tIndex.Lower); + if (combined_table) |table| + return table.contains(c, .lower) + else + return isLowerNaive(c); } +/// Returns whether the character has some graphical representation and can be printed. pub fn isPrint(c: u8) bool { - return inTable(c, tIndex.Graph) or c == ' '; + return c >= ' ' and c <= '~'; } pub fn isPunct(c: u8) bool { - return inTable(c, tIndex.Punct); + if (combined_table) |table| + return table.contains(c, .punct) + else + return isPunctNaive(c); } pub fn isSpace(c: u8) bool { - return inTable(c, tIndex.Space); + if (combined_table) |table| + return table.contains(c, .space) + else + return isSpaceNaive(c); } -/// All the values for which isSpace() returns true. This may be used with -/// e.g. std.mem.trim() to trim whiteSpace. +/// All the values for which `isSpace()` returns `true`. +/// This may be used with e.g. `std.mem.trim()` to trim spaces. pub const spaces = [_]u8{ ' ', '\t', '\n', '\r', control_code.VT, control_code.FF }; test "spaces" { - const testing = std.testing; for (spaces) |space| try testing.expect(isSpace(space)); var i: u8 = 0; @@ -240,11 +238,18 @@ test "spaces" { } pub fn isUpper(c: u8) bool { - return inTable(c, tIndex.Upper); + if (combined_table) |table| + return table.contains(c, .upper) + else + return isUpperNaive(c); } +/// Returns whether the character is a hexadecimal digit. This is case-insensitive. pub fn isXDigit(c: u8) bool { - return inTable(c, tIndex.Hex); + if (combined_table) |table| + return table.contains(c, .hexadecimal) + else + return isXDigitNaive(c); } pub fn isASCII(c: u8) bool { @@ -255,6 +260,7 @@ pub fn isBlank(c: u8) bool { return (c == ' ') or (c == '\x09'); } +/// Upper-cases the character and returns it as-is if it's already upper-cased. pub fn toUpper(c: u8) u8 { if (isLower(c)) { return c & 0b11011111; @@ -263,6 +269,7 @@ pub fn toUpper(c: u8) u8 { } } +/// Lower-cases the character and returns it as-is if it's already lower-cased. pub fn toLower(c: u8) u8 { if (isUpper(c)) { return c | 0b00100000; @@ -272,15 +279,50 @@ pub fn toLower(c: u8) u8 { } test "ascii character classes" { - const testing = std.testing; + try testing.expect(!isCntrl('a')); + try testing.expect(!isCntrl('z')); + try testing.expect(isCntrl(control_code.NUL)); + try testing.expect(isCntrl(control_code.FF)); + try testing.expect(isCntrl(control_code.US)); try testing.expect('C' == toUpper('c')); try testing.expect(':' == toUpper(':')); try testing.expect('\xab' == toUpper('\xab')); + try testing.expect(!isUpper('z')); + try testing.expect('c' == toLower('C')); + try testing.expect(':' == toLower(':')); + try testing.expect('\xab' == toLower('\xab')); + try testing.expect(!isLower('Z')); + + try testing.expect(isAlNum('Z')); + try testing.expect(isAlNum('z')); + try testing.expect(isAlNum('5')); + try testing.expect(isAlNum('5')); + try testing.expect(!isAlNum('!')); + + try testing.expect(!isAlpha('5')); try testing.expect(isAlpha('c')); try testing.expect(!isAlpha('5')); + try testing.expect(isSpace(' ')); + try testing.expect(isSpace('\t')); + try testing.expect(isSpace('\r')); + try testing.expect(isSpace('\n')); + try testing.expect(!isSpace('.')); + + try testing.expect(!isXDigit('g')); + try testing.expect(isXDigit('b')); + try testing.expect(isXDigit('9')); + + try testing.expect(!isDigit('~')); + try testing.expect(isDigit('0')); + try testing.expect(isDigit('9')); + + try testing.expect(isPrint(' ')); + try testing.expect(isPrint('@')); + try testing.expect(isPrint('~')); + try testing.expect(!isPrint(control_code.ESC)); } /// Writes a lower case copy of `ascii_string` to `output`. @@ -296,7 +338,7 @@ pub fn lowerString(output: []u8, ascii_string: []const u8) []u8 { test "lowerString" { var buf: [1024]u8 = undefined; const result = lowerString(&buf, "aBcDeFgHiJkLmNOPqrst0234+💩!"); - try std.testing.expectEqualStrings("abcdefghijklmnopqrst0234+💩!", result); + try testing.expectEqualStrings("abcdefghijklmnopqrst0234+💩!", result); } /// Allocates a lower case copy of `ascii_string`. @@ -307,9 +349,9 @@ pub fn allocLowerString(allocator: std.mem.Allocator, ascii_string: []const u8) } test "allocLowerString" { - const result = try allocLowerString(std.testing.allocator, "aBcDeFgHiJkLmNOPqrst0234+💩!"); - defer std.testing.allocator.free(result); - try std.testing.expectEqualStrings("abcdefghijklmnopqrst0234+💩!", result); + const result = try allocLowerString(testing.allocator, "aBcDeFgHiJkLmNOPqrst0234+💩!"); + defer testing.allocator.free(result); + try testing.expectEqualStrings("abcdefghijklmnopqrst0234+💩!", result); } /// Writes an upper case copy of `ascii_string` to `output`. @@ -325,7 +367,7 @@ pub fn upperString(output: []u8, ascii_string: []const u8) []u8 { test "upperString" { var buf: [1024]u8 = undefined; const result = upperString(&buf, "aBcDeFgHiJkLmNOPqrst0234+💩!"); - try std.testing.expectEqualStrings("ABCDEFGHIJKLMNOPQRST0234+💩!", result); + try testing.expectEqualStrings("ABCDEFGHIJKLMNOPQRST0234+💩!", result); } /// Allocates an upper case copy of `ascii_string`. @@ -336,12 +378,12 @@ pub fn allocUpperString(allocator: std.mem.Allocator, ascii_string: []const u8) } test "allocUpperString" { - const result = try allocUpperString(std.testing.allocator, "aBcDeFgHiJkLmNOPqrst0234+💩!"); - defer std.testing.allocator.free(result); - try std.testing.expectEqualStrings("ABCDEFGHIJKLMNOPQRST0234+💩!", result); + const result = try allocUpperString(testing.allocator, "aBcDeFgHiJkLmNOPqrst0234+💩!"); + defer testing.allocator.free(result); + try testing.expectEqualStrings("ABCDEFGHIJKLMNOPQRST0234+💩!", result); } -/// Compares strings `a` and `b` case insensitively and returns whether they are equal. +/// Compares strings `a` and `b` case-insensitively and returns whether they are equal. pub fn eqlIgnoreCase(a: []const u8, b: []const u8) bool { if (a.len != b.len) return false; for (a) |a_c, i| { @@ -351,9 +393,9 @@ pub fn eqlIgnoreCase(a: []const u8, b: []const u8) bool { } test "eqlIgnoreCase" { - try std.testing.expect(eqlIgnoreCase("HEl💩Lo!", "hel💩lo!")); - try std.testing.expect(!eqlIgnoreCase("hElLo!", "hello! ")); - try std.testing.expect(!eqlIgnoreCase("hElLo!", "helro!")); + try testing.expect(eqlIgnoreCase("HEl💩Lo!", "hel💩lo!")); + try testing.expect(!eqlIgnoreCase("hElLo!", "hello! ")); + try testing.expect(!eqlIgnoreCase("hElLo!", "helro!")); } pub fn startsWithIgnoreCase(haystack: []const u8, needle: []const u8) bool { @@ -361,8 +403,8 @@ pub fn startsWithIgnoreCase(haystack: []const u8, needle: []const u8) bool { } test "ascii.startsWithIgnoreCase" { - try std.testing.expect(startsWithIgnoreCase("boB", "Bo")); - try std.testing.expect(!startsWithIgnoreCase("Needle in hAyStAcK", "haystack")); + try testing.expect(startsWithIgnoreCase("boB", "Bo")); + try testing.expect(!startsWithIgnoreCase("Needle in hAyStAcK", "haystack")); } pub fn endsWithIgnoreCase(haystack: []const u8, needle: []const u8) bool { @@ -370,8 +412,8 @@ pub fn endsWithIgnoreCase(haystack: []const u8, needle: []const u8) bool { } test "ascii.endsWithIgnoreCase" { - try std.testing.expect(endsWithIgnoreCase("Needle in HaYsTaCk", "haystack")); - try std.testing.expect(!endsWithIgnoreCase("BoB", "Bo")); + try testing.expect(endsWithIgnoreCase("Needle in HaYsTaCk", "haystack")); + try testing.expect(!endsWithIgnoreCase("BoB", "Bo")); } /// Finds `substr` in `container`, ignoring case, starting at `start_index`. @@ -393,12 +435,11 @@ pub fn indexOfIgnoreCase(container: []const u8, substr: []const u8) ?usize { } test "indexOfIgnoreCase" { - try std.testing.expect(indexOfIgnoreCase("one Two Three Four", "foUr").? == 14); - try std.testing.expect(indexOfIgnoreCase("one two three FouR", "gOur") == null); - try std.testing.expect(indexOfIgnoreCase("foO", "Foo").? == 0); - try std.testing.expect(indexOfIgnoreCase("foo", "fool") == null); - - try std.testing.expect(indexOfIgnoreCase("FOO foo", "fOo").? == 0); + try testing.expect(indexOfIgnoreCase("one Two Three Four", "foUr").? == 14); + try testing.expect(indexOfIgnoreCase("one two three FouR", "gOur") == null); + try testing.expect(indexOfIgnoreCase("foO", "Foo").? == 0); + try testing.expect(indexOfIgnoreCase("foo", "fool") == null); + try testing.expect(indexOfIgnoreCase("FOO foo", "fOo").? == 0); } /// Compares two slices of numbers lexicographically. O(n). @@ -415,8 +456,7 @@ pub fn orderIgnoreCase(lhs: []const u8, rhs: []const u8) std.math.Order { return std.math.order(lhs.len, rhs.len); } -/// Returns true if lhs < rhs, false otherwise -/// TODO rename "IgnoreCase" to "Insensitive" in this entire file. +/// Returns whether lhs < rhs. pub fn lessThanIgnoreCase(lhs: []const u8, rhs: []const u8) bool { return orderIgnoreCase(lhs, rhs) == .lt; }