#region Copyright & License Information /* * Copyright 2007-2021 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. For more * information, see COPYING. */ #endregion using System; using System.Collections; using System.Collections.Generic; namespace OpenRA.Primitives { static class LongBitSetAllocator where T : class { static readonly Cache Bits = new Cache(Allocate); static long allBits = 1; static long nextBits = 1; static long Allocate(string value) { lock (Bits) { var bits = nextBits; allBits |= bits; nextBits <<= 1; if (nextBits == 0) throw new OverflowException("Trying to allocate bit index outside of index 64."); return bits; } } public static long GetBits(string[] values) { long bits = 0; lock (Bits) foreach (var value in values) bits |= Bits[value]; return bits; } public static long GetBitsNoAlloc(string[] values) { // Map strings to existing bits; do not allocate missing values new bits long bits = 0; lock (Bits) foreach (var value in values) if (Bits.TryGetValue(value, out var valueBit)) bits |= valueBit; return bits; } public static IEnumerable GetStrings(long bits) { var values = new List(); lock (Bits) foreach (var kvp in Bits) if ((kvp.Value & bits) != 0) values.Add(kvp.Key); return values; } public static bool BitsContainString(long bits, string value) { long valueBit; lock (Bits) if (!Bits.TryGetValue(value, out valueBit)) return false; return (bits & valueBit) != 0; } public static void Reset() { lock (Bits) { Bits.Clear(); nextBits = 1; } } public static long Mask => allBits; } // Opitmized BitSet to be used only when guaranteed to be no more than 64 values. public readonly struct LongBitSet : IEnumerable, IEquatable> where T : class { readonly long bits; public LongBitSet(params string[] values) : this(LongBitSetAllocator.GetBits(values)) { } LongBitSet(long bits) { this.bits = bits; } public static LongBitSet FromStringsNoAlloc(string[] values) { return new LongBitSet(LongBitSetAllocator.GetBitsNoAlloc(values)) { }; } public static void Reset() { LongBitSetAllocator.Reset(); } public override string ToString() { return BitSetAllocator.GetStrings(bits).JoinWith(","); } public static bool operator ==(LongBitSet me, LongBitSet other) { return me.bits == other.bits; } public static bool operator !=(LongBitSet me, LongBitSet other) { return !(me == other); } public static LongBitSet operator ~(LongBitSet me) { return new LongBitSet(me.bits ^ LongBitSetAllocator.Mask); } public bool Equals(LongBitSet other) { return other == this; } public override bool Equals(object obj) { return obj is LongBitSet && Equals((LongBitSet)obj); } public override int GetHashCode() { return bits.GetHashCode(); } public bool IsEmpty => bits == 0; public bool IsProperSubsetOf(LongBitSet other) { return IsSubsetOf(other) && !SetEquals(other); } public bool IsProperSupersetOf(LongBitSet other) { return IsSupersetOf(other) && !SetEquals(other); } public bool IsSubsetOf(LongBitSet other) { return (bits | other.bits) == other.bits; } public bool IsSupersetOf(LongBitSet other) { return (bits | other.bits) == bits; } public bool Overlaps(LongBitSet other) { return (bits & other.bits) != 0; } public bool SetEquals(LongBitSet other) { return bits == other.bits; } public bool Contains(string value) { return BitSetAllocator.BitsContainString(bits, value); } public IEnumerator GetEnumerator() { return BitSetAllocator.GetStrings(bits).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public LongBitSet Except(LongBitSet other) { return new LongBitSet(bits & ~other.bits); } public LongBitSet Intersect(LongBitSet other) { return new LongBitSet(bits & other.bits); } public LongBitSet SymmetricExcept(LongBitSet other) { return new LongBitSet(bits ^ other.bits); } public LongBitSet Union(LongBitSet other) { return new LongBitSet(bits | other.bits); } } }