#if FAT using System; using System.Collections.Generic; using System.Threading; using LinqInternal.Collections.ThreadSafe; using LinqInternal.Core; using LinqInternal.Threading; namespace LinqInternal.Collections.Specialized { [Serializable] internal sealed partial class FlagArray { private readonly IReadOnlyCollection _asReadOnly; private readonly int _capacity; private int[] _entries; public FlagArray(FlagArray prototype) { if (ReferenceEquals(prototype, null)) { throw new ArgumentNullException("prototype", "prototype is null."); } _capacity = prototype._capacity; _entries = ArrayReservoir.GetArray(GetLength(_capacity)); prototype._entries.CopyTo(_entries, 0); _asReadOnly = new ExtendedReadOnlyCollection(this); } public FlagArray(int capacity) { if (capacity < 0) { throw new ArgumentOutOfRangeException("capacity", "length < 0"); } _capacity = capacity; _entries = ArrayReservoir.GetArray(GetLength(_capacity)); _asReadOnly = new ExtendedReadOnlyCollection(this); } public FlagArray(int capacity, bool defaultValue) : this(capacity) { if (defaultValue) { Fill(true); } } ~FlagArray() { // Assume anything could have been set to null, start no sync operation, this could be running during DomainUnload if (!GCMonitor.FinalizingForUnload) { RecycleExtracted(); } } public int Capacity { get { return _capacity; } } public int Count { get { var count = 0; var index = 0; var newindex = 0; foreach (var entry in _entries) { newindex += 32; if (newindex <= _capacity) { count += NumericHelper.PopulationCount(entry); index = newindex; } else { foreach (var bit in entry.BinaryReverse().BitsBinary()) { if (bit == 1) { count++; } index++; if (index == _capacity) { break; } } break; } } return count; } } public IEnumerable Flags { get { var index = 0; foreach (var entry in _entries) { if (entry == 0) { index += 32; if (index >= _capacity) { yield break; } } else { foreach (var bit in entry.BinaryReverse().BitsBinary()) { if (bit == 1) { yield return index; } index++; if (index == _capacity) { yield break; } } } } } } bool ICollection.IsReadOnly { get { return false; } } public bool this[int index] { get { var entryIndex = index >> 5; var bit = index & 31; var mask = 1 << bit; return GetBit(entryIndex, mask); } set { var entryIndex = index >> 5; var bit = index & 31; var mask = 1 << bit; if (value) { SetBit(entryIndex, mask); } else { UnsetBit(entryIndex, mask); } } } public FlagArray Clone() { return new FlagArray(this); } public bool Contains(bool item) { var index = 0; var newindex = 0; var check = item ? 0 : -1; foreach (var entry in _entries) { newindex += 32; if (newindex <= _capacity) { if (entry != check) { return true; } index = newindex; } else { foreach (var bit in entry.BinaryReverse().BitsBinary()) { if ((bit == 1) == item) { return true; } index++; if (index == _capacity) { break; } } break; } } return false; } public bool Contains(bool item, IEqualityComparer comparer) { return System.Linq.Enumerable.Contains(this, item, comparer); } public void CopyTo(bool[] array, int arrayIndex) { Extensions.CanCopyTo(_capacity, array, arrayIndex); Extensions.CopyTo(this, array, arrayIndex); } public void CopyTo(bool[] array) { Extensions.CanCopyTo(_capacity, array); Extensions.CopyTo(this, array); } public void CopyTo(bool[] array, int arrayIndex, int countLimit) { Extensions.CanCopyTo(array, arrayIndex, countLimit); Extensions.CopyTo(this, array, arrayIndex, countLimit); } public IEnumerator GetEnumerator() { var index = 0; foreach (var entry in _entries) { foreach (var bit in entry.BinaryReverse().BitsBinary()) { yield return bit == 1; index++; if (index == _capacity) { yield break; } } } } #if !NETCOREAPP1_1 object ICloneable.Clone() { return Clone(); } #endif void ICollection.Add(bool item) { throw new NotSupportedException(); } void ICollection.Clear() { throw new NotSupportedException(); } bool ICollection.Remove(bool item) { throw new NotSupportedException(); } void IList.Insert(int index, bool item) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } public int IndexOf(bool item) { return Extensions.IndexOf(this, item); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } private void Fill(bool value) { var entryValue = value ? unchecked((int)0xffffffff) : 0; for (var index = 0; index < GetLength(_capacity); index++) { _entries[index] = entryValue; } } private bool GetBit(int index, int mask) { return (Volatile.Read(ref _entries[index]) & mask) != 0; } private int GetLength(int length) { return (length >> 5) + ((length & 31) == 0 ? 0 : 1); } private void RecycleExtracted() { // Assume anything could have been set to null, start no sync operation, this could be running during DomainUnload var entries = _entries; if (entries != null) { ArrayReservoir.DonateArray(entries); _entries = null; } } private void SetBit(int index, int mask) { again: var readed = Volatile.Read(ref _entries[index]); if ((readed & mask) == 0) { if (Interlocked.CompareExchange(ref _entries[index], readed | mask, readed) != readed) { goto again; } } } private void UnsetBit(int index, int mask) { again: var readed = Volatile.Read(ref _entries[index]); if ((readed & mask) != 0) { if (Interlocked.CompareExchange(ref _entries[index], readed & ~mask, readed) != readed) { goto again; } } } } internal sealed partial class FlagArray : IList, IExtendedCollection, ICloneable { IReadOnlyCollection IExtendedCollection.AsReadOnly { get { return _asReadOnly; } } bool IExtendedCollection.Remove(bool item, IEqualityComparer comparer) { throw new NotSupportedException(); } } } #endif