| ... | @@ -7,7 +7,42 @@ const mem = std.mem; | ... | @@ -7,7 +7,42 @@ const mem = std.mem; |
| 7 | /// | 7 | /// |
| 8 | /// `kvs_list` expects a list of `struct { []const u8, V }` (key-value pair) tuples. | 8 | /// `kvs_list` expects a list of `struct { []const u8, V }` (key-value pair) tuples. |
| 9 | /// You can pass `struct { []const u8 }` (only keys) tuples if `V` is `void`. | 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 | const precomputed = comptime blk: { | 46 | const precomputed = comptime blk: { |
| 12 | @setEvalBranchQuota(1500); | 47 | @setEvalBranchQuota(1500); |
| 13 | const KV = struct { | 48 | const KV = struct { |
| ... | @@ -76,7 +111,7 @@ pub fn ComptimeStringMap(comptime V: type, comptime kvs_list: anytype) type { | ... | @@ -76,7 +111,7 @@ pub fn ComptimeStringMap(comptime V: type, comptime kvs_list: anytype) type { |
| 76 | const kv = precomputed.sorted_kvs[i]; | 111 | const kv = precomputed.sorted_kvs[i]; |
| 77 | if (kv.key.len != str.len) | 112 | if (kv.key.len != str.len) |
| 78 | return null; | 113 | return null; |
| 79 | if (mem.eql(u8, kv.key, str)) | 114 | if (eql(kv.key, str)) |
| 80 | return kv.value; | 115 | return kv.value; |
| 81 | i += 1; | 116 | i += 1; |
| 82 | if (i >= precomputed.sorted_kvs.len) | 117 | if (i >= precomputed.sorted_kvs.len) |
| ... | @@ -180,3 +215,20 @@ fn testSet(comptime map: anytype) !void { | ... | @@ -180,3 +215,20 @@ fn testSet(comptime map: anytype) !void { |
| 180 | try std.testing.expect(!map.has("missing")); | 215 | try std.testing.expect(!map.has("missing")); |
| 181 | try std.testing.expect(map.has("these")); | 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 | } |