| ... | ... | @@ -7,7 +7,42 @@ const mem = std.mem; |
| 7 | 7 | /// |
| 8 | 8 | /// `kvs_list` expects a list of `struct { []const u8, V }` (key-value pair) tuples. |
| 9 | 9 | /// You can pass `struct { []const u8 }` (only keys) tuples if `V` is `void`. |
| 10 | | pub fn ComptimeStringMap(comptime V: type, comptime kvs_list: anytype) type { |
| 10 | pub fn ComptimeStringMap( |
| 11 | comptime V: type, |
| 12 | comptime kvs_list: anytype, |
| 13 | ) type { |
| 14 | return ComptimeStringMapWithEql(V, kvs_list, defaultEql); |
| 15 | } |
| 16 | |
| 17 | /// Like `std.mem.eql`, but takes advantage of the fact that the lengths |
| 18 | /// of `a` and `b` are known to be equal. |
| 19 | pub fn defaultEql(a: []const u8, b: []const u8) bool { |
| 20 | if (a.ptr == b.ptr) return true; |
| 21 | for (a, b) |a_elem, b_elem| { |
| 22 | if (a_elem != b_elem) return false; |
| 23 | } |
| 24 | return true; |
| 25 | } |
| 26 | |
| 27 | /// Like `std.ascii.eqlIgnoreCase` but takes advantage of the fact that |
| 28 | /// the lengths of `a` and `b` are known to be equal. |
| 29 | pub fn eqlAsciiIgnoreCase(a: []const u8, b: []const u8) bool { |
| 30 | if (a.ptr == b.ptr) return true; |
| 31 | for (a, b) |a_c, b_c| { |
| 32 | if (std.ascii.toLower(a_c) != std.ascii.toLower(b_c)) return false; |
| 33 | } |
| 34 | return true; |
| 35 | } |
| 36 | |
| 37 | /// ComptimeStringMap, but accepts an equality function (`eql`). |
| 38 | /// The `eql` function is only called to determine the equality |
| 39 | /// of equal length strings. Any strings that are not equal length |
| 40 | /// are never compared using the `eql` function. |
| 41 | pub fn ComptimeStringMapWithEql( |
| 42 | comptime V: type, |
| 43 | comptime kvs_list: anytype, |
| 44 | comptime eql: fn (a: []const u8, b: []const u8) bool, |
| 45 | ) type { |
| 11 | 46 | const precomputed = comptime blk: { |
| 12 | 47 | @setEvalBranchQuota(1500); |
| 13 | 48 | const KV = struct { |
| ... | ... | @@ -76,7 +111,7 @@ pub fn ComptimeStringMap(comptime V: type, comptime kvs_list: anytype) type { |
| 76 | 111 | const kv = precomputed.sorted_kvs[i]; |
| 77 | 112 | if (kv.key.len != str.len) |
| 78 | 113 | return null; |
| 79 | | if (mem.eql(u8, kv.key, str)) |
| 114 | if (eql(kv.key, str)) |
| 80 | 115 | return kv.value; |
| 81 | 116 | i += 1; |
| 82 | 117 | if (i >= precomputed.sorted_kvs.len) |
| ... | ... | @@ -180,3 +215,20 @@ fn testSet(comptime map: anytype) !void { |
| 180 | 215 | try std.testing.expect(!map.has("missing")); |
| 181 | 216 | try std.testing.expect(map.has("these")); |
| 182 | 217 | } |
| 218 | |
| 219 | test "ComptimeStringMapWithEql" { |
| 220 | const map = ComptimeStringMapWithEql(TestEnum, .{ |
| 221 | .{ "these", .D }, |
| 222 | .{ "have", .A }, |
| 223 | .{ "nothing", .B }, |
| 224 | .{ "incommon", .C }, |
| 225 | .{ "samelen", .E }, |
| 226 | }, eqlAsciiIgnoreCase); |
| 227 | |
| 228 | try testMap(map); |
| 229 | try std.testing.expectEqual(TestEnum.A, map.get("HAVE").?); |
| 230 | try std.testing.expectEqual(TestEnum.E, map.get("SameLen").?); |
| 231 | try std.testing.expect(null == map.get("SameLength")); |
| 232 | |
| 233 | try std.testing.expect(map.has("ThESe")); |
| 234 | } |