| ... | @@ -1267,7 +1267,7 @@ pub fn IndexedArray(comptime I: type, comptime V: type, comptime Ext: ?fn (type) | ... | @@ -1267,7 +1267,7 @@ pub fn IndexedArray(comptime I: type, comptime V: type, comptime Ext: ?fn (type) |
| 1267 | /// /// The key type which this indexer converts to indices | 1267 | /// /// The key type which this indexer converts to indices |
| 1268 | /// pub const Key: type, | 1268 | /// pub const Key: type, |
| 1269 | /// /// The number of indexes in the dense mapping | 1269 | /// /// The number of indexes in the dense mapping |
| 1270 | /// pub const count: usize, | 1270 | /// pub const count: comptime_int, |
| 1271 | /// /// Converts from a key to an index | 1271 | /// /// Converts from a key to an index |
| 1272 | /// pub fn indexOf(Key) usize; | 1272 | /// pub fn indexOf(Key) usize; |
| 1273 | /// /// Converts from an index to a key | 1273 | /// /// Converts from an index to a key |
| ... | @@ -1278,8 +1278,8 @@ pub fn ensureIndexer(comptime T: type) void { | ... | @@ -1278,8 +1278,8 @@ pub fn ensureIndexer(comptime T: type) void { |
| 1278 | comptime { | 1278 | comptime { |
| 1279 | if (!@hasDecl(T, "Key")) @compileError("Indexer must have decl Key: type."); | 1279 | if (!@hasDecl(T, "Key")) @compileError("Indexer must have decl Key: type."); |
| 1280 | if (@TypeOf(T.Key) != type) @compileError("Indexer.Key must be a type."); | 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."); | 1281 | if (!@hasDecl(T, "count")) @compileError("Indexer must have decl count: comptime_int."); |
| 1282 | if (@TypeOf(T.count) != usize) @compileError("Indexer.count must be a usize."); | 1282 | if (@TypeOf(T.count) != comptime_int) @compileError("Indexer.count must be a comptime_int."); |
| 1283 | if (!@hasDecl(T, "indexOf")) @compileError("Indexer.indexOf must be a fn (Key) usize."); | 1283 | if (!@hasDecl(T, "indexOf")) @compileError("Indexer.indexOf must be a fn (Key) usize."); |
| 1284 | if (@TypeOf(T.indexOf) != fn (T.Key) usize) @compileError("Indexer must have decl indexOf: fn (Key) usize."); | 1284 | if (@TypeOf(T.indexOf) != fn (T.Key) usize) @compileError("Indexer must have decl indexOf: fn (Key) usize."); |
| 1285 | if (!@hasDecl(T, "keyForIndex")) @compileError("Indexer must have decl keyForIndex: fn (usize) Key."); | 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,7 +1290,7 @@ pub fn ensureIndexer(comptime T: type) void { |
| 1290 | test "std.enums.ensureIndexer" { | 1290 | test "std.enums.ensureIndexer" { |
| 1291 | ensureIndexer(struct { | 1291 | ensureIndexer(struct { |
| 1292 | pub const Key = u32; | 1292 | pub const Key = u32; |
| 1293 | pub const count: usize = 8; | 1293 | pub const count: comptime_int = 8; |
| 1294 | pub fn indexOf(k: Key) usize { | 1294 | pub fn indexOf(k: Key) usize { |
| 1295 | return @as(usize, @intCast(k)); | 1295 | return @as(usize, @intCast(k)); |
| 1296 | } | 1296 | } |
| ... | @@ -1302,7 +1302,36 @@ test "std.enums.ensureIndexer" { | ... | @@ -1302,7 +1302,36 @@ test "std.enums.ensureIndexer" { |
| 1302 | | 1302 | |
| 1303 | pub fn EnumIndexer(comptime E: type) type { | 1303 | pub fn EnumIndexer(comptime E: type) type { |
| 1304 | if (!@typeInfo(E).Enum.is_exhaustive) { | 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 | const const_fields = std.meta.fields(E); | 1337 | const const_fields = std.meta.fields(E); |
| ... | @@ -1312,7 +1341,7 @@ pub fn EnumIndexer(comptime E: type) type { | ... | @@ -1312,7 +1341,7 @@ pub fn EnumIndexer(comptime E: type) type { |
| 1312 | if (fields_len == 0) { | 1341 | if (fields_len == 0) { |
| 1313 | return struct { | 1342 | return struct { |
| 1314 | pub const Key = E; | 1343 | pub const Key = E; |
| 1315 | pub const count: usize = 0; | 1344 | pub const count: comptime_int = 0; |
| 1316 | pub fn indexOf(e: E) usize { | 1345 | pub fn indexOf(e: E) usize { |
| 1317 | _ = e; | 1346 | _ = e; |
| 1318 | unreachable; | 1347 | unreachable; |
| ... | @@ -1343,7 +1372,7 @@ pub fn EnumIndexer(comptime E: type) type { | ... | @@ -1343,7 +1372,7 @@ pub fn EnumIndexer(comptime E: type) type { |
| 1343 | if (max - min == fields.len - 1) { | 1372 | if (max - min == fields.len - 1) { |
| 1344 | return struct { | 1373 | return struct { |
| 1345 | pub const Key = E; | 1374 | pub const Key = E; |
| 1346 | pub const count = fields_len; | 1375 | pub const count: comptime_int = fields_len; |
| 1347 | pub fn indexOf(e: E) usize { | 1376 | pub fn indexOf(e: E) usize { |
| 1348 | return @as(usize, @intCast(@intFromEnum(e) - min)); | 1377 | return @as(usize, @intCast(@intFromEnum(e) - min)); |
| 1349 | } | 1378 | } |
| ... | @@ -1361,7 +1390,7 @@ pub fn EnumIndexer(comptime E: type) type { | ... | @@ -1361,7 +1390,7 @@ pub fn EnumIndexer(comptime E: type) type { |
| 1361 | | 1390 | |
| 1362 | return struct { | 1391 | return struct { |
| 1363 | pub const Key = E; | 1392 | pub const Key = E; |
| 1364 | pub const count = fields_len; | 1393 | pub const count: comptime_int = fields_len; |
| 1365 | pub fn indexOf(e: E) usize { | 1394 | pub fn indexOf(e: E) usize { |
| 1366 | for (keys, 0..) |k, i| { | 1395 | for (keys, 0..) |k, i| { |
| 1367 | if (k == e) return i; | 1396 | if (k == e) return i; |
| ... | @@ -1374,12 +1403,61 @@ pub fn EnumIndexer(comptime E: type) type { | ... | @@ -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 | test "std.enums.EnumIndexer dense zeroed" { | 1455 | test "std.enums.EnumIndexer dense zeroed" { |
| 1378 | const E = enum(u2) { b = 1, a = 0, c = 2 }; | 1456 | const E = enum(u2) { b = 1, a = 0, c = 2 }; |
| 1379 | const Indexer = EnumIndexer(E); | 1457 | const Indexer = EnumIndexer(E); |
| 1380 | ensureIndexer(Indexer); | 1458 | ensureIndexer(Indexer); |
| 1381 | try testing.expectEqual(E, Indexer.Key); | 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 | try testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a)); | 1462 | try testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a)); |
| 1385 | try testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b)); | 1463 | try testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b)); |
| ... | @@ -1395,7 +1473,7 @@ test "std.enums.EnumIndexer dense positive" { | ... | @@ -1395,7 +1473,7 @@ test "std.enums.EnumIndexer dense positive" { |
| 1395 | const Indexer = EnumIndexer(E); | 1473 | const Indexer = EnumIndexer(E); |
| 1396 | ensureIndexer(Indexer); | 1474 | ensureIndexer(Indexer); |
| 1397 | try testing.expectEqual(E, Indexer.Key); | 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 | try testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a)); | 1478 | try testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a)); |
| 1401 | try testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b)); | 1479 | try testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b)); |
| ... | @@ -1411,7 +1489,7 @@ test "std.enums.EnumIndexer dense negative" { | ... | @@ -1411,7 +1489,7 @@ test "std.enums.EnumIndexer dense negative" { |
| 1411 | const Indexer = EnumIndexer(E); | 1489 | const Indexer = EnumIndexer(E); |
| 1412 | ensureIndexer(Indexer); | 1490 | ensureIndexer(Indexer); |
| 1413 | try testing.expectEqual(E, Indexer.Key); | 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 | try testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a)); | 1494 | try testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a)); |
| 1417 | try testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b)); | 1495 | try testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b)); |
| ... | @@ -1427,7 +1505,7 @@ test "std.enums.EnumIndexer sparse" { | ... | @@ -1427,7 +1505,7 @@ test "std.enums.EnumIndexer sparse" { |
| 1427 | const Indexer = EnumIndexer(E); | 1505 | const Indexer = EnumIndexer(E); |
| 1428 | ensureIndexer(Indexer); | 1506 | ensureIndexer(Indexer); |
| 1429 | try testing.expectEqual(E, Indexer.Key); | 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 | try testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a)); | 1510 | try testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a)); |
| 1433 | try testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b)); | 1511 | try testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b)); |
| ... | @@ -1443,5 +1521,5 @@ test "std.enums.EnumIndexer empty" { | ... | @@ -1443,5 +1521,5 @@ test "std.enums.EnumIndexer empty" { |
| 1443 | const Indexer = EnumIndexer(E); | 1521 | const Indexer = EnumIndexer(E); |
| 1444 | ensureIndexer(Indexer); | 1522 | ensureIndexer(Indexer); |
| 1445 | try testing.expectEqual(E, Indexer.Key); | 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 | } |