//****************************************************************************************************** // FileBackedHashSet.cs - Gbtc // // Copyright © 2014, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may // not use this file except in compliance with the License. You may obtain a copy of the License at: // // http://www.opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 12/02/2014 - Stephen C. Wills // Generated original version of source code. // //****************************************************************************************************** using System; using System.Collections; using System.Collections.Generic; using System.IO; namespace GSF.Collections { /// /// Represents a lookup table backed by a file, with very little memory overhead. /// /// The type of the items in the lookup table. public sealed class FileBackedHashSet : ISet, IDisposable { #region [ Members ] // Fields private FileBackedLookupTable m_lookupTable; #endregion #region [ Constructors ] /// /// Creates a new instance of the class. /// /// cannot be serialized. /// /// This constructor uses the default equality comparer for file backed lookup tables, /// which is not the same as the default equality comparer for /// objects. This is because the default implementation of /// does not provide guarantees about consistency across platforms, or even implementations /// of the CLR. Instead, the default equality comparer uses a byte-for-byte comparison to /// determine equality between keys and a CRC-32 for its hash code implementation. This /// means the performance of the hashing function is dependent on the performance of the /// serialization function. /// public FileBackedHashSet() : this(Path.GetTempFileName(), (IEqualityComparer)null) { } /// /// Creates a new instance of the class. /// /// The path to the file used to store the lookup table. /// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by . /// is null. /// cannot be serialized. /// /// This constructor uses the default equality comparer for file backed lookup tables, /// which is not the same as the default equality comparer for /// objects. This is because the default implementation of /// does not provide guarantees about consistency across platforms, or even implementations /// of the CLR. Instead, the default equality comparer uses a byte-for-byte comparison to /// determine equality between keys and a CRC-32 for its hash code implementation. This /// means the performance of the hashing function is dependent on the performance of the /// serialization function. /// public FileBackedHashSet(string filePath) : this(filePath, (IEqualityComparer)null) { } /// /// Creates a new instance of the class. /// /// The equality comparer used to compare items in the hash set. /// cannot be serialized. public FileBackedHashSet(IEqualityComparer comparer) : this(Path.GetTempFileName(), comparer) { } /// /// Creates a new instance of the class. /// /// The enumerable whose elements are copied to this hash set. /// is null. /// cannot be serialized. /// /// This constructor uses the default equality comparer for file backed lookup tables, /// which is not the same as the default equality comparer for /// objects. This is because the default implementation of /// does not provide guarantees about consistency across platforms, or even implementations /// of the CLR. Instead, the default equality comparer uses a byte-for-byte comparison to /// determine equality between keys and a CRC-32 for its hash code implementation. This /// means the performance of the hashing function is dependent on the performance of the /// serialization function. /// public FileBackedHashSet(IEnumerable enumerable) : this(enumerable, null) { } /// /// Creates a new instance of the class. /// /// The path to the file used to store the lookup table. /// The equality comparer used to compare items in the hash set. /// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by . /// is null. /// cannot be serialized. public FileBackedHashSet(string filePath, IEqualityComparer comparer) { m_lookupTable = new FileBackedLookupTable(LookupTableType.HashSet, filePath, comparer); } /// /// Creates a new instance of the class. /// /// The path to the file used to store the lookup table. /// The enumerable whose elements are copied to this hash set. /// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by . /// is null or is null. /// cannot be serialized. /// /// This constructor uses the default equality comparer for file backed lookup tables, /// which is not the same as the default equality comparer for /// objects. This is because the default implementation of /// does not provide guarantees about consistency across platforms, or even implementations /// of the CLR. Instead, the default equality comparer uses a byte-for-byte comparison to /// determine equality between keys and a CRC-32 for its hash code implementation. This /// means the performance of the hashing function is dependent on the performance of the /// serialization function. /// public FileBackedHashSet(string filePath, IEnumerable enumerable) : this(filePath, enumerable, null) { } /// /// Creates a new instance of the class. /// /// The enumerable whose elements are copied to this hash set. /// The equality comparer used to compare items in the hash set. /// is null. /// cannot be serialized. public FileBackedHashSet(IEnumerable enumerable, IEqualityComparer comparer) : this(Path.GetTempFileName(), enumerable, comparer) { } /// /// Creates a new instance of the class. /// /// The path to the file used to store the lookup table. /// The enumerable whose elements are copied to this hash set. /// The equality comparer used to compare items in the hash set. /// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by . /// is null or is null. /// cannot be serialized. public FileBackedHashSet(string filePath, IEnumerable enumerable, IEqualityComparer comparer) { m_lookupTable = new FileBackedLookupTable(LookupTableType.HashSet, filePath, comparer); foreach (T item in enumerable) Add(item); } #endregion #region [ Properties ] /// /// Gets or sets the path to the file backing this hash set. /// /// FilePath is set and is a zero-length string, contains only white space, or contains one or more invalid characters as defined by . /// /// Changes to this property will cause the file to close if the file is already opened. /// Data will not be automatically written from the old file to the new file. /// public string FilePath { get { return m_lookupTable.FilePath; } set { m_lookupTable.FilePath = value; } } /// /// Gets the number of elements contained in the . /// /// /// The number of elements contained in the . /// public int Count { get { return m_lookupTable.Count; } } /// /// Gets a value indicating whether the is read-only. /// /// /// true if the is read-only; otherwise, false. /// public bool IsReadOnly { get { return m_lookupTable.IsReadOnly; } } /// /// Gets the default signature used by the /// if no user-defined signature is supplied. /// public byte[] DefaultSignature { get { return new Guid(FileBackedLookupTable.HashSetSignature).ToRfcBytes(); } } /// /// Gets or sets the signature of the file backing the lookup table. /// /// Attempt is made to set Signature to a null value. /// Attempt is made to set Signature to a value larger than the maximum allowed size. /// Attempt is made to modify Signature of a read-only lookup table. public byte[] Signature { get { return m_lookupTable.Signature; } set { m_lookupTable.Signature = value; } } /// /// Gets or sets the size of the cache used /// to store data from the file in memory. /// public long CacheSize { get { return m_lookupTable.CacheSize; } set { m_lookupTable.CacheSize = value; } } /// /// Gets the number of operations that fragment the /// lookup table that have occurred since the last /// time the lookup table was compacted. /// /// /// This value is not stored in the file and may therefore /// be inaccurate if the lookup table has not been compacted /// since the last time it was opened. /// public int FragmentationCount { get { return m_lookupTable.FragmentationCount; } } #endregion #region [ Methods ] /// /// Opens the file backing this hash set. /// /// File is already open. public void Open() { m_lookupTable.Open(); } /// /// Opens the file backing this hash set in read-only mode. /// /// File is already open. public void OpenRead() { m_lookupTable.OpenRead(); } /// /// Adds an element to the current set and returns a value to indicate if the element was successfully added. /// /// The element to add to the set. /// /// true if the element is added to the set; false if the element is already in the set. /// public bool Add(T item) { return m_lookupTable.TryAdd(item, null); } /// /// Removes the first occurrence of a specific object from the . /// /// The object to remove from the . /// /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . /// /// The is read-only. public bool Remove(T item) { return m_lookupTable.Remove(item); } /// /// Removes all elements that match the conditions defined by the specified predicate from a collection. /// /// The delegate that defines the conditions of the elements to remove. /// The number of elements that were removed from the collection. /// match is null /// The is read-only. public int RemoveWhere(Predicate match) { int removedCount; if ((object)match == null) throw new ArgumentNullException(nameof(match)); removedCount = 0; m_lookupTable.UnmarkAll(); foreach (T item in this) { if (match(item)) { m_lookupTable.TryMark(item); removedCount++; } } m_lookupTable.RemoveMarked(); return removedCount; } /// /// Determines whether the contains a specific value. /// /// The object to locate in the . /// /// true if is found in the ; otherwise, false. /// public bool Contains(T item) { return m_lookupTable.ContainsKey(item); } /// /// Modifies the current set so that it contains all elements that are present in either the current set or the specified collection. /// /// The collection to compare to the current set. /// is null. /// The is read-only. public void UnionWith(IEnumerable other) { if ((object)other == null) throw new ArgumentNullException(nameof(other)); foreach (T item in other) Add(item); } /// /// Modifies the current set so that it contains only elements that are also in a specified collection. /// /// The collection to compare to the current set. /// is null. /// The is read-only. public void IntersectWith(IEnumerable other) { if ((object)other == null) throw new ArgumentNullException(nameof(other)); m_lookupTable.UnmarkAll(); foreach (T item in other) m_lookupTable.TryMark(item); m_lookupTable.RemoveUnmarked(); } /// /// Removes all elements in the specified collection from the current set. /// /// The collection of items to remove from the set. /// is null. /// The is read-only. public void ExceptWith(IEnumerable other) { if ((object)other == null) throw new ArgumentNullException(nameof(other)); foreach (T item in other) m_lookupTable.Remove(item); } /// /// Modifies the current set so that it contains only elements that are present /// either in the current set or in the specified collection, but not both. /// /// The collection to compare to the current set. /// is null. /// The is read-only. public void SymmetricExceptWith(IEnumerable other) { List list; if ((object)other == null) throw new ArgumentNullException(nameof(other)); list = new List(); m_lookupTable.UnmarkAll(); foreach (T item in other) { if (!m_lookupTable.TryMark(item)) list.Add(item); } m_lookupTable.RemoveMarked(); foreach (T item in list) Add(item); } /// /// Determines whether a set is a subset of a specified collection. /// /// The collection to compare to the current set. /// /// true if the current set is a subset of ; otherwise, false. /// /// is null. /// The is read-only. public bool IsSubsetOf(IEnumerable other) { if ((object)other == null) throw new ArgumentNullException(nameof(other)); m_lookupTable.UnmarkAll(); foreach (T item in other) m_lookupTable.TryMark(item); return m_lookupTable.AllMarked(); } /// /// Determines whether the current set is a superset of a specified collection. /// /// The collection to compare to the current set. /// /// true if the current set is a superset of ; otherwise, false. /// /// is null. public bool IsSupersetOf(IEnumerable other) { if ((object)other == null) throw new ArgumentNullException(nameof(other)); foreach (T item in other) { if (!Contains(item)) return false; } return true; } /// /// Determines whether the current set is a proper (strict) superset of a specified collection. /// /// The collection to compare to the current set. /// /// true if the current set is a proper superset of ; otherwise, false. /// /// is null. /// The is read-only. public bool IsProperSupersetOf(IEnumerable other) { if ((object)other == null) throw new ArgumentNullException(nameof(other)); m_lookupTable.UnmarkAll(); foreach (T item in other) { if (!m_lookupTable.TryMark(item)) return false; } return !m_lookupTable.AllMarked(); } /// /// Determines whether the current set is a proper (strict) subset of a specified collection. /// /// The collection to compare to the current set. /// /// true if the current set is a proper subset of ; otherwise, false. /// /// is null. /// The is read-only. public bool IsProperSubsetOf(IEnumerable other) { bool canBeProperSubset; if ((object)other == null) throw new ArgumentNullException(nameof(other)); canBeProperSubset = false; m_lookupTable.UnmarkAll(); foreach (T item in other) { if (!m_lookupTable.TryMark(item)) canBeProperSubset = true; } return canBeProperSubset && m_lookupTable.AllMarked(); } /// /// Determines whether the current set overlaps with the specified collection. /// /// The collection to compare to the current set. /// /// true if the current set and share at least one common element; otherwise, false. /// /// is null. public bool Overlaps(IEnumerable other) { if ((object)other == null) throw new ArgumentNullException(nameof(other)); foreach (T item in other) { if (Contains(item)) return true; } return false; } /// /// Determines whether the current set and the specified collection contain the same elements. /// /// The collection to compare to the current set. /// /// true if the current set is equal to ; otherwise, false. /// /// is null. /// The is read-only. public bool SetEquals(IEnumerable other) { if ((object)other == null) throw new ArgumentNullException(nameof(other)); m_lookupTable.UnmarkAll(); foreach (T item in other) { if (!m_lookupTable.TryMark(item)) return false; } return m_lookupTable.AllMarked(); } /// /// Removes all items from the . /// /// The is read-only. public void Clear() { m_lookupTable.Clear(); } /// /// Copies the elements of the to an , starting at a particular index. /// /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. /// The zero-based index in at which copying begins. /// is null. /// is less than 0. /// The number of elements in the source is greater than the available space from to the end of the destination . public void CopyTo(T[] array, int arrayIndex) { if ((object)array == null) throw new ArgumentNullException(nameof(array)); if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex)); if (m_lookupTable.Count > array.Length - arrayIndex) throw new ArgumentException("Not enough available space in array to copy elements from hash set"); foreach (T item in this) array[arrayIndex++] = item; } /// /// Returns an enumerator that iterates through the collection. /// /// /// A that can be used to iterate through the collection. /// /// 1 public IEnumerator GetEnumerator() { return m_lookupTable.GetKeys().GetEnumerator(); } /// /// Defragments the item section of the hash set, /// which gets fragmented after removing items. /// public void Compact() { m_lookupTable.Compact(); } /// /// Closes the file backing this hash set. /// public void Close() { m_lookupTable.Close(); } /// /// Releases all the resources used by the object. /// public void Dispose() { m_lookupTable.Dispose(); } /// /// Adds an item to the . /// /// The object to add to the . /// The is read-only. void ICollection.Add(T item) { Add(item); } /// /// Returns an enumerator that iterates through a collection. /// /// /// An object that can be used to iterate through the collection. /// /// 2 IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion } }