Created
May 26, 2020 22:49
-
-
Save squeek502/7f7db10c520bacff4c24b22c9102db8d to your computer and use it in GitHub Desktop.
An attempt at making https://github.com/ziglang/zig/pull/5442 reusable
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
const mem = std.mem; | |
pub fn stringLookup(comptime V: type, comptime values: var) type { | |
const precomputed = comptime blk: { | |
@setEvalBranchQuota(2000); | |
const KV = struct { | |
key: []const u8, | |
value: V, | |
}; | |
var sorted_kvs: [values.len]KV = undefined; | |
const lenAsc = (struct { | |
fn lenAsc(a: KV, b: KV) bool { | |
return a.key.len < b.key.len; | |
} | |
}).lenAsc; | |
var init_i = 0; | |
for (values) |kv| { | |
sorted_kvs[init_i] = .{.key = kv.@"0", .value = kv.@"1"}; | |
init_i += 1; | |
} | |
std.sort.sort(KV, &sorted_kvs, lenAsc); | |
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 { | |
fn lookup(str: []const u8) ?V { | |
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 (mem.eql(u8, kv.key, str)) | |
return kv.value; | |
i += 1; | |
if (i >= precomputed.sorted_kvs.len) | |
return null; | |
} | |
} | |
}; | |
} | |
test "stringLookup" { | |
const E1 = enum { | |
A, | |
B, | |
C, | |
D, | |
}; | |
const lookup = stringLookup(E1, .{ | |
.{"these", .D}, | |
.{"have", .A}, | |
.{"nothing", .B}, | |
.{"incommon", .C}, | |
}); | |
std.testing.expectEqual(E1.A, lookup.lookup("have").?); | |
std.testing.expectEqual(E1.B, lookup.lookup("nothing").?); | |
std.testing.expect(null == lookup.lookup("missing")); | |
std.testing.expectEqual(E1.D, lookup.lookup("these").?); | |
} | |
pub fn stringToEnum(comptime T: type, str: []const u8) ?T { | |
const kvs = comptime build_kvs: { | |
// This is kind of bizarre. In order to generate an array of structs | |
// that play nice with anonymous list literals, we need to give them | |
// "0" and "1" field names. | |
const EnumKV = struct { | |
@"0": []const u8, | |
@"1": T, | |
}; | |
var kvs_array: [@typeInfo(T).Enum.fields.len]EnumKV = undefined; | |
var i: usize = 0; | |
inline for (@typeInfo(T).Enum.fields) |enumField| { | |
kvs_array[i] = .{.@"0" = enumField.name, .@"1" = @field(T, enumField.name)}; | |
i += 1; | |
} | |
break :build_kvs kvs_array[0..]; | |
}; | |
const lookup = comptime stringLookup(T, kvs); | |
return lookup.lookup(str); | |
} | |
test "std.meta.stringToEnum" { | |
const E1 = enum { | |
A, | |
B, | |
D, | |
E, | |
F, | |
G, | |
H, | |
I, | |
J, | |
}; | |
std.testing.expect(E1.A == stringToEnum(E1, "A").?); | |
std.testing.expect(E1.B == stringToEnum(E1, "B").?); | |
std.testing.expect(null == stringToEnum(E1, "C")); | |
std.testing.expect(E1.I == stringToEnum(E1, "I").?); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment