| ... | ... | @@ -227,6 +227,8 @@ test "ascii character classes" { |
| 227 | 227 | testing.expect(isSpace(' ')); |
| 228 | 228 | } |
| 229 | 229 | |
| 230 | /// Allocates a lower case copy of `ascii_string`. |
| 231 | /// Caller owns returned string and must free with `allocator`. |
| 230 | 232 | pub fn allocLowerString(allocator: *std.mem.Allocator, ascii_string: []const u8) ![]u8 { |
| 231 | 233 | const result = try allocator.alloc(u8, ascii_string.len); |
| 232 | 234 | for (result) |*c, i| { |
| ... | ... | @@ -241,6 +243,23 @@ test "allocLowerString" { |
| 241 | 243 | std.testing.expect(std.mem.eql(u8, "abcdefghijklmnopqrst0234+💩!", result)); |
| 242 | 244 | } |
| 243 | 245 | |
| 246 | /// Allocates an upper case copy of `ascii_string`. |
| 247 | /// Caller owns returned string and must free with `allocator`. |
| 248 | pub 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 | |
| 256 | test "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. |
| 244 | 263 | pub fn eqlIgnoreCase(a: []const u8, b: []const u8) bool { |
| 245 | 264 | if (a.len != b.len) return false; |
| 246 | 265 | for (a) |a_c, i| { |
| ... | ... | @@ -255,20 +274,43 @@ test "eqlIgnoreCase" { |
| 255 | 274 | std.testing.expect(!eqlIgnoreCase("hElLo!", "helro!")); |
| 256 | 275 | } |
| 257 | 276 | |
| 258 | | /// Finds `substr` in `container`, starting at `start_index`. |
| 259 | | /// TODO boyer-moore algorithm |
| 260 | | pub fn indexOfIgnoreCasePos(container: []const u8, start_index: usize, substr: []const u8) ?usize { |
| 277 | /// Return `true` if `a` and `b` are equal, case sensitive. |
| 278 | pub fn eqlMatchCase(a: []const u8, b: []const u8) bool { |
| 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 | |
| 286 | test "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 | |
| 292 | fn indexOfPos(container: []const u8, start_index: usize, substr: []const u8, comptime case_sensitive: bool) ?usize { |
| 261 | 293 | if (substr.len > container.len) return null; |
| 262 | 294 | |
| 263 | 295 | var i: usize = start_index; |
| 264 | 296 | const end = container.len - substr.len; |
| 265 | 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 | 304 | return null; |
| 269 | 305 | } |
| 270 | 306 | |
| 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 |
| 309 | pub 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. |
| 272 | 314 | pub fn indexOfIgnoreCase(container: []const u8, substr: []const u8) ?usize { |
| 273 | 315 | return indexOfIgnoreCasePos(container, 0, substr); |
| 274 | 316 | } |
| ... | ... | @@ -281,3 +323,22 @@ test "indexOfIgnoreCase" { |
| 281 | 323 | |
| 282 | 324 | std.testing.expect(indexOfIgnoreCase("FOO foo", "fOo").? == 0); |
| 283 | 325 | } |
| 326 | |
| 327 | /// Finds `substr` in `container`, case sensitive, starting at `start_index`. |
| 328 | pub 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. |
| 333 | pub fn indexOfMatchCase(container: []const u8, substr: []const u8) ?usize { |
| 334 | return indexOfMatchCasePos(container, 0, substr); |
| 335 | } |
| 336 | |
| 337 | test "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 | } |