Skip to content

Commit ac1e73e

Browse files
committed
std.enums: fix EnumIndexer branch quota
It's quite silly to have this override which nonetheless makes assumptions about the input type. Encode the actual complexity of the sort. Also, simplify the sorting logic, and fix a bug (grab min and max *after* the sort, not *before*!)
1 parent 04d7b49 commit ac1e73e

File tree

1 file changed

+34
-20
lines changed

1 file changed

+34
-20
lines changed

lib/std/enums.zig

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,9 +1317,9 @@ test "EnumSet non-exhaustive" {
13171317
}
13181318

13191319
pub fn EnumIndexer(comptime E: type) type {
1320-
// Assumes that the enum fields are sorted in ascending order (optimistic).
1321-
// Unsorted enums may require the user to manually increase the quota.
1322-
@setEvalBranchQuota(3 * @typeInfo(E).@"enum".fields.len + eval_branch_quota_cushion);
1320+
// n log n for `std.mem.sortUnstable` call below.
1321+
const fields_len = @typeInfo(E).@"enum".fields.len;
1322+
@setEvalBranchQuota(3 * fields_len * std.math.log2(@max(fields_len, 1)) + eval_branch_quota_cushion);
13231323

13241324
if (!@typeInfo(E).@"enum".is_exhaustive) {
13251325
const BackingInt = @typeInfo(E).@"enum".tag_type;
@@ -1354,10 +1354,6 @@ pub fn EnumIndexer(comptime E: type) type {
13541354
};
13551355
}
13561356

1357-
const const_fields = @typeInfo(E).@"enum".fields;
1358-
var fields = const_fields[0..const_fields.len].*;
1359-
const fields_len = fields.len;
1360-
13611357
if (fields_len == 0) {
13621358
return struct {
13631359
pub const Key = E;
@@ -1373,22 +1369,17 @@ pub fn EnumIndexer(comptime E: type) type {
13731369
};
13741370
}
13751371

1376-
const min = fields[0].value;
1377-
const max = fields[fields.len - 1].value;
1378-
1379-
const SortContext = struct {
1380-
fields: []EnumField,
1372+
var fields: [fields_len]EnumField = @typeInfo(E).@"enum".fields[0..].*;
13811373

1382-
pub fn lessThan(comptime ctx: @This(), comptime a: usize, comptime b: usize) bool {
1383-
return ctx.fields[a].value < ctx.fields[b].value;
1374+
std.mem.sortUnstable(EnumField, &fields, {}, struct {
1375+
fn lessThan(ctx: void, lhs: EnumField, rhs: EnumField) bool {
1376+
ctx;
1377+
return lhs.value < rhs.value;
13841378
}
1379+
}.lessThan);
13851380

1386-
pub fn swap(comptime ctx: @This(), comptime a: usize, comptime b: usize) void {
1387-
return std.mem.swap(EnumField, &ctx.fields[a], &ctx.fields[b]);
1388-
}
1389-
};
1390-
std.sort.insertionContext(0, fields_len, SortContext{ .fields = &fields });
1391-
1381+
const min = fields[0].value;
1382+
const max = fields[fields_len - 1].value;
13921383
if (max - min == fields.len - 1) {
13931384
return struct {
13941385
pub const Key = E;
@@ -1538,6 +1529,29 @@ test "EnumIndexer empty" {
15381529
try testing.expectEqual(0, Indexer.count);
15391530
}
15401531

1532+
test "EnumIndexer large dense unsorted" {
1533+
@setEvalBranchQuota(500_000); // many `comptimePrint`s
1534+
// Make an enum with 500 fields with values in *descending* order.
1535+
const E = @Type(.{ .@"enum" = .{
1536+
.tag_type = u32,
1537+
.fields = comptime fields: {
1538+
var fields: [500]EnumField = undefined;
1539+
for (&fields, 0..) |*f, i| f.* = .{
1540+
.name = std.fmt.comptimePrint("f{d}", .{i}),
1541+
.value = 500 - i,
1542+
};
1543+
break :fields &fields;
1544+
},
1545+
.decls = &.{},
1546+
.is_exhaustive = true,
1547+
} });
1548+
const Indexer = EnumIndexer(E);
1549+
try testing.expectEqual(E.f0, Indexer.keyForIndex(499));
1550+
try testing.expectEqual(E.f499, Indexer.keyForIndex(0));
1551+
try testing.expectEqual(499, Indexer.indexOf(.f0));
1552+
try testing.expectEqual(0, Indexer.indexOf(.f499));
1553+
}
1554+
15411555
test values {
15421556
const E = enum {
15431557
X,

0 commit comments

Comments
 (0)