blob: 692800b1f03dc081d85c725a559cfa77695fc04a [file] [log] [blame]
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library dart2js.util.enumset;
import 'dart:collection';
import 'package:meta/meta.dart';
extension<E extends Enum> on E {
int get mask {
assert(index < 64);
return 1 << index;
}
}
int _fold<E extends Enum>(Iterable<E> values) =>
values.fold(0, (acc, e) => acc | e.mask);
/// A set of enum values based on a bit mask of the shifted enum indices. The
/// enum indices used must be less than 64.
extension type const EnumSet<E extends Enum>(int mask) {
/// Creates an empty set.
const EnumSet.empty() : mask = 0;
/// Creates a singleton set containing [value].
EnumSet.fromValue(E value) : mask = value.mask;
/// Creates a set containing [values].
EnumSet.fromValues(Iterable<E> values) : mask = _fold(values);
/// Returns a set containing all enum values in [this] as well as [enumValue].
@useResult
EnumSet<E> operator +(E enumValue) => EnumSet(mask | enumValue.mask);
/// Returns a set containing all enum values in [this] except for [enumValue].
@useResult
EnumSet<E> operator -(E enumValue) => EnumSet(mask & ~enumValue.mask);
/// Returns a set with the bit for [value] enabled or disabled depending on
/// [state].
@useResult
EnumSet<E> update(E value, bool state) => state ? this + value : this - value;
/// Returns a new set containing all values in both this and the [other] set.
@useResult
EnumSet<E> intersection(EnumSet<E> other) {
return EnumSet(mask & other.mask);
}
/// Returns a new set containing all values either in this set or in the
/// [other] set.
@useResult
EnumSet<E> union(EnumSet<E> other) {
return EnumSet(mask | other.mask);
}
/// Returns a new set containing all values in this set that are not in the
/// [other] set.
@useResult
EnumSet<E> setMinus(EnumSet<E> other) {
return EnumSet(mask & ~other.mask);
}
/// Returns `true` if [enumValue] is in this set.
bool contains(E enumValue) {
return (mask & enumValue.mask) != 0;
}
/// Returns `true` if this and [other] have any elements in common.
bool intersects(EnumSet<E> other) {
return (mask & other.mask) != 0;
}
/// Returns `true` if this set is empty.
bool get isEmpty => mask == 0;
/// Returns `true` if this set is not empty.
bool get isNotEmpty => mask != 0;
/// Returns an [Iterable] of the values is in this set using [values] to
/// convert the stored indices to enum values.
///
/// The method is typically called with the `values` property of the enum
/// class as argument:
///
/// EnumSet<EnumClass> set = ...
/// Iterable<EnumClass> iterable = set.iterable(EnumClass.values);
///
Iterable<E> iterable(List<E> values) {
// We may store extra data in the bits unused by the enum values, but that
// will result in iteration attempting to look up enum values out of bounds.
// We can avoid this by masking off such bits.
return _EnumSetIterable(mask & ((1 << values.length) - 1), values);
}
}
class _EnumSetIterable<E extends Enum> extends IterableBase<E> {
final int _mask;
final List<E> _values;
_EnumSetIterable(this._mask, this._values);
@override
Iterator<E> get iterator => _EnumSetIterator(_mask, _values);
}
class _EnumSetIterator<E extends Enum> implements Iterator<E> {
int _mask;
final List<E> _values;
E? _current;
_EnumSetIterator(this._mask, this._values);
@override
E get current => _current!;
@override
bool moveNext() {
if (_mask == 0) {
_current = null;
return false;
}
final value = _current = _values[_mask.bitLength - 1];
_mask &= ~value.mask;
return true;
}
}