| ... | ... | @@ -1317,9 +1317,9 @@ test "EnumSet non-exhaustive" { |
| 1317 | 1317 | } |
| 1318 | 1318 | |
| 1319 | 1319 | 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); |
| 1323 | 1323 | |
| 1324 | 1324 | if (!@typeInfo(E).@"enum".is_exhaustive) { |
| 1325 | 1325 | const BackingInt = @typeInfo(E).@"enum".tag_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 | 1357 | if (fields_len == 0) { |
| 1362 | 1358 | return struct { |
| 1363 | 1359 | pub const Key = E; |
| ... | ... | @@ -1373,22 +1369,17 @@ pub fn EnumIndexer(comptime E: type) type { |
| 1373 | 1369 | }; |
| 1374 | 1370 | } |
| 1375 | 1371 | |
| 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..].*; |
| 1381 | 1373 | |
| 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; |
| 1384 | 1378 | } |
| 1379 | }.lessThan); |
| 1385 | 1380 | |
| 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; |
| 1392 | 1383 | if (max - min == fields.len - 1) { |
| 1393 | 1384 | return struct { |
| 1394 | 1385 | pub const Key = E; |
| ... | ... | @@ -1538,6 +1529,29 @@ test "EnumIndexer empty" { |
| 1538 | 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 | 1555 | test values { |
| 1542 | 1556 | const E = enum { |
| 1543 | 1557 | X, |