Skip to content

Commit 04ac028

Browse files
rootbeerVexu
authored andcommitted
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 commit 04ac028

File tree

1 file changed

+64
-1
lines changed

1 file changed

+64
-1
lines changed

lib/std/comptime_string_map.zig

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,15 @@ pub fn ComptimeStringMapWithEql(
4343
comptime kvs_list: anytype,
4444
comptime eql: fn (a: []const u8, b: []const u8) bool,
4545
) type {
46-
const precomputed = comptime blk: {
46+
const empty_list = kvs_list.len == 0;
47+
const precomputed = blk: {
4748
@setEvalBranchQuota(1500);
4849
const KV = struct {
4950
key: []const u8,
5051
value: V,
5152
};
53+
if (empty_list)
54+
break :blk .{};
5255
var sorted_kvs: [kvs_list.len]KV = undefined;
5356
for (kvs_list, 0..) |kv, i| {
5457
if (V != void) {
@@ -103,10 +106,16 @@ pub fn ComptimeStringMapWithEql(
103106

104107
/// Returns the value for the key if any, else null.
105108
pub fn get(str: []const u8) ?V {
109+
if (empty_list)
110+
return null;
111+
106112
return precomputed.sorted_kvs[getIndex(str) orelse return null].value;
107113
}
108114

109115
pub fn getIndex(str: []const u8) ?usize {
116+
if (empty_list)
117+
return null;
118+
110119
if (str.len < precomputed.min_len or str.len > precomputed.max_len)
111120
return null;
112121

@@ -143,6 +152,9 @@ test "ComptimeStringMap list literal of list literals" {
143152
});
144153

145154
try testMap(map);
155+
156+
// Default comparison is case sensitive
157+
try std.testing.expect(null == map.get("NOTHING"));
146158
}
147159

148160
test "ComptimeStringMap array of structs" {
@@ -181,6 +193,9 @@ fn testMap(comptime map: anytype) !void {
181193

182194
try std.testing.expect(!map.has("missing"));
183195
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"));
184199
}
185200

186201
test "ComptimeStringMap void value type, slice of structs" {
@@ -195,6 +210,9 @@ test "ComptimeStringMap void value type, slice of structs" {
195210
const map = ComptimeStringMap(void, slice);
196211

197212
try testSet(map);
213+
214+
// Default comparison is case sensitive
215+
try std.testing.expect(null == map.get("NOTHING"));
198216
}
199217

200218
test "ComptimeStringMap void value type, list literal of list literals" {
@@ -218,6 +236,9 @@ fn testSet(comptime map: anytype) !void {
218236

219237
try std.testing.expect(!map.has("missing"));
220238
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"));
221242
}
222243

223244
test "ComptimeStringMapWithEql" {
@@ -236,3 +257,45 @@ test "ComptimeStringMapWithEql" {
236257

237258
try std.testing.expect(map.has("ThESe"));
238259
}
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+
}

0 commit comments

Comments
 (0)