/* * LargeArray.cs * * Copyright © 2009 * Diversified Technical Systems, Inc. * All Rights Reserved */ using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; using System.Text; using DTS.Common.Utilities; using DTS.Common.Utilities.DotNetProgrammingConstructs; using DTS.Common.Utilities.Logging; namespace DTS.Common.Utilities.IO.MemoryMap { /// /// A generic memory-mapped array. /// /// /// /// The type of object to be contained in this collection. /// /// public abstract partial class LargeArray : Exceptional, IDisposable, ICollection, IEnumerable, ICloneable, IList where T : struct { /// /// Initialize an instance of a . /// /// /// /// The size of the collection. /// /// public LargeArray(ulong size) : this(size, null, null) { } /// /// Initialize an instance of a . /// /// /// /// The size of the collection. /// /// /// /// The name of the directory where this LargeArray will store the temporary /// serializations of its items. If null, then the default location will be used. /// /// /// /// The file name prefix under which this LargeArray will store temporary /// serializations of its items. Initialized to DefaultFilePrefix's value at object /// creation, unless an alternate is specified by the user. If null, then the default prefix /// will be used. /// /// public LargeArray(ulong size, string scratchFileDirectory, string scratchFilePrefix) { try { IsMemoryMapped = false; Size = size; ViewSize = 0xFFFF; //50 * 1024; //50 * 1024; // 50 * 1024 * 1024; ScratchDirectory = scratchFileDirectory ?? DefaultScratchDirectory; FilePrefix = scratchFilePrefix ?? DefaultFilePrefix; IsMemoryMapped = CreateScratchFile(size, ScratchDirectory, FilePrefix); } catch (System.Exception ex) { throw new Exception("encountered problem constructing " + GetType().FullName, ex); } } /// /// Finalizer. Can't find anyplace else to put this file removal and have it /// work, since something in the mapping code is tenaciously holding onto it. /// ~LargeArray() { // // Delete our scratch file. // File.Delete(FullScratchFilename); } /// /// A shared object to be shared by all instances /// of to ensure that no two try to use the same scratch filename. /// protected static NameGeneratorLock NameLock { get { try { return _NameLock; } catch (System.Exception ex) { throw new Exception("encountered problem getting name generator lock", ex); } } } private static readonly NameGeneratorLock _NameLock = new NameGeneratorLock(); /// /// Get the next unique scratch file number designation. /// protected static ulong NextScratchFileNumberDesignation { get { try { lock (NameLock) { return _NextScratchFileNumberDesignation++; } } catch (System.Exception ex) { throw new Exception("encountered problem getting next scratch file number designation", ex); } } } private static ulong _NextScratchFileNumberDesignation = 0; /// /// The default name of the directory where MegArrays will store the temporary /// serializations of their items. /// public static string DefaultScratchDirectory { get => _DefaultScratchDirectory.Value; set => _DefaultScratchDirectory.Value = value; } private static readonly Property _DefaultScratchDirectory = new Property( typeof(LargeArray).FullName + ".DefaultScratchDirectory", "C:\\\\Temp\\", true ); /// /// The name of the directory where this LargeArray will store its temporary /// serializations of its items. Initialized to DefaultScratchDirectory's value at object /// creation, unless an alternate is specified by the user. /// public string ScratchDirectory { get => _ScratchDirectory.Value; set => _ScratchDirectory.Value = value; } private readonly Property _ScratchDirectory = new Property( typeof(LargeArray).FullName + ".ScratchDirectory", null, false ); /// /// The default prefix name for MegArrays temporary serializations. /// public static string DefaultFilePrefix { get => _DefaultFilePrefix.Value; set => _DefaultFilePrefix.Value = value; } private static readonly Property _DefaultFilePrefix = new Property( typeof(LargeArray).FullName + ".DefaultFilePrefix", "largearray.tmp.", true ); /// /// The file name prefix under which this LargeArray will store temporary /// serializations of its items. Initialized to DefaultFilePrefix's value at object /// creation, unless an alternate is specified by the user. /// public string FilePrefix { get => _FilePrefix.Value; set => _FilePrefix.Value = value; } private readonly Property _FilePrefix = new Property( typeof(LargeArray).FullName + ".FilePrefix", null, false ); /// /// Get the filename that this will be /// using for its memory-mapped serialization file. /// public string ScratchFilename { get { try { if (null == _ScratchFilename) _ScratchFilename = GenerateNewScratchFilename(); return _ScratchFilename; } catch (System.Exception ex) { throw new Exception("encountered problem getting scratch filename property for " + GetType().FullName, ex); } } } private string _ScratchFilename = null; /// /// Get the fully qualified filename that this /// will be using for its memory-mapped serialization file. /// public string FullScratchFilename { get { try { if (null == _FullScratchFilename) _FullScratchFilename = ScratchDirectory + ScratchFilename; return _FullScratchFilename; } catch (System.Exception ex) { throw new Exception("encountered problem getting fully qualified scratch filename property for " + GetType().FullName, ex); } } } private string _FullScratchFilename = null; /// /// Generate a new unique filename. /// /// /// /// A unique filename. /// /// private string GenerateNewScratchFilename() { try { return FilePrefix + NextScratchFileNumberDesignation.ToString(); } catch (System.Exception ex) { throw new Exception("encounterd problem generating new scratch filename", ex); } } /// /// Get the maximum size of this . /// public ulong Size { get; } /// /// Get flag indicating whether or not this is /// currently associated with a disk file. /// private bool IsMemoryMapped { get; set; } /// /// Get the view size used by our memory mapping mechanism. /// protected uint ViewSize { get; } /// /// The size of the basic datum type stored by this object. /// public abstract uint DatumSize { get; } /// /// The value to be substituted for all array values when "clear" is invoked. /// protected abstract T DatumClearValue { get; } /// /// Create the scratch file that will store this object's items. /// /// /// /// The size of the scratch file to be created. /// /// /// /// The path of the directory to contain the newly-created scratch /// file. It will be created if it does not already exist. /// /// /// /// The name of the file to be created. /// /// /// /// true if the file has been successfully created, false otherwise. /// /// private bool CreateScratchFile(ulong size, string directory, string filename) { try { // Make sure we have someplace to put this file. if (!Directory.Exists(directory)) Directory.CreateDirectory(directory); // Other files of this name should not exist. if (File.Exists(filename)) throw new ScratchFileAlreadyExistsException("the scratch file \"" + FullScratchFilename + "\" already exists"); APILogger.Log("writing 35 ", FullScratchFilename); lock (this) { using (var fileWriter = new BinaryWriter(new FileStream(FullScratchFilename, FileMode.Create))) { var byteSize = Size * DatumSize; for (ulong i = 0; i < byteSize; i++) fileWriter.Write((Byte)0x0); } } return true; } catch (System.Exception ex) { throw new Exception("encountered problem setting up scratch file for " + GetType().FullName, ex); } } /// /// Create a file map view array to associate this object with a file representation. /// /// /// /// A association with with object's data file. /// /// protected FileMapViewArray ViewArray { get { try { if (!IsMemoryMapped) throw new Exception(); else { if (!_ViewArray.IsInitialized) { var fileInfo = new FileInfo(FullScratchFilename); // Make a new mapViewArray for the file with proper offset and // readonly privilages to protect sample integrity. _ViewArray.Value = new FileMapViewArray(Path.GetFullPath(FullScratchFilename), MapAccess.FileMapAllAccess, MapProtection.PageReadWrite, 0, (fileInfo.Length), (int)ViewSize); } return _ViewArray.Value; } } catch (System.Exception ex) { throw new Exception("encountered problem getting view array", ex); } } } private readonly Property _ViewArray = new Property( typeof(LargeArray).FullName + ".FileMap", null, false ); /// /// Get the datum at the specified index. /// /// /// /// The index of the datum sought. /// /// /// /// The datum of type T at the specified index of the memory mapped file. /// /// protected abstract T GetDatumAtIndex(ulong index); /// /// Set the specified index to the specified datum. /// /// /// /// The datum to be inserted at the specified index. /// /// /// /// The index of the specified datum's destination. /// /// protected abstract void SetDatumAtIndex(T datum, ulong index); #region IDisposable Interface // ********************************************************************* // ******************** BEGIN IDisposable Interface ******************** // ********************************************************************* /// /// Let go our our resources. /// public void Dispose() { try { if (IsMemoryMapped) { ViewArray.Dispose(); IsMemoryMapped = false; } } catch (System.Exception ex) { throw new Exception("encountered problem disposing of resources for " + GetType().FullName, ex); } } // ******************************************************************* // ******************** END IDisposable Interface ******************** // ******************************************************************* #endregion #region ICollection Interface // ********************************************************************* // ******************** BEGIN ICollection Interface ******************** // ********************************************************************* /// /// Gets the number of elements contained in the . /// /// public int Count { get { try { if (LargeCount > int.MaxValue) throw new LargeOverflowException("\"large\" ulong-sized count is too big to cast to int-sized version"); else return (int)LargeCount; } catch (System.Exception ex) { throw new Exception("encountered problem getting item count for " + GetType().FullName, ex); } } } /// /// Gets a value indicating whether access to the /// is synchronized (thread safe). Returns true if access to the /// is synchronized (thread safe); otherwise, false. /// /// public bool IsSynchronized { get { try { return true; } catch (System.Exception ex) { throw new Exception("encountered problem getting synchronization capability for " + GetType().FullName, ex); } } } /// /// Get an object that can be used to synchronize access to the System.Collections.ICollection. /// public object SyncRoot { get { try { return this; } catch (System.Exception ex) { throw new Exception("encountered problem getting synchronization object for " + GetType().FullName, ex); } } } /// /// Copies the elements of the to a , /// starting at a particular index. If the array is too small /// (we're storing more than max int items) then an exception will be thrown. /// /// /// /// The one-dimensional that is the destination of the elements /// copied from System.Collections.ICollection. The must have zero-based /// indexing. /// /// /// /// The zero-based index inn the array at which copying begins. /// /// /// /// The array is null. /// /// /// /// The index is less than zero. /// /// /// /// The array is multidimensional OR... /// the index is equal to or greater than the length of the array OR... /// the number of elements in the source System.Collections.ICollection is greater than the available /// space from index to the end of the destination array. /// /// /// /// The type of the source System.Collections.ICollection cannot be cast automatically /// to the type of the destination array. /// /// public void CopyTo(Array array, int index) { try { if (null == array) throw new ArgumentNullException("cannot copy to null array reference"); else if (index < 0) throw new ArgumentOutOfRangeException("cannot copy from negative index (" + index.ToString() + ")"); else if (array.Rank > 1) throw new ArgumentException("cannot copy to multidimensional array (parameter array rank is " + array.Rank.ToString() + ")"); else if ((ulong)(array.Length - index) < LargeCount) throw new ArgumentException("target array is too small (" + (array.Length - index).ToString() + " < " + LargeCount.ToString() + ")"); else { ulong source_index; int target_index; for (source_index = 0, target_index = index; source_index < LargeCount; target_index++, source_index++) { ((object[])array)[target_index] = this[source_index]; } } } catch (ArgumentNullException) { throw; } catch (ArgumentOutOfRangeException) { throw; } catch (ArgumentException) { throw; } catch (System.Exception ex) { throw new Exception("encountered problem copying to " + GetType().FullName, ex); } } // ******************************************************************* // ******************** END ICollection Interface ******************** // ******************************************************************* #endregion #region IEnumerable Interface // ********************************************************************* // ******************** BEGIN IEnumerable Interface ******************** // ********************************************************************* /// /// Returns an enumerator that iterates through a collection. /// /// /// /// An System.Collections.IEnumerator object that can be used to iterate through /// the collection. /// /// public IEnumerator GetEnumerator() { try { return new Enumerator(this); } catch (System.Exception ex) { throw new Exception("encountered problem getting iterator for " + GetType().FullName, ex); } } // ******************************************************************* // ******************** END IEnumerable Interface ******************** // ******************************************************************* #endregion #region IList Interface // *************************************************************** // ******************** BEGIN IList Interface ******************** // *************************************************************** /// /// Gets a value indicating whether the has a fixed size. /// Returns true if it has a fixed size; otherwise, false. /// public bool IsFixedSize { get { try { return true; } catch (System.Exception ex) { throw new Exception("encountered problem determining whether " + GetType().FullName + " has fixed size", ex); } } } /// /// Get a value indicating whether or not this /// object is read-only. Returns true if it is read-only; false otherwise. /// public bool IsReadOnly { get { try { return false; } catch (System.Exception ex) { throw new Exception("encountered problem determining read-only-ness of " + GetType().FullName, ex); } } } /// /// Get/set the element at the specified index. /// /// /// /// The zero-based index of the element to gat or sat. /// /// /// /// The element at the specified index. /// /// /// /// The index is not a valid index in the . /// /// /// /// The property is not set and the is read-only. /// /// public virtual object this[int index] { get { try { if (index < 0) throw new ArgumentOutOfRangeException("cannot access element at negative index (" + index.ToString() + ")"); else return this[(ulong)index]; } catch (ArgumentOutOfRangeException) { throw; } catch (NotSupportedException) { throw; } catch (System.Exception ex) { throw new Exception("encountered problem getting item at index " + index.ToString() + " in " + GetType().FullName, ex); } } set { try { if (index < 0) throw new ArgumentOutOfRangeException("cannot access element at negative index (" + index.ToString() + ")"); else if (IsReadOnly) throw new NotSupportedException("cannot assign value to read-only collection"); else this[(ulong)index] = (T)value; } catch (ArgumentOutOfRangeException) { throw; } catch (NotSupportedException) { throw; } catch (System.Exception ex) { throw new Exception("encountered problem setting item at index " + index.ToString() + " in " + GetType().FullName, ex); } } } /// /// Add an item to the . /// /// /// /// The to add to the . /// /// /// /// The position into which the new element was inserted. /// /// /// /// The is read-only OR... /// The has a fixed size. /// /// public int Add(object value) { try { throw new NotSupportedException("operation is not supported for read-only objects"); } catch (NotSupportedException ex) { throw ex; } catch (System.Exception ex) { throw new Exception("encountered problem adding item to " + GetType().FullName, ex); } } /// /// Remove all items from the . /// /// /// /// The is read-only. /// /// public void Clear() { try { // TODO: Add some status notification to this thing. for (ulong i = 0; i < LargeCount; i++) this[i] = DatumClearValue; } catch (NotSupportedException) { throw; } catch (System.Exception ex) { throw new Exception("encountered problem clearing " + GetType().FullName, ex); } } /// /// Determines whether the contains a specific value. /// /// /// /// The to locate in the . /// /// /// /// true if the is found in the ; /// false otherwise. /// /// public bool Contains(object value) { try { var itemFound = false; for (ulong i = 0; i < LargeCount && !itemFound; i++) if (this[i].Equals(value)) itemFound = true; return itemFound; } catch (System.Exception ex) { throw new Exception("encountered problem determining whether " + GetType().FullName + " contains the value: " + value.ToString(), ex); } } /// /// Determine the index of a specific item in the . /// /// /// /// The to be located in the . /// /// /// /// the index value if found in the list; -1 otherwise. /// /// public int IndexOf(object value) { try { var index = -1; var largeIndex = LargeIndexOf(value); if (null == largeIndex) index = -1; else if (largeIndex > int.MaxValue) throw new LargeOverflowException("index of object is to large to be represented by an int (" + largeIndex.ToString() + ")"); else index = (int)largeIndex; return index; } catch (System.Exception ex) { throw new Exception("encountered problem determining the index of object \"" + value.ToString() + "\" in " + GetType().FullName, ex); } } /// /// Insert an item into the at the specified index. /// /// /// /// The zero-based index at which the value should be inserted. /// /// /// /// The to insert into the . /// /// /// /// The index is not a valid index in the /// /// /// /// The is read-only OR... /// The has a fixed size. /// /// public void Insert(int index, object value) { try { throw new NotSupportedException("operation is not supported for read-only objects"); } catch (ArgumentOutOfRangeException) { throw; } catch (NotSupportedException) { throw; } catch (System.Exception ex) { throw new Exception("encountered problem inserting item \"" + value.ToString() + "\" into " + GetType().FullName + " at index " + index.ToString(), ex); } } /// /// Remove the first occurance of a specific object from the . /// /// /// /// The to be removed from the /// /// /// /// The is read-only OR... /// the has a fixed size. /// /// public void Remove(object value) { try { throw new NotSupportedException("operation is not supported for read-only objects"); } catch (NotSupportedException ex) { throw new Exception("encountered problme removing first occurance of object " + value.ToString() + " from " + GetType().FullName, ex); } } /// /// Remove the item at the specified index. /// /// /// /// The zero-based index of the item to remove. /// /// /// /// The index is not a valid index in the . /// /// /// /// The is read-only OR... /// the has a fixed size. /// /// public void RemoveAt(int index) { try { throw new NotSupportedException("operation is not supported for read-only objects"); } catch (NotSupportedException) { throw; } catch (ArgumentOutOfRangeException) { throw; } catch (System.Exception ex) { throw new Exception("encountered problem removing the " + GetType().FullName + " item at index " + index.ToString(), ex); } } // ************************************************************* // ******************** END IList Interface ******************** // ************************************************************* #endregion #region "Large" ICollection Interface // ***************************************************************************** // ******************** BEGIN "Large" ICollection Interface ******************** // ***************************************************************************** /// /// Gets the number of elements contained in the . /// /// public ulong LargeCount { get { try { return Size; } catch (System.Exception ex) { throw new Exception("encountered problem getting item count for " + GetType().FullName, ex); } } } /// /// Copies the elements of the to a , /// starting at a particular index. If the array is too small /// (we're storing more than max int items) then an exception will be thrown. /// /// /// /// The one-dimensional that is the destination of the elements /// copied from System.Collections.ICollection. The must have zero-based /// indexing. /// /// /// /// The zero-based index inn the array at which copying begins. /// /// /// /// The array is null. /// /// /// /// The array is multidimensional OR... /// the index is equal to or greater than the length of the array OR... /// the number of elements i nthe source System.Collections.ICollection is greater than the available /// space from index to the end of the destination array. /// /// /// /// The type of the source System.Collections.ICollection cannot be cast automatically /// to the type of the destination array. /// /// public void LargeCopyTo(Array array, ulong index) { try { if (null == array) throw new ArgumentNullException("cannot copy to null array reference"); else if (index < 0) throw new ArgumentOutOfRangeException("cannot copy from negative index (" + index.ToString() + ")"); else if (array.Rank > 1) throw new ArgumentException("cannot copy to multidimensional array (parameter array rank is " + array.Rank.ToString() + ")"); else if (((ulong)(array.Length) - index) < LargeCount) throw new ArgumentException("target array is too small (" + ((ulong)(array.Length) - index).ToString() + " < " + LargeCount.ToString() + ")"); else { ulong source_index; ulong target_index; for (source_index = 0, target_index = index; source_index < LargeCount; target_index++, source_index++) { ((object[])array)[target_index] = this[source_index]; } } } catch (ArgumentNullException) { throw; } catch (ArgumentOutOfRangeException) { throw; } catch (ArgumentException) { throw; } catch (System.Exception ex) { throw new Exception("encountered problem copying to " + GetType().FullName, ex); } } // *************************************************************************** // ******************** END "Large" ICollection Interface ******************** // *************************************************************************** #endregion #region "Large" IList Interface // *********************************************************************** // ******************** BEGIN "Large" IList Interface ******************** // *********************************************************************** /// /// Get/set the element at the specified index. /// /// /// /// The zero-based index of the element to gat or sat. /// /// /// /// The element at the specified index. /// /// /// /// The index is not a valid index in the . /// /// /// /// The property is not set and the is read-only. /// /// public T this[ulong index] { get { try { return GetDatumAtIndex(index); } catch (ArgumentOutOfRangeException) { throw; } catch (NotSupportedException) { throw; } catch (System.Exception ex) { throw new Exception("encountered problem getting item at index " + index.ToString() + " in " + GetType().FullName, ex); } } set { try { SetDatumAtIndex(value, index); } catch (ArgumentOutOfRangeException) { throw; } catch (NotSupportedException) { throw; } catch (System.Exception ex) { throw new Exception("encountered problem setting item at index " + index.ToString() + " in " + GetType().FullName, ex); } } } /// /// Add an item to the . /// /// /// /// The to add to the . /// /// /// /// The position into which the new element was inserted. /// /// /// /// The is read-only OR... /// The has a fixed size. /// /// public ulong LargeAdd(object value) { try { throw new NotSupportedException("operation is not supported for read-only objects"); } catch (NotSupportedException ex) { throw ex; } catch (System.Exception ex) { throw new Exception("encountered problem adding item to " + GetType().FullName, ex); } } /// /// Determine the index of a specific item in the . /// /// /// /// The to be located in the . /// /// /// /// the index value if found in the list; null otherwise. /// /// public ulong? LargeIndexOf(object value) { try { ulong? objectIndex = null; for (ulong i = 0; i < LargeCount && null == objectIndex; i++) if (this[i].Equals(value)) objectIndex = i; return objectIndex; } catch (System.Exception ex) { throw new Exception("encountered problem determining the index of object \"" + value.ToString() + "\" in " + GetType().FullName, ex); } } /// /// Insert an item into the at the specified index. /// /// /// /// The zero-based index at which the value should be inserted. /// /// /// /// The to insert into the . /// /// /// /// The index is not a valid index in the /// /// /// /// The is read-only OR... /// The has a fixed size. /// /// public void LargeInsert(ulong index, object value) { try { throw new NotSupportedException("operation is not supported for read-only objects"); } catch (ArgumentOutOfRangeException ex) { throw ex; } catch (NotSupportedException ex) { throw ex; } catch (System.Exception ex) { throw new Exception("encountered problem inserting item \"" + value.ToString() + "\" into " + GetType().FullName + " at index " + index.ToString(), ex); } } /// /// Remove the item at the specified index. /// /// /// /// The zero-based index of the item to remove. /// /// /// /// The index is not a valid index in the . /// /// /// /// The is read-only OR... /// the has a fixed size. /// /// public void LargeRemoveAt(ulong index) { try { throw new NotSupportedException("operation is not supported for read-only objects"); } catch (NotSupportedException) { throw; } catch (ArgumentOutOfRangeException) { throw; } catch (System.Exception ex) { throw new Exception("encountered problem removing the " + GetType().FullName + " item at index " + index.ToString(), ex); } } // ********************************************************************* // ******************** END "Large" IList Interface ******************** // ********************************************************************* #endregion #region ICloneable Interface // ******************************************************************** // ******************** BEGIN ICloneable Interface ******************** // ******************************************************************** /// /// create a new object that is a copy of the current instance /// /// a new object of the same type with all objects duplicated or referenced public abstract object Clone(); // ****************************************************************** // ******************** END ICloneable Interface ******************** // ****************************************************************** #endregion } }