// Copyright (c) 2016 Daniel Grunwald // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons // to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. #nullable enable using System; using System.Collections.Generic; using System.Diagnostics; using System.Numerics; using System.Text; namespace ICSharpCode.Decompiler.Util { /// /// Improved version of BitArray /// public class BitSet { const int BitsPerWord = 64; const int Log2BitsPerWord = 6; const ulong Mask = 0xffffffffffffffffUL; readonly ulong[] words; static int WordIndex(int bitIndex) { Debug.Assert(bitIndex >= 0); return bitIndex >> Log2BitsPerWord; } /// /// Creates a new bitset, where initially all bits are zero. /// public BitSet(int capacity) { this.words = new ulong[Math.Max(1, WordIndex(capacity + BitsPerWord - 1))]; } private BitSet(ulong[] bits) { this.words = bits; } public BitSet Clone() { return new BitSet((ulong[])words.Clone()); } public bool this[int index] { get { return (words[WordIndex(index)] & (1UL << index)) != 0; } set { if (value) Set(index); else Clear(index); } } /// /// Gets whether at least one bit is set. /// public bool Any() { for (int i = 0; i < words.Length; i++) { if (words[i] != 0) return true; } return false; } /// /// Gets whether all bits in the specified range are set. /// public bool All(int startIndex, int endIndex) { Debug.Assert(startIndex <= endIndex); if (startIndex >= endIndex) { return true; } int startWordIndex = WordIndex(startIndex); int endWordIndex = WordIndex(endIndex - 1); ulong startMask = Mask << startIndex; ulong endMask = Mask >> -endIndex; // same as (Mask >> (64 - (endIndex % 64))) if (startWordIndex == endWordIndex) { return (words[startWordIndex] & (startMask & endMask)) == (startMask & endMask); } else { if ((words[startWordIndex] & startMask) != startMask) return false; for (int i = startWordIndex + 1; i < endWordIndex; i++) { if (words[i] != ulong.MaxValue) return false; } return (words[endWordIndex] & endMask) == endMask; } } /// /// Gets whether both bitsets have the same content. /// public bool SetEquals(BitSet other) { Debug.Assert(words.Length == other.words.Length); for (int i = 0; i < words.Length; i++) { if (words[i] != other.words[i]) return false; } return true; } /// /// Gets whether this set is a subset of other, or equal. /// public bool IsSubsetOf(BitSet other) { for (int i = 0; i < words.Length; i++) { if ((words[i] & ~other.words[i]) != 0) return false; } return true; } /// /// Gets whether this set is a superset of other, or equal. /// public bool IsSupersetOf(BitSet other) { return other.IsSubsetOf(this); } public bool IsProperSubsetOf(BitSet other) { return IsSubsetOf(other) && !SetEquals(other); } public bool IsProperSupersetOf(BitSet other) { return IsSupersetOf(other) && !SetEquals(other); } /// /// Gets whether at least one bit is set in both bitsets. /// public bool Overlaps(BitSet other) { for (int i = 0; i < words.Length; i++) { if ((words[i] & other.words[i]) != 0) return true; } return false; } public void UnionWith(BitSet other) { Debug.Assert(words.Length == other.words.Length); for (int i = 0; i < words.Length; i++) { words[i] |= other.words[i]; } } public void IntersectWith(BitSet other) { for (int i = 0; i < words.Length; i++) { words[i] &= other.words[i]; } } public void Set(int index) { words[WordIndex(index)] |= (1UL << index); } /// /// Sets all bits i; where startIndex <= i < endIndex. /// public void Set(int startIndex, int endIndex) { Debug.Assert(startIndex <= endIndex); if (startIndex >= endIndex) { return; } int startWordIndex = WordIndex(startIndex); int endWordIndex = WordIndex(endIndex - 1); ulong startMask = Mask << startIndex; ulong endMask = Mask >> -endIndex; // same as (Mask >> (64 - (endIndex % 64))) if (startWordIndex == endWordIndex) { words[startWordIndex] |= (startMask & endMask); } else { words[startWordIndex] |= startMask; for (int i = startWordIndex + 1; i < endWordIndex; i++) { words[i] = ulong.MaxValue; } words[endWordIndex] |= endMask; } } // Note: intentionally no SetAll(), because it would also set the // extra bits (due to the capacity being rounded up to a full word). public void Clear(int index) { words[WordIndex(index)] &= ~(1UL << index); } /// /// Clear all bits i; where startIndex <= i < endIndex. /// public void Clear(int startIndex, int endIndex) { Debug.Assert(startIndex <= endIndex); if (startIndex >= endIndex) { return; } int startWordIndex = WordIndex(startIndex); int endWordIndex = WordIndex(endIndex - 1); ulong startMask = Mask << startIndex; ulong endMask = Mask >> -endIndex; // same as (Mask >> (64 - (endIndex % 64))) if (startWordIndex == endWordIndex) { words[startWordIndex] &= ~(startMask & endMask); } else { words[startWordIndex] &= ~startMask; for (int i = startWordIndex + 1; i < endWordIndex; i++) { words[i] = 0; } words[endWordIndex] &= ~endMask; } } public void ClearAll() { for (int i = 0; i < words.Length; i++) { words[i] = 0; } } public int NextSetBit(int startIndex, int endIndex) { Debug.Assert(startIndex <= endIndex); if (startIndex >= endIndex) { return -1; } int startWordIndex = WordIndex(startIndex); int endWordIndex = WordIndex(endIndex - 1); ulong startMask = Mask << startIndex; ulong endMask = Mask >> -endIndex; // same as (Mask >> (64 - (endIndex % 64))) if (startWordIndex == endWordIndex) { ulong maskedWord = words[startWordIndex] & startMask & endMask; if (maskedWord != 0) { return startWordIndex * 64 + BitOperations.TrailingZeroCount(maskedWord); } } else { ulong maskedWord = words[startWordIndex] & startMask; if (maskedWord != 0) { return startWordIndex * 64 + BitOperations.TrailingZeroCount(maskedWord); } for (int i = startWordIndex + 1; i < endWordIndex; i++) { maskedWord = words[i]; if (maskedWord != 0) { return i * 64 + BitOperations.TrailingZeroCount(maskedWord); } } maskedWord = words[endWordIndex] & endMask; if (maskedWord != 0) { return endWordIndex * 64 + BitOperations.TrailingZeroCount(maskedWord); } } return -1; } public IEnumerable SetBits(int startIndex, int endIndex) { while (true) { int next = NextSetBit(startIndex, endIndex); if (next == -1) break; yield return next; startIndex = next + 1; } } public void ReplaceWith(BitSet incoming) { Debug.Assert(words.Length == incoming.words.Length); Array.Copy(incoming.words, 0, words, 0, words.Length); } public override string ToString() { StringBuilder b = new StringBuilder(); b.Append('{'); for (int i = 0; i < words.Length * BitsPerWord; i++) { if (this[i]) { if (b.Length > 1) b.Append(", "); if (b.Length > 500) { b.Append("..."); break; } b.Append(i); } } b.Append('}'); return b.ToString(); } } }