| ... | @@ -240,7 +240,7 @@ test nameCast { | ... | @@ -240,7 +240,7 @@ test nameCast { |
| 240 | } | 240 | } |
| 241 | | 241 | |
| 242 | /// A set of enum elements, backed by a bitfield. If the enum | 242 | /// A set of enum elements, backed by a bitfield. If the enum |
| 243 | /// is not dense, a mapping will be constructed from enum values | 243 | /// is exhaustive but not dense, a mapping will be constructed from enum values |
| 244 | /// to dense indices. This type does no dynamic allocation and | 244 | /// to dense indices. This type does no dynamic allocation and |
| 245 | /// can be copied by value. | 245 | /// can be copied by value. |
| 246 | pub fn EnumSet(comptime E: type) type { | 246 | pub fn EnumSet(comptime E: type) type { |
| ... | @@ -263,11 +263,21 @@ pub fn EnumSet(comptime E: type) type { | ... | @@ -263,11 +263,21 @@ pub fn EnumSet(comptime E: type) type { |
| 263 | pub fn init(init_values: EnumFieldStruct(E, bool, false)) Self { | 263 | pub fn init(init_values: EnumFieldStruct(E, bool, false)) Self { |
| 264 | @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len); | 264 | @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len); |
| 265 | var result: Self = .{}; | 265 | var result: Self = .{}; |
| 266 | inline for (0..Self.len) |i| { | 266 | if (@typeInfo(E).Enum.is_exhaustive) { |
| 267 | const key = comptime Indexer.keyForIndex(i); | 267 | inline for (0..Self.len) |i| { |
| 268 | const tag = @tagName(key); | 268 | const key = comptime Indexer.keyForIndex(i); |
| 269 | if (@field(init_values, tag)) { | 269 | const tag = @tagName(key); |
| 270 | result.bits.set(i); | 270 | if (@field(init_values, tag)) { |
| | 271 | result.bits.set(i); |
| | 272 | } |
| | 273 | } |
| | 274 | } else { |
| | 275 | inline for (std.meta.fields(E)) |field| { |
| | 276 | const key = @field(E, field.name); |
| | 277 | if (@field(init_values, field.name)) { |
| | 278 | const i = comptime Indexer.indexOf(key); |
| | 279 | result.bits.set(i); |
| | 280 | } |
| 271 | } | 281 | } |
| 272 | } | 282 | } |
| 273 | return result; | 283 | return result; |
| ... | @@ -416,7 +426,7 @@ pub fn EnumSet(comptime E: type) type { | ... | @@ -416,7 +426,7 @@ pub fn EnumSet(comptime E: type) type { |
| 416 | } | 426 | } |
| 417 | | 427 | |
| 418 | /// A map keyed by an enum, backed by a bitfield and a dense array. | 428 | /// A map keyed by an enum, backed by a bitfield and a dense array. |
| 419 | /// If the enum is not dense, a mapping will be constructed from | 429 | /// If the enum is exhaustive but not dense, a mapping will be constructed from |
| 420 | /// enum values to dense indices. This type does no dynamic | 430 | /// enum values to dense indices. This type does no dynamic |
| 421 | /// allocation and can be copied by value. | 431 | /// allocation and can be copied by value. |
| 422 | pub fn EnumMap(comptime E: type, comptime V: type) type { | 432 | pub fn EnumMap(comptime E: type, comptime V: type) type { |
| ... | @@ -444,12 +454,23 @@ pub fn EnumMap(comptime E: type, comptime V: type) type { | ... | @@ -444,12 +454,23 @@ pub fn EnumMap(comptime E: type, comptime V: type) type { |
| 444 | pub fn init(init_values: EnumFieldStruct(E, ?Value, null)) Self { | 454 | pub fn init(init_values: EnumFieldStruct(E, ?Value, null)) Self { |
| 445 | @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len); | 455 | @setEvalBranchQuota(2 * @typeInfo(E).Enum.fields.len); |
| 446 | var result: Self = .{}; | 456 | var result: Self = .{}; |
| 447 | inline for (0..Self.len) |i| { | 457 | if (@typeInfo(E).Enum.is_exhaustive) { |
| 448 | const key = comptime Indexer.keyForIndex(i); | 458 | inline for (0..Self.len) |i| { |
| 449 | const tag = @tagName(key); | 459 | const key = comptime Indexer.keyForIndex(i); |
| 450 | if (@field(init_values, tag)) |*v| { | 460 | const tag = @tagName(key); |
| 451 | result.bits.set(i); | 461 | if (@field(init_values, tag)) |*v| { |
| 452 | result.values[i] = v.*; | 462 | result.bits.set(i); |
| | 463 | result.values[i] = v.*; |
| | 464 | } |
| | 465 | } |
| | 466 | } else { |
| | 467 | inline for (std.meta.fields(E)) |field| { |
| | 468 | const key = @field(E, field.name); |
| | 469 | if (@field(init_values, field.name)) |*v| { |
| | 470 | const i = comptime Indexer.indexOf(key); |
| | 471 | result.bits.set(i); |
| | 472 | result.values[i] = v.*; |
| | 473 | } |
| 453 | } | 474 | } |
| 454 | } | 475 | } |
| 455 | return result; | 476 | return result; |
| ... | @@ -1222,6 +1243,23 @@ test "EnumSet const iterator" { | ... | @@ -1222,6 +1243,23 @@ test "EnumSet const iterator" { |
| 1222 | try testing.expect(result.eql(diag_move)); | 1243 | try testing.expect(result.eql(diag_move)); |
| 1223 | } | 1244 | } |
| 1224 | | 1245 | |
| | 1246 | test "EnumSet non-exhaustive" { |
| | 1247 | const BitIndices = enum(u4) { |
| | 1248 | a = 0, |
| | 1249 | b = 1, |
| | 1250 | c = 4, |
| | 1251 | _, |
| | 1252 | }; |
| | 1253 | const BitField = EnumSet(BitIndices); |
| | 1254 | |
| | 1255 | var flags = BitField.init(.{ .a = true, .b = true }); |
| | 1256 | flags.insert(.c); |
| | 1257 | flags.remove(.a); |
| | 1258 | try testing.expect(!flags.contains(.a)); |
| | 1259 | try testing.expect(flags.contains(.b)); |
| | 1260 | try testing.expect(flags.contains(.c)); |
| | 1261 | } |
| | 1262 | |
| 1225 | pub fn EnumIndexer(comptime E: type) type { | 1263 | pub fn EnumIndexer(comptime E: type) type { |
| 1226 | // Assumes that the enum fields are sorted in ascending order (optimistic). | 1264 | // Assumes that the enum fields are sorted in ascending order (optimistic). |
| 1227 | // Unsorted enums may require the user to manually increase the quota. | 1265 | // Unsorted enums may require the user to manually increase the quota. |