1908 lines · 70.8 KB · C#
RawDownload
//******************************************************************************************************//  FileBackedLookupTableBase.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;using System.Linq;using System.Linq.Expressions;using System.Reflection;using System.Runtime.Serialization.Formatters.Binary;using GSF.IO;using GSF.IO.Checksums; namespace GSF.Collections{    internal enum LookupTableType    {        /// <summary>        /// Dictionary with keys and values in the item nodes.        /// </summary>        Dictionary,         /// <summary>        /// HashSet with lookup node markers for set operations.        /// </summary>        HashSet    }     internal sealed class FileBackedLookupTable<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>, IDisposable    {        #region [ Members ]         // Nested Types         private class HeaderNode        {            public const int SignatureSize = 16;            public const int FixedSize = SignatureSize + 4 * sizeof(long);             public HeaderNode(LookupTableType type)            {                if (type == LookupTableType.Dictionary)                    Signature = Guid.Parse(DictionarySignature).ToRfcBytes();                else                    Signature = Guid.Parse(HashSetSignature).ToRfcBytes();            }             public byte[] Signature;            public long Count;            public long Capacity;            public long ItemSectionPointer;            public long EndOfFilePointer;        }         private class JournalNode        {            public const int FixedSize = sizeof(int) + 3 * sizeof(long) + sizeof(int);             public const int None = 0;            public const int Set = 1;            public const int Delete = 2;            public const int GrowLookupSection = 3;            public const int RebuildLookupTable = 4;            public const int WriteItemNodePointers = 5;            public const int Truncate = 6;            public const int Clear = 7;             public int Operation;            public long LookupPointer;            public long ItemPointer;            public long Sync;            public int Checksum;        }         private class LookupNode        {            public long ItemPointer;            public int Marker;        }         private class ItemNode        {            // Size of only the fixed size portion of            // the item node (not including key and value)            public const int FixedSize = 2 * sizeof(long) + sizeof(int);             public long LookupPointer;            public long NextItemPointer;            public int HashCode;            public TKey Key;            public TValue Value;        }         private class CRCStream : Stream        {            #region [ Members ]             // Fields            private Crc32 m_checksum = new Crc32();             #endregion             #region [ Properties ]             public override bool CanRead => false;            public override bool CanSeek => false;            public override bool CanWrite => true;            public uint Value => m_checksum.Value;             #endregion             #region [ Methods ]             public override void Flush() { }             public void Reset() =>                m_checksum.Reset();             public override void Write(byte[] buffer, int offset, int count) =>                m_checksum.Update(buffer, offset, count);             #endregion             #region [ Not Supported ]             public override long Length            {                get                {                    throw new NotSupportedException();                }            }             public override long Position            {                get                {                    throw new NotSupportedException();                }                 set                {                    throw new NotSupportedException();                }            }             public override int Read(byte[] buffer, int offset, int count)            {                throw new NotSupportedException();            }             public override long Seek(long offset, SeekOrigin origin)            {                throw new NotSupportedException();            }             public override void SetLength(long value)            {                throw new NotSupportedException();            }             #endregion        }         // The default equality comparer for strings does not guarantee        // consistency across platforms or versions of the CLR. This comparer        // uses CRC-32 for its hashing function to provide that guarantee.        private class DefaultKeyComparer : IEqualityComparer<TKey>        {            public bool Equals(TKey x, TKey y)            {                using (MemoryStream xStream = new MemoryStream())                using (MemoryStream yStream = new MemoryStream())                {                    WriteKeyAction(xStream, x);                    WriteKeyAction(yStream, y);                     if (xStream.Length != yStream.Length)                        return false;                     byte[] xBuffer = xStream.GetBuffer();                    byte[] yBuffer = yStream.GetBuffer();                     for (int i = 0; i < xStream.Length; i++)                    {                        if (xBuffer[i] != yBuffer[i])                            return false;                    }                     return true;                }            }             public int GetHashCode(TKey obj)            {                using (CRCStream crcStream = new CRCStream())                {                    WriteKeyAction(crcStream, obj);                    return unchecked((int)crcStream.Value);                }            }             public static readonly DefaultKeyComparer Default = new DefaultKeyComparer();        }         private class KeysEnumerable : IEnumerable<TKey>        {            private Func<IEnumerator<TKey>> m_getEnumeratorFunc;             public KeysEnumerable(Func<IEnumerator<TKey>> getEnumeratorFunc)            {                m_getEnumeratorFunc = getEnumeratorFunc;            }             public IEnumerator<TKey> GetEnumerator()            {                return m_getEnumeratorFunc();            }             IEnumerator IEnumerable.GetEnumerator()            {                return GetEnumerator();            }        }         // Constants        public const string DictionarySignature = "3165E4F9-203B-4741-A186-EA34659A94B7";        public const string HashSetSignature = "6527713F-78AE-43DA-8E37-718AFED99927";        private const double MaximumLoadFactor = 0.7D;        private const int MaximumChainedEmptyNodes = 3;         // Fields        private HeaderNode m_headerNode;        private JournalNode m_journalNode;         private CachedFileStream m_fileStream;        private BinaryWriter m_fileWriter;        private BinaryReader m_fileReader;         private string m_filePath;        private IEqualityComparer<TKey> m_keyComparer;        private int m_fragmentationCount;         private LookupTableType m_lookupTableType;         #endregion         #region [ Constructors ]         /// <summary>        /// Creates a new instance of the <see cref="FileBackedLookupTable{TKey, TValue}"/> class.        /// </summary>        /// <param name="lookupTableType">Type of the lookup table used to tweak the file format.</param>        /// <exception cref="InvalidOperationException">Either <typeparamref name="TKey"/> or <typeparamref name="TValue"/> cannot be serialized.</exception>        public FileBackedLookupTable(LookupTableType lookupTableType)            : this(lookupTableType, (IEqualityComparer<TKey>)null)        {        }         /// <summary>        /// Creates a new instance of the <see cref="FileBackedLookupTable{TKey, TValue}"/> class.        /// </summary>        /// <param name="lookupTableType">Type of the lookup table used to tweak the file format.</param>        /// <param name="filePath">The path to the file used to store the lookup table.</param>        /// <exception cref="ArgumentException"><paramref name="filePath"/> is a zero-length string, contains only white space, or contains one or more invalid characters as defined by <see cref="Path.GetInvalidPathChars"/>.</exception>        /// <exception cref="ArgumentNullException"><paramref name="filePath"/> is null.</exception>        /// <exception cref="InvalidOperationException">Either <typeparamref name="TKey"/> or <typeparamref name="TValue"/> cannot be serialized.</exception>        public FileBackedLookupTable(LookupTableType lookupTableType, string filePath)            : this(lookupTableType, filePath, null)        {        }         /// <summary>        /// Creates a new instance of the <see cref="FileBackedLookupTable{TKey, TValue}"/> class.        /// </summary>        /// <param name="lookupTableType">Type of the lookup table used to tweak the file format.</param>        /// <param name="keyComparer">The equality comparer used to compare keys in the lookup table.</param>        /// <exception cref="InvalidOperationException">Either <typeparamref name="TKey"/> or <typeparamref name="TValue"/> cannot be serialized.</exception>        public FileBackedLookupTable(LookupTableType lookupTableType, IEqualityComparer<TKey> keyComparer)            : this(lookupTableType, Path.GetTempFileName(), keyComparer)        {        }         /// <summary>        /// Creates a new instance of the <see cref="FileBackedLookupTable{TKey, TValue}"/> class.        /// </summary>        /// <param name="lookupTableType">Type of the lookup table used to tweak the file format.</param>        /// <param name="filePath">The path to the file used to store the lookup table.</param>        /// <param name="keyComparer">The equality comparer used to compare keys in the lookup table.</param>        /// <exception cref="ArgumentException"><paramref name="filePath"/> is a zero-length string, contains only white space, or contains one or more invalid characters as defined by <see cref="Path.GetInvalidPathChars"/>.</exception>        /// <exception cref="ArgumentNullException"><paramref name="filePath"/> is null.</exception>        /// <exception cref="InvalidOperationException">Either <typeparamref name="TKey"/> or <typeparamref name="TValue"/> cannot be serialized.</exception>        public FileBackedLookupTable(LookupTableType lookupTableType, string filePath, IEqualityComparer<TKey> keyComparer)        {            if ((object)WriteKeyAction == null || (object)ReadKeyFunc == null)                throw new InvalidOperationException("Unable to create FileBackedLookupTable with keys that are not serializable");             if (lookupTableType == LookupTableType.Dictionary && ((object)WriteValueAction == null || (object)ReadValueFunc == null))                throw new InvalidOperationException("Unable to create FileBackedLookupTable with values that are not serializable");             if ((object)filePath == null)                throw new ArgumentNullException(nameof(filePath));             if (string.IsNullOrWhiteSpace(filePath))                throw new ArgumentException("Path is zero-length or contains only whitespace", nameof(filePath));             m_lookupTableType = lookupTableType;             FilePath = filePath;             if ((object)keyComparer != null)                m_keyComparer = keyComparer;            else                m_keyComparer = DefaultKeyComparer.Default;        }         #endregion         #region [ Properties ]         /// <summary>        /// Gets or sets the path to the file backing this lookup table.        /// </summary>        /// <exception cref="ArgumentException">FilePath contains one or more invalid characters as defined by <see cref="Path.GetInvalidPathChars"/>.</exception>        /// <remarks>        /// 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.        /// </remarks>        public string FilePath        {            get            {                return m_filePath;            }            set            {                char[] invalidPathChars;                 if (m_filePath != value)                {                    if (!string.IsNullOrWhiteSpace(value))                    {                        invalidPathChars = Path.GetInvalidPathChars();                         if (value.Any(invalidPathChars.Contains))                            throw new ArgumentException($"Path contains one or more invalid characters: {value}", nameof(value));                    }                     Close();                    m_filePath = value;                }            }        }         /// <summary>        /// Gets the number of elements contained in the <see cref="FileBackedLookupTable{TKey, TValue}"/>.        /// </summary>        /// <returns>        /// The number of elements contained in the <see cref="FileBackedLookupTable{TKey, TValue}"/>.        /// </returns>        public int Count        {            get            {                if ((object)m_fileStream == null)                    OpenImplicit();                 return (int)m_headerNode.Count;            }        }         /// <summary>        /// Gets a value indicating whether the <see cref="FileBackedLookupTable{TKey, TValue}"/> is read-only.        /// </summary>        /// <returns>        /// true if the <see cref="FileBackedLookupTable{TKey, TValue}"/> is read-only; otherwise, false.        /// </returns>        public bool IsReadOnly        {            get            {                if ((object)m_fileStream == null)                    OpenImplicit();                 return (object)m_fileWriter == null;            }        }         /// <summary>        /// Gets or sets the signature of the file backing the lookup table.        /// </summary>        /// <exception cref="ArgumentNullException">Attempt is made to set Signature to a null value.</exception>        /// <exception cref="ArgumentException">Attempt is made to set Signature to a value larger than the maximum allowed size.</exception>        /// <exception cref="NotSupportedException">Attempt is made to modify Signature of a read-only lookup table.</exception>        public byte[] Signature        {            get            {                byte[] signature;                 if ((object)m_fileStream == null)                    OpenImplicit();                 signature = new byte[HeaderNode.SignatureSize];                Buffer.BlockCopy(m_headerNode.Signature, 0, signature, 0, HeaderNode.SignatureSize);                return signature;            }            set            {                if ((object)value == null)                    throw new ArgumentNullException(nameof(value));                 if (value.Length > HeaderNode.SignatureSize)                    throw new ArgumentException("Attempt was made to set signature to a value larger than the maximum signature size of " + HeaderNode.SignatureSize);                 FailIfReadOnly();                 Buffer.BlockCopy(value, 0, m_headerNode.Signature, 0, value.Length);                Array.Clear(m_headerNode.Signature, value.Length, HeaderNode.SignatureSize - value.Length);                Write(m_headerNode);            }        }         /// <summary>        /// Gets or sets the element with the specified key.        /// </summary>        /// <returns>        /// The element with the specified key.        /// </returns>        /// <param name="key">The key of the element to get or set.</param>        /// <exception cref="ArgumentNullException"><paramref name="key"/> is null.</exception>        /// <exception cref="KeyNotFoundException">The property is retrieved and <paramref name="key"/> is not found.</exception>        /// <exception cref="NotSupportedException">The property is set and the <see cref="FileBackedLookupTable{TKey, TValue}"/> is read-only.</exception>        public TValue this[TKey key]        {            get            {                TValue value;                 if ((object)key == null)                    throw new ArgumentNullException(nameof(key));                 if (!TryGetValue(key, out value))                    throw new KeyNotFoundException($"Item with the given key ({key}) was not found in the lookup table: {m_filePath}");                 return value;            }            set            {                long lookupPointer;                long itemPointer;                ItemNode itemNode;                 long count;                 if ((object)key == null)                    throw new ArgumentNullException(nameof(key));                 FailIfReadOnly();                 Find(key, out lookupPointer, out itemPointer);                 if (itemPointer >= m_headerNode.ItemSectionPointer)                    count = m_headerNode.Count;                else                    count = m_headerNode.Count + 1;                 if (count > m_headerNode.Capacity * MaximumLoadFactor)                {                    Grow();                    Find(key, out lookupPointer, out itemPointer);                }                 itemNode = new ItemNode();                itemNode.LookupPointer = lookupPointer;                itemNode.HashCode = m_keyComparer.GetHashCode(key);                itemNode.Key = key;                itemNode.Value = value; 

Lines 1–500 of 1908.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t