Skip to content

Commit c9613e3

Browse files
squeek502andrewrk
authored andcommitted
ComptimeStringMap: Add version that takes an equality function
This will allow users to construct e.g. a ComptimeStringMap that uses case-insensitive ASCII comparison. Note: the previous ComptimeStringMap API is unchanged (i.e. this does not break any existing code).
1 parent 6998233 commit c9613e3

File tree

2 files changed

+58
-3
lines changed

2 files changed

+58
-3
lines changed

lib/std/comptime_string_map.zig

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,42 @@ const mem = std.mem;
77
///
88
/// `kvs_list` expects a list of `struct { []const u8, V }` (key-value pair) tuples.
99
/// 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 {
1146
const precomputed = comptime blk: {
1247
@setEvalBranchQuota(1500);
1348
const KV = struct {
@@ -76,7 +111,7 @@ pub fn ComptimeStringMap(comptime V: type, comptime kvs_list: anytype) type {
76111
const kv = precomputed.sorted_kvs[i];
77112
if (kv.key.len != str.len)
78113
return null;
79-
if (mem.eql(u8, kv.key, str))
114+
if (eql(kv.key, str))
80115
return kv.value;
81116
i += 1;
82117
if (i >= precomputed.sorted_kvs.len)
@@ -180,3 +215,20 @@ fn testSet(comptime map: anytype) !void {
180215
try std.testing.expect(!map.has("missing"));
181216
try std.testing.expect(map.has("these"));
182217
}
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+
}

lib/std/std.zig

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ pub const BufMap = @import("buf_map.zig").BufMap;
1616
pub const BufSet = @import("buf_set.zig").BufSet;
1717
/// Deprecated: use `process.Child`.
1818
pub const ChildProcess = @import("child_process.zig").ChildProcess;
19-
pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringMap;
19+
pub const ComptimeStringMap = comptime_string_map.ComptimeStringMap;
20+
pub const ComptimeStringMapWithEql = comptime_string_map.ComptimeStringMapWithEql;
2021
pub const DoublyLinkedList = @import("linked_list.zig").DoublyLinkedList;
2122
pub const DynLib = @import("dynamic_library.zig").DynLib;
2223
pub const DynamicBitSet = bit_set.DynamicBitSet;
@@ -74,6 +75,8 @@ pub const coff = @import("coff.zig");
7475
/// Compression algorithms such as zlib, zstd, etc.
7576
pub const compress = @import("compress.zig");
7677

78+
pub const comptime_string_map = @import("comptime_string_map.zig");
79+
7780
/// Cryptography.
7881
pub const crypto = @import("crypto.zig");
7982

0 commit comments

Comments
 (0)