aboutsummaryrefslogtreecommitdiff
path: root/lib/std/comptime_string_map.zig
diff options
context:
space:
mode:
authorPat Tullmann <pat.github@tullmann.org>2023-12-08 19:33:39 -0800
committerVeikka Tuominen <git@vexu.eu>2024-01-05 05:09:24 +0200
commit04ac028a2cd20ea430dd6a05b956363ec182b6ff (patch)
tree4cb9ec2bc8185c90b5bbd7b1ae1adc56823dd808 /lib/std/comptime_string_map.zig
parent45836678d76014f94d35a2ffea983c21a57426c4 (diff)
downloadzig-04ac028a2cd20ea430dd6a05b956363ec182b6ff.tar.gz
zig-04ac028a2cd20ea430dd6a05b956363ec182b6ff.zip
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
Diffstat (limited to 'lib/std/comptime_string_map.zig')
-rw-r--r--lib/std/comptime_string_map.zig65
1 files changed, 64 insertions, 1 deletions
diff --git a/lib/std/comptime_string_map.zig b/lib/std/comptime_string_map.zig
index 486677e4b3..f4da1490b7 100644
--- a/lib/std/comptime_string_map.zig
+++ b/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").?);
+}