| ... | ... | @@ -1267,7 +1267,7 @@ pub fn IndexedArray(comptime I: type, comptime V: type, comptime Ext: ?fn (type) |
| 1267 | 1267 | /// /// The key type which this indexer converts to indices |
| 1268 | 1268 | /// pub const Key: type, |
| 1269 | 1269 | /// /// The number of indexes in the dense mapping |
| 1270 | | /// pub const count: usize, |
| 1270 | /// pub const count: comptime_int, |
| 1271 | 1271 | /// /// Converts from a key to an index |
| 1272 | 1272 | /// pub fn indexOf(Key) usize; |
| 1273 | 1273 | /// /// Converts from an index to a key |
| ... | ... | @@ -1278,8 +1278,8 @@ pub fn ensureIndexer(comptime T: type) void { |
| 1278 | 1278 | comptime { |
| 1279 | 1279 | if (!@hasDecl(T, "Key")) @compileError("Indexer must have decl Key: type."); |
| 1280 | 1280 | if (@TypeOf(T.Key) != type) @compileError("Indexer.Key must be a type."); |
| 1281 | | if (!@hasDecl(T, "count")) @compileError("Indexer must have decl count: usize."); |
| 1282 | | if (@TypeOf(T.count) != usize) @compileError("Indexer.count must be a usize."); |
| 1281 | if (!@hasDecl(T, "count")) @compileError("Indexer must have decl count: comptime_int."); |
| 1282 | if (@TypeOf(T.count) != comptime_int) @compileError("Indexer.count must be a comptime_int."); |
| 1283 | 1283 | if (!@hasDecl(T, "indexOf")) @compileError("Indexer.indexOf must be a fn (Key) usize."); |
| 1284 | 1284 | if (@TypeOf(T.indexOf) != fn (T.Key) usize) @compileError("Indexer must have decl indexOf: fn (Key) usize."); |
| 1285 | 1285 | if (!@hasDecl(T, "keyForIndex")) @compileError("Indexer must have decl keyForIndex: fn (usize) Key."); |
| ... | ... | @@ -1290,7 +1290,7 @@ pub fn ensureIndexer(comptime T: type) void { |
| 1290 | 1290 | test "std.enums.ensureIndexer" { |
| 1291 | 1291 | ensureIndexer(struct { |
| 1292 | 1292 | pub const Key = u32; |
| 1293 | | pub const count: usize = 8; |
| 1293 | pub const count: comptime_int = 8; |
| 1294 | 1294 | pub fn indexOf(k: Key) usize { |
| 1295 | 1295 | return @as(usize, @intCast(k)); |
| 1296 | 1296 | } |
| ... | ... | @@ -1302,7 +1302,36 @@ test "std.enums.ensureIndexer" { |
| 1302 | 1302 | |
| 1303 | 1303 | pub fn EnumIndexer(comptime E: type) type { |
| 1304 | 1304 | if (!@typeInfo(E).Enum.is_exhaustive) { |
| 1305 | | @compileError("Cannot create an enum indexer for a non-exhaustive enum."); |
| 1305 | const BackingInt = @typeInfo(E).Enum.tag_type; |
| 1306 | if (@bitSizeOf(BackingInt) > @bitSizeOf(usize)) |
| 1307 | @compileError("Cannot create an enum indexer for a given non-exhaustive enum, tag_type is larger than usize."); |
| 1308 | |
| 1309 | return struct { |
| 1310 | pub const Key: type = E; |
| 1311 | |
| 1312 | const backing_int_sign = @typeInfo(BackingInt).Int.signedness; |
| 1313 | const min_value = std.math.minInt(BackingInt); |
| 1314 | const max_value = std.math.maxInt(BackingInt); |
| 1315 | |
| 1316 | const RangeType = std.meta.Int(.unsigned, @bitSizeOf(BackingInt)); |
| 1317 | pub const count: comptime_int = std.math.maxInt(RangeType) + 1; |
| 1318 | |
| 1319 | pub fn indexOf(e: E) usize { |
| 1320 | if (backing_int_sign == .unsigned) |
| 1321 | return @intFromEnum(e); |
| 1322 | |
| 1323 | return if (@intFromEnum(e) < 0) |
| 1324 | @intCast(@intFromEnum(e) - min_value) |
| 1325 | else |
| 1326 | @as(RangeType, -min_value) + @as(RangeType, @intCast(@intFromEnum(e))); |
| 1327 | } |
| 1328 | pub fn keyForIndex(i: usize) E { |
| 1329 | if (backing_int_sign == .unsigned) |
| 1330 | return @enumFromInt(i); |
| 1331 | |
| 1332 | return @enumFromInt(@as(std.meta.Int(.signed, @bitSizeOf(RangeType) + 1), @intCast(i)) + min_value); |
| 1333 | } |
| 1334 | }; |
| 1306 | 1335 | } |
| 1307 | 1336 | |
| 1308 | 1337 | const const_fields = std.meta.fields(E); |
| ... | ... | @@ -1312,7 +1341,7 @@ pub fn EnumIndexer(comptime E: type) type { |
| 1312 | 1341 | if (fields_len == 0) { |
| 1313 | 1342 | return struct { |
| 1314 | 1343 | pub const Key = E; |
| 1315 | | pub const count: usize = 0; |
| 1344 | pub const count: comptime_int = 0; |
| 1316 | 1345 | pub fn indexOf(e: E) usize { |
| 1317 | 1346 | _ = e; |
| 1318 | 1347 | unreachable; |
| ... | ... | @@ -1343,7 +1372,7 @@ pub fn EnumIndexer(comptime E: type) type { |
| 1343 | 1372 | if (max - min == fields.len - 1) { |
| 1344 | 1373 | return struct { |
| 1345 | 1374 | pub const Key = E; |
| 1346 | | pub const count = fields_len; |
| 1375 | pub const count: comptime_int = fields_len; |
| 1347 | 1376 | pub fn indexOf(e: E) usize { |
| 1348 | 1377 | return @as(usize, @intCast(@intFromEnum(e) - min)); |
| 1349 | 1378 | } |
| ... | ... | @@ -1361,7 +1390,7 @@ pub fn EnumIndexer(comptime E: type) type { |
| 1361 | 1390 | |
| 1362 | 1391 | return struct { |
| 1363 | 1392 | pub const Key = E; |
| 1364 | | pub const count = fields_len; |
| 1393 | pub const count: comptime_int = fields_len; |
| 1365 | 1394 | pub fn indexOf(e: E) usize { |
| 1366 | 1395 | for (keys, 0..) |k, i| { |
| 1367 | 1396 | if (k == e) return i; |
| ... | ... | @@ -1374,12 +1403,61 @@ pub fn EnumIndexer(comptime E: type) type { |
| 1374 | 1403 | }; |
| 1375 | 1404 | } |
| 1376 | 1405 | |
| 1406 | test "EnumIndexer non-exhaustive" { |
| 1407 | const backing_ints = [_]type{ |
| 1408 | i1, |
| 1409 | i2, |
| 1410 | i3, |
| 1411 | i4, |
| 1412 | i8, |
| 1413 | i16, |
| 1414 | std.meta.Int(.signed, @bitSizeOf(isize) - 1), |
| 1415 | isize, |
| 1416 | u1, |
| 1417 | u2, |
| 1418 | u3, |
| 1419 | u4, |
| 1420 | u16, |
| 1421 | std.meta.Int(.unsigned, @bitSizeOf(usize) - 1), |
| 1422 | usize, |
| 1423 | }; |
| 1424 | inline for (backing_ints) |BackingInt| { |
| 1425 | const E = enum(BackingInt) { |
| 1426 | number_zero_tag = 0, |
| 1427 | _, |
| 1428 | }; |
| 1429 | const Indexer = EnumIndexer(E); |
| 1430 | ensureIndexer(Indexer); |
| 1431 | |
| 1432 | const min_tag: E = @enumFromInt(std.math.minInt(BackingInt)); |
| 1433 | const max_tag: E = @enumFromInt(std.math.maxInt(BackingInt)); |
| 1434 | |
| 1435 | const RangedType = std.meta.Int(.unsigned, @bitSizeOf(BackingInt)); |
| 1436 | const max_index: comptime_int = std.math.maxInt(RangedType); |
| 1437 | const number_zero_tag_index: usize = switch (@typeInfo(BackingInt).Int.signedness) { |
| 1438 | .unsigned => 0, |
| 1439 | .signed => std.math.divCeil(comptime_int, max_index, 2) catch unreachable, |
| 1440 | }; |
| 1441 | |
| 1442 | try testing.expectEqual(E, Indexer.Key); |
| 1443 | try testing.expectEqual(max_index + 1, Indexer.count); |
| 1444 | |
| 1445 | try testing.expectEqual(@as(usize, 0), Indexer.indexOf(min_tag)); |
| 1446 | try testing.expectEqual(number_zero_tag_index, Indexer.indexOf(E.number_zero_tag)); |
| 1447 | try testing.expectEqual(@as(usize, max_index), Indexer.indexOf(max_tag)); |
| 1448 | |
| 1449 | try testing.expectEqual(min_tag, Indexer.keyForIndex(0)); |
| 1450 | try testing.expectEqual(E.number_zero_tag, Indexer.keyForIndex(number_zero_tag_index)); |
| 1451 | try testing.expectEqual(max_tag, Indexer.keyForIndex(max_index)); |
| 1452 | } |
| 1453 | } |
| 1454 | |
| 1377 | 1455 | test "std.enums.EnumIndexer dense zeroed" { |
| 1378 | 1456 | const E = enum(u2) { b = 1, a = 0, c = 2 }; |
| 1379 | 1457 | const Indexer = EnumIndexer(E); |
| 1380 | 1458 | ensureIndexer(Indexer); |
| 1381 | 1459 | try testing.expectEqual(E, Indexer.Key); |
| 1382 | | try testing.expectEqual(@as(usize, 3), Indexer.count); |
| 1460 | try testing.expectEqual(3, Indexer.count); |
| 1383 | 1461 | |
| 1384 | 1462 | try testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a)); |
| 1385 | 1463 | try testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b)); |
| ... | ... | @@ -1395,7 +1473,7 @@ test "std.enums.EnumIndexer dense positive" { |
| 1395 | 1473 | const Indexer = EnumIndexer(E); |
| 1396 | 1474 | ensureIndexer(Indexer); |
| 1397 | 1475 | try testing.expectEqual(E, Indexer.Key); |
| 1398 | | try testing.expectEqual(@as(usize, 3), Indexer.count); |
| 1476 | try testing.expectEqual(3, Indexer.count); |
| 1399 | 1477 | |
| 1400 | 1478 | try testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a)); |
| 1401 | 1479 | try testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b)); |
| ... | ... | @@ -1411,7 +1489,7 @@ test "std.enums.EnumIndexer dense negative" { |
| 1411 | 1489 | const Indexer = EnumIndexer(E); |
| 1412 | 1490 | ensureIndexer(Indexer); |
| 1413 | 1491 | try testing.expectEqual(E, Indexer.Key); |
| 1414 | | try testing.expectEqual(@as(usize, 3), Indexer.count); |
| 1492 | try testing.expectEqual(3, Indexer.count); |
| 1415 | 1493 | |
| 1416 | 1494 | try testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a)); |
| 1417 | 1495 | try testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b)); |
| ... | ... | @@ -1427,7 +1505,7 @@ test "std.enums.EnumIndexer sparse" { |
| 1427 | 1505 | const Indexer = EnumIndexer(E); |
| 1428 | 1506 | ensureIndexer(Indexer); |
| 1429 | 1507 | try testing.expectEqual(E, Indexer.Key); |
| 1430 | | try testing.expectEqual(@as(usize, 3), Indexer.count); |
| 1508 | try testing.expectEqual(3, Indexer.count); |
| 1431 | 1509 | |
| 1432 | 1510 | try testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a)); |
| 1433 | 1511 | try testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b)); |
| ... | ... | @@ -1443,5 +1521,5 @@ test "std.enums.EnumIndexer empty" { |
| 1443 | 1521 | const Indexer = EnumIndexer(E); |
| 1444 | 1522 | ensureIndexer(Indexer); |
| 1445 | 1523 | try testing.expectEqual(E, Indexer.Key); |
| 1446 | | try testing.expectEqual(@as(usize, 0), Indexer.count); |
| 1524 | try testing.expectEqual(0, Indexer.count); |
| 1447 | 1525 | } |