Commit 04ac028a2c

Pat Tullmann <pat.github@tullmann.org>
2023-12-09 04:33:39
std.CompTimeStringMap*: support empty initialization list
Add tests for empty initialization, and some more corner cases (empty key, very long key, duplicate keys). Fixes #18212
1 parent 4583667
Changed files (1)
lib/std/comptime_string_map.zig
@@ -43,12 +43,15 @@ pub fn ComptimeStringMapWithEql(
     comptime kvs_list: anytype,
     comptime eql: fn (a: []const u8, b: []const u8) bool,
 ) type {
-    const precomputed = comptime blk: {
+    const empty_list = kvs_list.len == 0;
+    const precomputed = blk: {
         @setEvalBranchQuota(1500);
         const KV = struct {
             key: []const u8,
             value: V,
         };
+        if (empty_list)
+            break :blk .{};
         var sorted_kvs: [kvs_list.len]KV = undefined;
         for (kvs_list, 0..) |kv, i| {
             if (V != void) {
@@ -103,10 +106,16 @@ pub fn ComptimeStringMapWithEql(
 
         /// Returns the value for the key if any, else null.
         pub fn get(str: []const u8) ?V {
+            if (empty_list)
+                return null;
+
             return precomputed.sorted_kvs[getIndex(str) orelse return null].value;
         }
 
         pub fn getIndex(str: []const u8) ?usize {
+            if (empty_list)
+                return null;
+
             if (str.len < precomputed.min_len or str.len > precomputed.max_len)
                 return null;
 
@@ -143,6 +152,9 @@ test "ComptimeStringMap list literal of list literals" {
     });
 
     try testMap(map);
+
+    // Default comparison is case sensitive
+    try std.testing.expect(null == map.get("NOTHING"));
 }
 
 test "ComptimeStringMap array of structs" {
@@ -181,6 +193,9 @@ fn testMap(comptime map: anytype) !void {
 
     try std.testing.expect(!map.has("missing"));
     try std.testing.expect(map.has("these"));
+
+    try std.testing.expect(null == map.get(""));
+    try std.testing.expect(null == map.get("averylongstringthathasnomatches"));
 }
 
 test "ComptimeStringMap void value type, slice of structs" {
@@ -195,6 +210,9 @@ test "ComptimeStringMap void value type, slice of structs" {
     const map = ComptimeStringMap(void, slice);
 
     try testSet(map);
+
+    // Default comparison is case sensitive
+    try std.testing.expect(null == map.get("NOTHING"));
 }
 
 test "ComptimeStringMap void value type, list literal of list literals" {
@@ -218,6 +236,9 @@ fn testSet(comptime map: anytype) !void {
 
     try std.testing.expect(!map.has("missing"));
     try std.testing.expect(map.has("these"));
+
+    try std.testing.expect(null == map.get(""));
+    try std.testing.expect(null == map.get("averylongstringthathasnomatches"));
 }
 
 test "ComptimeStringMapWithEql" {
@@ -236,3 +257,45 @@ test "ComptimeStringMapWithEql" {
 
     try std.testing.expect(map.has("ThESe"));
 }
+
+test "ComptimeStringMap empty" {
+    const m1 = ComptimeStringMap(usize, .{});
+    try std.testing.expect(null == m1.get("anything"));
+
+    const m2 = ComptimeStringMapWithEql(usize, .{}, eqlAsciiIgnoreCase);
+    try std.testing.expect(null == m2.get("anything"));
+}
+
+test "ComptimeStringMap redundant entries" {
+    const map = ComptimeStringMap(TestEnum, .{
+        .{ "redundant", .D },
+        .{ "theNeedle", .A },
+        .{ "redundant", .B },
+        .{ "re" ++ "dundant", .C },
+        .{ "redun" ++ "dant", .E },
+    });
+
+    // No promises about which one you get:
+    try std.testing.expect(null != map.get("redundant"));
+
+    // Default map is not case sensitive:
+    try std.testing.expect(null == map.get("REDUNDANT"));
+
+    try std.testing.expectEqual(TestEnum.A, map.get("theNeedle").?);
+}
+
+test "ComptimeStringMap redundant insensitive" {
+    const map = ComptimeStringMapWithEql(TestEnum, .{
+        .{ "redundant", .D },
+        .{ "theNeedle", .A },
+        .{ "redundanT", .B },
+        .{ "RE" ++ "dundant", .C },
+        .{ "redun" ++ "DANT", .E },
+    }, eqlAsciiIgnoreCase);
+
+    // No promises about which result you'll get ...
+    try std.testing.expect(null != map.get("REDUNDANT"));
+    try std.testing.expect(null != map.get("ReDuNdAnT"));
+
+    try std.testing.expectEqual(TestEnum.A, map.get("theNeedle").?);
+}