Commit 96ae451bbe
Changed files (4)
lib
lib/std/meta/trait.zig
@@ -408,6 +408,84 @@ test "std.meta.trait.isTuple" {
testing.expect(isTuple(@TypeOf(t3)));
}
+/// Returns true if the passed type will coerce to []const u8.
+/// Any of the following are considered strings:
+/// ```
+/// []const u8, [:S]const u8, *const [N]u8, *const [N:S]u8,
+/// []u8, [:S]u8, *[:S]u8, *[N:S]u8.
+/// ```
+/// These types are not considered strings:
+/// ```
+/// u8, [N]u8, [*]const u8, [*:0]const u8,
+/// [*]const [N]u8, []const u16, []const i8,
+/// *const u8, ?[]const u8, ?*const [N]u8.
+/// ```
+pub fn isZigString(comptime T: type) bool {
+ comptime {
+ // Only pointer types can be strings, no optionals
+ const info = @typeInfo(T);
+ if (info != .Pointer) return false;
+
+ const ptr = &info.Pointer;
+ // Check for CV qualifiers that would prevent coerction to []const u8
+ if (ptr.is_volatile or ptr.is_allowzero) return false;
+
+ // If it's already a slice, simple check.
+ if (ptr.size == .Slice) {
+ return ptr.child == u8;
+ }
+
+ // Otherwise check if it's an array type that coerces to slice.
+ if (ptr.size == .One) {
+ const child = @typeInfo(ptr.child);
+ if (child == .Array) {
+ const arr = &child.Array;
+ return arr.child == u8;
+ }
+ }
+
+ return false;
+ }
+}
+
+test "std.meta.trait.isZigString" {
+ testing.expect(isZigString([]const u8));
+ testing.expect(isZigString([]u8));
+ testing.expect(isZigString([:0]const u8));
+ testing.expect(isZigString([:0]u8));
+ testing.expect(isZigString([:5]const u8));
+ testing.expect(isZigString([:5]u8));
+ testing.expect(isZigString(*const [0]u8));
+ testing.expect(isZigString(*[0]u8));
+ testing.expect(isZigString(*const [0:0]u8));
+ testing.expect(isZigString(*[0:0]u8));
+ testing.expect(isZigString(*const [0:5]u8));
+ testing.expect(isZigString(*[0:5]u8));
+ testing.expect(isZigString(*const [10]u8));
+ testing.expect(isZigString(*[10]u8));
+ testing.expect(isZigString(*const [10:0]u8));
+ testing.expect(isZigString(*[10:0]u8));
+ testing.expect(isZigString(*const [10:5]u8));
+ testing.expect(isZigString(*[10:5]u8));
+
+ testing.expect(!isZigString(u8));
+ testing.expect(!isZigString([4]u8));
+ testing.expect(!isZigString([4:0]u8));
+ testing.expect(!isZigString([*]const u8));
+ testing.expect(!isZigString([*]const [4]u8));
+ testing.expect(!isZigString([*c]const u8));
+ testing.expect(!isZigString([*c]const [4]u8));
+ testing.expect(!isZigString([*:0]const u8));
+ testing.expect(!isZigString([*:0]const u8));
+ testing.expect(!isZigString(*[]const u8));
+ testing.expect(!isZigString(?[]const u8));
+ testing.expect(!isZigString(?*const [4]u8));
+ testing.expect(!isZigString([]allowzero u8));
+ testing.expect(!isZigString([]volatile u8));
+ testing.expect(!isZigString(*allowzero [4]u8));
+ testing.expect(!isZigString(*volatile [4]u8));
+}
+
pub fn hasDecls(comptime T: type, comptime names: anytype) bool {
inline for (names) |name| {
if (!@hasDecl(T, name))
lib/std/bit_set.zig
@@ -176,7 +176,7 @@ pub fn IntegerBitSet(comptime size: u16) type {
/// The default options (.{}) will iterate indices of set bits in
/// ascending order. Modifications to the underlying bit set may
/// or may not be observed by the iterator.
- pub fn iterator(self: *const Self, comptime options: IteratorOptions) Iterator(options.direction) {
+ pub fn iterator(self: *const Self, comptime options: IteratorOptions) Iterator(options) {
return .{
.bits_remain = switch (options.kind) {
.set => self.mask,
@@ -185,7 +185,11 @@ pub fn IntegerBitSet(comptime size: u16) type {
};
}
- fn Iterator(comptime direction: IteratorOptions.Direction) type {
+ pub fn Iterator(comptime options: IteratorOptions) type {
+ return SingleWordIterator(options.direction);
+ }
+
+ fn SingleWordIterator(comptime direction: IteratorOptions.Direction) type {
return struct {
const IterSelf = @This();
// all bits which have not yet been iterated over
@@ -425,8 +429,12 @@ pub fn ArrayBitSet(comptime MaskIntType: type, comptime size: usize) type {
/// The default options (.{}) will iterate indices of set bits in
/// ascending order. Modifications to the underlying bit set may
/// or may not be observed by the iterator.
- pub fn iterator(self: *const Self, comptime options: IteratorOptions) BitSetIterator(MaskInt, options) {
- return BitSetIterator(MaskInt, options).init(&self.masks, last_item_mask);
+ pub fn iterator(self: *const Self, comptime options: IteratorOptions) Iterator(options) {
+ return Iterator(options).init(&self.masks, last_item_mask);
+ }
+
+ pub fn Iterator(comptime options: IteratorOptions) type {
+ return BitSetIterator(MaskInt, options);
}
fn maskBit(index: usize) MaskInt {
@@ -700,11 +708,15 @@ pub const DynamicBitSetUnmanaged = struct {
/// ascending order. Modifications to the underlying bit set may
/// or may not be observed by the iterator. Resizing the underlying
/// bit set invalidates the iterator.
- pub fn iterator(self: *const Self, comptime options: IteratorOptions) BitSetIterator(MaskInt, options) {
+ pub fn iterator(self: *const Self, comptime options: IteratorOptions) Iterator(options) {
const num_masks = numMasks(self.bit_length);
const padding_bits = num_masks * @bitSizeOf(MaskInt) - self.bit_length;
const last_item_mask = (~@as(MaskInt, 0)) >> @intCast(ShiftInt, padding_bits);
- return BitSetIterator(MaskInt, options).init(self.masks[0..num_masks], last_item_mask);
+ return Iterator(options).init(self.masks[0..num_masks], last_item_mask);
+ }
+
+ pub fn Iterator(comptime options: IteratorOptions) type {
+ return BitSetIterator(MaskInt, options);
}
fn maskBit(index: usize) MaskInt {
@@ -858,9 +870,11 @@ pub const DynamicBitSet = struct {
/// ascending order. Modifications to the underlying bit set may
/// or may not be observed by the iterator. Resizing the underlying
/// bit set invalidates the iterator.
- pub fn iterator(self: *Self, comptime options: IteratorOptions) BitSetIterator(MaskInt, options) {
+ pub fn iterator(self: *Self, comptime options: IteratorOptions) Iterator(options) {
return self.unmanaged.iterator(options);
}
+
+ pub const Iterator = DynamicBitSetUnmanaged.Iterator;
};
/// Options for configuring an iterator over a bit set
lib/std/enums.zig
@@ -0,0 +1,1281 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! This module contains utilities and data structures for working with enums.
+
+const std = @import("std.zig");
+const assert = std.debug.assert;
+const testing = std.testing;
+const EnumField = std.builtin.TypeInfo.EnumField;
+
+/// Returns a struct with a field matching each unique named enum element.
+/// If the enum is extern and has multiple names for the same value, only
+/// the first name is used. Each field is of type Data and has the provided
+/// default, which may be undefined.
+pub fn EnumFieldStruct(comptime E: type, comptime Data: type, comptime field_default: ?Data) type {
+ const StructField = std.builtin.TypeInfo.StructField;
+ var fields: []const StructField = &[_]StructField{};
+ for (uniqueFields(E)) |field, i| {
+ fields = fields ++ &[_]StructField{.{
+ .name = field.name,
+ .field_type = Data,
+ .default_value = field_default,
+ .is_comptime = false,
+ .alignment = if (@sizeOf(Data) > 0) @alignOf(Data) else 0,
+ }};
+ }
+ return @Type(.{ .Struct = .{
+ .layout = .Auto,
+ .fields = fields,
+ .decls = &[_]std.builtin.TypeInfo.Declaration{},
+ .is_tuple = false,
+ }});
+}
+
+/// Looks up the supplied fields in the given enum type.
+/// Uses only the field names, field values are ignored.
+/// The result array is in the same order as the input.
+pub fn valuesFromFields(comptime E: type, comptime fields: []const EnumField) []const E {
+ comptime {
+ var result: [fields.len]E = undefined;
+ for (fields) |f, i| {
+ result[i] = @field(E, f.name);
+ }
+ return &result;
+ }
+}
+
+test "std.enums.valuesFromFields" {
+ const E = extern enum { a, b, c, d = 0 };
+ const fields = valuesFromFields(E, &[_]EnumField{
+ .{ .name = "b", .value = undefined },
+ .{ .name = "a", .value = undefined },
+ .{ .name = "a", .value = undefined },
+ .{ .name = "d", .value = undefined },
+ });
+ testing.expectEqual(E.b, fields[0]);
+ testing.expectEqual(E.a, fields[1]);
+ testing.expectEqual(E.d, fields[2]); // a == d
+ testing.expectEqual(E.d, fields[3]);
+}
+
+/// Returns the set of all named values in the given enum, in
+/// declaration order.
+pub fn values(comptime E: type) []const E {
+ return comptime valuesFromFields(E, @typeInfo(E).Enum.fields);
+}
+
+test "std.enum.values" {
+ const E = extern enum { a, b, c, d = 0 };
+ testing.expectEqualSlices(E, &.{.a, .b, .c, .d}, values(E));
+}
+
+/// Returns the set of all unique named values in the given enum, in
+/// declaration order. For repeated values in extern enums, only the
+/// first name for each value is included.
+pub fn uniqueValues(comptime E: type) []const E {
+ return comptime valuesFromFields(E, uniqueFields(E));
+}
+
+test "std.enum.uniqueValues" {
+ const E = extern enum { a, b, c, d = 0, e, f = 3 };
+ testing.expectEqualSlices(E, &.{.a, .b, .c, .f}, uniqueValues(E));
+
+ const F = enum { a, b, c };
+ testing.expectEqualSlices(F, &.{.a, .b, .c}, uniqueValues(F));
+}
+
+/// Returns the set of all unique field values in the given enum, in
+/// declaration order. For repeated values in extern enums, only the
+/// first name for each value is included.
+pub fn uniqueFields(comptime E: type) []const EnumField {
+ comptime {
+ const info = @typeInfo(E).Enum;
+ const raw_fields = info.fields;
+ // Only extern enums can contain duplicates,
+ // so fast path other types.
+ if (info.layout != .Extern) {
+ return raw_fields;
+ }
+
+ var unique_fields: []const EnumField = &[_]EnumField{};
+ outer:
+ for (raw_fields) |candidate| {
+ for (unique_fields) |u| {
+ if (u.value == candidate.value)
+ continue :outer;
+ }
+ unique_fields = unique_fields ++ &[_]EnumField{candidate};
+ }
+
+ return unique_fields;
+ }
+}
+
+/// Determines the length of a direct-mapped enum array, indexed by
+/// @intCast(usize, @enumToInt(enum_value)). The enum must be exhaustive.
+/// If the enum contains any fields with values that cannot be represented
+/// by usize, a compile error is issued. The max_unused_slots parameter limits
+/// the total number of items which have no matching enum key (holes in the enum
+/// numbering). So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots
+/// must be at least 3, to allow unused slots 0, 3, and 4.
+fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int) comptime_int {
+ const info = @typeInfo(E).Enum;
+ if (!info.is_exhaustive) {
+ @compileError("Cannot create direct array of non-exhaustive enum "++@typeName(E));
+ }
+
+ var max_value: comptime_int = -1;
+ const max_usize: comptime_int = ~@as(usize, 0);
+ const fields = uniqueFields(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;
+}
+
+/// Initializes an array of Data which can be indexed by
+/// @intCast(usize, @enumToInt(enum_value)). The enum must be exhaustive.
+/// If the enum contains any fields with values that cannot be represented
+/// by usize, a compile error is issued. The max_unused_slots parameter limits
+/// the total number of items which have no matching enum key (holes in the enum
+/// numbering). So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots
+/// must be at least 3, to allow unused slots 0, 3, and 4.
+/// The init_values parameter must be a struct with field names that match the enum values.
+/// If the enum has multiple fields with the same value, the name of the first one must
+/// be used.
+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,
+ });
+
+ testing.expectEqual([7]bool, @TypeOf(array));
+ testing.expectEqual(true, array[4]);
+ testing.expectEqual(false, array[6]);
+ testing.expectEqual(true, array[2]);
+}
+
+/// Initializes an array of Data which can be indexed by
+/// @intCast(usize, @enumToInt(enum_value)). The enum must be exhaustive.
+/// If the enum contains any fields with values that cannot be represented
+/// by usize, a compile error is issued. The max_unused_slots parameter limits
+/// the total number of items which have no matching enum key (holes in the enum
+/// numbering). So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots
+/// must be at least 3, to allow unused slots 0, 3, and 4.
+/// The init_values parameter must be a struct with field names that match the enum values.
+/// If the enum has multiple fields with the same value, the name of the first one must
+/// be used.
+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, i| {
+ 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,
+ });
+
+ testing.expectEqual([7]bool, @TypeOf(array));
+ testing.expectEqual(true, array[4]);
+ testing.expectEqual(false, array[6]);
+ testing.expectEqual(false, array[2]);
+}
+
+/// Cast an enum literal, value, or string to the enum value of type E
+/// with the same name.
+pub fn nameCast(comptime E: type, comptime value: anytype) E {
+ comptime {
+ const V = @TypeOf(value);
+ if (V == E) return 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)) {
+ return @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 { a = 0, b = 1 };
+ const B = enum { a = 1, b = 0 };
+ testing.expectEqual(A.a, nameCast(A, .a));
+ testing.expectEqual(A.a, nameCast(A, A.a));
+ testing.expectEqual(A.a, nameCast(A, B.a));
+ testing.expectEqual(A.a, nameCast(A, "a"));
+ testing.expectEqual(A.a, nameCast(A, @as(*const[1]u8, "a")));
+ testing.expectEqual(A.a, nameCast(A, @as([:0]const u8, "a")));
+ testing.expectEqual(A.a, nameCast(A, @as([]const u8, "a")));
+
+ testing.expectEqual(B.a, nameCast(B, .a));
+ testing.expectEqual(B.a, nameCast(B, A.a));
+ testing.expectEqual(B.a, nameCast(B, B.a));
+ testing.expectEqual(B.a, nameCast(B, "a"));
+
+ testing.expectEqual(B.b, nameCast(B, .b));
+ testing.expectEqual(B.b, nameCast(B, A.b));
+ testing.expectEqual(B.b, nameCast(B, B.b));
+ testing.expectEqual(B.b, nameCast(B, "b"));
+}
+
+/// A set of enum elements, backed by a bitfield. If the enum
+/// is not dense, a mapping will be constructed from enum values
+/// 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) {
+ comptime const key = Indexer.keyForIndex(i);
+ comptime const tag = @tagName(key);
+ if (@field(init_values, tag)) {
+ result.bits.set(i);
+ }
+ }
+ return result;
+ }
+ };
+ }
+ };
+ return IndexedSet(EnumIndexer(E), mixin.EnumSetExt);
+}
+
+/// A map keyed by an enum, backed by a bitfield and a dense array.
+/// If the enum is not dense, a mapping will be constructed from
+/// 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) {
+ comptime const key = Indexer.keyForIndex(i);
+ comptime const tag = @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,
+ };
+ std.mem.set(V, &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) {
+ comptime const key = Indexer.keyForIndex(i);
+ comptime const tag = @tagName(key);
+ result.values[i] = @field(init_values, tag);
+ }
+ return result;
+ }
+ };
+ }
+ };
+ return IndexedMap(EnumIndexer(E), V, mixin.EnumMapExt);
+}
+
+/// An array keyed by an enum, backed by a dense array.
+/// If the enum is not dense, a mapping will be constructed from
+/// 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);
+}
+
+/// Pass this function as the Ext parameter to Indexed* if you
+/// do not want to attach any extensions. This parameter was
+/// originally an optional, but optional generic functions
+/// seem to be broken at the moment.
+/// TODO: Once #8169 is fixed, consider switching this param
+/// back to an optional.
+pub fn NoExtension(comptime Self: type) type {
+ 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(Self);
+
+ /// The indexing rules for converting between keys and indices.
+ pub const Indexer = I;
+ /// 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(),
+
+ /// Returns a set containing all possible keys.
+ pub fn initFull() Self {
+ return .{ .bits = BitSet.initFull() };
+ }
+
+ /// 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 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: *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;
+ }
+ };
+ };
+}
+
+/// 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(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];
+ }
+
+ /// 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(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;
+ std.mem.set(Value, &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: usize,
+/// /// 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: 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 {
+ 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 = uniqueFields(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 { unreachable; }
+ pub fn keyForIndex(i: usize) E { unreachable; }
+ };
+ }
+ std.sort.sort(EnumField, &fields, {}, ascByValue);
+ const min = fields[0].value;
+ const max = fields[fields.len-1].value;
+ 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 {
+ // TODO fix addition semantics. This calculation
+ // gives up some safety to avoid artificially limiting
+ // the range of signed enum values to max_isize.
+ 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) |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{ b = 1, a = 0, c = 2 };
+ const Indexer = EnumIndexer(E);
+ ensureIndexer(Indexer);
+ testing.expectEqual(E, Indexer.Key);
+ testing.expectEqual(@as(usize, 3), Indexer.count);
+
+ testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
+ testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
+ testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
+
+ testing.expectEqual(E.a, Indexer.keyForIndex(0));
+ testing.expectEqual(E.b, Indexer.keyForIndex(1));
+ 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);
+ testing.expectEqual(E, Indexer.Key);
+ testing.expectEqual(@as(usize, 3), Indexer.count);
+
+ testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
+ testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
+ testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
+
+ testing.expectEqual(E.a, Indexer.keyForIndex(0));
+ testing.expectEqual(E.b, Indexer.keyForIndex(1));
+ 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);
+ testing.expectEqual(E, Indexer.Key);
+ testing.expectEqual(@as(usize, 3), Indexer.count);
+
+ testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
+ testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
+ testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
+
+ testing.expectEqual(E.a, Indexer.keyForIndex(0));
+ testing.expectEqual(E.b, Indexer.keyForIndex(1));
+ 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);
+ testing.expectEqual(E, Indexer.Key);
+ testing.expectEqual(@as(usize, 3), Indexer.count);
+
+ testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
+ testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
+ testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
+
+ testing.expectEqual(E.a, Indexer.keyForIndex(0));
+ testing.expectEqual(E.b, Indexer.keyForIndex(1));
+ testing.expectEqual(E.c, Indexer.keyForIndex(2));
+}
+
+test "std.enums.EnumIndexer repeats" {
+ const E = extern enum{ a = -2, c = 6, b = 4, b2 = 4 };
+ const Indexer = EnumIndexer(E);
+ ensureIndexer(Indexer);
+ testing.expectEqual(E, Indexer.Key);
+ testing.expectEqual(@as(usize, 3), Indexer.count);
+
+ testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
+ testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
+ testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));
+
+ testing.expectEqual(E.a, Indexer.keyForIndex(0));
+ testing.expectEqual(E.b, Indexer.keyForIndex(1));
+ testing.expectEqual(E.c, Indexer.keyForIndex(2));
+}
+
+test "std.enums.EnumSet" {
+ const E = extern enum { a, b, c, d, e = 0 };
+ const Set = EnumSet(E);
+ testing.expectEqual(E, Set.Key);
+ testing.expectEqual(EnumIndexer(E), Set.Indexer);
+ testing.expectEqual(@as(usize, 4), Set.len);
+
+ // Empty sets
+ const empty = Set{};
+ comptime testing.expect(empty.count() == 0);
+
+ var empty_b = Set.init(.{});
+ testing.expect(empty_b.count() == 0);
+
+ const empty_c = comptime Set.init(.{});
+ comptime testing.expect(empty_c.count() == 0);
+
+ const full = Set.initFull();
+ testing.expect(full.count() == Set.len);
+
+ const full_b = comptime Set.initFull();
+ comptime testing.expect(full_b.count() == Set.len);
+
+ testing.expectEqual(false, empty.contains(.a));
+ testing.expectEqual(false, empty.contains(.b));
+ testing.expectEqual(false, empty.contains(.c));
+ testing.expectEqual(false, empty.contains(.d));
+ testing.expectEqual(false, empty.contains(.e));
+ {
+ var iter = empty_b.iterator();
+ testing.expectEqual(@as(?E, null), iter.next());
+ }
+
+ var mut = Set.init(.{
+ .a=true, .c=true,
+ });
+ testing.expectEqual(@as(usize, 2), mut.count());
+ testing.expectEqual(true, mut.contains(.a));
+ testing.expectEqual(false, mut.contains(.b));
+ testing.expectEqual(true, mut.contains(.c));
+ testing.expectEqual(false, mut.contains(.d));
+ testing.expectEqual(true, mut.contains(.e)); // aliases a
+ {
+ var it = mut.iterator();
+ testing.expectEqual(@as(?E, .a), it.next());
+ testing.expectEqual(@as(?E, .c), it.next());
+ testing.expectEqual(@as(?E, null), it.next());
+ }
+
+ mut.toggleAll();
+ testing.expectEqual(@as(usize, 2), mut.count());
+ testing.expectEqual(false, mut.contains(.a));
+ testing.expectEqual(true, mut.contains(.b));
+ testing.expectEqual(false, mut.contains(.c));
+ testing.expectEqual(true, mut.contains(.d));
+ testing.expectEqual(false, mut.contains(.e)); // aliases a
+ {
+ var it = mut.iterator();
+ testing.expectEqual(@as(?E, .b), it.next());
+ testing.expectEqual(@as(?E, .d), it.next());
+ testing.expectEqual(@as(?E, null), it.next());
+ }
+
+ mut.toggleSet(Set.init(.{ .a=true, .b=true }));
+ testing.expectEqual(@as(usize, 2), mut.count());
+ testing.expectEqual(true, mut.contains(.a));
+ testing.expectEqual(false, mut.contains(.b));
+ testing.expectEqual(false, mut.contains(.c));
+ testing.expectEqual(true, mut.contains(.d));
+ testing.expectEqual(true, mut.contains(.e)); // aliases a
+
+ mut.setUnion(Set.init(.{ .a=true, .b=true }));
+ testing.expectEqual(@as(usize, 3), mut.count());
+ testing.expectEqual(true, mut.contains(.a));
+ testing.expectEqual(true, mut.contains(.b));
+ testing.expectEqual(false, mut.contains(.c));
+ testing.expectEqual(true, mut.contains(.d));
+
+ mut.remove(.c);
+ mut.remove(.b);
+ testing.expectEqual(@as(usize, 2), mut.count());
+ testing.expectEqual(true, mut.contains(.a));
+ testing.expectEqual(false, mut.contains(.b));
+ testing.expectEqual(false, mut.contains(.c));
+ testing.expectEqual(true, mut.contains(.d));
+
+ mut.setIntersection(Set.init(.{ .a=true, .b=true }));
+ testing.expectEqual(@as(usize, 1), mut.count());
+ testing.expectEqual(true, mut.contains(.a));
+ testing.expectEqual(false, mut.contains(.b));
+ testing.expectEqual(false, mut.contains(.c));
+ testing.expectEqual(false, mut.contains(.d));
+
+ mut.insert(.a);
+ mut.insert(.b);
+ testing.expectEqual(@as(usize, 2), mut.count());
+ testing.expectEqual(true, mut.contains(.a));
+ testing.expectEqual(true, mut.contains(.b));
+ testing.expectEqual(false, mut.contains(.c));
+ testing.expectEqual(false, mut.contains(.d));
+
+ mut.setPresent(.a, false);
+ mut.toggle(.b);
+ mut.toggle(.c);
+ mut.setPresent(.d, true);
+ testing.expectEqual(@as(usize, 2), mut.count());
+ testing.expectEqual(false, mut.contains(.a));
+ testing.expectEqual(false, mut.contains(.b));
+ testing.expectEqual(true, mut.contains(.c));
+ testing.expectEqual(true, mut.contains(.d));
+}
+
+test "std.enums.EnumArray void" {
+ const E = extern enum { a, b, c, d, e = 0 };
+ const ArrayVoid = EnumArray(E, void);
+ testing.expectEqual(E, ArrayVoid.Key);
+ testing.expectEqual(EnumIndexer(E), ArrayVoid.Indexer);
+ testing.expectEqual(void, ArrayVoid.Value);
+ testing.expectEqual(@as(usize, 4), ArrayVoid.len);
+
+ const undef = ArrayVoid.initUndefined();
+ var inst = ArrayVoid.initFill({});
+ const inst2 = ArrayVoid.init(.{ .a = {}, .b = {}, .c = {}, .d = {} });
+ const inst3 = ArrayVoid.initDefault({}, .{});
+
+ _ = inst.get(.a);
+ _ = inst.getPtr(.b);
+ _ = inst.getPtrConst(.c);
+ inst.set(.a, {});
+
+ var it = inst.iterator();
+ testing.expectEqual(E.a, it.next().?.key);
+ testing.expectEqual(E.b, it.next().?.key);
+ testing.expectEqual(E.c, it.next().?.key);
+ testing.expectEqual(E.d, it.next().?.key);
+ testing.expect(it.next() == null);
+}
+
+test "std.enums.EnumArray sized" {
+ const E = extern enum { a, b, c, d, e = 0 };
+ const Array = EnumArray(E, usize);
+ testing.expectEqual(E, Array.Key);
+ testing.expectEqual(EnumIndexer(E), Array.Indexer);
+ testing.expectEqual(usize, Array.Value);
+ testing.expectEqual(@as(usize, 4), Array.len);
+
+ const undef = Array.initUndefined();
+ var inst = Array.initFill(5);
+ const inst2 = Array.init(.{ .a = 1, .b = 2, .c = 3, .d = 4 });
+ const inst3 = Array.initDefault(6, .{.b = 4, .c = 2});
+
+ testing.expectEqual(@as(usize, 5), inst.get(.a));
+ testing.expectEqual(@as(usize, 5), inst.get(.b));
+ testing.expectEqual(@as(usize, 5), inst.get(.c));
+ testing.expectEqual(@as(usize, 5), inst.get(.d));
+
+ testing.expectEqual(@as(usize, 1), inst2.get(.a));
+ testing.expectEqual(@as(usize, 2), inst2.get(.b));
+ testing.expectEqual(@as(usize, 3), inst2.get(.c));
+ testing.expectEqual(@as(usize, 4), inst2.get(.d));
+
+ testing.expectEqual(@as(usize, 6), inst3.get(.a));
+ testing.expectEqual(@as(usize, 4), inst3.get(.b));
+ testing.expectEqual(@as(usize, 2), inst3.get(.c));
+ testing.expectEqual(@as(usize, 6), inst3.get(.d));
+
+ testing.expectEqual(&inst.values[0], inst.getPtr(.a));
+ testing.expectEqual(&inst.values[1], inst.getPtr(.b));
+ testing.expectEqual(&inst.values[2], inst.getPtr(.c));
+ testing.expectEqual(&inst.values[3], inst.getPtr(.d));
+
+ testing.expectEqual(@as(*const usize, &inst.values[0]), inst.getPtrConst(.a));
+ testing.expectEqual(@as(*const usize, &inst.values[1]), inst.getPtrConst(.b));
+ testing.expectEqual(@as(*const usize, &inst.values[2]), inst.getPtrConst(.c));
+ testing.expectEqual(@as(*const usize, &inst.values[3]), inst.getPtrConst(.d));
+
+ inst.set(.c, 8);
+ testing.expectEqual(@as(usize, 5), inst.get(.a));
+ testing.expectEqual(@as(usize, 5), inst.get(.b));
+ testing.expectEqual(@as(usize, 8), inst.get(.c));
+ testing.expectEqual(@as(usize, 5), inst.get(.d));
+
+ var it = inst.iterator();
+ const Entry = Array.Entry;
+ testing.expectEqual(@as(?Entry, Entry{
+ .key = .a,
+ .value = &inst.values[0],
+ }), it.next());
+ testing.expectEqual(@as(?Entry, Entry{
+ .key = .b,
+ .value = &inst.values[1],
+ }), it.next());
+ testing.expectEqual(@as(?Entry, Entry{
+ .key = .c,
+ .value = &inst.values[2],
+ }), it.next());
+ testing.expectEqual(@as(?Entry, Entry{
+ .key = .d,
+ .value = &inst.values[3],
+ }), it.next());
+ testing.expectEqual(@as(?Entry, null), it.next());
+}
+
+test "std.enums.EnumMap void" {
+ const E = extern enum { a, b, c, d, e = 0 };
+ const Map = EnumMap(E, void);
+ testing.expectEqual(E, Map.Key);
+ testing.expectEqual(EnumIndexer(E), Map.Indexer);
+ testing.expectEqual(void, Map.Value);
+ testing.expectEqual(@as(usize, 4), Map.len);
+
+ const b = Map.initFull({});
+ testing.expectEqual(@as(usize, 4), b.count());
+
+ const c = Map.initFullWith(.{ .a = {}, .b = {}, .c = {}, .d = {} });
+ testing.expectEqual(@as(usize, 4), c.count());
+
+ const d = Map.initFullWithDefault({}, .{ .b = {} });
+ testing.expectEqual(@as(usize, 4), d.count());
+
+ var a = Map.init(.{ .b = {}, .d = {} });
+ testing.expectEqual(@as(usize, 2), a.count());
+ testing.expectEqual(false, a.contains(.a));
+ testing.expectEqual(true, a.contains(.b));
+ testing.expectEqual(false, a.contains(.c));
+ testing.expectEqual(true, a.contains(.d));
+ testing.expect(a.get(.a) == null);
+ testing.expect(a.get(.b) != null);
+ testing.expect(a.get(.c) == null);
+ testing.expect(a.get(.d) != null);
+ testing.expect(a.getPtr(.a) == null);
+ testing.expect(a.getPtr(.b) != null);
+ testing.expect(a.getPtr(.c) == null);
+ testing.expect(a.getPtr(.d) != null);
+ testing.expect(a.getPtrConst(.a) == null);
+ testing.expect(a.getPtrConst(.b) != null);
+ testing.expect(a.getPtrConst(.c) == null);
+ testing.expect(a.getPtrConst(.d) != null);
+ _ = a.getPtrAssertContains(.b);
+ _ = a.getAssertContains(.d);
+
+ a.put(.a, {});
+ a.put(.a, {});
+ a.putUninitialized(.c).* = {};
+ a.putUninitialized(.c).* = {};
+
+ testing.expectEqual(@as(usize, 4), a.count());
+ testing.expect(a.get(.a) != null);
+ testing.expect(a.get(.b) != null);
+ testing.expect(a.get(.c) != null);
+ testing.expect(a.get(.d) != null);
+
+ a.remove(.a);
+ _ = a.fetchRemove(.c);
+
+ var iter = a.iterator();
+ const Entry = Map.Entry;
+ testing.expectEqual(E.b, iter.next().?.key);
+ testing.expectEqual(E.d, iter.next().?.key);
+ testing.expect(iter.next() == null);
+}
+
+test "std.enums.EnumMap sized" {
+ const E = extern enum { a, b, c, d, e = 0 };
+ const Map = EnumMap(E, usize);
+ testing.expectEqual(E, Map.Key);
+ testing.expectEqual(EnumIndexer(E), Map.Indexer);
+ testing.expectEqual(usize, Map.Value);
+ testing.expectEqual(@as(usize, 4), Map.len);
+
+ const b = Map.initFull(5);
+ testing.expectEqual(@as(usize, 4), b.count());
+ testing.expect(b.contains(.a));
+ testing.expect(b.contains(.b));
+ testing.expect(b.contains(.c));
+ testing.expect(b.contains(.d));
+ testing.expectEqual(@as(?usize, 5), b.get(.a));
+ testing.expectEqual(@as(?usize, 5), b.get(.b));
+ testing.expectEqual(@as(?usize, 5), b.get(.c));
+ testing.expectEqual(@as(?usize, 5), b.get(.d));
+
+ const c = Map.initFullWith(.{ .a = 1, .b = 2, .c = 3, .d = 4 });
+ testing.expectEqual(@as(usize, 4), c.count());
+ testing.expect(c.contains(.a));
+ testing.expect(c.contains(.b));
+ testing.expect(c.contains(.c));
+ testing.expect(c.contains(.d));
+ testing.expectEqual(@as(?usize, 1), c.get(.a));
+ testing.expectEqual(@as(?usize, 2), c.get(.b));
+ testing.expectEqual(@as(?usize, 3), c.get(.c));
+ testing.expectEqual(@as(?usize, 4), c.get(.d));
+
+ const d = Map.initFullWithDefault(6, .{ .b = 2, .c = 4 });
+ testing.expectEqual(@as(usize, 4), d.count());
+ testing.expect(d.contains(.a));
+ testing.expect(d.contains(.b));
+ testing.expect(d.contains(.c));
+ testing.expect(d.contains(.d));
+ testing.expectEqual(@as(?usize, 6), d.get(.a));
+ testing.expectEqual(@as(?usize, 2), d.get(.b));
+ testing.expectEqual(@as(?usize, 4), d.get(.c));
+ testing.expectEqual(@as(?usize, 6), d.get(.d));
+
+ var a = Map.init(.{ .b = 2, .d = 4 });
+ testing.expectEqual(@as(usize, 2), a.count());
+ testing.expectEqual(false, a.contains(.a));
+ testing.expectEqual(true, a.contains(.b));
+ testing.expectEqual(false, a.contains(.c));
+ testing.expectEqual(true, a.contains(.d));
+
+ testing.expectEqual(@as(?usize, null), a.get(.a));
+ testing.expectEqual(@as(?usize, 2), a.get(.b));
+ testing.expectEqual(@as(?usize, null), a.get(.c));
+ testing.expectEqual(@as(?usize, 4), a.get(.d));
+
+ testing.expectEqual(@as(?*usize, null), a.getPtr(.a));
+ testing.expectEqual(@as(?*usize, &a.values[1]), a.getPtr(.b));
+ testing.expectEqual(@as(?*usize, null), a.getPtr(.c));
+ testing.expectEqual(@as(?*usize, &a.values[3]), a.getPtr(.d));
+
+ testing.expectEqual(@as(?*const usize, null), a.getPtrConst(.a));
+ testing.expectEqual(@as(?*const usize, &a.values[1]), a.getPtrConst(.b));
+ testing.expectEqual(@as(?*const usize, null), a.getPtrConst(.c));
+ testing.expectEqual(@as(?*const usize, &a.values[3]), a.getPtrConst(.d));
+
+ testing.expectEqual(@as(*const usize, &a.values[1]), a.getPtrAssertContains(.b));
+ testing.expectEqual(@as(*const usize, &a.values[3]), a.getPtrAssertContains(.d));
+ testing.expectEqual(@as(usize, 2), a.getAssertContains(.b));
+ testing.expectEqual(@as(usize, 4), a.getAssertContains(.d));
+
+ a.put(.a, 3);
+ a.put(.a, 5);
+ a.putUninitialized(.c).* = 7;
+ a.putUninitialized(.c).* = 9;
+
+ testing.expectEqual(@as(usize, 4), a.count());
+ testing.expectEqual(@as(?usize, 5), a.get(.a));
+ testing.expectEqual(@as(?usize, 2), a.get(.b));
+ testing.expectEqual(@as(?usize, 9), a.get(.c));
+ testing.expectEqual(@as(?usize, 4), a.get(.d));
+
+ a.remove(.a);
+ testing.expectEqual(@as(?usize, null), a.fetchRemove(.a));
+ testing.expectEqual(@as(?usize, 9), a.fetchRemove(.c));
+ a.remove(.c);
+
+ var iter = a.iterator();
+ const Entry = Map.Entry;
+ testing.expectEqual(@as(?Entry, Entry{
+ .key = .b, .value = &a.values[1],
+ }), iter.next());
+ testing.expectEqual(@as(?Entry, Entry{
+ .key = .d, .value = &a.values[3],
+ }), iter.next());
+ testing.expectEqual(@as(?Entry, null), iter.next());
+}
lib/std/std.zig
@@ -20,6 +20,9 @@ pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringM
pub const DynLib = @import("dynamic_library.zig").DynLib;
pub const DynamicBitSet = bit_set.DynamicBitSet;
pub const DynamicBitSetUnmanaged = bit_set.DynamicBitSetUnmanaged;
+pub const EnumArray = enums.EnumArray;
+pub const EnumMap = enums.EnumMap;
+pub const EnumSet = enums.EnumSet;
pub const HashMap = hash_map.HashMap;
pub const HashMapUnmanaged = hash_map.HashMapUnmanaged;
pub const MultiArrayList = @import("multi_array_list.zig").MultiArrayList;
@@ -54,6 +57,7 @@ pub const cstr = @import("cstr.zig");
pub const debug = @import("debug.zig");
pub const dwarf = @import("dwarf.zig");
pub const elf = @import("elf.zig");
+pub const enums = @import("enums.zig");
pub const event = @import("event.zig");
pub const fifo = @import("fifo.zig");
pub const fmt = @import("fmt.zig");