diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fb63a57fd..28fea9c492 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -222,7 +222,7 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/c/linux.zig" "${CMAKE_SOURCE_DIR}/lib/std/child_process.zig" "${CMAKE_SOURCE_DIR}/lib/std/coff.zig" - "${CMAKE_SOURCE_DIR}/lib/std/comptime_string_map.zig" + "${CMAKE_SOURCE_DIR}/lib/std/static_string_map.zig" "${CMAKE_SOURCE_DIR}/lib/std/crypto.zig" "${CMAKE_SOURCE_DIR}/lib/std/crypto/blake3.zig" "${CMAKE_SOURCE_DIR}/lib/std/crypto/siphash.zig" diff --git a/lib/compiler/aro/aro/LangOpts.zig b/lib/compiler/aro/aro/LangOpts.zig index f2c15c599b..e7b2ebf6c0 100644 --- a/lib/compiler/aro/aro/LangOpts.zig +++ b/lib/compiler/aro/aro/LangOpts.zig @@ -47,7 +47,7 @@ pub const Standard = enum { /// Working Draft for ISO C23 with GNU extensions gnu23, - const NameMap = std.ComptimeStringMap(Standard, .{ + const NameMap = std.StaticStringMap(Standard).initComptime(.{ .{ "c89", .c89 }, .{ "c90", .c89 }, .{ "iso9899:1990", .c89 }, .{ "iso9899:199409", .iso9899 }, .{ "gnu89", .gnu89 }, .{ "gnu90", .gnu89 }, .{ "c99", .c99 }, .{ "iso9899:1999", .c99 }, .{ "c9x", .c99 }, diff --git a/lib/compiler/aro/aro/Preprocessor.zig b/lib/compiler/aro/aro/Preprocessor.zig index dc7be1ffc5..06193bf3e4 100644 --- a/lib/compiler/aro/aro/Preprocessor.zig +++ b/lib/compiler/aro/aro/Preprocessor.zig @@ -1709,7 +1709,7 @@ fn expandFuncMacro( } if (!pp.comp.langopts.standard.atLeast(.c23)) break :res not_found; - const attrs = std.ComptimeStringMap([]const u8, .{ + const attrs = std.StaticStringMap([]const u8).initComptime(.{ .{ "deprecated", "201904L\n" }, .{ "fallthrough", "201904L\n" }, .{ "maybe_unused", "201904L\n" }, diff --git a/lib/compiler/aro/aro/Tokenizer.zig b/lib/compiler/aro/aro/Tokenizer.zig index 0f2b2ac4b7..c5a84b8cc0 100644 --- a/lib/compiler/aro/aro/Tokenizer.zig +++ b/lib/compiler/aro/aro/Tokenizer.zig @@ -872,7 +872,7 @@ pub const Token = struct { }; } - const all_kws = std.ComptimeStringMap(Id, .{ + const all_kws = std.StaticStringMap(Id).initComptime(.{ .{ "auto", auto: { @setEvalBranchQuota(3000); break :auto .keyword_auto; diff --git a/lib/compiler/resinator/errors.zig b/lib/compiler/resinator/errors.zig index 44a9d46163..909824c594 100644 --- a/lib/compiler/resinator/errors.zig +++ b/lib/compiler/resinator/errors.zig @@ -240,7 +240,7 @@ pub const ErrorDetails = struct { // see https://github.com/ziglang/zig/issues/15395 _: u26 = 0, - pub const strings = std.ComptimeStringMap([]const u8, .{ + pub const strings = std.StaticStringMap([]const u8).initComptime(.{ .{ "number", "number" }, .{ "number_expression", "number expression" }, .{ "string_literal", "quoted string literal" }, diff --git a/lib/compiler/resinator/rc.zig b/lib/compiler/resinator/rc.zig index 00cb455058..a434e26c80 100644 --- a/lib/compiler/resinator/rc.zig +++ b/lib/compiler/resinator/rc.zig @@ -47,7 +47,10 @@ pub const Resource = enum { fontdir_num, manifest_num, - const map = std.ComptimeStringMapWithEql(Resource, .{ + const map = std.StaticStringMapWithEql( + Resource, + std.static_string_map.eqlAsciiIgnoreCase, + ).initComptime(.{ .{ "ACCELERATORS", .accelerators }, .{ "BITMAP", .bitmap }, .{ "CURSOR", .cursor }, @@ -67,7 +70,7 @@ pub const Resource = enum { .{ "TOOLBAR", .toolbar }, .{ "VERSIONINFO", .versioninfo }, .{ "VXD", .vxd }, - }, std.comptime_string_map.eqlAsciiIgnoreCase); + }); pub fn fromString(bytes: SourceBytes) Resource { const maybe_ordinal = res.NameOrOrdinal.maybeOrdinalFromString(bytes); @@ -157,20 +160,26 @@ pub const OptionalStatements = enum { menu, style, - pub const map = std.ComptimeStringMapWithEql(OptionalStatements, .{ + pub const map = std.StaticStringMapWithEql( + OptionalStatements, + std.static_string_map.eqlAsciiIgnoreCase, + ).initComptime(.{ .{ "CHARACTERISTICS", .characteristics }, .{ "LANGUAGE", .language }, .{ "VERSION", .version }, - }, std.comptime_string_map.eqlAsciiIgnoreCase); + }); - pub const dialog_map = std.ComptimeStringMapWithEql(OptionalStatements, .{ + pub const dialog_map = std.StaticStringMapWithEql( + OptionalStatements, + std.static_string_map.eqlAsciiIgnoreCase, + ).initComptime(.{ .{ "CAPTION", .caption }, .{ "CLASS", .class }, .{ "EXSTYLE", .exstyle }, .{ "FONT", .font }, .{ "MENU", .menu }, .{ "STYLE", .style }, - }, std.comptime_string_map.eqlAsciiIgnoreCase); + }); }; pub const Control = enum { @@ -197,7 +206,10 @@ pub const Control = enum { state3, userbutton, - pub const map = std.ComptimeStringMapWithEql(Control, .{ + pub const map = std.StaticStringMapWithEql( + Control, + std.static_string_map.eqlAsciiIgnoreCase, + ).initComptime(.{ .{ "AUTO3STATE", .auto3state }, .{ "AUTOCHECKBOX", .autocheckbox }, .{ "AUTORADIOBUTTON", .autoradiobutton }, @@ -220,7 +232,7 @@ pub const Control = enum { .{ "SCROLLBAR", .scrollbar }, .{ "STATE3", .state3 }, .{ "USERBUTTON", .userbutton }, - }, std.comptime_string_map.eqlAsciiIgnoreCase); + }); pub fn hasTextParam(control: Control) bool { switch (control) { @@ -231,14 +243,17 @@ pub const Control = enum { }; pub const ControlClass = struct { - pub const map = std.ComptimeStringMapWithEql(res.ControlClass, .{ + pub const map = std.StaticStringMapWithEql( + res.ControlClass, + std.static_string_map.eqlAsciiIgnoreCase, + ).initComptime(.{ .{ "BUTTON", .button }, .{ "EDIT", .edit }, .{ "STATIC", .static }, .{ "LISTBOX", .listbox }, .{ "SCROLLBAR", .scrollbar }, .{ "COMBOBOX", .combobox }, - }, std.comptime_string_map.eqlAsciiIgnoreCase); + }); /// Like `map.get` but works on WTF16 strings, for use with parsed /// string literals ("BUTTON", or even "\x42UTTON") @@ -280,10 +295,13 @@ pub const MenuItem = enum { menuitem, popup, - pub const map = std.ComptimeStringMapWithEql(MenuItem, .{ + pub const map = std.StaticStringMapWithEql( + MenuItem, + std.static_string_map.eqlAsciiIgnoreCase, + ).initComptime(.{ .{ "MENUITEM", .menuitem }, .{ "POPUP", .popup }, - }, std.comptime_string_map.eqlAsciiIgnoreCase); + }); pub fn isSeparator(bytes: []const u8) bool { return std.ascii.eqlIgnoreCase(bytes, "SEPARATOR"); @@ -297,14 +315,17 @@ pub const MenuItem = enum { menubarbreak, menubreak, - pub const map = std.ComptimeStringMapWithEql(Option, .{ + pub const map = std.StaticStringMapWithEql( + Option, + std.static_string_map.eqlAsciiIgnoreCase, + ).initComptime(.{ .{ "CHECKED", .checked }, .{ "GRAYED", .grayed }, .{ "HELP", .help }, .{ "INACTIVE", .inactive }, .{ "MENUBARBREAK", .menubarbreak }, .{ "MENUBREAK", .menubreak }, - }, std.comptime_string_map.eqlAsciiIgnoreCase); + }); }; }; @@ -312,10 +333,13 @@ pub const ToolbarButton = enum { button, separator, - pub const map = std.ComptimeStringMapWithEql(ToolbarButton, .{ + pub const map = std.StaticStringMapWithEql( + ToolbarButton, + std.static_string_map.eqlAsciiIgnoreCase, + ).initComptime(.{ .{ "BUTTON", .button }, .{ "SEPARATOR", .separator }, - }, std.comptime_string_map.eqlAsciiIgnoreCase); + }); }; pub const VersionInfo = enum { @@ -327,7 +351,10 @@ pub const VersionInfo = enum { file_type, file_subtype, - pub const map = std.ComptimeStringMapWithEql(VersionInfo, .{ + pub const map = std.StaticStringMapWithEql( + VersionInfo, + std.static_string_map.eqlAsciiIgnoreCase, + ).initComptime(.{ .{ "FILEVERSION", .file_version }, .{ "PRODUCTVERSION", .product_version }, .{ "FILEFLAGSMASK", .file_flags_mask }, @@ -335,17 +362,20 @@ pub const VersionInfo = enum { .{ "FILEOS", .file_os }, .{ "FILETYPE", .file_type }, .{ "FILESUBTYPE", .file_subtype }, - }, std.comptime_string_map.eqlAsciiIgnoreCase); + }); }; pub const VersionBlock = enum { block, value, - pub const map = std.ComptimeStringMapWithEql(VersionBlock, .{ + pub const map = std.StaticStringMapWithEql( + VersionBlock, + std.static_string_map.eqlAsciiIgnoreCase, + ).initComptime(.{ .{ "BLOCK", .block }, .{ "VALUE", .value }, - }, std.comptime_string_map.eqlAsciiIgnoreCase); + }); }; /// Keywords that are be the first token in a statement and (if so) dictate how the rest @@ -356,12 +386,15 @@ pub const TopLevelKeywords = enum { characteristics, stringtable, - pub const map = std.ComptimeStringMapWithEql(TopLevelKeywords, .{ + pub const map = std.StaticStringMapWithEql( + TopLevelKeywords, + std.static_string_map.eqlAsciiIgnoreCase, + ).initComptime(.{ .{ "LANGUAGE", .language }, .{ "VERSION", .version }, .{ "CHARACTERISTICS", .characteristics }, .{ "STRINGTABLE", .stringtable }, - }, std.comptime_string_map.eqlAsciiIgnoreCase); + }); }; pub const CommonResourceAttributes = enum { @@ -375,7 +408,10 @@ pub const CommonResourceAttributes = enum { shared, nonshared, - pub const map = std.ComptimeStringMapWithEql(CommonResourceAttributes, .{ + pub const map = std.StaticStringMapWithEql( + CommonResourceAttributes, + std.static_string_map.eqlAsciiIgnoreCase, + ).initComptime(.{ .{ "PRELOAD", .preload }, .{ "LOADONCALL", .loadoncall }, .{ "FIXED", .fixed }, @@ -385,7 +421,7 @@ pub const CommonResourceAttributes = enum { .{ "IMPURE", .impure }, .{ "SHARED", .shared }, .{ "NONSHARED", .nonshared }, - }, std.comptime_string_map.eqlAsciiIgnoreCase); + }); }; pub const AcceleratorTypeAndOptions = enum { @@ -396,12 +432,15 @@ pub const AcceleratorTypeAndOptions = enum { shift, control, - pub const map = std.ComptimeStringMapWithEql(AcceleratorTypeAndOptions, .{ + pub const map = std.StaticStringMapWithEql( + AcceleratorTypeAndOptions, + std.static_string_map.eqlAsciiIgnoreCase, + ).initComptime(.{ .{ "VIRTKEY", .virtkey }, .{ "ASCII", .ascii }, .{ "NOINVERT", .noinvert }, .{ "ALT", .alt }, .{ "SHIFT", .shift }, .{ "CONTROL", .control }, - }, std.comptime_string_map.eqlAsciiIgnoreCase); + }); }; diff --git a/lib/std/comptime_string_map.zig b/lib/std/comptime_string_map.zig deleted file mode 100644 index 2bfcad1e77..0000000000 --- a/lib/std/comptime_string_map.zig +++ /dev/null @@ -1,320 +0,0 @@ -const std = @import("std.zig"); -const mem = std.mem; - -/// Comptime string map optimized for small sets of disparate string keys. -/// Works by separating the keys by length at comptime and only checking strings of -/// equal length at runtime. -/// -/// `kvs_list` expects a list of `struct { []const u8, V }` (key-value pair) tuples. -/// You can pass `struct { []const u8 }` (only keys) tuples if `V` is `void`. -pub fn ComptimeStringMap( - comptime V: type, - comptime kvs_list: anytype, -) type { - return ComptimeStringMapWithEql(V, kvs_list, defaultEql); -} - -/// Like `std.mem.eql`, but takes advantage of the fact that the lengths -/// of `a` and `b` are known to be equal. -pub fn defaultEql(a: []const u8, b: []const u8) bool { - if (a.ptr == b.ptr) return true; - for (a, b) |a_elem, b_elem| { - if (a_elem != b_elem) return false; - } - return true; -} - -/// Like `std.ascii.eqlIgnoreCase` but takes advantage of the fact that -/// the lengths of `a` and `b` are known to be equal. -pub fn eqlAsciiIgnoreCase(a: []const u8, b: []const u8) bool { - if (a.ptr == b.ptr) return true; - for (a, b) |a_c, b_c| { - if (std.ascii.toLower(a_c) != std.ascii.toLower(b_c)) return false; - } - return true; -} - -/// ComptimeStringMap, but accepts an equality function (`eql`). -/// The `eql` function is only called to determine the equality -/// of equal length strings. Any strings that are not equal length -/// are never compared using the `eql` function. -pub fn ComptimeStringMapWithEql( - comptime V: type, - comptime kvs_list: anytype, - comptime eql: fn (a: []const u8, b: []const u8) bool, -) type { - 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) { - sorted_kvs[i] = .{ .key = kv.@"0", .value = kv.@"1" }; - } else { - sorted_kvs[i] = .{ .key = kv.@"0", .value = {} }; - } - } - - const SortContext = struct { - kvs: []KV, - - pub fn lessThan(ctx: @This(), a: usize, b: usize) bool { - return ctx.kvs[a].key.len < ctx.kvs[b].key.len; - } - - pub fn swap(ctx: @This(), a: usize, b: usize) void { - return std.mem.swap(KV, &ctx.kvs[a], &ctx.kvs[b]); - } - }; - mem.sortUnstableContext(0, sorted_kvs.len, SortContext{ .kvs = &sorted_kvs }); - - const min_len = sorted_kvs[0].key.len; - const max_len = sorted_kvs[sorted_kvs.len - 1].key.len; - var len_indexes: [max_len + 1]usize = undefined; - var len: usize = 0; - var i: usize = 0; - while (len <= max_len) : (len += 1) { - // find the first keyword len == len - while (len > sorted_kvs[i].key.len) { - i += 1; - } - len_indexes[len] = i; - } - break :blk .{ - .min_len = min_len, - .max_len = max_len, - .sorted_kvs = sorted_kvs, - .len_indexes = len_indexes, - }; - }; - - return struct { - /// Array of `struct { key: []const u8, value: V }` where `value` is `void{}` if `V` is `void`. - /// Sorted by `key` length. - pub const kvs = precomputed.sorted_kvs; - - /// Checks if the map has a value for the key. - pub fn has(str: []const u8) bool { - return get(str) != null; - } - - /// 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; - - var i = precomputed.len_indexes[str.len]; - while (true) { - const kv = precomputed.sorted_kvs[i]; - if (kv.key.len != str.len) - return null; - if (eql(kv.key, str)) - return i; - i += 1; - if (i >= precomputed.sorted_kvs.len) - return null; - } - } - }; -} - -const TestEnum = enum { - A, - B, - C, - D, - E, -}; - -test "list literal of list literals" { - const map = ComptimeStringMap(TestEnum, .{ - .{ "these", .D }, - .{ "have", .A }, - .{ "nothing", .B }, - .{ "incommon", .C }, - .{ "samelen", .E }, - }); - - try testMap(map); - - // Default comparison is case sensitive - try std.testing.expect(null == map.get("NOTHING")); -} - -test "array of structs" { - const KV = struct { []const u8, TestEnum }; - const map = ComptimeStringMap(TestEnum, [_]KV{ - .{ "these", .D }, - .{ "have", .A }, - .{ "nothing", .B }, - .{ "incommon", .C }, - .{ "samelen", .E }, - }); - - try testMap(map); -} - -test "slice of structs" { - const KV = struct { []const u8, TestEnum }; - const slice: []const KV = &[_]KV{ - .{ "these", .D }, - .{ "have", .A }, - .{ "nothing", .B }, - .{ "incommon", .C }, - .{ "samelen", .E }, - }; - const map = ComptimeStringMap(TestEnum, slice); - - try testMap(map); -} - -fn testMap(comptime map: anytype) !void { - try std.testing.expectEqual(TestEnum.A, map.get("have").?); - try std.testing.expectEqual(TestEnum.B, map.get("nothing").?); - try std.testing.expect(null == map.get("missing")); - try std.testing.expectEqual(TestEnum.D, map.get("these").?); - try std.testing.expectEqual(TestEnum.E, map.get("samelen").?); - - 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 "void value type, slice of structs" { - const KV = struct { []const u8 }; - const slice: []const KV = &[_]KV{ - .{"these"}, - .{"have"}, - .{"nothing"}, - .{"incommon"}, - .{"samelen"}, - }; - const map = ComptimeStringMap(void, slice); - - try testSet(map); - - // Default comparison is case sensitive - try std.testing.expect(null == map.get("NOTHING")); -} - -test "void value type, list literal of list literals" { - const map = ComptimeStringMap(void, .{ - .{"these"}, - .{"have"}, - .{"nothing"}, - .{"incommon"}, - .{"samelen"}, - }); - - try testSet(map); -} - -fn testSet(comptime map: anytype) !void { - try std.testing.expectEqual({}, map.get("have").?); - try std.testing.expectEqual({}, map.get("nothing").?); - try std.testing.expect(null == map.get("missing")); - try std.testing.expectEqual({}, map.get("these").?); - try std.testing.expectEqual({}, map.get("samelen").?); - - 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" { - const map = ComptimeStringMapWithEql(TestEnum, .{ - .{ "these", .D }, - .{ "have", .A }, - .{ "nothing", .B }, - .{ "incommon", .C }, - .{ "samelen", .E }, - }, eqlAsciiIgnoreCase); - - try testMap(map); - try std.testing.expectEqual(TestEnum.A, map.get("HAVE").?); - try std.testing.expectEqual(TestEnum.E, map.get("SameLen").?); - try std.testing.expect(null == map.get("SameLength")); - - try std.testing.expect(map.has("ThESe")); -} - -test "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 "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 "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").?); -} - -test "comptime-only value" { - const map = std.ComptimeStringMap(type, .{ - .{ "a", struct { - pub const foo = 1; - } }, - .{ "b", struct { - pub const foo = 2; - } }, - .{ "c", struct { - pub const foo = 3; - } }, - }); - - try std.testing.expect(map.get("a").?.foo == 1); - try std.testing.expect(map.get("b").?.foo == 2); - try std.testing.expect(map.get("c").?.foo == 3); - try std.testing.expect(map.get("d") == null); -} diff --git a/lib/std/crypto/Certificate.zig b/lib/std/crypto/Certificate.zig index b1cc4fc095..1e3bb9ca0b 100644 --- a/lib/std/crypto/Certificate.zig +++ b/lib/std/crypto/Certificate.zig @@ -19,7 +19,7 @@ pub const Algorithm = enum { md5WithRSAEncryption, curveEd25519, - pub const map = std.ComptimeStringMap(Algorithm, .{ + pub const map = std.StaticStringMap(Algorithm).initComptime(.{ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05 }, .sha1WithRSAEncryption }, .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B }, .sha256WithRSAEncryption }, .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C }, .sha384WithRSAEncryption }, @@ -52,7 +52,7 @@ pub const AlgorithmCategory = enum { X9_62_id_ecPublicKey, curveEd25519, - pub const map = std.ComptimeStringMap(AlgorithmCategory, .{ + pub const map = std.StaticStringMap(AlgorithmCategory).initComptime(.{ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }, .rsaEncryption }, .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01 }, .X9_62_id_ecPublicKey }, .{ &[_]u8{ 0x2B, 0x65, 0x70 }, .curveEd25519 }, @@ -73,7 +73,7 @@ pub const Attribute = enum { pkcs9_emailAddress, domainComponent, - pub const map = std.ComptimeStringMap(Attribute, .{ + pub const map = std.StaticStringMap(Attribute).initComptime(.{ .{ &[_]u8{ 0x55, 0x04, 0x03 }, .commonName }, .{ &[_]u8{ 0x55, 0x04, 0x05 }, .serialNumber }, .{ &[_]u8{ 0x55, 0x04, 0x06 }, .countryName }, @@ -94,7 +94,7 @@ pub const NamedCurve = enum { secp521r1, X9_62_prime256v1, - pub const map = std.ComptimeStringMap(NamedCurve, .{ + pub const map = std.StaticStringMap(NamedCurve).initComptime(.{ .{ &[_]u8{ 0x2B, 0x81, 0x04, 0x00, 0x22 }, .secp384r1 }, .{ &[_]u8{ 0x2B, 0x81, 0x04, 0x00, 0x23 }, .secp521r1 }, .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 }, .X9_62_prime256v1 }, @@ -130,7 +130,7 @@ pub const ExtensionId = enum { netscape_cert_type, netscape_comment, - pub const map = std.ComptimeStringMap(ExtensionId, .{ + pub const map = std.StaticStringMap(ExtensionId).initComptime(.{ .{ &[_]u8{ 0x55, 0x04, 0x03 }, .commonName }, .{ &[_]u8{ 0x55, 0x1D, 0x01 }, .authority_key_identifier }, .{ &[_]u8{ 0x55, 0x1D, 0x07 }, .subject_alt_name }, diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 3aa932cf01..488632b34c 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -1641,7 +1641,7 @@ test "walker" { // iteration order of walker is undefined, so need lookup maps to check against - const expected_paths = std.ComptimeStringMap(void, .{ + const expected_paths = std.StaticStringMap(void).initComptime(.{ .{"dir1"}, .{"dir2"}, .{"dir3"}, @@ -1651,7 +1651,7 @@ test "walker" { .{"dir3" ++ fs.path.sep_str ++ "sub2" ++ fs.path.sep_str ++ "subsub1"}, }); - const expected_basenames = std.ComptimeStringMap(void, .{ + const expected_basenames = std.StaticStringMap(void).initComptime(.{ .{"dir1"}, .{"dir2"}, .{"dir3"}, @@ -1661,8 +1661,8 @@ test "walker" { .{"subsub1"}, }); - for (expected_paths.kvs) |kv| { - try tmp.dir.makePath(kv.key); + for (expected_paths.keys()) |key| { + try tmp.dir.makePath(key); } var walker = try tmp.dir.walk(testing.allocator); diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index 837bdc63c7..9da1e428f6 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -1570,7 +1570,7 @@ pub const RequestOptions = struct { }; fn validateUri(uri: Uri, arena: Allocator) !struct { Connection.Protocol, Uri } { - const protocol_map = std.ComptimeStringMap(Connection.Protocol, .{ + const protocol_map = std.StaticStringMap(Connection.Protocol).initComptime(.{ .{ "http", .plain }, .{ "ws", .plain }, .{ "https", .tls }, diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 3ad2c2de13..0dcbe64548 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -19,7 +19,7 @@ pub const isTag = @compileError("deprecated; use 'tagged_value == @field(E, tag_ /// Returns the variant of an enum type, `T`, which is named `str`, or `null` if no such variant exists. pub fn stringToEnum(comptime T: type, str: []const u8) ?T { - // Using ComptimeStringMap here is more performant, but it will start to take too + // Using StaticStringMap here is more performant, but it will start to take too // long to compile if the enum is large enough, due to the current limits of comptime // performance when doing things like constructing lookup maps at comptime. // TODO The '100' here is arbitrary and should be increased when possible: @@ -34,7 +34,7 @@ pub fn stringToEnum(comptime T: type, str: []const u8) ?T { } break :build_kvs kvs_array[0..]; }; - const map = std.ComptimeStringMap(T, kvs); + const map = std.StaticStringMap(T).initComptime(kvs); return map.get(str); } else { inline for (@typeInfo(T).Enum.fields) |enumField| { diff --git a/lib/std/static_string_map.zig b/lib/std/static_string_map.zig new file mode 100644 index 0000000000..1136bde587 --- /dev/null +++ b/lib/std/static_string_map.zig @@ -0,0 +1,502 @@ +const std = @import("std.zig"); +const mem = std.mem; + +/// Static string map optimized for small sets of disparate string keys. +/// Works by separating the keys by length at initialization and only checking +/// strings of equal length at runtime. +pub fn StaticStringMap(comptime V: type) type { + return StaticStringMapWithEql(V, defaultEql); +} + +/// Like `std.mem.eql`, but takes advantage of the fact that the lengths +/// of `a` and `b` are known to be equal. +pub fn defaultEql(a: []const u8, b: []const u8) bool { + if (a.ptr == b.ptr) return true; + for (a, b) |a_elem, b_elem| { + if (a_elem != b_elem) return false; + } + return true; +} + +/// Like `std.ascii.eqlIgnoreCase` but takes advantage of the fact that +/// the lengths of `a` and `b` are known to be equal. +pub fn eqlAsciiIgnoreCase(a: []const u8, b: []const u8) bool { + if (a.ptr == b.ptr) return true; + for (a, b) |a_c, b_c| { + if (std.ascii.toLower(a_c) != std.ascii.toLower(b_c)) return false; + } + return true; +} + +/// StaticStringMap, but accepts an equality function (`eql`). +/// The `eql` function is only called to determine the equality +/// of equal length strings. Any strings that are not equal length +/// are never compared using the `eql` function. +pub fn StaticStringMapWithEql( + comptime V: type, + comptime eql: fn (a: []const u8, b: []const u8) bool, +) type { + return struct { + kvs: *const KVs = &empty_kvs, + len_indexes: [*]const u32 = &empty_len_indexes, + len_indexes_len: u32 = 0, + min_len: u32 = std.math.maxInt(u32), + max_len: u32 = 0, + + pub const KV = struct { + key: []const u8, + value: V, + }; + + const Self = @This(); + const KVs = struct { + keys: [*]const []const u8, + values: [*]const V, + len: u32, + }; + const empty_kvs = KVs{ + .keys = &empty_keys, + .values = &empty_vals, + .len = 0, + }; + const empty_len_indexes = [0]u32{}; + const empty_keys = [0][]const u8{}; + const empty_vals = [0]V{}; + + /// Returns a map backed by static, comptime allocated memory. + /// + /// `kvs_list` must be either a list of `struct { []const u8, V }` + /// (key-value pair) tuples, or a list of `struct { []const u8 }` + /// (only keys) tuples if `V` is `void`. + pub inline fn initComptime(comptime kvs_list: anytype) Self { + comptime { + @setEvalBranchQuota(30 * kvs_list.len); + var self = Self{}; + if (kvs_list.len == 0) + return self; + + var sorted_keys: [kvs_list.len][]const u8 = undefined; + var sorted_vals: [kvs_list.len]V = undefined; + + self.initSortedKVs(kvs_list, &sorted_keys, &sorted_vals); + const final_keys = sorted_keys; + const final_vals = sorted_vals; + self.kvs = &.{ + .keys = &final_keys, + .values = &final_vals, + .len = @intCast(kvs_list.len), + }; + + var len_indexes: [self.max_len + 1]u32 = undefined; + self.initLenIndexes(&len_indexes); + const final_len_indexes = len_indexes; + self.len_indexes = &final_len_indexes; + self.len_indexes_len = @intCast(len_indexes.len); + return self; + } + } + + /// Returns a map backed by memory allocated with `allocator`. + /// + /// Handles `kvs_list` the same way as `initComptime()`. + pub fn init(kvs_list: anytype, allocator: mem.Allocator) !Self { + var self = Self{}; + if (kvs_list.len == 0) + return self; + + const sorted_keys = try allocator.alloc([]const u8, kvs_list.len); + errdefer allocator.free(sorted_keys); + const sorted_vals = try allocator.alloc(V, kvs_list.len); + errdefer allocator.free(sorted_vals); + const kvs = try allocator.create(KVs); + errdefer allocator.destroy(kvs); + + self.initSortedKVs(kvs_list, sorted_keys, sorted_vals); + kvs.* = .{ + .keys = sorted_keys.ptr, + .values = sorted_vals.ptr, + .len = kvs_list.len, + }; + self.kvs = kvs; + + const len_indexes = try allocator.alloc(u32, self.max_len + 1); + self.initLenIndexes(len_indexes); + self.len_indexes = len_indexes.ptr; + self.len_indexes_len = @intCast(len_indexes.len); + return self; + } + + /// this method should only be used with init() and not with initComptime(). + pub fn deinit(self: Self, allocator: mem.Allocator) void { + allocator.free(self.len_indexes[0..self.len_indexes_len]); + allocator.free(self.kvs.keys[0..self.kvs.len]); + allocator.free(self.kvs.values[0..self.kvs.len]); + allocator.destroy(self.kvs); + } + + const SortContext = struct { + keys: [][]const u8, + vals: []V, + + pub fn lessThan(ctx: @This(), a: usize, b: usize) bool { + return ctx.keys[a].len < ctx.keys[b].len; + } + + pub fn swap(ctx: @This(), a: usize, b: usize) void { + std.mem.swap([]const u8, &ctx.keys[a], &ctx.keys[b]); + std.mem.swap(V, &ctx.vals[a], &ctx.vals[b]); + } + }; + + fn initSortedKVs( + self: *Self, + kvs_list: anytype, + sorted_keys: [][]const u8, + sorted_vals: []V, + ) void { + for (kvs_list, 0..) |kv, i| { + sorted_keys[i] = kv.@"0"; + sorted_vals[i] = if (V == void) {} else kv.@"1"; + self.min_len = @intCast(@min(self.min_len, kv.@"0".len)); + self.max_len = @intCast(@max(self.max_len, kv.@"0".len)); + } + mem.sortUnstableContext(0, sorted_keys.len, SortContext{ + .keys = sorted_keys, + .vals = sorted_vals, + }); + } + + fn initLenIndexes(self: Self, len_indexes: []u32) void { + var len: usize = 0; + var i: u32 = 0; + while (len <= self.max_len) : (len += 1) { + // find the first keyword len == len + while (len > self.kvs.keys[i].len) { + i += 1; + } + len_indexes[len] = i; + } + } + + /// Checks if the map has a value for the key. + pub fn has(self: Self, str: []const u8) bool { + return self.get(str) != null; + } + + /// Returns the value for the key if any, else null. + pub fn get(self: Self, str: []const u8) ?V { + if (self.kvs.len == 0) + return null; + + return self.kvs.values[self.getIndex(str) orelse return null]; + } + + pub fn getIndex(self: Self, str: []const u8) ?usize { + const kvs = self.kvs.*; + if (kvs.len == 0) + return null; + + if (str.len < self.min_len or str.len > self.max_len) + return null; + + var i = self.len_indexes[str.len]; + while (true) { + const key = kvs.keys[i]; + if (key.len != str.len) + return null; + if (eql(key, str)) + return i; + i += 1; + if (i >= kvs.len) + return null; + } + } + + /// Returns the longest key, value pair where key is a prefix of `str` + /// else null. + pub fn getLongestPrefix(self: Self, str: []const u8) ?KV { + if (self.kvs.len == 0) + return null; + const i = self.getLongestPrefixIndex(str) orelse return null; + const kvs = self.kvs.*; + return .{ + .key = kvs.keys[i], + .value = kvs.values[i], + }; + } + + pub fn getLongestPrefixIndex(self: Self, str: []const u8) ?usize { + if (self.kvs.len == 0) + return null; + + if (str.len < self.min_len) + return null; + + var len = @min(self.max_len, str.len); + while (len >= self.min_len) : (len -= 1) { + if (self.getIndex(str[0..len])) |i| + return i; + } + return null; + } + + pub fn keys(self: Self) []const []const u8 { + const kvs = self.kvs.*; + return kvs.keys[0..kvs.len]; + } + + pub fn values(self: Self) []const V { + const kvs = self.kvs.*; + return kvs.values[0..kvs.len]; + } + }; +} + +const TestEnum = enum { A, B, C, D, E }; +const TestMap = StaticStringMap(TestEnum); +const TestKV = struct { []const u8, TestEnum }; +const TestMapVoid = StaticStringMap(void); +const TestKVVoid = struct { []const u8 }; +const TestMapWithEql = StaticStringMapWithEql(TestEnum, eqlAsciiIgnoreCase); +const testing = std.testing; +const test_alloc = testing.allocator; + +test "list literal of list literals" { + const slice = [_]TestKV{ + .{ "these", .D }, + .{ "have", .A }, + .{ "nothing", .B }, + .{ "incommon", .C }, + .{ "samelen", .E }, + }; + const map = TestMap.initComptime(slice); + try testMap(map); + // Default comparison is case sensitive + try testing.expect(null == map.get("NOTHING")); + + // runtime init(), deinit() + const map_rt = try TestMap.init(slice, test_alloc); + defer map_rt.deinit(test_alloc); + try testMap(map_rt); + // Default comparison is case sensitive + try testing.expect(null == map_rt.get("NOTHING")); +} + +test "array of structs" { + const slice = [_]TestKV{ + .{ "these", .D }, + .{ "have", .A }, + .{ "nothing", .B }, + .{ "incommon", .C }, + .{ "samelen", .E }, + }; + + try testMap(TestMap.initComptime(slice)); +} + +test "slice of structs" { + const slice = [_]TestKV{ + .{ "these", .D }, + .{ "have", .A }, + .{ "nothing", .B }, + .{ "incommon", .C }, + .{ "samelen", .E }, + }; + + try testMap(TestMap.initComptime(slice)); +} + +fn testMap(map: anytype) !void { + try testing.expectEqual(TestEnum.A, map.get("have").?); + try testing.expectEqual(TestEnum.B, map.get("nothing").?); + try testing.expect(null == map.get("missing")); + try testing.expectEqual(TestEnum.D, map.get("these").?); + try testing.expectEqual(TestEnum.E, map.get("samelen").?); + + try testing.expect(!map.has("missing")); + try testing.expect(map.has("these")); + + try testing.expect(null == map.get("")); + try testing.expect(null == map.get("averylongstringthathasnomatches")); +} + +test "void value type, slice of structs" { + const slice = [_]TestKVVoid{ + .{"these"}, + .{"have"}, + .{"nothing"}, + .{"incommon"}, + .{"samelen"}, + }; + const map = TestMapVoid.initComptime(slice); + try testSet(map); + // Default comparison is case sensitive + try testing.expect(null == map.get("NOTHING")); +} + +test "void value type, list literal of list literals" { + const slice = [_]TestKVVoid{ + .{"these"}, + .{"have"}, + .{"nothing"}, + .{"incommon"}, + .{"samelen"}, + }; + + try testSet(TestMapVoid.initComptime(slice)); +} + +fn testSet(map: TestMapVoid) !void { + try testing.expectEqual({}, map.get("have").?); + try testing.expectEqual({}, map.get("nothing").?); + try testing.expect(null == map.get("missing")); + try testing.expectEqual({}, map.get("these").?); + try testing.expectEqual({}, map.get("samelen").?); + + try testing.expect(!map.has("missing")); + try testing.expect(map.has("these")); + + try testing.expect(null == map.get("")); + try testing.expect(null == map.get("averylongstringthathasnomatches")); +} + +fn testStaticStringMapWithEql(map: TestMapWithEql) !void { + try testMap(map); + try testing.expectEqual(TestEnum.A, map.get("HAVE").?); + try testing.expectEqual(TestEnum.E, map.get("SameLen").?); + try testing.expect(null == map.get("SameLength")); + try testing.expect(map.has("ThESe")); +} + +test "StaticStringMapWithEql" { + const slice = [_]TestKV{ + .{ "these", .D }, + .{ "have", .A }, + .{ "nothing", .B }, + .{ "incommon", .C }, + .{ "samelen", .E }, + }; + + try testStaticStringMapWithEql(TestMapWithEql.initComptime(slice)); +} + +test "empty" { + const m1 = StaticStringMap(usize).initComptime(.{}); + try testing.expect(null == m1.get("anything")); + + const m2 = StaticStringMapWithEql(usize, eqlAsciiIgnoreCase).initComptime(.{}); + try testing.expect(null == m2.get("anything")); + + const m3 = try StaticStringMap(usize).init(.{}, test_alloc); + try testing.expect(null == m3.get("anything")); + + const m4 = try StaticStringMapWithEql(usize, eqlAsciiIgnoreCase).init(.{}, test_alloc); + try testing.expect(null == m4.get("anything")); +} + +test "redundant entries" { + const slice = [_]TestKV{ + .{ "redundant", .D }, + .{ "theNeedle", .A }, + .{ "redundant", .B }, + .{ "re" ++ "dundant", .C }, + .{ "redun" ++ "dant", .E }, + }; + const map = TestMap.initComptime(slice); + + // No promises about which one you get: + try testing.expect(null != map.get("redundant")); + + // Default map is not case sensitive: + try testing.expect(null == map.get("REDUNDANT")); + + try testing.expectEqual(TestEnum.A, map.get("theNeedle").?); +} + +test "redundant insensitive" { + const slice = [_]TestKV{ + .{ "redundant", .D }, + .{ "theNeedle", .A }, + .{ "redundanT", .B }, + .{ "RE" ++ "dundant", .C }, + .{ "redun" ++ "DANT", .E }, + }; + + const map = TestMapWithEql.initComptime(slice); + + // No promises about which result you'll get ... + try testing.expect(null != map.get("REDUNDANT")); + try testing.expect(null != map.get("ReDuNdAnT")); + try testing.expectEqual(TestEnum.A, map.get("theNeedle").?); +} + +test "comptime-only value" { + const map = StaticStringMap(type).initComptime(.{ + .{ "a", struct { + pub const foo = 1; + } }, + .{ "b", struct { + pub const foo = 2; + } }, + .{ "c", struct { + pub const foo = 3; + } }, + }); + + try testing.expect(map.get("a").?.foo == 1); + try testing.expect(map.get("b").?.foo == 2); + try testing.expect(map.get("c").?.foo == 3); + try testing.expect(map.get("d") == null); +} + +test "getLongestPrefix" { + const slice = [_]TestKV{ + .{ "a", .A }, + .{ "aa", .B }, + .{ "aaa", .C }, + .{ "aaaa", .D }, + }; + + const map = TestMap.initComptime(slice); + + try testing.expectEqual(null, map.getLongestPrefix("")); + try testing.expectEqual(null, map.getLongestPrefix("bar")); + try testing.expectEqualStrings("aaaa", map.getLongestPrefix("aaaabar").?.key); + try testing.expectEqualStrings("aaa", map.getLongestPrefix("aaabar").?.key); +} + +test "getLongestPrefix2" { + const slice = [_]struct { []const u8, u8 }{ + .{ "one", 1 }, + .{ "two", 2 }, + .{ "three", 3 }, + .{ "four", 4 }, + .{ "five", 5 }, + .{ "six", 6 }, + .{ "seven", 7 }, + .{ "eight", 8 }, + .{ "nine", 9 }, + }; + const map = StaticStringMap(u8).initComptime(slice); + + try testing.expectEqual(1, map.get("one")); + try testing.expectEqual(null, map.get("o")); + try testing.expectEqual(null, map.get("onexxx")); + try testing.expectEqual(9, map.get("nine")); + try testing.expectEqual(null, map.get("n")); + try testing.expectEqual(null, map.get("ninexxx")); + try testing.expectEqual(null, map.get("xxx")); + + try testing.expectEqual(1, map.getLongestPrefix("one").?.value); + try testing.expectEqual(1, map.getLongestPrefix("onexxx").?.value); + try testing.expectEqual(null, map.getLongestPrefix("o")); + try testing.expectEqual(null, map.getLongestPrefix("on")); + try testing.expectEqual(9, map.getLongestPrefix("nine").?.value); + try testing.expectEqual(9, map.getLongestPrefix("ninexxx").?.value); + try testing.expectEqual(null, map.getLongestPrefix("n")); + try testing.expectEqual(null, map.getLongestPrefix("xxx")); +} + +test "long kvs_list doesn't exceed @setEvalBranchQuota" { + _ = TestMapVoid.initComptime([1]TestKVVoid{.{"x"}} ** 1_000); +} diff --git a/lib/std/std.zig b/lib/std/std.zig index 8aa12ff31a..bd8c6db276 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -16,8 +16,8 @@ pub const BufMap = @import("buf_map.zig").BufMap; pub const BufSet = @import("buf_set.zig").BufSet; /// Deprecated: use `process.Child`. pub const ChildProcess = @import("child_process.zig").ChildProcess; -pub const ComptimeStringMap = comptime_string_map.ComptimeStringMap; -pub const ComptimeStringMapWithEql = comptime_string_map.ComptimeStringMapWithEql; +pub const StaticStringMap = static_string_map.StaticStringMap; +pub const StaticStringMapWithEql = static_string_map.StaticStringMapWithEql; pub const DoublyLinkedList = @import("linked_list.zig").DoublyLinkedList; pub const DynLib = @import("dynamic_library.zig").DynLib; pub const DynamicBitSet = bit_set.DynamicBitSet; @@ -62,7 +62,7 @@ pub const builtin = @import("builtin.zig"); pub const c = @import("c.zig"); pub const coff = @import("coff.zig"); pub const compress = @import("compress.zig"); -pub const comptime_string_map = @import("comptime_string_map.zig"); +pub const static_string_map = @import("static_string_map.zig"); pub const crypto = @import("crypto.zig"); pub const debug = @import("debug.zig"); pub const dwarf = @import("dwarf.zig"); diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index a52007eabf..9be6a9f605 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -10125,7 +10125,7 @@ fn calleeExpr( } } -const primitive_instrs = std.ComptimeStringMap(Zir.Inst.Ref, .{ +const primitive_instrs = std.StaticStringMap(Zir.Inst.Ref).initComptime(.{ .{ "anyerror", .anyerror_type }, .{ "anyframe", .anyframe_type }, .{ "anyopaque", .anyopaque_type }, @@ -10173,14 +10173,14 @@ const primitive_instrs = std.ComptimeStringMap(Zir.Inst.Ref, .{ comptime { // These checks ensure that std.zig.primitives stays in sync with the primitive->Zir map. const primitives = std.zig.primitives; - for (primitive_instrs.kvs) |kv| { - if (!primitives.isPrimitive(kv.key)) { - @compileError("std.zig.isPrimitive() is not aware of Zir instr '" ++ @tagName(kv.value) ++ "'"); + for (primitive_instrs.keys(), primitive_instrs.values()) |key, value| { + if (!primitives.isPrimitive(key)) { + @compileError("std.zig.isPrimitive() is not aware of Zir instr '" ++ @tagName(value) ++ "'"); } } - for (primitives.names.kvs) |kv| { - if (primitive_instrs.get(kv.key) == null) { - @compileError("std.zig.primitives entry '" ++ kv.key ++ "' does not have a corresponding Zir instr"); + for (primitives.names.keys()) |key| { + if (primitive_instrs.get(key) == null) { + @compileError("std.zig.primitives entry '" ++ key ++ "' does not have a corresponding Zir instr"); } } } diff --git a/lib/std/zig/BuiltinFn.zig b/lib/std/zig/BuiltinFn.zig index 11d6a17303..4bea0278fa 100644 --- a/lib/std/zig/BuiltinFn.zig +++ b/lib/std/zig/BuiltinFn.zig @@ -160,7 +160,7 @@ param_count: ?u8, pub const list = list: { @setEvalBranchQuota(3000); - break :list std.ComptimeStringMap(@This(), .{ + break :list std.StaticStringMap(@This()).initComptime(.{ .{ "@addWithOverflow", .{ diff --git a/lib/std/zig/primitives.zig b/lib/std/zig/primitives.zig index e4e96e55ac..ea12268f1f 100644 --- a/lib/std/zig/primitives.zig +++ b/lib/std/zig/primitives.zig @@ -2,7 +2,7 @@ const std = @import("std"); /// Set of primitive type and value names. /// Does not include `_` or integer type names. -pub const names = std.ComptimeStringMap(void, .{ +pub const names = std.StaticStringMap(void).initComptime(.{ .{"anyerror"}, .{"anyframe"}, .{"anyopaque"}, diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 00193c4173..336f394211 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -2886,11 +2886,11 @@ fn renderIdentifier(r: *Render, token_index: Ast.TokenIndex, space: Space, quote // If we read the whole thing, we have to do further checks. const longest_keyword_or_primitive_len = comptime blk: { var longest = 0; - for (primitives.names.kvs) |kv| { - if (kv.key.len > longest) longest = kv.key.len; + for (primitives.names.keys()) |key| { + if (key.len > longest) longest = key.len; } - for (std.zig.Token.keywords.kvs) |kv| { - if (kv.key.len > longest) longest = kv.key.len; + for (std.zig.Token.keywords.keys()) |key| { + if (key.len > longest) longest = key.len; } break :blk longest; }; diff --git a/lib/std/zig/tokenizer.zig b/lib/std/zig/tokenizer.zig index 72f65afb3a..6f9a232c48 100644 --- a/lib/std/zig/tokenizer.zig +++ b/lib/std/zig/tokenizer.zig @@ -9,7 +9,7 @@ pub const Token = struct { end: usize, }; - pub const keywords = std.ComptimeStringMap(Tag, .{ + pub const keywords = std.StaticStringMap(Tag).initComptime(.{ .{ "addrspace", .keyword_addrspace }, .{ "align", .keyword_align }, .{ "allowzero", .keyword_allowzero }, diff --git a/src/Compilation.zig b/src/Compilation.zig index 7af3d7bfd1..3532a9d5b5 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -265,7 +265,7 @@ pub const CRTFile = struct { /// Supported languages for "zig clang -x ". /// Loosely based on llvm-project/clang/include/clang/Driver/Types.def -pub const LangToExt = std.ComptimeStringMap(FileExt, .{ +pub const LangToExt = std.StaticStringMap(FileExt).initComptime(.{ .{ "c", .c }, .{ "c-header", .h }, .{ "c++", .cpp }, diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 1725658a37..8e999a418a 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -116,7 +116,7 @@ const ValueRenderLocation = enum { const BuiltinInfo = enum { none, bits }; -const reserved_idents = std.ComptimeStringMap(void, .{ +const reserved_idents = std.StaticStringMap(void).initComptime(.{ // C language .{ "alignas", { @setEvalBranchQuota(4000); diff --git a/src/link/Wasm/types.zig b/src/link/Wasm/types.zig index ebb2ddf895..43d3089579 100644 --- a/src/link/Wasm/types.zig +++ b/src/link/Wasm/types.zig @@ -244,7 +244,7 @@ pub const Feature = struct { } }; -pub const known_features = std.ComptimeStringMap(Feature.Tag, .{ +pub const known_features = std.StaticStringMap(Feature.Tag).initComptime(.{ .{ "atomics", .atomics }, .{ "bulk-memory", .bulk_memory }, .{ "exception-handling", .exception_handling }, diff --git a/src/translate_c.zig b/src/translate_c.zig index bf6b8ec1d8..8b9863d38c 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -671,7 +671,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co return addTopLevelDecl(c, var_name, node); } -const builtin_typedef_map = std.ComptimeStringMap([]const u8, .{ +const builtin_typedef_map = std.StaticStringMap([]const u8).initComptime(.{ .{ "uint8_t", "u8" }, .{ "int8_t", "i8" }, .{ "uint16_t", "u16" }, diff --git a/test/src/Cases.zig b/test/src/Cases.zig index df512e91d0..f84d0d61db 100644 --- a/test/src/Cases.zig +++ b/test/src/Cases.zig @@ -993,7 +993,7 @@ const TestManifest = struct { config_map: std.StringHashMap([]const u8), trailing_bytes: []const u8 = "", - const valid_keys = std.ComptimeStringMap(void, .{ + const valid_keys = std.StaticStringMap(void).initComptime(.{ .{ "is_test", {} }, .{ "output_mode", {} }, .{ "target", {} }, diff --git a/tools/gen_spirv_spec.zig b/tools/gen_spirv_spec.zig index f96e4c0fb3..0ba27c49a3 100644 --- a/tools/gen_spirv_spec.zig +++ b/tools/gen_spirv_spec.zig @@ -45,7 +45,7 @@ const OperandKindMap = std.ArrayHashMap(StringPair, OperandKind, StringPairConte /// Khronos made it so that these names are not defined explicitly, so /// we need to hardcode it (like they did). /// See https://github.com/KhronosGroup/SPIRV-Registry/ -const set_names = std.ComptimeStringMap([]const u8, .{ +const set_names = std.StaticStringMap([]const u8).initComptime(.{ .{ "opencl.std.100", "OpenCL.std" }, .{ "glsl.std.450", "GLSL.std.450" }, .{ "opencl.debuginfo.100", "OpenCL.DebugInfo.100" }, diff --git a/tools/generate_linux_syscalls.zig b/tools/generate_linux_syscalls.zig index 8c066f749e..ba368e1f0b 100644 --- a/tools/generate_linux_syscalls.zig +++ b/tools/generate_linux_syscalls.zig @@ -9,7 +9,7 @@ const fmt = std.fmt; const zig = std.zig; const fs = std.fs; -const stdlib_renames = std.ComptimeStringMap([]const u8, .{ +const stdlib_renames = std.StaticStringMap([]const u8).initComptime(.{ // Most 64-bit archs. .{ "newfstatat", "fstatat64" }, // POWER.