authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2023-09-14 02:35:39-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-09-14 11:03:15-07:00
logc9613e3d5cf601bdbcbad039949a0721248f54a3
tree22cf636796fd3609747d9f356a94e5a645119b9e
parent69982339ed3c35df890f205fada0bb5772abdb63

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).

2 files changed, 58 insertions(+), 3 deletions(-)

lib/std/comptime_string_map.zig+54-2
...@@ -7,7 +7,42 @@ const mem = std.mem;...@@ -7,7 +7,42 @@ const mem = std.mem;
7///7///
8/// `kvs_list` expects a list of `struct { []const u8, V }` (key-value pair) tuples.8/// `kvs_list` expects a list of `struct { []const u8, V }` (key-value pair) tuples.
9/// You can pass `struct { []const u8 }` (only keys) tuples if `V` is `void`.9/// You can pass `struct { []const u8 }` (only keys) tuples if `V` is `void`.
10pub fn ComptimeStringMap(comptime V: type, comptime kvs_list: anytype) type {10pub 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.
19pub 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.
29pub 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.
41pub fn ComptimeStringMapWithEql(
42 comptime V: type,
43 comptime kvs_list: anytype,
44 comptime eql: fn (a: []const u8, b: []const u8) bool,
45) type {
11 const precomputed = comptime blk: {46 const precomputed = comptime blk: {
12 @setEvalBranchQuota(1500);47 @setEvalBranchQuota(1500);
13 const KV = struct {48 const KV = struct {
...@@ -76,7 +111,7 @@ pub fn ComptimeStringMap(comptime V: type, comptime kvs_list: anytype) type {...@@ -76,7 +111,7 @@ pub fn ComptimeStringMap(comptime V: type, comptime kvs_list: anytype) type {
76 const kv = precomputed.sorted_kvs[i];111 const kv = precomputed.sorted_kvs[i];
77 if (kv.key.len != str.len)112 if (kv.key.len != str.len)
78 return null;113 return null;
79 if (mem.eql(u8, kv.key, str))114 if (eql(kv.key, str))
80 return kv.value;115 return kv.value;
81 i += 1;116 i += 1;
82 if (i >= precomputed.sorted_kvs.len)117 if (i >= precomputed.sorted_kvs.len)
...@@ -180,3 +215,20 @@ fn testSet(comptime map: anytype) !void {...@@ -180,3 +215,20 @@ fn testSet(comptime map: anytype) !void {
180 try std.testing.expect(!map.has("missing"));215 try std.testing.expect(!map.has("missing"));
181 try std.testing.expect(map.has("these"));216 try std.testing.expect(map.has("these"));
182}217}
218
219test "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+4-1
...@@ -16,7 +16,8 @@ pub const BufMap = @import("buf_map.zig").BufMap;...@@ -16,7 +16,8 @@ pub const BufMap = @import("buf_map.zig").BufMap;
16pub const BufSet = @import("buf_set.zig").BufSet;16pub const BufSet = @import("buf_set.zig").BufSet;
17/// Deprecated: use `process.Child`.17/// Deprecated: use `process.Child`.
18pub const ChildProcess = @import("child_process.zig").ChildProcess;18pub const ChildProcess = @import("child_process.zig").ChildProcess;
19pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringMap;19pub const ComptimeStringMap = comptime_string_map.ComptimeStringMap;
20pub const ComptimeStringMapWithEql = comptime_string_map.ComptimeStringMapWithEql;
20pub const DoublyLinkedList = @import("linked_list.zig").DoublyLinkedList;21pub const DoublyLinkedList = @import("linked_list.zig").DoublyLinkedList;
21pub const DynLib = @import("dynamic_library.zig").DynLib;22pub const DynLib = @import("dynamic_library.zig").DynLib;
22pub const DynamicBitSet = bit_set.DynamicBitSet;23pub const DynamicBitSet = bit_set.DynamicBitSet;
...@@ -74,6 +75,8 @@ pub const coff = @import("coff.zig");...@@ -74,6 +75,8 @@ pub const coff = @import("coff.zig");
74/// Compression algorithms such as zlib, zstd, etc.75/// Compression algorithms such as zlib, zstd, etc.
75pub const compress = @import("compress.zig");76pub const compress = @import("compress.zig");
7677
78pub const comptime_string_map = @import("comptime_string_map.zig");
79
77/// Cryptography.80/// Cryptography.
78pub const crypto = @import("crypto.zig");81pub const crypto = @import("crypto.zig");
7982