From 17f83ace03a3bcb4f42958b43527792c186cd498 Mon Sep 17 00:00:00 2001 From: mlugg Date: Thu, 7 Mar 2024 15:49:11 +0000 Subject: [PATCH] std.enums: remove IndexedMap, IndexedArray, IndexedSet Searching GitHub indicated that the only use of these types in the wild is support in getty-zig, and testing for that support. This eliminates 3 more uses of usingnamespace from the standard library, and removes some unnecessarily complex generic code. --- lib/std/enums.zig | 962 +++++++++++++++++++++------------------------- 1 file changed, 429 insertions(+), 533 deletions(-) diff --git a/lib/std/enums.zig b/lib/std/enums.zig index 7247b5dc88..0da3fb1871 100644 --- a/lib/std/enums.zig +++ b/lib/std/enums.zig @@ -241,27 +241,174 @@ test nameCast { /// to dense indices. This type does no dynamic allocation and /// can be copied by value. pub fn EnumSet(comptime E: type) type { - const mixin = struct { - fn EnumSetExt(comptime Self: type) type { - const Indexer = Self.Indexer; - return struct { - /// Initializes the set using a struct of bools - 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 struct { + const Self = @This(); + + /// The indexing rules for converting between keys and indices. + pub const Indexer = EnumIndexer(E); + /// The element type for this set. + pub const Key = Indexer.Key; + + const BitSet = std.StaticBitSet(Indexer.count); + + /// The maximum number of items in this set. + pub const len = Indexer.count; + + bits: BitSet = BitSet.initEmpty(), + + /// Initializes the set using a struct of bools + pub fn init(init_values: EnumFieldStruct(E, bool, false)) Self { + var result: Self = .{}; + inline for (0..Self.len) |i| { + const key = comptime Indexer.keyForIndex(i); + const tag = @tagName(key); + if (@field(init_values, tag)) { + result.bits.set(i); } - }; + } + return result; } + + /// Returns a set containing no keys. + pub fn initEmpty() Self { + return .{ .bits = BitSet.initEmpty() }; + } + + /// Returns a set containing all possible keys. + pub fn initFull() Self { + return .{ .bits = BitSet.initFull() }; + } + + /// Returns a set containing multiple keys. + pub fn initMany(keys: []const Key) Self { + var set = initEmpty(); + for (keys) |key| set.insert(key); + return set; + } + + /// Returns a set containing a single key. + pub fn initOne(key: Key) Self { + return initMany(&[_]Key{key}); + } + + /// Returns the number of keys in the set. + pub fn count(self: Self) usize { + return self.bits.count(); + } + + /// Checks if a key is in the set. + pub fn contains(self: Self, key: Key) bool { + return self.bits.isSet(Indexer.indexOf(key)); + } + + /// Puts a key in the set. + pub fn insert(self: *Self, key: Key) void { + self.bits.set(Indexer.indexOf(key)); + } + + /// Removes a key from the set. + pub fn remove(self: *Self, key: Key) void { + self.bits.unset(Indexer.indexOf(key)); + } + + /// Changes the presence of a key in the set to match the passed bool. + pub fn setPresent(self: *Self, key: Key, present: bool) void { + self.bits.setValue(Indexer.indexOf(key), present); + } + + /// Toggles the presence of a key in the set. If the key is in + /// the set, removes it. Otherwise adds it. + pub fn toggle(self: *Self, key: Key) void { + self.bits.toggle(Indexer.indexOf(key)); + } + + /// Toggles the presence of all keys in the passed set. + pub fn toggleSet(self: *Self, other: Self) void { + self.bits.toggleSet(other.bits); + } + + /// Toggles all possible keys in the set. + pub fn toggleAll(self: *Self) void { + self.bits.toggleAll(); + } + + /// Adds all keys in the passed set to this set. + pub fn setUnion(self: *Self, other: Self) void { + self.bits.setUnion(other.bits); + } + + /// Removes all keys which are not in the passed set. + pub fn setIntersection(self: *Self, other: Self) void { + self.bits.setIntersection(other.bits); + } + + /// Returns true iff both sets have the same keys. + pub fn eql(self: Self, other: Self) bool { + return self.bits.eql(other.bits); + } + + /// Returns true iff all the keys in this set are + /// in the other set. The other set may have keys + /// not found in this set. + pub fn subsetOf(self: Self, other: Self) bool { + return self.bits.subsetOf(other.bits); + } + + /// Returns true iff this set contains all the keys + /// in the other set. This set may have keys not + /// found in the other set. + pub fn supersetOf(self: Self, other: Self) bool { + return self.bits.supersetOf(other.bits); + } + + /// Returns a set with all the keys not in this set. + pub fn complement(self: Self) Self { + return .{ .bits = self.bits.complement() }; + } + + /// Returns a set with keys that are in either this + /// set or the other set. + pub fn unionWith(self: Self, other: Self) Self { + return .{ .bits = self.bits.unionWith(other.bits) }; + } + + /// Returns a set with keys that are in both this + /// set and the other set. + pub fn intersectWith(self: Self, other: Self) Self { + return .{ .bits = self.bits.intersectWith(other.bits) }; + } + + /// Returns a set with keys that are in either this + /// set or the other set, but not both. + pub fn xorWith(self: Self, other: Self) Self { + return .{ .bits = self.bits.xorWith(other.bits) }; + } + + /// Returns a set with keys that are in this set + /// except for keys in the other set. + pub fn differenceWith(self: Self, other: Self) Self { + return .{ .bits = self.bits.differenceWith(other.bits) }; + } + + /// Returns an iterator over this set, which iterates in + /// index order. Modifications to the set during iteration + /// may or may not be observed by the iterator, but will + /// not invalidate it. + 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; + } + }; }; - return IndexedSet(EnumIndexer(E), mixin.EnumSetExt); } /// A map keyed by an enum, backed by a bitfield and a dense array. @@ -269,58 +416,211 @@ pub fn EnumSet(comptime E: type) type { /// enum values to dense indices. This type does no dynamic /// allocation and can be copied by value. 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 { - /// Initializes the map using a sparse struct of optionals - 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; - } - /// Initializes a full mapping with all keys set to value. - /// Consider using EnumArray instead if the map will remain full. - pub fn initFull(value: V) Self { - var result = Self{ - .bits = Self.BitSet.initFull(), - .values = undefined, - }; - @memset(&result.values, value); - return result; - } - /// Initializes a full mapping with supplied values. - /// Consider using EnumArray instead if the map will remain full. - pub fn initFullWith(init_values: EnumFieldStruct(E, V, @as(?V, null))) Self { - return initFullWithDefault(@as(?V, null), init_values); - } - /// Initializes a full mapping with a provided default. - /// Consider using EnumArray instead if the map will remain full. - 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 struct { + const Self = @This(); + + /// The index mapping for this map + pub const Indexer = EnumIndexer(E); + /// The key type used to index this map + pub const Key = Indexer.Key; + /// The value type stored in this map + pub const Value = V; + /// The number of possible keys in the map + pub const len = Indexer.count; + + const BitSet = std.StaticBitSet(Indexer.count); + + /// Bits determining whether items are in the map + bits: BitSet = BitSet.initEmpty(), + /// Values of items in the map. If the associated + /// bit is zero, the value is undefined. + values: [Indexer.count]Value = undefined, + + /// Initializes the map using a sparse struct of optionals + pub fn init(init_values: EnumFieldStruct(E, ?Value, null)) Self { + var result: Self = .{}; + inline for (0..Self.len) |i| { + const key = comptime Indexer.keyForIndex(i); + const tag = @tagName(key); + if (@field(init_values, tag)) |*v| { + result.bits.set(i); + result.values[i] = v.*; } + } + } + + /// Initializes a full mapping with all keys set to value. + /// Consider using EnumArray instead if the map will remain full. + pub fn initFull(value: Value) Self { + var result: Self = .{ + .bits = Self.BitSet.initFull(), + .values = undefined, + }; + @memset(&result.values, value); + return result; + } + + /// Initializes a full mapping with supplied values. + /// Consider using EnumArray instead if the map will remain full. + pub fn initFullWith(init_values: EnumFieldStruct(E, Value, null)) Self { + return initFullWithDefault(null, init_values); + } + + /// Initializes a full mapping with a provided default. + /// Consider using EnumArray instead if the map will remain full. + pub fn initFullWithDefault(comptime default: ?Value, init_values: EnumFieldStruct(E, Value, default)) Self { + var result: Self = .{ + .bits = Self.BitSet.initFull(), + .values = undefined, + }; + inline for (0..Self.len) |i| { + const key = comptime Indexer.keyForIndex(i); + const tag = @tagName(key); + result.values[i] = @field(init_values, tag); + } + return result; + } + + /// The number of items in the map. + pub fn count(self: Self) usize { + return self.bits.count(); + } + + /// Checks if the map contains an item. + pub fn contains(self: Self, key: Key) bool { + return self.bits.isSet(Indexer.indexOf(key)); + } + + /// Gets the value associated with a key. + /// If the key is not in the map, returns null. + pub fn get(self: Self, key: Key) ?Value { + const index = Indexer.indexOf(key); + return if (self.bits.isSet(index)) self.values[index] else null; + } + + /// Gets the value associated with a key, which must + /// exist in the map. + pub fn getAssertContains(self: Self, key: Key) Value { + const index = Indexer.indexOf(key); + assert(self.bits.isSet(index)); + return self.values[index]; + } + + /// Gets the address of the value associated with a key. + /// If the key is not in the map, returns null. + pub fn getPtr(self: *Self, key: Key) ?*Value { + const index = Indexer.indexOf(key); + return if (self.bits.isSet(index)) &self.values[index] else null; + } + + /// Gets the address of the const value associated with a key. + /// If the key is not in the map, returns 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; + } + + /// Gets the address of the value associated with a key. + /// The key must be present in the map. + pub fn getPtrAssertContains(self: *Self, key: Key) *Value { + const index = Indexer.indexOf(key); + assert(self.bits.isSet(index)); + return &self.values[index]; + } + + /// Gets the address of the const value associated with a key. + /// The key must be present in the map. + pub fn getPtrConstAssertContains(self: *const Self, key: Key) *const Value { + const index = Indexer.indexOf(key); + assert(self.bits.isSet(index)); + return &self.values[index]; + } + + /// Adds the key to the map with the supplied value. + /// If the key is already in the map, overwrites the value. + pub fn put(self: *Self, key: Key, value: Value) void { + const index = Indexer.indexOf(key); + self.bits.set(index); + self.values[index] = value; + } + + /// Adds the key to the map with an undefined value. + /// If the key is already in the map, the value becomes undefined. + /// A pointer to the value is returned, which should be + /// used to initialize the 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]; + } + + /// Sets the value associated with the key in the map, + /// and returns the old value. If the key was not in + /// the map, returns null. + 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; + } + + /// Removes a key from the map. If the key was not in the map, + /// does nothing. + pub fn remove(self: *Self, key: Key) void { + const index = Indexer.indexOf(key); + self.bits.unset(index); + self.values[index] = undefined; + } + + /// Removes a key from the map, and returns the old value. + /// If the key was not in the map, returns null. + 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; + } + + /// Returns an iterator over the map, which visits items in index order. + /// Modifications to the underlying map may or may not be observed by + /// the iterator, but will not invalidate it. + pub fn iterator(self: *Self) Iterator { + return .{ + .inner = self.bits.iterator(.{}), + .values = &self.values, }; } + + /// An entry in the map. + pub const Entry = struct { + /// The key associated with this entry. + /// Modifying this key will not change the map. + key: Key, + + /// A pointer to the value in the map associated + /// with this key. Modifications through this + /// pointer will modify the underlying data. + 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; + } + }; }; - return IndexedMap(EnumIndexer(E), V, mixin.EnumMapExt); } /// A multiset of enum elements up to a count of usize. Backed @@ -724,197 +1024,98 @@ test EnumMultiset { /// enum values to dense indices. This type does no dynamic /// allocation and can be copied by value. 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 { - /// Initializes all values in the enum array - pub fn init(init_values: EnumFieldStruct(E, V, @as(?V, null))) Self { - return initDefault(@as(?V, null), init_values); - } - - /// Initializes values in the enum array, with the specified default. - 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); -} - -fn NoExtension(comptime Self: type) type { - _ = Self; - return NoExt; -} -const NoExt = struct {}; - -/// A set type with an Indexer mapping from keys to indices. -/// Presence or absence is stored as a dense bitfield. This -/// type does no allocation and can be copied by value. -pub fn IndexedSet(comptime I: type, comptime Ext: ?fn (type) type) type { - comptime ensureIndexer(I); return struct { const Self = @This(); - pub usingnamespace (Ext orelse NoExtension)(Self); - - /// The indexing rules for converting between keys and indices. - pub const Indexer = I; - /// The element type for this set. + /// The index mapping for this map + pub const Indexer = EnumIndexer(E); + /// The key type used to index this map pub const Key = Indexer.Key; - - const BitSet = std.StaticBitSet(Indexer.count); - - /// The maximum number of items in this set. + /// The value type stored in this map + pub const Value = V; + /// The number of possible keys in the map pub const len = Indexer.count; - bits: BitSet = BitSet.initEmpty(), + values: [Indexer.count]Value, - /// Returns a set containing no keys. - pub fn initEmpty() Self { - return .{ .bits = BitSet.initEmpty() }; + pub fn init(init_values: EnumFieldStruct(E, Value, null)) Self { + return initDefault(null, init_values); } - /// Returns a set containing all possible keys. - pub fn initFull() Self { - return .{ .bits = BitSet.initFull() }; + /// Initializes values in the enum array, with the specified default. + pub fn initDefault(comptime default: ?Value, init_values: EnumFieldStruct(E, Value, default)) Self { + var result: Self = .{ .values = undefined }; + inline for (0..Self.len) |i| { + const key = comptime Indexer.keyForIndex(i); + const tag = @tagName(key); + result.values[i] = @field(init_values, tag); + } + return result; } - /// Returns a set containing multiple keys. - pub fn initMany(keys: []const Key) Self { - var set = initEmpty(); - for (keys) |key| set.insert(key); - return set; + pub fn initUndefined() Self { + return Self{ .values = undefined }; } - /// Returns a set containing a single key. - pub fn initOne(key: Key) Self { - return initMany(&[_]Key{key}); + pub fn initFill(v: Value) Self { + var self: Self = undefined; + @memset(&self.values, v); + return self; } - /// Returns the number of keys in the set. - pub fn count(self: Self) usize { - return self.bits.count(); + /// Returns the value in the array associated with a key. + pub fn get(self: Self, key: Key) Value { + return self.values[Indexer.indexOf(key)]; } - /// Checks if a key is in the set. - pub fn contains(self: Self, key: Key) bool { - return self.bits.isSet(Indexer.indexOf(key)); + /// Returns a pointer to the slot in the array associated with a key. + pub fn getPtr(self: *Self, key: Key) *Value { + return &self.values[Indexer.indexOf(key)]; } - /// Puts a key in the set. - pub fn insert(self: *Self, key: Key) void { - self.bits.set(Indexer.indexOf(key)); + /// Returns a const pointer to the slot in the array associated with a key. + pub fn getPtrConst(self: *const Self, key: Key) *const Value { + return &self.values[Indexer.indexOf(key)]; } - /// Removes a key from the set. - pub fn remove(self: *Self, key: Key) void { - self.bits.unset(Indexer.indexOf(key)); + /// Sets the value in the slot associated with a key. + pub fn set(self: *Self, key: Key, value: Value) void { + self.values[Indexer.indexOf(key)] = value; } - /// Changes the presence of a key in the set to match the passed bool. - pub fn setPresent(self: *Self, key: Key, present: bool) void { - self.bits.setValue(Indexer.indexOf(key), present); + /// Iterates over the items in the array, in index order. + pub fn iterator(self: *Self) Iterator { + return .{ + .values = &self.values, + }; } - /// Toggles the presence of a key in the set. If the key is in - /// the set, removes it. Otherwise adds it. - pub fn toggle(self: *Self, key: Key) void { - self.bits.toggle(Indexer.indexOf(key)); - } + /// An entry in the array. + pub const Entry = struct { + /// The key associated with this entry. + /// Modifying this key will not change the array. + key: Key, - /// Toggles the presence of all keys in the passed set. - pub fn toggleSet(self: *Self, other: Self) void { - self.bits.toggleSet(other.bits); - } - - /// Toggles all possible keys in the set. - pub fn toggleAll(self: *Self) void { - self.bits.toggleAll(); - } - - /// Adds all keys in the passed set to this set. - pub fn setUnion(self: *Self, other: Self) void { - self.bits.setUnion(other.bits); - } - - /// Removes all keys which are not in the passed set. - pub fn setIntersection(self: *Self, other: Self) void { - self.bits.setIntersection(other.bits); - } - - /// Returns true iff both sets have the same keys. - pub fn eql(self: Self, other: Self) bool { - return self.bits.eql(other.bits); - } - - /// Returns true iff all the keys in this set are - /// in the other set. The other set may have keys - /// not found in this set. - pub fn subsetOf(self: Self, other: Self) bool { - return self.bits.subsetOf(other.bits); - } - - /// Returns true iff this set contains all the keys - /// in the other set. This set may have keys not - /// found in the other set. - pub fn supersetOf(self: Self, other: Self) bool { - return self.bits.supersetOf(other.bits); - } - - /// Returns a set with all the keys not in this set. - pub fn complement(self: Self) Self { - return .{ .bits = self.bits.complement() }; - } - - /// Returns a set with keys that are in either this - /// set or the other set. - pub fn unionWith(self: Self, other: Self) Self { - return .{ .bits = self.bits.unionWith(other.bits) }; - } - - /// Returns a set with keys that are in both this - /// set and the other set. - pub fn intersectWith(self: Self, other: Self) Self { - return .{ .bits = self.bits.intersectWith(other.bits) }; - } - - /// Returns a set with keys that are in either this - /// set or the other set, but not both. - pub fn xorWith(self: Self, other: Self) Self { - return .{ .bits = self.bits.xorWith(other.bits) }; - } - - /// Returns a set with keys that are in this set - /// except for keys in the other set. - pub fn differenceWith(self: Self, other: Self) Self { - return .{ .bits = self.bits.differenceWith(other.bits) }; - } - - /// Returns an iterator over this set, which iterates in - /// index order. Modifications to the set during iteration - /// may or may not be observed by the iterator, but will - /// not invalidate it. - pub fn iterator(self: *const Self) Iterator { - return .{ .inner = self.bits.iterator(.{}) }; - } + /// A pointer to the value in the array associated + /// with this key. Modifications through this + /// pointer will modify the underlying data. + value: *Value, + }; pub const Iterator = struct { - inner: BitSet.Iterator(.{}), + index: usize = 0, + values: *[Indexer.count]Value, - pub fn next(self: *Iterator) ?Key { - return if (self.inner.next()) |index| - Indexer.keyForIndex(index) - else - null; + 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; } }; }; @@ -1012,305 +1213,6 @@ test "EnumSet const iterator" { try testing.expect(result.eql(diag_move)); } -/// A map from keys to values, using an index lookup. Uses a -/// bitfield to track presence and a dense array of values. -/// This type does no allocation and can be copied by value. -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 orelse NoExtension)(Self); - - /// The index mapping for this map - pub const Indexer = I; - /// The key type used to index this map - pub const Key = Indexer.Key; - /// The value type stored in this map - pub const Value = V; - /// The number of possible keys in the map - pub const len = Indexer.count; - - const BitSet = std.StaticBitSet(Indexer.count); - - /// Bits determining whether items are in the map - bits: BitSet = BitSet.initEmpty(), - /// Values of items in the map. If the associated - /// bit is zero, the value is undefined. - values: [Indexer.count]Value = undefined, - - /// The number of items in the map. - pub fn count(self: Self) usize { - return self.bits.count(); - } - - /// Checks if the map contains an item. - pub fn contains(self: Self, key: Key) bool { - return self.bits.isSet(Indexer.indexOf(key)); - } - - /// Gets the value associated with a key. - /// If the key is not in the map, returns null. - pub fn get(self: Self, key: Key) ?Value { - const index = Indexer.indexOf(key); - return if (self.bits.isSet(index)) self.values[index] else null; - } - - /// Gets the value associated with a key, which must - /// exist in the map. - pub fn getAssertContains(self: Self, key: Key) Value { - const index = Indexer.indexOf(key); - assert(self.bits.isSet(index)); - return self.values[index]; - } - - /// Gets the address of the value associated with a key. - /// If the key is not in the map, returns null. - pub fn getPtr(self: *Self, key: Key) ?*Value { - const index = Indexer.indexOf(key); - return if (self.bits.isSet(index)) &self.values[index] else null; - } - - /// Gets the address of the const value associated with a key. - /// If the key is not in the map, returns 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; - } - - /// Gets the address of the value associated with a key. - /// The key must be present in the map. - pub fn getPtrAssertContains(self: *Self, key: Key) *Value { - const index = Indexer.indexOf(key); - assert(self.bits.isSet(index)); - return &self.values[index]; - } - - /// Gets the address of the const value associated with a key. - /// The key must be present in the map. - pub fn getPtrConstAssertContains(self: *const Self, key: Key) *const Value { - const index = Indexer.indexOf(key); - assert(self.bits.isSet(index)); - return &self.values[index]; - } - - /// Adds the key to the map with the supplied value. - /// If the key is already in the map, overwrites the value. - pub fn put(self: *Self, key: Key, value: Value) void { - const index = Indexer.indexOf(key); - self.bits.set(index); - self.values[index] = value; - } - - /// Adds the key to the map with an undefined value. - /// If the key is already in the map, the value becomes undefined. - /// A pointer to the value is returned, which should be - /// used to initialize the 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]; - } - - /// Sets the value associated with the key in the map, - /// and returns the old value. If the key was not in - /// the map, returns null. - 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; - } - - /// Removes a key from the map. If the key was not in the map, - /// does nothing. - pub fn remove(self: *Self, key: Key) void { - const index = Indexer.indexOf(key); - self.bits.unset(index); - self.values[index] = undefined; - } - - /// Removes a key from the map, and returns the old value. - /// If the key was not in the map, returns null. - 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; - } - - /// Returns an iterator over the map, which visits items in index order. - /// Modifications to the underlying map may or may not be observed by - /// the iterator, but will not invalidate it. - pub fn iterator(self: *Self) Iterator { - return .{ - .inner = self.bits.iterator(.{}), - .values = &self.values, - }; - } - - /// An entry in the map. - pub const Entry = struct { - /// The key associated with this entry. - /// Modifying this key will not change the map. - key: Key, - - /// A pointer to the value in the map associated - /// with this key. Modifications through this - /// pointer will modify the underlying data. - 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; - } - }; - }; -} - -/// A dense array of values, using an indexed lookup. -/// This type does no allocation and can be copied by value. -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 orelse NoExtension)(Self); - - /// The index mapping for this map - pub const Indexer = I; - /// The key type used to index this map - pub const Key = Indexer.Key; - /// The value type stored in this map - pub const Value = V; - /// The number of possible keys in the map - 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; - @memset(&self.values, v); - return self; - } - - /// Returns the value in the array associated with a key. - pub fn get(self: Self, key: Key) Value { - return self.values[Indexer.indexOf(key)]; - } - - /// Returns a pointer to the slot in the array associated with a key. - pub fn getPtr(self: *Self, key: Key) *Value { - return &self.values[Indexer.indexOf(key)]; - } - - /// Returns a const pointer to the slot in the array associated with a key. - pub fn getPtrConst(self: *const Self, key: Key) *const Value { - return &self.values[Indexer.indexOf(key)]; - } - - /// Sets the value in the slot associated with a key. - pub fn set(self: *Self, key: Key, value: Value) void { - self.values[Indexer.indexOf(key)] = value; - } - - /// Iterates over the items in the array, in index order. - pub fn iterator(self: *Self) Iterator { - return .{ - .values = &self.values, - }; - } - - /// An entry in the array. - pub const Entry = struct { - /// The key associated with this entry. - /// Modifying this key will not change the array. - key: Key, - - /// A pointer to the value in the array associated - /// with this key. Modifications through this - /// pointer will modify the underlying data. - 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; - } - }; - }; -} - -/// Verifies that a type is a valid Indexer, providing a helpful -/// compile error if not. An Indexer maps a comptime-known set -/// of keys to a dense set of zero-based indices. -/// The indexer interface must look like this: -/// ``` -/// struct { -/// /// The key type which this indexer converts to indices -/// pub const Key: type, -/// /// The number of indexes in the dense mapping -/// pub const count: comptime_int, -/// /// Converts from a key to an index -/// pub fn indexOf(Key) usize; -/// /// Converts from an index to a key -/// pub fn keyForIndex(usize) Key; -/// } -/// ``` -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: comptime_int."); - if (@TypeOf(T.count) != comptime_int) @compileError("Indexer.count must be a comptime_int."); - 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 ensureIndexer { - ensureIndexer(struct { - pub const Key = u32; - pub const count: comptime_int = 8; - pub fn indexOf(k: Key) usize { - return @as(usize, @intCast(k)); - } - pub fn keyForIndex(index: usize) Key { - return @as(Key, @intCast(index)); - } - }); -} - pub fn EnumIndexer(comptime E: type) type { if (!@typeInfo(E).Enum.is_exhaustive) { const BackingInt = @typeInfo(E).Enum.tag_type; @@ -1438,7 +1340,6 @@ test "EnumIndexer non-exhaustive" { _, }; const Indexer = EnumIndexer(E); - ensureIndexer(Indexer); const min_tag: E = @enumFromInt(std.math.minInt(BackingInt)); const max_tag: E = @enumFromInt(std.math.maxInt(BackingInt)); @@ -1466,7 +1367,6 @@ test "EnumIndexer non-exhaustive" { test "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(3, Indexer.count); @@ -1482,7 +1382,6 @@ test "EnumIndexer dense zeroed" { test "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(3, Indexer.count); @@ -1498,7 +1397,6 @@ test "EnumIndexer dense positive" { test "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(3, Indexer.count); @@ -1514,7 +1412,6 @@ test "EnumIndexer dense negative" { test "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(3, Indexer.count); @@ -1530,7 +1427,6 @@ test "EnumIndexer sparse" { test "EnumIndexer empty" { const E = enum {}; const Indexer = EnumIndexer(E); - ensureIndexer(Indexer); try testing.expectEqual(E, Indexer.Key); try testing.expectEqual(0, Indexer.count); }