const std = @import("std");
const assert = std.debug.assert;
const testing = std.testing;
const EnumField = std.builtin.Type.EnumField;
pub fn EnumFieldStruct(comptime E: type, comptime Data: type, comptime field_default: ?Data) type {
const StructField = std.builtin.Type.StructField;
var fields: []const StructField = &[_]StructField{};
for (std.meta.fields(E)) |field| {
fields = fields ++ &[_]StructField{.{
.name = field.name,
.type = Data,
.default_value = if (field_default) |d| @ptrCast(?*const anyopaque, &d) else null,
.is_comptime = false,
.alignment = if (@sizeOf(Data) > 0) @alignOf(Data) else 0,
}};
}
return @Type(.{ .Struct = .{
.layout = .Auto,
.fields = fields,
.decls = &.{},
.is_tuple = false,
} });
}
pub inline fn valuesFromFields(comptime E: type, comptime fields: []const EnumField) []const E {
comptime {
var result: [fields.len]E = undefined;
for (fields, 0..) |f, i| {
result[i] = @field(E, f.name);
}
return &result;
}
}
pub fn values(comptime E: type) []const E {
return comptime valuesFromFields(E, @typeInfo(E).Enum.fields);
}
pub fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int) comptime_int {
var max_value: comptime_int = -1;
const max_usize: comptime_int = ~@as(usize, 0);
const fields = std.meta.fields(E);
for (fields) |f| {
if (f.value < 0) {
@compileError("Cannot create a direct enum array for " ++ @typeName(E) ++ ", field ." ++ f.name ++ " has a negative value.");
}
if (f.value > max_value) {
if (f.value > max_usize) {
@compileError("Cannot create a direct enum array for " ++ @typeName(E) ++ ", field ." ++ f.name ++ " is larger than the max value of usize.");
}
max_value = f.value;
}
}
const unused_slots = max_value + 1 - fields.len;
if (unused_slots > max_unused_slots) {
const unused_str = std.fmt.comptimePrint("{d}", .{unused_slots});
const allowed_str = std.fmt.comptimePrint("{d}", .{max_unused_slots});
@compileError("Cannot create a direct enum array for " ++ @typeName(E) ++ ". It would have " ++ unused_str ++ " unused slots, but only " ++ allowed_str ++ " are allowed.");
}
return max_value + 1;
}
pub fn directEnumArray(
comptime E: type,
comptime Data: type,
comptime max_unused_slots: comptime_int,
init_values: EnumFieldStruct(E, Data, null),
) [directEnumArrayLen(E, max_unused_slots)]Data {
return directEnumArrayDefault(E, Data, null, max_unused_slots, init_values);
}
test "std.enums.directEnumArray" {
const E = enum(i4) { a = 4, b = 6, c = 2 };
var runtime_false: bool = false;
const array = directEnumArray(E, bool, 4, .{
.a = true,
.b = runtime_false,
.c = true,
});
try testing.expectEqual([7]bool, @TypeOf(array));
try testing.expectEqual(true, array[4]);
try testing.expectEqual(false, array[6]);
try testing.expectEqual(true, array[2]);
}
pub fn directEnumArrayDefault(
comptime E: type,
comptime Data: type,
comptime default: ?Data,
comptime max_unused_slots: comptime_int,
init_values: EnumFieldStruct(E, Data, default),
) [directEnumArrayLen(E, max_unused_slots)]Data {
const len = comptime directEnumArrayLen(E, max_unused_slots);
var result: [len]Data = if (default) |d| [_]Data{d} ** len else undefined;
inline for (@typeInfo(@TypeOf(init_values)).Struct.fields) |f| {
const enum_value = @field(E, f.name);
const index = @intCast(usize, @enumToInt(enum_value));
result[index] = @field(init_values, f.name);
}
return result;
}
test "std.enums.directEnumArrayDefault" {
const E = enum(i4) { a = 4, b = 6, c = 2 };
var runtime_false: bool = false;
const array = directEnumArrayDefault(E, bool, false, 4, .{
.a = true,
.b = runtime_false,
});
try testing.expectEqual([7]bool, @TypeOf(array));
try testing.expectEqual(true, array[4]);
try testing.expectEqual(false, array[6]);
try testing.expectEqual(false, array[2]);
}
test "std.enums.directEnumArrayDefault slice" {
const E = enum(i4) { a = 4, b = 6, c = 2 };
var runtime_b = "b";
const array = directEnumArrayDefault(E, []const u8, "default", 4, .{
.a = "a",
.b = runtime_b,
});
try testing.expectEqual([7][]const u8, @TypeOf(array));
try testing.expectEqualSlices(u8, "a", array[4]);
try testing.expectEqualSlices(u8, "b", array[6]);
try testing.expectEqualSlices(u8, "default", array[2]);
}
pub fn nameCast(comptime E: type, comptime value: anytype) E {
return comptime blk: {
const V = @TypeOf(value);
if (V == E) break :blk value;
var name: ?[]const u8 = switch (@typeInfo(V)) {
.EnumLiteral, .Enum => @tagName(value),
.Pointer => if (std.meta.trait.isZigString(V)) value else null,
else => null,
};
if (name) |n| {
if (@hasField(E, n)) {
break :blk @field(E, n);
}
@compileError("Enum " ++ @typeName(E) ++ " has no field named " ++ n);
}
@compileError("Cannot cast from " ++ @typeName(@TypeOf(value)) ++ " to " ++ @typeName(E));
};
}
test "std.enums.nameCast" {
const A = enum(u1) { a = 0, b = 1 };
const B = enum(u1) { a = 1, b = 0 };
try testing.expectEqual(A.a, nameCast(A, .a));
try testing.expectEqual(A.a, nameCast(A, A.a));
try testing.expectEqual(A.a, nameCast(A, B.a));
try testing.expectEqual(A.a, nameCast(A, "a"));
try testing.expectEqual(A.a, nameCast(A, @as(*const [1]u8, "a")));
try testing.expectEqual(A.a, nameCast(A, @as([:0]const u8, "a")));
try testing.expectEqual(A.a, nameCast(A, @as([]const u8, "a")));
try testing.expectEqual(B.a, nameCast(B, .a));
try testing.expectEqual(B.a, nameCast(B, A.a));
try testing.expectEqual(B.a, nameCast(B, B.a));
try testing.expectEqual(B.a, nameCast(B, "a"));
try testing.expectEqual(B.b, nameCast(B, .b));
try testing.expectEqual(B.b, nameCast(B, A.b));
try testing.expectEqual(B.b, nameCast(B, B.b));
try testing.expectEqual(B.b, nameCast(B, "b"));
}
pub fn EnumSet(comptime E: type) type {
const mixin = struct {
fn EnumSetExt(comptime Self: type) type {
const Indexer = Self.Indexer;
return struct {
pub fn init(init_values: EnumFieldStruct(E, bool, false)) Self {
var result = Self{};
comptime var i: usize = 0;
inline while (i < Self.len) : (i += 1) {
const key = comptime Indexer.keyForIndex(i);
const tag = comptime @tagName(key);
if (@field(init_values, tag)) {
result.bits.set(i);
}
}
return result;
}
};
}
};
return IndexedSet(EnumIndexer(E), mixin.EnumSetExt);
}
pub fn EnumMap(comptime E: type, comptime V: type) type {
const mixin = struct {
fn EnumMapExt(comptime Self: type) type {
const Indexer = Self.Indexer;
return struct {
pub fn init(init_values: EnumFieldStruct(E, ?V, @as(?V, null))) Self {
var result = Self{};
comptime var i: usize = 0;
inline while (i < Self.len) : (i += 1) {
const key = comptime Indexer.keyForIndex(i);
const tag = comptime @tagName(key);
if (@field(init_values, tag)) |*v| {
result.bits.set(i);
result.values[i] = v.*;
}
}
return result;
}
pub fn initFull(value: V) Self {
var result = Self{
.bits = Self.BitSet.initFull(),
.values = undefined,
};
std.mem.set(V, &result.values, value);
return result;
}
pub fn initFullWith(init_values: EnumFieldStruct(E, V, @as(?V, null))) Self {
return initFullWithDefault(@as(?V, null), init_values);
}
pub fn initFullWithDefault(comptime default: ?V, init_values: EnumFieldStruct(E, V, default)) Self {
var result = Self{
.bits = Self.BitSet.initFull(),
.values = undefined,
};
comptime var i: usize = 0;
inline while (i < Self.len) : (i += 1) {
const key = comptime Indexer.keyForIndex(i);
const tag = comptime @tagName(key);
result.values[i] = @field(init_values, tag);
}
return result;
}
};
}
};
return IndexedMap(EnumIndexer(E), V, mixin.EnumMapExt);
}
pub fn EnumMultiset(comptime E: type) type {
return BoundedEnumMultiset(E, usize);
}
pub fn BoundedEnumMultiset(comptime E: type, comptime CountSize: type) type {
return struct {
const Self = @This();
counts: EnumArray(E, CountSize),
pub fn init(init_counts: EnumFieldStruct(E, CountSize, 0)) Self {
var self = initWithCount(0);
inline for (@typeInfo(E).Enum.fields) |field| {
const c = @field(init_counts, field.name);
const key = @intToEnum(E, field.value);
self.counts.set(key, c);
}
return self;
}
pub fn initEmpty() Self {
return initWithCount(0);
}
pub fn initWithCount(comptime c: CountSize) Self {
return .{
.counts = EnumArray(E, CountSize).initDefault(c, .{}),
};
}
pub fn count(self: Self) usize {
var sum: usize = 0;
for (self.counts.values) |c| {
sum += c;
}
return sum;
}
pub fn contains(self: Self, key: E) bool {
return self.counts.get(key) > 0;
}
pub fn removeAll(self: *Self, key: E) void {
return self.counts.set(key, 0);
}
pub fn addAssertSafe(self: *Self, key: E, c: CountSize) void {
self.counts.getPtr(key).* += c;
}
pub fn add(self: *Self, key: E, c: CountSize) error{Overflow}!void {
self.counts.set(key, try std.math.add(CountSize, self.counts.get(key), c));
}
pub fn remove(self: *Self, key: E, c: CountSize) void {
self.counts.getPtr(key).* -= @min(self.getCount(key), c);
}
pub fn getCount(self: Self, key: E) CountSize {
return self.counts.get(key);
}
pub fn setCount(self: *Self, key: E, c: CountSize) void {
self.counts.set(key, c);
}
pub fn addSetAssertSafe(self: *Self, other: Self) void {
inline for (@typeInfo(E).Enum.fields) |field| {
const key = @intToEnum(E, field.value);
self.addAssertSafe(key, other.getCount(key));
}
}
pub fn addSet(self: *Self, other: Self) error{Overflow}!void {
inline for (@typeInfo(E).Enum.fields) |field| {
const key = @intToEnum(E, field.value);
try self.add(key, other.getCount(key));
}
}
pub fn removeSet(self: *Self, other: Self) void {
inline for (@typeInfo(E).Enum.fields) |field| {
const key = @intToEnum(E, field.value);
self.remove(key, other.getCount(key));
}
}
pub fn eql(self: Self, other: Self) bool {
inline for (@typeInfo(E).Enum.fields) |field| {
const key = @intToEnum(E, field.value);
if (self.getCount(key) != other.getCount(key)) {
return false;
}
}
return true;
}
pub fn subsetOf(self: Self, other: Self) bool {
inline for (@typeInfo(E).Enum.fields) |field| {
const key = @intToEnum(E, field.value);
if (self.getCount(key) > other.getCount(key)) {
return false;
}
}
return true;
}
pub fn supersetOf(self: Self, other: Self) bool {
inline for (@typeInfo(E).Enum.fields) |field| {
const key = @intToEnum(E, field.value);
if (self.getCount(key) < other.getCount(key)) {
return false;
}
}
return true;
}
pub fn plusAssertSafe(self: Self, other: Self) Self {
var result = self;
result.addSetAssertSafe(other);
return result;
}
pub fn plus(self: Self, other: Self) error{Overflow}!Self {
var result = self;
try result.addSet(other);
return result;
}
pub fn minus(self: Self, other: Self) Self {
var result = self;
result.removeSet(other);
return result;
}
pub const Entry = EnumArray(E, CountSize).Entry;
pub const Iterator = EnumArray(E, CountSize).Iterator;
pub fn iterator(self: *Self) Iterator {
return self.counts.iterator();
}
};
}
test "EnumMultiset" {
const Ball = enum { red, green, blue };
const empty = EnumMultiset(Ball).initEmpty();
const r0_g1_b2 = EnumMultiset(Ball).init(.{
.red = 0,
.green = 1,
.blue = 2,
});
const ten_of_each = EnumMultiset(Ball).initWithCount(10);
try testing.expectEqual(empty.count(), 0);
try testing.expectEqual(r0_g1_b2.count(), 3);
try testing.expectEqual(ten_of_each.count(), 30);
try testing.expect(!empty.contains(.red));
try testing.expect(!empty.contains(.green));
try testing.expect(!empty.contains(.blue));
try testing.expect(!r0_g1_b2.contains(.red));
try testing.expect(r0_g1_b2.contains(.green));
try testing.expect(r0_g1_b2.contains(.blue));
try testing.expect(ten_of_each.contains(.red));
try testing.expect(ten_of_each.contains(.green));
try testing.expect(ten_of_each.contains(.blue));
{
var copy = ten_of_each;
copy.removeAll(.red);
try testing.expect(!copy.contains(.red));
copy.removeAll(.red);
try testing.expect(!copy.contains(.red));
}
{
var copy = ten_of_each;
copy.addAssertSafe(.red, 6);
try testing.expectEqual(copy.getCount(.red), 16);
}
{
var copy = ten_of_each;
try copy.add(.red, 6);
try testing.expectEqual(copy.getCount(.red), 16);
try testing.expectError(error.Overflow, copy.add(.red, std.math.maxInt(usize)));
}
{
var copy = ten_of_each;
copy.remove(.red, 4);
try testing.expectEqual(copy.getCount(.red), 6);
copy.remove(.green, 14);
try testing.expectEqual(copy.getCount(.green), 0);
}
try testing.expectEqual(empty.getCount(.green), 0);
try testing.expectEqual(r0_g1_b2.getCount(.green), 1);
try testing.expectEqual(ten_of_each.getCount(.green), 10);
{
var copy = empty;
copy.setCount(.red, 6);
try testing.expectEqual(copy.getCount(.red), 6);
}
{
var copy = r0_g1_b2;
copy.addSetAssertSafe(ten_of_each);
try testing.expectEqual(copy.getCount(.red), 10);
try testing.expectEqual(copy.getCount(.green), 11);
try testing.expectEqual(copy.getCount(.blue), 12);
}
{
var copy = r0_g1_b2;
try copy.addSet(ten_of_each);
try testing.expectEqual(copy.getCount(.red), 10);
try testing.expectEqual(copy.getCount(.green), 11);
try testing.expectEqual(copy.getCount(.blue), 12);
const full = EnumMultiset(Ball).initWithCount(std.math.maxInt(usize));
try testing.expectError(error.Overflow, copy.addSet(full));
}
{
var copy = ten_of_each;
copy.removeSet(r0_g1_b2);
try testing.expectEqual(copy.getCount(.red), 10);
try testing.expectEqual(copy.getCount(.green), 9);
try testing.expectEqual(copy.getCount(.blue), 8);
copy.removeSet(ten_of_each);
try testing.expectEqual(copy.getCount(.red), 0);
try testing.expectEqual(copy.getCount(.green), 0);
try testing.expectEqual(copy.getCount(.blue), 0);
}
try testing.expect(empty.eql(empty));
try testing.expect(r0_g1_b2.eql(r0_g1_b2));
try testing.expect(ten_of_each.eql(ten_of_each));
try testing.expect(!empty.eql(r0_g1_b2));
try testing.expect(!r0_g1_b2.eql(ten_of_each));
try testing.expect(!ten_of_each.eql(empty));
try testing.expect(empty.subsetOf(empty));
try testing.expect(r0_g1_b2.subsetOf(r0_g1_b2));
try testing.expect(empty.subsetOf(r0_g1_b2));
try testing.expect(r0_g1_b2.subsetOf(ten_of_each));
try testing.expect(!ten_of_each.subsetOf(r0_g1_b2));
try testing.expect(!r0_g1_b2.subsetOf(empty));
try testing.expect(empty.supersetOf(empty));
try testing.expect(r0_g1_b2.supersetOf(r0_g1_b2));
try testing.expect(r0_g1_b2.supersetOf(empty));
try testing.expect(ten_of_each.supersetOf(r0_g1_b2));
try testing.expect(!r0_g1_b2.supersetOf(ten_of_each));
try testing.expect(!empty.supersetOf(r0_g1_b2));
{
const r10 = EnumMultiset(Ball).init(.{
.red = 10,
});
const b10 = EnumMultiset(Ball).init(.{
.blue = 10,
});
try testing.expect(!r10.subsetOf(b10));
try testing.expect(!b10.subsetOf(r10));
try testing.expect(!r10.supersetOf(b10));
try testing.expect(!b10.supersetOf(r10));
}
{
const result = r0_g1_b2.plusAssertSafe(ten_of_each);
try testing.expectEqual(result.getCount(.red), 10);
try testing.expectEqual(result.getCount(.green), 11);
try testing.expectEqual(result.getCount(.blue), 12);
}
{
const result = try r0_g1_b2.plus(ten_of_each);
try testing.expectEqual(result.getCount(.red), 10);
try testing.expectEqual(result.getCount(.green), 11);
try testing.expectEqual(result.getCount(.blue), 12);
const full = EnumMultiset(Ball).initWithCount(std.math.maxInt(usize));
try testing.expectError(error.Overflow, result.plus(full));
}
{
const result = ten_of_each.minus(r0_g1_b2);
try testing.expectEqual(result.getCount(.red), 10);
try testing.expectEqual(result.getCount(.green), 9);
try testing.expectEqual(result.getCount(.blue), 8);
}
{
const result = ten_of_each.minus(r0_g1_b2).minus(ten_of_each);
try testing.expectEqual(result.getCount(.red), 0);
try testing.expectEqual(result.getCount(.green), 0);
try testing.expectEqual(result.getCount(.blue), 0);
}
{
var copy = empty;
var it = copy.iterator();
var entry = it.next().?;
try testing.expectEqual(entry.key, .red);
try testing.expectEqual(entry.value.*, 0);
entry = it.next().?;
try testing.expectEqual(entry.key, .green);
try testing.expectEqual(entry.value.*, 0);
entry = it.next().?;
try testing.expectEqual(entry.key, .blue);
try testing.expectEqual(entry.value.*, 0);
try testing.expectEqual(it.next(), null);
}
{
var copy = r0_g1_b2;
var it = copy.iterator();
var entry = it.next().?;
try testing.expectEqual(entry.key, .red);
try testing.expectEqual(entry.value.*, 0);
entry = it.next().?;
try testing.expectEqual(entry.key, .green);
try testing.expectEqual(entry.value.*, 1);
entry = it.next().?;
try testing.expectEqual(entry.key, .blue);
try testing.expectEqual(entry.value.*, 2);
try testing.expectEqual(it.next(), null);
}
}
pub fn EnumArray(comptime E: type, comptime V: type) type {
const mixin = struct {
fn EnumArrayExt(comptime Self: type) type {
const Indexer = Self.Indexer;
return struct {
pub fn init(init_values: EnumFieldStruct(E, V, @as(?V, null))) Self {
return initDefault(@as(?V, null), init_values);
}
pub fn initDefault(comptime default: ?V, init_values: EnumFieldStruct(E, V, default)) Self {
var result = Self{ .values = undefined };
comptime var i: usize = 0;
inline while (i < Self.len) : (i += 1) {
const key = comptime Indexer.keyForIndex(i);
const tag = @tagName(key);
result.values[i] = @field(init_values, tag);
}
return result;
}
};
}
};
return IndexedArray(EnumIndexer(E), V, mixin.EnumArrayExt);
}
pub fn NoExtension(comptime Self: type) type {
_ = Self;
return NoExt;
}
const NoExt = struct {};
pub fn IndexedSet(comptime I: type, comptime Ext: fn (type) type) type {
comptime ensureIndexer(I);
return struct {
const Self = @This();
pub usingnamespace Ext(Self);
pub const Indexer = I;
pub const Key = Indexer.Key;
const BitSet = std.StaticBitSet(Indexer.count);
pub const len = Indexer.count;
bits: BitSet = BitSet.initEmpty(),
pub fn initEmpty() Self {
return .{ .bits = BitSet.initEmpty() };
}
pub fn initFull() Self {
return .{ .bits = BitSet.initFull() };
}
pub fn initMany(keys: []const Key) Self {
var set = initEmpty();
for (keys) |key| set.insert(key);
return set;
}
pub fn initOne(key: Key) Self {
return initMany(&[_]Key{key});
}
pub fn count(self: Self) usize {
return self.bits.count();
}
pub fn contains(self: Self, key: Key) bool {
return self.bits.isSet(Indexer.indexOf(key));
}
pub fn insert(self: *Self, key: Key) void {
self.bits.set(Indexer.indexOf(key));
}
pub fn remove(self: *Self, key: Key) void {
self.bits.unset(Indexer.indexOf(key));
}
pub fn setPresent(self: *Self, key: Key, present: bool) void {
self.bits.setValue(Indexer.indexOf(key), present);
}
pub fn toggle(self: *Self, key: Key) void {
self.bits.toggle(Indexer.indexOf(key));
}
pub fn toggleSet(self: *Self, other: Self) void {
self.bits.toggleSet(other.bits);
}
pub fn toggleAll(self: *Self) void {
self.bits.toggleAll();
}
pub fn setUnion(self: *Self, other: Self) void {
self.bits.setUnion(other.bits);
}
pub fn setIntersection(self: *Self, other: Self) void {
self.bits.setIntersection(other.bits);
}
pub fn eql(self: Self, other: Self) bool {
return self.bits.eql(other.bits);
}
pub fn subsetOf(self: Self, other: Self) bool {
return self.bits.subsetOf(other.bits);
}
pub fn supersetOf(self: Self, other: Self) bool {
return self.bits.supersetOf(other.bits);
}
pub fn complement(self: Self) Self {
return .{ .bits = self.bits.complement() };
}
pub fn unionWith(self: Self, other: Self) Self {
return .{ .bits = self.bits.unionWith(other.bits) };
}
pub fn intersectWith(self: Self, other: Self) Self {
return .{ .bits = self.bits.intersectWith(other.bits) };
}
pub fn xorWith(self: Self, other: Self) Self {
return .{ .bits = self.bits.xorWith(other.bits) };
}
pub fn differenceWith(self: Self, other: Self) Self {
return .{ .bits = self.bits.differenceWith(other.bits) };
}
pub fn iterator(self: *const Self) Iterator {
return .{ .inner = self.bits.iterator(.{}) };
}
pub const Iterator = struct {
inner: BitSet.Iterator(.{}),
pub fn next(self: *Iterator) ?Key {
return if (self.inner.next()) |index|
Indexer.keyForIndex(index)
else
null;
}
};
};
}
test "pure EnumSet fns" {
const Suit = enum { spades, hearts, clubs, diamonds };
const empty = EnumSet(Suit).initEmpty();
const full = EnumSet(Suit).initFull();
const black = EnumSet(Suit).initMany(&[_]Suit{ .spades, .clubs });
const red = EnumSet(Suit).initMany(&[_]Suit{ .hearts, .diamonds });
try testing.expect(empty.eql(empty));
try testing.expect(full.eql(full));
try testing.expect(!empty.eql(full));
try testing.expect(!full.eql(empty));
try testing.expect(!empty.eql(black));
try testing.expect(!full.eql(red));
try testing.expect(!red.eql(empty));
try testing.expect(!black.eql(full));
try testing.expect(empty.subsetOf(empty));
try testing.expect(empty.subsetOf(full));
try testing.expect(full.subsetOf(full));
try testing.expect(!black.subsetOf(red));
try testing.expect(!red.subsetOf(black));
try testing.expect(full.supersetOf(full));
try testing.expect(full.supersetOf(empty));
try testing.expect(empty.supersetOf(empty));
try testing.expect(!black.supersetOf(red));
try testing.expect(!red.supersetOf(black));
try testing.expect(empty.complement().eql(full));
try testing.expect(full.complement().eql(empty));
try testing.expect(black.complement().eql(red));
try testing.expect(red.complement().eql(black));
try testing.expect(empty.unionWith(empty).eql(empty));
try testing.expect(empty.unionWith(full).eql(full));
try testing.expect(full.unionWith(full).eql(full));
try testing.expect(full.unionWith(empty).eql(full));
try testing.expect(black.unionWith(red).eql(full));
try testing.expect(red.unionWith(black).eql(full));
try testing.expect(empty.intersectWith(empty).eql(empty));
try testing.expect(empty.intersectWith(full).eql(empty));
try testing.expect(full.intersectWith(full).eql(full));
try testing.expect(full.intersectWith(empty).eql(empty));
try testing.expect(black.intersectWith(red).eql(empty));
try testing.expect(red.intersectWith(black).eql(empty));
try testing.expect(empty.xorWith(empty).eql(empty));
try testing.expect(empty.xorWith(full).eql(full));
try testing.expect(full.xorWith(full).eql(empty));
try testing.expect(full.xorWith(empty).eql(full));
try testing.expect(black.xorWith(red).eql(full));
try testing.expect(red.xorWith(black).eql(full));
try testing.expect(empty.differenceWith(empty).eql(empty));
try testing.expect(empty.differenceWith(full).eql(empty));
try testing.expect(full.differenceWith(full).eql(empty));
try testing.expect(full.differenceWith(empty).eql(full));
try testing.expect(full.differenceWith(red).eql(black));
try testing.expect(full.differenceWith(black).eql(red));
}
test "std.enums.EnumSet const iterator" {
const Direction = enum { up, down, left, right };
const diag_move = init: {
var move = EnumSet(Direction).initEmpty();
move.insert(.right);
move.insert(.up);
break :init move;
};
var result = EnumSet(Direction).initEmpty();
var it = diag_move.iterator();
while (it.next()) |dir| {
result.insert(dir);
}
try testing.expect(result.eql(diag_move));
}
pub fn IndexedMap(comptime I: type, comptime V: type, comptime Ext: fn (type) type) type {
comptime ensureIndexer(I);
return struct {
const Self = @This();
pub usingnamespace Ext(Self);
pub const Indexer = I;
pub const Key = Indexer.Key;
pub const Value = V;
pub const len = Indexer.count;
const BitSet = std.StaticBitSet(Indexer.count);
bits: BitSet = BitSet.initEmpty(),
values: [Indexer.count]Value = undefined,
pub fn count(self: Self) usize {
return self.bits.count();
}
pub fn contains(self: Self, key: Key) bool {
return self.bits.isSet(Indexer.indexOf(key));
}
pub fn get(self: Self, key: Key) ?Value {
const index = Indexer.indexOf(key);
return if (self.bits.isSet(index)) self.values[index] else null;
}
pub fn getAssertContains(self: Self, key: Key) Value {
const index = Indexer.indexOf(key);
assert(self.bits.isSet(index));
return self.values[index];
}
pub fn getPtr(self: *Self, key: Key) ?*Value {
const index = Indexer.indexOf(key);
return if (self.bits.isSet(index)) &self.values[index] else null;
}
pub fn getPtrConst(self: *const Self, key: Key) ?*const Value {
const index = Indexer.indexOf(key);
return if (self.bits.isSet(index)) &self.values[index] else null;
}
pub fn getPtrAssertContains(self: *Self, key: Key) *Value {
const index = Indexer.indexOf(key);
assert(self.bits.isSet(index));
return &self.values[index];
}
pub fn put(self: *Self, key: Key, value: Value) void {
const index = Indexer.indexOf(key);
self.bits.set(index);
self.values[index] = value;
}
pub fn putUninitialized(self: *Self, key: Key) *Value {
const index = Indexer.indexOf(key);
self.bits.set(index);
self.values[index] = undefined;
return &self.values[index];
}
pub fn fetchPut(self: *Self, key: Key, value: Value) ?Value {
const index = Indexer.indexOf(key);
const result: ?Value = if (self.bits.isSet(index)) self.values[index] else null;
self.bits.set(index);
self.values[index] = value;
return result;
}
pub fn remove(self: *Self, key: Key) void {
const index = Indexer.indexOf(key);
self.bits.unset(index);
self.values[index] = undefined;
}
pub fn fetchRemove(self: *Self, key: Key) ?Value {
const index = Indexer.indexOf(key);
const result: ?Value = if (self.bits.isSet(index)) self.values[index] else null;
self.bits.unset(index);
self.values[index] = undefined;
return result;
}
pub fn iterator(self: *Self) Iterator {
return .{
.inner = self.bits.iterator(.{}),
.values = &self.values,
};
}
pub const Entry = struct {
key: Key,
value: *Value,
};
pub const Iterator = struct {
inner: BitSet.Iterator(.{}),
values: *[Indexer.count]Value,
pub fn next(self: *Iterator) ?Entry {
return if (self.inner.next()) |index|
Entry{
.key = Indexer.keyForIndex(index),
.value = &self.values[index],
}
else
null;
}
};
};
}
pub fn IndexedArray(comptime I: type, comptime V: type, comptime Ext: fn (type) type) type {
comptime ensureIndexer(I);
return struct {
const Self = @This();
pub usingnamespace Ext(Self);
pub const Indexer = I;
pub const Key = Indexer.Key;
pub const Value = V;
pub const len = Indexer.count;
values: [Indexer.count]Value,
pub fn initUndefined() Self {
return Self{ .values = undefined };
}
pub fn initFill(v: Value) Self {
var self: Self = undefined;
std.mem.set(Value, &self.values, v);
return self;
}
pub fn get(self: Self, key: Key) Value {
return self.values[Indexer.indexOf(key)];
}
pub fn getPtr(self: *Self, key: Key) *Value {
return &self.values[Indexer.indexOf(key)];
}
pub fn getPtrConst(self: *const Self, key: Key) *const Value {
return &self.values[Indexer.indexOf(key)];
}
pub fn set(self: *Self, key: Key, value: Value) void {
self.values[Indexer.indexOf(key)] = value;
}
pub fn iterator(self: *Self) Iterator {
return .{
.values = &self.values,
};
}
pub const Entry = struct {
key: Key,
value: *Value,
};
pub const Iterator = struct {
index: usize = 0,
values: *[Indexer.count]Value,
pub fn next(self: *Iterator) ?Entry {
const index = self.index;
if (index < Indexer.count) {
self.index += 1;
return Entry{
.key = Indexer.keyForIndex(index),
.value = &self.values[index],
};
}
return null;
}
};
};
}
pub fn ensureIndexer(comptime T: type) void {
comptime {
if (!@hasDecl(T, "Key")) @compileError("Indexer must have decl Key: type.");
if (@TypeOf(T.Key) != type) @compileError("Indexer.Key must be a type.");
if (!@hasDecl(T, "count")) @compileError("Indexer must have decl count: usize.");
if (@TypeOf(T.count) != usize) @compileError("Indexer.count must be a usize.");
if (!@hasDecl(T, "indexOf")) @compileError("Indexer.indexOf must be a fn(Key)usize.");
if (@TypeOf(T.indexOf) != fn (T.Key) usize) @compileError("Indexer must have decl indexOf: fn(Key)usize.");
if (!@hasDecl(T, "keyForIndex")) @compileError("Indexer must have decl keyForIndex: fn(usize)Key.");
if (@TypeOf(T.keyForIndex) != fn (usize) T.Key) @compileError("Indexer.keyForIndex must be a fn(usize)Key.");
}
}
test "std.enums.ensureIndexer" {
ensureIndexer(struct {
pub const Key = u32;
pub const count: usize = 8;
pub fn indexOf(k: Key) usize {
return @intCast(usize, k);
}
pub fn keyForIndex(index: usize) Key {
return @intCast(Key, index);
}
});
}
fn ascByValue(ctx: void, comptime a: EnumField, comptime b: EnumField) bool {
_ = ctx;
return a.value < b.value;
}
pub fn EnumIndexer(comptime E: type) type {
if (!@typeInfo(E).Enum.is_exhaustive) {
@compileError("Cannot create an enum indexer for a non-exhaustive enum.");
}
const const_fields = std.meta.fields(E);
var fields = const_fields[0..const_fields.len].*;
if (fields.len == 0) {
return struct {
pub const Key = E;
pub const count: usize = 0;
pub fn indexOf(e: E) usize {
_ = e;
unreachable;
}
pub fn keyForIndex(i: usize) E {
_ = i;
unreachable;
}
};
}
std.sort.sort(EnumField, &fields, {}, ascByValue);
const min = fields[0].value;
const max = fields[fields.len - 1].value;
const fields_len = fields.len;
if (max - min == fields.len - 1) {
return struct {
pub const Key = E;
pub const count = fields_len;
pub fn indexOf(e: E) usize {
return @intCast(usize, @enumToInt(e) - min);
}
pub fn keyForIndex(i: usize) E {
const enum_value = if (min < 0) @bitCast(isize, i) +% min else i + min;
return @intToEnum(E, @intCast(std.meta.Tag(E), enum_value));
}
};
}
const keys = valuesFromFields(E, &fields);
return struct {
pub const Key = E;
pub const count = fields_len;
pub fn indexOf(e: E) usize {
for (keys, 0..) |k, i| {
if (k == e) return i;
}
unreachable;
}
pub fn keyForIndex(i: usize) E {
return keys[i];
}
};
}
test "std.enums.EnumIndexer dense zeroed" {
const E = enum(u2) { b = 1, a = 0, c = 2 };
const Indexer = EnumIndexer(E);
ensureIndexer(Indexer);
try testing.expectEqual(E, Indexer.Key);
try testing.expectEqual(@as(usize, 3), Indexer.count);
try testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
try testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
try testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
try testing.expectEqual(E.a, Indexer.keyForIndex(0));
try testing.expectEqual(E.b, Indexer.keyForIndex(1));
try testing.expectEqual(E.c, Indexer.keyForIndex(2));
}
test "std.enums.EnumIndexer dense positive" {
const E = enum(u4) { c = 6, a = 4, b = 5 };
const Indexer = EnumIndexer(E);
ensureIndexer(Indexer);
try testing.expectEqual(E, Indexer.Key);
try testing.expectEqual(@as(usize, 3), Indexer.count);
try testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
try testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
try testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
try testing.expectEqual(E.a, Indexer.keyForIndex(0));
try testing.expectEqual(E.b, Indexer.keyForIndex(1));
try testing.expectEqual(E.c, Indexer.keyForIndex(2));
}
test "std.enums.EnumIndexer dense negative" {
const E = enum(i4) { a = -6, c = -4, b = -5 };
const Indexer = EnumIndexer(E);
ensureIndexer(Indexer);
try testing.expectEqual(E, Indexer.Key);
try testing.expectEqual(@as(usize, 3), Indexer.count);
try testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
try testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
try testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
try testing.expectEqual(E.a, Indexer.keyForIndex(0));
try testing.expectEqual(E.b, Indexer.keyForIndex(1));
try testing.expectEqual(E.c, Indexer.keyForIndex(2));
}
test "std.enums.EnumIndexer sparse" {
const E = enum(i4) { a = -2, c = 6, b = 4 };
const Indexer = EnumIndexer(E);
ensureIndexer(Indexer);
try testing.expectEqual(E, Indexer.Key);
try testing.expectEqual(@as(usize, 3), Indexer.count);
try testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
try testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
try testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
try testing.expectEqual(E.a, Indexer.keyForIndex(0));
try testing.expectEqual(E.b, Indexer.keyForIndex(1));
try testing.expectEqual(E.c, Indexer.keyForIndex(2));
}