Source
pub fn EnumIndexer(comptime E: type) type {
// Assumes that the enum fields are sorted in ascending order (optimistic).
// Unsorted enums may require the user to manually increase the quota.
@setEvalBranchQuota(3 * @typeInfo(E).@"enum".fields.len + eval_branch_quota_cushion);
if (!@typeInfo(E).@"enum".is_exhaustive) {
const BackingInt = @typeInfo(E).@"enum".tag_type;
if (@bitSizeOf(BackingInt) > @bitSizeOf(usize))
@compileError("Cannot create an enum indexer for a given non-exhaustive enum, tag_type is larger than usize.");
return struct {
pub const Key: type = E;
const backing_int_sign = @typeInfo(BackingInt).int.signedness;
const min_value = std.math.minInt(BackingInt);
const max_value = std.math.maxInt(BackingInt);
const RangeType = std.meta.Int(.unsigned, @bitSizeOf(BackingInt));
pub const count: comptime_int = std.math.maxInt(RangeType) + 1;
pub fn indexOf(e: E) usize {
if (backing_int_sign == .unsigned)
return @intFromEnum(e);
return if (@intFromEnum(e) < 0)
@intCast(@intFromEnum(e) - min_value)
else
@as(RangeType, -min_value) + @as(RangeType, @intCast(@intFromEnum(e)));
}
pub fn keyForIndex(i: usize) E {
if (backing_int_sign == .unsigned)
return @enumFromInt(i);
return @enumFromInt(@as(std.meta.Int(.signed, @bitSizeOf(RangeType) + 1), @intCast(i)) + min_value);
}
};
}
const const_fields = @typeInfo(E).@"enum".fields;
var fields = const_fields[0..const_fields.len].*;
const fields_len = fields.len;
if (fields_len == 0) {
return struct {
pub const Key = E;
pub const count: comptime_int = 0;
pub fn indexOf(e: E) usize {
_ = e;
unreachable;
}
pub fn keyForIndex(i: usize) E {
_ = i;
unreachable;
}
};
}
const min = fields[0].value;
const max = fields[fields.len - 1].value;
const SortContext = struct {
fields: []EnumField,
pub fn lessThan(comptime ctx: @This(), comptime a: usize, comptime b: usize) bool {
return ctx.fields[a].value < ctx.fields[b].value;
}
pub fn swap(comptime ctx: @This(), comptime a: usize, comptime b: usize) void {
return std.mem.swap(EnumField, &ctx.fields[a], &ctx.fields[b]);
}
};
std.sort.insertionContext(0, fields_len, SortContext{ .fields = &fields });
if (max - min == fields.len - 1) {
return struct {
pub const Key = E;
pub const count: comptime_int = fields_len;
pub fn indexOf(e: E) usize {
return @as(usize, @intCast(@intFromEnum(e) - min));
}
pub fn keyForIndex(i: usize) E {
// TODO fix addition semantics. This calculation
// gives up some safety to avoid artificially limiting
// the range of signed enum values to max_isize.
const enum_value = if (min < 0) @as(isize, @bitCast(i)) +% min else i + min;
return @as(E, @enumFromInt(@as(@typeInfo(E).@"enum".tag_type, @intCast(enum_value))));
}
};
}
const keys = valuesFromFields(E, &fields);
return struct {
pub const Key = E;
pub const count: comptime_int = fields_len;
pub fn indexOf(e: E) usize {
for (keys, 0..) |k, i| {
if (k == e) return i;
}
unreachable;
}
pub fn keyForIndex(i: usize) E {
return keys[i];
}
};
}