| ... | ... | @@ -43,12 +43,15 @@ pub fn ComptimeStringMapWithEql( |
| 43 | 43 | comptime kvs_list: anytype, |
| 44 | 44 | comptime eql: fn (a: []const u8, b: []const u8) bool, |
| 45 | 45 | ) type { |
| 46 | | const precomputed = comptime blk: { |
| 46 | const empty_list = kvs_list.len == 0; |
| 47 | const precomputed = blk: { |
| 47 | 48 | @setEvalBranchQuota(1500); |
| 48 | 49 | const KV = struct { |
| 49 | 50 | key: []const u8, |
| 50 | 51 | value: V, |
| 51 | 52 | }; |
| 53 | if (empty_list) |
| 54 | break :blk .{}; |
| 52 | 55 | var sorted_kvs: [kvs_list.len]KV = undefined; |
| 53 | 56 | for (kvs_list, 0..) |kv, i| { |
| 54 | 57 | if (V != void) { |
| ... | ... | @@ -103,10 +106,16 @@ pub fn ComptimeStringMapWithEql( |
| 103 | 106 | |
| 104 | 107 | /// Returns the value for the key if any, else null. |
| 105 | 108 | pub fn get(str: []const u8) ?V { |
| 109 | if (empty_list) |
| 110 | return null; |
| 111 | |
| 106 | 112 | return precomputed.sorted_kvs[getIndex(str) orelse return null].value; |
| 107 | 113 | } |
| 108 | 114 | |
| 109 | 115 | pub fn getIndex(str: []const u8) ?usize { |
| 116 | if (empty_list) |
| 117 | return null; |
| 118 | |
| 110 | 119 | if (str.len < precomputed.min_len or str.len > precomputed.max_len) |
| 111 | 120 | return null; |
| 112 | 121 | |
| ... | ... | @@ -143,6 +152,9 @@ test "ComptimeStringMap list literal of list literals" { |
| 143 | 152 | }); |
| 144 | 153 | |
| 145 | 154 | try testMap(map); |
| 155 | |
| 156 | // Default comparison is case sensitive |
| 157 | try std.testing.expect(null == map.get("NOTHING")); |
| 146 | 158 | } |
| 147 | 159 | |
| 148 | 160 | test "ComptimeStringMap array of structs" { |
| ... | ... | @@ -181,6 +193,9 @@ fn testMap(comptime map: anytype) !void { |
| 181 | 193 | |
| 182 | 194 | try std.testing.expect(!map.has("missing")); |
| 183 | 195 | try std.testing.expect(map.has("these")); |
| 196 | |
| 197 | try std.testing.expect(null == map.get("")); |
| 198 | try std.testing.expect(null == map.get("averylongstringthathasnomatches")); |
| 184 | 199 | } |
| 185 | 200 | |
| 186 | 201 | test "ComptimeStringMap void value type, slice of structs" { |
| ... | ... | @@ -195,6 +210,9 @@ test "ComptimeStringMap void value type, slice of structs" { |
| 195 | 210 | const map = ComptimeStringMap(void, slice); |
| 196 | 211 | |
| 197 | 212 | try testSet(map); |
| 213 | |
| 214 | // Default comparison is case sensitive |
| 215 | try std.testing.expect(null == map.get("NOTHING")); |
| 198 | 216 | } |
| 199 | 217 | |
| 200 | 218 | test "ComptimeStringMap void value type, list literal of list literals" { |
| ... | ... | @@ -218,6 +236,9 @@ fn testSet(comptime map: anytype) !void { |
| 218 | 236 | |
| 219 | 237 | try std.testing.expect(!map.has("missing")); |
| 220 | 238 | try std.testing.expect(map.has("these")); |
| 239 | |
| 240 | try std.testing.expect(null == map.get("")); |
| 241 | try std.testing.expect(null == map.get("averylongstringthathasnomatches")); |
| 221 | 242 | } |
| 222 | 243 | |
| 223 | 244 | test "ComptimeStringMapWithEql" { |
| ... | ... | @@ -236,3 +257,45 @@ test "ComptimeStringMapWithEql" { |
| 236 | 257 | |
| 237 | 258 | try std.testing.expect(map.has("ThESe")); |
| 238 | 259 | } |
| 260 | |
| 261 | test "ComptimeStringMap empty" { |
| 262 | const m1 = ComptimeStringMap(usize, .{}); |
| 263 | try std.testing.expect(null == m1.get("anything")); |
| 264 | |
| 265 | const m2 = ComptimeStringMapWithEql(usize, .{}, eqlAsciiIgnoreCase); |
| 266 | try std.testing.expect(null == m2.get("anything")); |
| 267 | } |
| 268 | |
| 269 | test "ComptimeStringMap redundant entries" { |
| 270 | const map = ComptimeStringMap(TestEnum, .{ |
| 271 | .{ "redundant", .D }, |
| 272 | .{ "theNeedle", .A }, |
| 273 | .{ "redundant", .B }, |
| 274 | .{ "re" ++ "dundant", .C }, |
| 275 | .{ "redun" ++ "dant", .E }, |
| 276 | }); |
| 277 | |
| 278 | // No promises about which one you get: |
| 279 | try std.testing.expect(null != map.get("redundant")); |
| 280 | |
| 281 | // Default map is not case sensitive: |
| 282 | try std.testing.expect(null == map.get("REDUNDANT")); |
| 283 | |
| 284 | try std.testing.expectEqual(TestEnum.A, map.get("theNeedle").?); |
| 285 | } |
| 286 | |
| 287 | test "ComptimeStringMap redundant insensitive" { |
| 288 | const map = ComptimeStringMapWithEql(TestEnum, .{ |
| 289 | .{ "redundant", .D }, |
| 290 | .{ "theNeedle", .A }, |
| 291 | .{ "redundanT", .B }, |
| 292 | .{ "RE" ++ "dundant", .C }, |
| 293 | .{ "redun" ++ "DANT", .E }, |
| 294 | }, eqlAsciiIgnoreCase); |
| 295 | |
| 296 | // No promises about which result you'll get ... |
| 297 | try std.testing.expect(null != map.get("REDUNDANT")); |
| 298 | try std.testing.expect(null != map.get("ReDuNdAnT")); |
| 299 | |
| 300 | try std.testing.expectEqual(TestEnum.A, map.get("theNeedle").?); |
| 301 | } |