4.6 KiB
4.6 KiB
std.StaticStringMap
Compile-time optimized string lookup. Perfect hash for small, fixed sets of string keys.
When to Use
- Keywords/reserved words lookup
- Command/option parsing
- Static configuration keys
- When string set is known at compile time
- Very fast O(1) lookups by string length
Basic Usage
const std = @import("std");
const keywords = std.StaticStringMap(enum { @"if", @"else", @"while", @"for" }).initComptime(.{
.{ "if", .@"if" },
.{ "else", .@"else" },
.{ "while", .@"while" },
.{ "for", .@"for" },
});
// Lookup
if (keywords.get("while")) |kw| {
switch (kw) {
.@"while" => std.debug.print("found while\n", .{}),
else => {},
}
}
// Check existence
if (keywords.has("if")) {
// it's a keyword
}
Void Value (Set)
// When you only need presence check, use void
const reserved = std.StaticStringMap(void).initComptime(.{
.{"break"},
.{"continue"},
.{"return"},
.{"defer"},
});
if (reserved.has("break")) {
std.debug.print("'break' is reserved\n", .{});
}
Case-Insensitive Lookup
const commands = std.StaticStringMapWithEql(
i32,
std.static_string_map.eqlAsciiIgnoreCase,
).initComptime(.{
.{ "help", 1 },
.{ "quit", 2 },
.{ "list", 3 },
});
// All find the same entry
_ = commands.get("HELP"); // 1
_ = commands.get("Help"); // 1
_ = commands.get("help"); // 1
Runtime Initialization
// When data isn't known at comptime
const pairs = [_]struct { []const u8, i32 }{
.{ "one", 1 },
.{ "two", 2 },
.{ "three", 3 },
};
const map = try std.StaticStringMap(i32).init(&pairs, allocator);
defer map.deinit(allocator);
_ = map.get("two"); // 2
Get Index
// Get the index in the internal array
if (keywords.getIndex("if")) |idx| {
// idx is position in sorted-by-length array
}
Longest Prefix Match
const prefixes = std.StaticStringMap(u32).initComptime(.{
.{ "/api", 1 },
.{ "/api/v1", 2 },
.{ "/api/v2", 3 },
});
// Find longest matching prefix
if (prefixes.getLongestPrefix("/api/v2/users")) |kv| {
std.debug.print("matched: {s} -> {}\n", .{ kv.key, kv.value });
// matched: /api/v2 -> 3
}
Access All Keys/Values
const all_keys = keywords.keys(); // []const []const u8
const all_values = keywords.values(); // []const EnumType
Complete Example: HTTP Method Parser
const std = @import("std");
const Method = enum {
GET,
POST,
PUT,
DELETE,
PATCH,
HEAD,
OPTIONS,
};
const methods = std.StaticStringMap(Method).initComptime(.{
.{ "GET", .GET },
.{ "POST", .POST },
.{ "PUT", .PUT },
.{ "DELETE", .DELETE },
.{ "PATCH", .PATCH },
.{ "HEAD", .HEAD },
.{ "OPTIONS", .OPTIONS },
});
fn parseMethod(s: []const u8) ?Method {
return methods.get(s);
}
pub fn main() void {
if (parseMethod("POST")) |m| {
std.debug.print("Method: {}\n", .{m}); // Method: POST
}
if (parseMethod("INVALID")) |_| {
unreachable;
} else {
std.debug.print("Invalid method\n", .{});
}
}
Complete Example: Config Parser
const std = @import("std");
const ConfigKey = enum {
host,
port,
debug,
timeout,
};
const config_keys = std.StaticStringMapWithEql(
ConfigKey,
std.static_string_map.eqlAsciiIgnoreCase,
).initComptime(.{
.{ "host", .host },
.{ "port", .port },
.{ "debug", .debug },
.{ "timeout", .timeout },
});
fn parseConfig(line: []const u8) ?struct { key: ConfigKey, value: []const u8 } {
const eq_idx = std.mem.indexOf(u8, line, "=") orelse return null;
const key_str = std.mem.trim(u8, line[0..eq_idx], " ");
const value = std.mem.trim(u8, line[eq_idx + 1 ..], " ");
const key = config_keys.get(key_str) orelse return null;
return .{ .key = key, .value = value };
}
pub fn main() void {
const lines = [_][]const u8{
"HOST = localhost",
"PORT = 8080",
"Debug = true",
};
for (lines) |line| {
if (parseConfig(line)) |cfg| {
std.debug.print("{}: {s}\n", .{ cfg.key, cfg.value });
}
}
}
How It Works
- Strings are sorted by length at compile time
- Lookup first checks length to narrow candidates
- Only strings of matching length are compared
- Very efficient for disparate key lengths
Notes
- Comptime version has zero runtime allocation
- Keys are grouped by length for fast rejection
- Use
eqlAsciiIgnoreCasefor case-insensitive matching - Runtime
init()requiresdeinit()to free memory - Best for small to medium-sized static string sets
- Not suitable for dynamic key sets (use HashMap instead)