//******************************************************************************************************
// FileBackedDictionary.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:
// ----------------------------------------------------------------------------------------------------
// 10/28/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;
namespace GSF.Collections
{
///
/// Represents a lookup table of key/value pairs backed by a file, with very little memory overhead.
///
/// The type of the keys in the lookup table.
/// The type of the values in the lookup table.
public sealed class FileBackedDictionary : IDictionary, IDisposable
{
#region [ Members ]
// Fields
private FileBackedLookupTable m_lookupTable;
#endregion
#region [ Constructors ]
///
/// Creates a new instance of the class.
///
/// Either or 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 FileBackedDictionary()
: this((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.
/// Either or 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 FileBackedDictionary(string filePath)
: this(filePath, (IEqualityComparer)null)
{
}
///
/// Creates a new instance of the class.
///
/// The dictionary whose elements are copied to this dictionary.
/// is null.
/// Either or 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 FileBackedDictionary(IDictionary dictionary)
: this(dictionary, null)
{
}
///
/// Creates a new instance of the class.
///
/// The equality comparer used to compare keys in the dictionary.
/// Either or cannot be serialized.
public FileBackedDictionary(IEqualityComparer keyComparer)
: this(Path.GetTempFileName(), keyComparer)
{
}
///
/// Creates a new instance of the class.
///
/// The path to the file used to store the lookup table.
/// The dictionary whose elements are copied to this dictionary.
/// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by .
/// is null or is null.
/// Either or 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 FileBackedDictionary(string filePath, IDictionary dictionary)
: this(filePath, dictionary, EqualityComparer.Default)
{
}
///
/// Creates a new instance of the class.
///
/// The path to the file used to store the lookup table.
/// The equality comparer used to compare keys in the dictionary.
/// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by .
/// is null.
/// Either or cannot be serialized.
public FileBackedDictionary(string filePath, IEqualityComparer keyComparer)
{
m_lookupTable = new FileBackedLookupTable(LookupTableType.Dictionary, filePath, keyComparer);
}
///
/// Creates a new instance of the class.
///
/// The dictionary whose elements are copied to this dictionary.
/// The equality comparer used to compare keys in the dictionary.
/// is null.
/// Either or cannot be serialized.
public FileBackedDictionary(IDictionary dictionary, IEqualityComparer keyComparer)
: this(Path.GetTempFileName(), dictionary, keyComparer)
{
}
///
/// Creates a new instance of the class.
///
/// The path to the file used to store the lookup table.
/// The dictionary whose elements are copied to this dictionary.
/// The equality comparer used to compare keys in the dictionary.
/// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by .
/// is null or is null.
/// Either or cannot be serialized.
public FileBackedDictionary(string filePath, IDictionary dictionary, IEqualityComparer keyComparer)
{
m_lookupTable = new FileBackedLookupTable(LookupTableType.Dictionary, filePath, keyComparer);
foreach (KeyValuePair kvp in dictionary)
Add(kvp);
}
#endregion
#region [ Properties ]
///
/// Gets or sets the path to the file backing this dictionary.
///
/// 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.DictionarySignature).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 element with the specified key.
///
///
/// The element with the specified key.
///
/// The key of the element to get or set.
/// is null.
/// The property is retrieved and is not found.
/// The property is set and the is read-only.
public TValue this[TKey key]
{
get
{
return m_lookupTable[key];
}
set
{
m_lookupTable[key] = value;
}
}
///
/// Gets an containing the keys of the .
///
///
/// An containing the keys of the object that implements .
///
public ICollection Keys
{
get
{
return GetKeys().ToList();
}
}
///
/// Gets an containing the values in the .
///
///
/// An containing the values in the object that implements .
///
public ICollection Values
{
get
{
return GetValues().ToList();
}
}
///
/// 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 dictionary.
///
/// 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 with the provided key and value to the .
///
/// The object to use as the key of the element to add.
/// The object to use as the value of the element to add.
/// is null.
/// An element with the same key already exists in the .
/// The is read-only.
public void Add(TKey key, TValue value)
{
m_lookupTable.Add(key, value);
}
///
/// Adds an item to the .
///
/// The object to add to the .
/// The is read-only.
public void Add(KeyValuePair item)
{
Add(item.Key, item.Value);
}
///
/// Removes the element with the specified key from the .
///
///
/// true if the element is successfully removed; otherwise, false.
/// This method also returns false if was not
/// found in the original .
///
/// The key of the element to remove.
/// is null.
/// The is read-only.
public bool Remove(TKey key)
{
return m_lookupTable.Remove(key);
}
///
/// Removes the first occurrence of a specific object from the .
///
///
/// true if was successfully removed from the ;
/// otherwise, false. This method also returns false if is not found in the original .
///
/// The object to remove from the .
/// The is read-only.
public bool Remove(KeyValuePair item)
{
return Remove(item.Key);
}
///
/// Determines whether the contains an element with the specified key.
///
///
/// true if the contains an element with the key; otherwise, false.
///
/// The key to locate in the .
/// is null.
public bool ContainsKey(TKey key)
{
return m_lookupTable.ContainsKey(key);
}
///
/// Determines whether the contains a specific value.
///
///
/// true if is found in the ; otherwise, false.
///
/// The object to locate in the .
public bool Contains(KeyValuePair item)
{
return ContainsKey(item.Key);
}
///
/// Gets the value associated with the specified key.
///
///
/// true if the object that implements contains an element with the specified key; otherwise, false.
///
/// The key whose value to get.
/// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized.
/// is null.
public bool TryGetValue(TKey key, out TValue value)
{
return m_lookupTable.TryGetValue(key, out value);
}
///
/// 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(KeyValuePair[] 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 dictionary");
foreach (KeyValuePair 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.GetEnumerator();
}
///
/// Gets an enumerable used to iterate only the keys in the dictionary.
///
/// An enumerable used to iterate only the keys in the dictionary.
public IEnumerable GetKeys()
{
return m_lookupTable.GetKeys();
}
///
/// Gets an enumerable used to iterate only the values in the dictionary.
///
/// An enumerable used to iterate only the values in the dictionary.
public IEnumerable GetValues()
{
return this.Select(kvp => kvp.Value);
}
///
/// Defragments the item section of the dictionary,
/// which gets fragmented after removing keys or updating values.
///
public void Compact()
{
m_lookupTable.Compact();
}
///
/// Closes the file backing this dictionary.
///
public void Close()
{
m_lookupTable.Close();
}
///
/// Releases all the resources used by the object.
///
public void Dispose()
{
m_lookupTable.Dispose();
}
///
/// 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
}
}