| ... | @@ -1317,9 +1317,9 @@ test "EnumSet non-exhaustive" { | ... | @@ -1317,9 +1317,9 @@ test "EnumSet non-exhaustive" { |
| 1317 | } | 1317 | } |
| 1318 | | 1318 | |
| 1319 | pub fn EnumIndexer(comptime E: type) type { | 1319 | pub fn EnumIndexer(comptime E: type) type { |
| 1320 | // Assumes that the enum fields are sorted in ascending order (optimistic). | 1320 | // n log n for `std.mem.sortUnstable` call below. |
| 1321 | // Unsorted enums may require the user to manually increase the quota. | 1321 | const fields_len = @typeInfo(E).@"enum".fields.len; |
| 1322 | @setEvalBranchQuota(3 * @typeInfo(E).@"enum".fields.len + eval_branch_quota_cushion); | 1322 | @setEvalBranchQuota(3 * fields_len * std.math.log2(@max(fields_len, 1)) + eval_branch_quota_cushion); |
| 1323 | | 1323 | |
| 1324 | if (!@typeInfo(E).@"enum".is_exhaustive) { | 1324 | if (!@typeInfo(E).@"enum".is_exhaustive) { |
| 1325 | const BackingInt = @typeInfo(E).@"enum".tag_type; | 1325 | const BackingInt = @typeInfo(E).@"enum".tag_type; |
| ... | @@ -1354,10 +1354,6 @@ pub fn EnumIndexer(comptime E: type) type { | ... | @@ -1354,10 +1354,6 @@ pub fn EnumIndexer(comptime E: type) type { |
| 1354 | }; | 1354 | }; |
| 1355 | } | 1355 | } |
| 1356 | | 1356 | |
| 1357 | const const_fields = @typeInfo(E).@"enum".fields; | | |
| 1358 | var fields = const_fields[0..const_fields.len].*; | | |
| 1359 | const fields_len = fields.len; | | |
| 1360 | | | |
| 1361 | if (fields_len == 0) { | 1357 | if (fields_len == 0) { |
| 1362 | return struct { | 1358 | return struct { |
| 1363 | pub const Key = E; | 1359 | pub const Key = E; |
| ... | @@ -1373,22 +1369,17 @@ pub fn EnumIndexer(comptime E: type) type { | ... | @@ -1373,22 +1369,17 @@ pub fn EnumIndexer(comptime E: type) type { |
| 1373 | }; | 1369 | }; |
| 1374 | } | 1370 | } |
| 1375 | | 1371 | |
| 1376 | const min = fields[0].value; | 1372 | var fields: [fields_len]EnumField = @typeInfo(E).@"enum".fields[0..].*; |
| 1377 | const max = fields[fields.len - 1].value; | | |
| 1378 | | | |
| 1379 | const SortContext = struct { | | |
| 1380 | fields: []EnumField, | | |
| 1381 | | 1373 | |
| 1382 | pub fn lessThan(comptime ctx: @This(), comptime a: usize, comptime b: usize) bool { | 1374 | std.mem.sortUnstable(EnumField, &fields, {}, struct { |
| 1383 | return ctx.fields[a].value < ctx.fields[b].value; | 1375 | fn lessThan(ctx: void, lhs: EnumField, rhs: EnumField) bool { |
| | 1376 | ctx; |
| | 1377 | return lhs.value < rhs.value; |
| 1384 | } | 1378 | } |
| | 1379 | }.lessThan); |
| 1385 | | 1380 | |
| 1386 | pub fn swap(comptime ctx: @This(), comptime a: usize, comptime b: usize) void { | 1381 | const min = fields[0].value; |
| 1387 | return std.mem.swap(EnumField, &ctx.fields[a], &ctx.fields[b]); | 1382 | const max = fields[fields_len - 1].value; |
| 1388 | } | | |
| 1389 | }; | | |
| 1390 | std.sort.insertionContext(0, fields_len, SortContext{ .fields = &fields }); | | |
| 1391 | | | |
| 1392 | if (max - min == fields.len - 1) { | 1383 | if (max - min == fields.len - 1) { |
| 1393 | return struct { | 1384 | return struct { |
| 1394 | pub const Key = E; | 1385 | pub const Key = E; |
| ... | @@ -1538,6 +1529,29 @@ test "EnumIndexer empty" { | ... | @@ -1538,6 +1529,29 @@ test "EnumIndexer empty" { |
| 1538 | try testing.expectEqual(0, Indexer.count); | 1529 | try testing.expectEqual(0, Indexer.count); |
| 1539 | } | 1530 | } |
| 1540 | | 1531 | |
| | 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 | |
| 1541 | test values { | 1555 | test values { |
| 1542 | const E = enum { | 1556 | const E = enum { |
| 1543 | X, | 1557 | X, |