using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using DTS.Common.DAS.Concepts.DAS.Channel; using DTS.Common.Exceptions; using DTS.Common.Utilities; using DTS.Common.Utilities.DotNetProgrammingConstructs; using DTS.Common.Utilities.IO.MemoryMap; namespace DTS.Serialization.SliceRaw { public partial class File { /// /// Representation of a timestamp. Changes made to this representation /// are immediately manifest in the associated timestamp file. /// public partial class PersistentDasTimestamp : ExceptionalList, ILargeDataAware, IDasTimestampHeader, IDisposable { /// /// The window into this persistent timestamp's soul. /// protected FileMapViewArray ViewArray { get => _ViewArray.Value; set => _ViewArray.Value = value; } private readonly Property _ViewArray = new Property( typeof(PersistentDasTimestamp).Namespace + ".File.PersistentDasTimestamp.ViewArray", null, false ); /// /// Get value indicating whether or not this class has been /// preinitialized from an existing serialization. /// protected bool IsInitializedFromFile { get => _IsInitializedFromFile.Value; private set => _IsInitializedFromFile.Value = value; } private readonly Property _IsInitializedFromFile = new Property( typeof(PersistentDasTimestamp).Namespace + ".File.PersistentDasTimestamp.IsInitializedFromFile", false, false ); private readonly object LockObject; /// /// Initialize an instance of this class. /// /// /// /// The filename of the associated timestamp data file. /// /// /// /// The timestamp header to be written into the associated timestamp data file. /// /// public PersistentDasTimestamp(string filename, BinaryDasTimestampHeader dasHeader, bool overwriteIfExists) { LockObject = new object(); try { IsInitializedFromFile = System.IO.File.Exists(Filename = filename); IsMemoryMapped = false; if (IsInitializedFromFile && !overwriteIfExists) return; if (null == dasHeader) throw new ArgumentNullException("das header is required to generate new file but none was provided"); lock (LockObject) { using (var fileWriter = new BinaryWriter(new FileStream(Filename, FileMode.OpenOrCreate))) { var fields = Enum.GetValues(typeof(Field)).Cast().ToArray(); // convert to byte[] foreach (var field in fields) { switch (field) { case Field.BeginningOfData: break; case Field.BeginningOfFile: break; case Field.Crc32: fileWriter.Write(dasHeader.Crc32); // public UInt32 break; case Field.HeaderVersionNumber: fileWriter.Write(this.dasHeader.HeaderVersionNumber = BinaryDasTimestampHeader.CurrentVersionNumber); _HeaderVersionNumberInitialized = true; break; case Field.MagicKey: fileWriter.Write(this.dasHeader.MagicKey = BinaryDasTimestampHeader.RequiredMagicKey); _MagicKeyInitialized = true; break; case Field.NumberOfBitsPerSample: fileWriter.Write(this.dasHeader.NumberOfBitsPerSample = dasHeader.NumberOfBitsPerSample); _NumberOfBitsPerSampleInitialized = true; break; case Field.NumberOfSamples: fileWriter.Write(this.dasHeader.NumberOfSamples = dasHeader.NumberOfSamples); _NumberOfSamplesInitialized = true; break; case Field.OffsetOfSampleDataStart: fileWriter.Write(this.dasHeader.OffsetOfSampleDataStart = dasHeader.OffsetOfSampleDataStart); _OffsetOfSampleDataStartInitialized = true; break; default: throw new NotSupportedException("SliceRaw::File::PersistentDasTimestamp(filename, dasHeader, overwriteIfExists) field not supported: " + field.ToString()); } } } if (0 == dasHeader.OffsetOfSampleDataStart) { //inconcievable. header data exists at the beginning. recalculate. dasHeader.OffsetOfSampleDataStart = (ulong)GetFileOffsetOf(Field.BeginningOfData); OffsetOfSampleDataStart = dasHeader.OffsetOfSampleDataStart; } } } catch (System.Exception ex) { throw new Exception("encountered problem constructing " + GetType().FullName, ex); } } // xxx Make this a recursive function, with the terminating condition being a call with the first case, which // returns the offset of "start of file". // public enum Field { BeginningOfFile, // UInt32 MagicKey, // public UInt32 HeaderVersionNumber, // public UInt32 OffsetOfSampleDataStart, // public UInt64 NumberOfSamples, // public UInt64 NumberOfBitsPerSample, // publid UInt32 Crc32, // public UInt32 BeginningOfData } private int GetSizeOfV1(Field field) { switch (field) { case Field.BeginningOfData: return 0; case Field.BeginningOfFile: return sizeof(uint); case Field.Crc32: return sizeof(uint); case Field.HeaderVersionNumber: return sizeof(uint); case Field.MagicKey: return sizeof(uint); case Field.NumberOfSamples: return sizeof(ulong); case Field.NumberOfBitsPerSample: return sizeof(uint); case Field.OffsetOfSampleDataStart: return sizeof(ulong); } return 0; } private int GetFileOffsetOfV1(Field field) { var previousField = (Field)((int)field - 1); var offset = GetFileOffsetOf(previousField) + GetSizeOfV1(previousField); return offset; } /// /// if field is one of the first 3, the offset doesn't change /// however for the rest we need to know the header version number and then we /// can get the file offset /// /// /// public int GetFileOffsetOf(Field field) { try { var offset = 0; //if we are header version number or less, the fields don't change position ... if (field == Field.HeaderVersionNumber || field == Field.MagicKey || field == Field.BeginningOfFile) { switch (field) { case Field.BeginningOfFile: offset = 0; break; case Field.MagicKey: offset = GetFileOffsetOf(Field.BeginningOfFile); break; case Field.HeaderVersionNumber: offset = GetFileOffsetOf(Field.MagicKey) + sizeof(uint); break; } } else { switch (HeaderVersionNumber) { case 0x01: return GetFileOffsetOfV1(field); } } return offset; } catch (System.Exception ex) { throw new Exception("encountered problem getting file offset for property", ex); } } /// /// Get value indicating whether or not all necessary properties of this /// class have been initialized to make this object useable. /// public bool IsInitialized { get { try { return _HeaderVersionNumberInitialized && _MagicKeyInitialized && _NumberOfSamplesInitialized && _NumberOfBitsPerSampleInitialized && _OffsetOfSampleDataStartInitialized; } catch (System.Exception ex) { throw new Exception("encountered problem determining initialization status", ex); } } } public bool IsDataArraySized => NumberOfSamples < int.MaxValue; /// /// Object to cache header property accesses. /// public BinaryDasTimestampHeader dasHeader = new BinaryDasTimestampHeader(); /// /// "Magic Key" relatively-UID for our binary file type. /// public uint MagicKey { get { try { if (!IsInitialized && !IsInitializedFromFile) throw new SliceRaw.File.PersistentChannel.NotInitializedException("object initialization has not completed"); if (!_MagicKeyInitialized) { lock (LockObject) { long valueOffset = GetFileOffsetOf(Field.MagicKey); using (var fileReader = new BinaryReader(new FileStream(Filename, FileMode.Open))) { var prevPosition = fileReader.BaseStream.Position; fileReader.BaseStream.Position = valueOffset; dasHeader.MagicKey = fileReader.ReadUInt32(); fileReader.BaseStream.Position = prevPosition; } } } return dasHeader.MagicKey; } catch (System.Exception ex) { throw new Exception("encountered problem getting " + GetType().FullName + ".MagicKey property value", ex); } } set { try { lock (LockObject) { var offset = GetFileOffsetOf(Field.MagicKey); using (var fileWriter = new BinaryWriter(new FileStream(Filename, FileMode.Open))) { fileWriter.Seek(offset, SeekOrigin.Begin); fileWriter.Write(dasHeader.MagicKey = value); } } _MagicKeyInitialized = true; // If at this point the persistent timestamp object has been initialized, go ahead and // keep the CRC current. if (IsInitialized) Crc32 = dasHeader.Crc32; } catch (System.Exception ex) { throw new Exception("encountered problem setting " + GetType().FullName + ".MagicKey property value", ex); } } } private bool _MagicKeyInitialized = false; /// /// header version number for our binary file type. /// public uint HeaderVersionNumber { get { try { if (!IsInitialized && !IsInitializedFromFile) throw new SliceRaw.File.PersistentChannel.NotInitializedException("object initialization has not completed"); if (!_HeaderVersionNumberInitialized) { lock (LockObject) { long valueOffset = GetFileOffsetOf(Field.HeaderVersionNumber); using (var fileReader = new BinaryReader(new FileStream(Filename, FileMode.Open))) { var prevPosition = fileReader.BaseStream.Position; fileReader.BaseStream.Position = valueOffset; dasHeader.HeaderVersionNumber = fileReader.ReadUInt32(); fileReader.BaseStream.Position = prevPosition; } } } return dasHeader.HeaderVersionNumber; } catch (System.Exception ex) { throw new Exception("encountered problem getting " + GetType().FullName + ".HeaderVersionNumber property value", ex); } } set { try { lock (LockObject) { var offset = GetFileOffsetOf(Field.HeaderVersionNumber); using (var fileWriter = new BinaryWriter(new FileStream(Filename, FileMode.Open))) { fileWriter.Seek(offset, SeekOrigin.Begin); fileWriter.Write(dasHeader.HeaderVersionNumber = value); } } _HeaderVersionNumberInitialized = true; // If at this point the persistent timestamp object has been initialized, go ahead and // keep the CRC current. if (IsInitialized) Crc32 = dasHeader.Crc32; } catch (System.Exception ex) { throw new Exception("encountered problem setting " + GetType().FullName + ".HeaderVersionNumber property value", ex); } } } private bool _HeaderVersionNumberInitialized = false; /// /// Get/set offset of sample data start value. /// public ulong OffsetOfSampleDataStart { get { try { if (!IsInitialized && !IsInitializedFromFile) throw new SliceRaw.File.PersistentChannel.NotInitializedException("object initialization has not completed"); if (!_OffsetOfSampleDataStartInitialized) { lock (LockObject) { long valueOffset = GetFileOffsetOf(Field.OffsetOfSampleDataStart); using (var fileReader = new BinaryReader(new FileStream(Filename, FileMode.Open))) { var prevPosition = fileReader.BaseStream.Position; fileReader.BaseStream.Position = valueOffset; dasHeader.OffsetOfSampleDataStart = fileReader.ReadUInt64(); fileReader.BaseStream.Position = prevPosition; _OffsetOfSampleDataStartInitialized = true; } } } return dasHeader.OffsetOfSampleDataStart; } catch (System.Exception ex) { throw new Exception("encountered problem getting " + GetType().FullName + ".OffsetOfSampleDataStart property value", ex); } } set { try { lock (LockObject) { var offset = GetFileOffsetOf(Field.OffsetOfSampleDataStart); //APILogger.Log("writing 9 ", Filename); using (var fileWriter = new BinaryWriter(new FileStream(Filename, FileMode.Open))) { fileWriter.Seek(offset, SeekOrigin.Begin); fileWriter.Write(dasHeader.OffsetOfSampleDataStart = value); } } _OffsetOfSampleDataStartInitialized = true; // If at this point the persistent timestamp object has been initialized, go ahead and // keep the CRC current. if (IsInitialized) Crc32 = dasHeader.Crc32; } catch (System.Exception ex) { throw new Exception("encountered problem setting " + GetType().FullName + ".OffsetOfSampleDataStart property value", ex); } } } private bool _OffsetOfSampleDataStartInitialized = false; /// /// Get/set number of samples. /// public ulong NumberOfSamples { get { try { if (!IsInitialized && !IsInitializedFromFile) throw new SliceRaw.File.PersistentChannel.NotInitializedException("object initialization has not completed"); if (!_NumberOfSamplesInitialized) { lock (LockObject) { long valueOffset = GetFileOffsetOf(Field.NumberOfSamples); using (var fileReader = new BinaryReader(new FileStream(Filename, FileMode.Open))) { var prevPosition = fileReader.BaseStream.Position; fileReader.BaseStream.Position = valueOffset; dasHeader.NumberOfSamples = fileReader.ReadUInt64(); fileReader.BaseStream.Position = prevPosition; _NumberOfSamplesInitialized = true; } } } return dasHeader.NumberOfSamples; } catch (System.Exception ex) { throw new Exception("encountered problem getting " + GetType().FullName + ".NumberOfSamples property value", ex); } } set { try { lock (LockObject) { var offset = GetFileOffsetOf(Field.NumberOfSamples); using (var fileWriter = new BinaryWriter(new FileStream(Filename, FileMode.Open))) { fileWriter.Seek(offset, SeekOrigin.Begin); fileWriter.Write(dasHeader.NumberOfSamples = value); } } _NumberOfSamplesInitialized = true; // If at this point the persistent timestamp object has been initialized, go ahead and // keep the CRC current. if (IsInitialized) Crc32 = dasHeader.Crc32; } catch (System.Exception ex) { throw new Exception("encountered problem setting " + GetType().FullName + ".NumberOfSamples property value", ex); } } } private bool _NumberOfSamplesInitialized = false; /// /// Get/set number of bits per sample. /// public uint NumberOfBitsPerSample { get { try { if (!IsInitialized && !IsInitializedFromFile) throw new SliceRaw.File.PersistentChannel.NotInitializedException("object initialization has not completed"); if (!_NumberOfBitsPerSampleInitialized) { //APILogger.Log("reading 11 ", Filename); lock (LockObject) { long valueOffset = GetFileOffsetOf(Field.NumberOfBitsPerSample); using (var fileReader = new BinaryReader(new FileStream(Filename, FileMode.Open))) { var prevPosition = fileReader.BaseStream.Position; fileReader.BaseStream.Position = valueOffset; dasHeader.NumberOfBitsPerSample = fileReader.ReadUInt32(); fileReader.BaseStream.Position = prevPosition; _NumberOfBitsPerSampleInitialized = true; } } } return dasHeader.NumberOfBitsPerSample; } catch (System.Exception ex) { throw new Exception("encountered problem getting " + GetType().FullName + ".NumberOfBitsPerSample property value", ex); } } set { try { lock (LockObject) { var offset = GetFileOffsetOf(Field.NumberOfBitsPerSample); using (var fileWriter = new BinaryWriter(new FileStream(Filename, FileMode.Open))) { fileWriter.Seek(offset, SeekOrigin.Begin); fileWriter.Write(dasHeader.NumberOfBitsPerSample = value); } } _NumberOfBitsPerSampleInitialized = true; // If at this point the persistent timestamp object has been initialized, go ahead and // keep the CRC current. if (IsInitialized) Crc32 = dasHeader.Crc32; } catch (System.Exception ex) { throw new Exception("encountered problem setting " + GetType().FullName + ".NumberOfBitsPerSample property value", ex); } } } private bool _NumberOfBitsPerSampleInitialized = false; /// /// size of view into memory-mapped file. /// protected int ViewSize { get; set; } private bool _IsViewSizeInitialized = false; /// /// to indicate whether or not a memory mapping has already been /// established for this object. /// protected bool IsMemoryMapped { get; set; } /// /// Get the length of this persistent timestamp's data. /// public long Length { get { try { if (NumberOfSamples > long.MaxValue) throw new ApplicationException("ulong overflowed while being cast to long"); return (long)NumberOfSamples; } catch (System.Exception ex) { throw new Exception("encountered problem determining total amount of timestamp data", ex); } } } /// /// Get the data count of this persistent timestamp's data. /// public new int Count { get { try { if (Length > int.MaxValue) throw new ApplicationException("long overflowed while being cast to int"); return (int)Length; } catch (System.Exception ex) { throw new Exception("encountered problem getting timestamp data count", ex); } } } /// /// Override long-indexing here. Return a sample of the underlying array. /// /// /// /// The index of the sample being affected. /// /// /// /// The sample at the specified index. /// /// public short this[long i] { get { try { return this[(ulong)i]; } catch (System.Exception ex) { throw new Exception("encountered problem getting long-indexable value at index " + i.ToString(), ex); } } set { try { this[(ulong)i] = value; } catch (System.Exception ex) { throw new Exception("encountered problem setting long-indexable value at index " + i.ToString(), ex); } } } public void UnSet() { lock (LockObject) { _Data.Value = new short[0]; _Data.UnInitialize(); } } // Override indexing operator here, return a sample of the underlying array. public short this[ulong i] { get { lock (LockObject) { try { var offsetSampleDataStart = OffsetOfSampleDataStart; if (!_IsViewSizeInitialized) { // // Memory map array object map view paging math seems to be a bit // screwed. If you don't make the page exactly the same size as the // system memory allocation granularity, it will start returning trash // data after the first page fault. // const int StandardPageSize = 65536; if (NumberOfSamples + offsetSampleDataStart > StandardPageSize) { ViewSize = StandardPageSize; } else { // // If the map size exceeds the file size, we'll get strange COM error // codes. // var numberOfDataBytes = NumberOfSamples * 2; if (numberOfDataBytes + offsetSampleDataStart > int.MaxValue) throw new Exception("overflowed view size variable (max value: " + int.MaxValue.ToString() + ") when downcasting number of bytes in test data (" + numberOfDataBytes.ToString() + ")"); ViewSize = (int)(numberOfDataBytes + offsetSampleDataStart); } _IsViewSizeInitialized = true; } // MMBAD: Possible inefficiency. Perhaps either for "debug" build, or find some other way // to verify this outside of the array access. // //if ( !IsInitialized && !IsInitializedFromFile ) // throw new NotInitializedException( "object initialization has not completed" ); //else if ( !IsMemoryMapped ) if (!IsMemoryMapped) { var fileInfo = new FileInfo(Filename); // Make a new mapViewArray for the file with proper offset and // readonly privilages to protect sample integrity. ViewArray = new FileMapViewArray(Path.GetFullPath(Filename), MapAccess.FileMapRead, MapProtection.PageReadOnly, offsetSampleDataStart, fileInfo.Length, ViewSize); IsMemoryMapped = true; } //http://manuscript.dts.local/f/cases/16463/HDF-export-runs-very-slowly long osdsLong = (long)(offsetSampleDataStart + 2 * i); return (short)((ViewArray[osdsLong] & 0xFFFF) + ((ViewArray[osdsLong + 1L] & 0xFFFF) << 8)); } catch (System.Exception ex) { throw new Exception("encountered problem getting short value at index " + i.ToString(), ex); } } } set { try { throw new NotSupportedException("SliceRaw::File::PersistentDasTimestamp::[] Sample Data is READ_ONLY."); } catch (System.Exception ex) { throw new Exception("encountered problem setting short value at index " + i.ToString(), ex); } } } /// /// Some older files will need this format to be invoked to fix slight formatting problems. /// public void RecalculateDataOffset() { try { // // Older SliceWARE data files have incorrect values for the offset of sample data start, // and number of data samples. Adjust them or we may run into trouble later. // if (0 == OffsetOfSampleDataStart) { var realDataStart = GetFileOffsetOf(Field.BeginningOfData); OffsetOfSampleDataStart = (ulong)realDataStart; NumberOfSamples -= (ulong)realDataStart; } } catch (System.Exception ex) { throw new Exception("encountered problem recalculating data offset", ex); } } /// /// Get/set the filename of the associated timestamp data file. /// public string Filename { get => _Filename.Value; private set => _Filename.Value = value; } private readonly Property _Filename = new Property( typeof(PersistentDasTimestamp).Namespace + ".File.PreAllocatedChannel.Filename", null, false ); public void ReplaceData(short[] newData) { if (newData.Length != _Data.Value.Length) { return; } _Data.Value = newData; try { lock (LockObject) { var offset = GetFileOffsetOf(Field.BeginningOfData); using (var fileWriter = new BinaryWriter(new FileStream(Filename, FileMode.Open))) { fileWriter.Seek(offset, SeekOrigin.Begin); foreach (var dataShort in newData) { fileWriter.Write(dataShort); } } } // If at this point the persistent timestamp object has been initialized, go ahead and // keep the CRC current. if (IsInitialized) Crc32 = dasHeader.Crc32; } catch (System.Exception ex) { throw new Exception("encountered problem replacing data", ex); } } /// /// Get/set data array value. /// public short[] Data { get { var lastDataSpot = -1; try { if (!IsInitialized && !IsInitializedFromFile) throw new SliceRaw.File.PersistentChannel.NotInitializedException("object initialization has not completed"); if (!IsDataArraySized) throw new SliceRaw.File.PersistentChannel.DataTooBigForArrayException("Data size (" + Count.ToString() + ") is too large to be safely handled within the application"); if (!_Data.IsInitialized) { // // If we haven't handed out one of these yet, make one up and then hang onto it // to use for subsequent requests to we don't have clones all over the place. // var dataArray = new short[Count]; for (var i = 0; i < dataArray.Length; i++) { lastDataSpot = i; dataArray[i] = this[i]; } _Data.Value = dataArray; } return _Data.Value; } catch (System.Exception ex) { string msg = string.Empty; if (lastDataSpot > 0) { throw new OutOfDataException($"Problem accessing sample {lastDataSpot} - {GetType().FullName} {ex}", lastDataSpot); } throw new Exception("encountered problem getting " + GetType().FullName + ".Data property value", ex); } } set { try { _Data.Value = value; } catch (System.Exception ex) { throw new Exception("encountered problem setting " + GetType().FullName + ".Data property value", ex); } } } private readonly Property _Data = new Property( typeof(PersistentDasTimestamp).Namespace + ".File.PreAllocatedChannel.Data", null, false ); /// /// Get/set binary writer to be used by data append session. /// private BinaryWriter AppendSessionWriter { get; set; } /// /// Prepare this object for repeated calls to AppendSessionData. /// public void BeginAppendSession() { try { AppendSessionWriter = new BinaryWriter(new FileStream(Filename, FileMode.Open)); } catch (System.Exception ex) { throw new Exception("encountered problem beginning data append session", ex); } } /// /// Secure object from repeated calls to AppendSessionData. /// public void EndAppendSession() { if (null == AppendSessionWriter) return; //don't repeat try { AppendSessionWriter.Close(); AppendSessionWriter.Dispose(); AppendSessionWriter = null; } catch (System.Exception ex) { throw new Exception("encountered problem ending data append session", ex); } } public int MaxAppendToWrite { get; set; } = 4; /// /// the timestamp data is striped over channel shorts /// List stampData = new List(); /// /// Append data to this das timestamp. Must be bracketed by BeginAppendSession and /// EndAppendSession method invocations. /// /// /// /// The striped data to be appended to this das timestamp. /// /// public void AppendSessionData(short[] data) { try { lock (LockObject) { stampData.Add(data); if (MaxAppendToWrite == stampData.Count) { //got all of our striped data, write this chunk AppendSessionWriter.Seek(0, SeekOrigin.End); var maxLength = stampData.Select(d => d.Length).Max(); for (var j = 0; j < maxLength; j++) { for (var i = 0; i < stampData.Count; i++) AppendSessionWriter.Write(j < stampData[i].Length ? stampData[i][j] : (short)0); } stampData.Clear(); } } } catch (System.Exception ex) { throw new Exception("encountered problem appending data to persistent timestamp representation", ex); } } /// /// Append data to this persistent timestamp. To be used for one-time applications. If we're streaming /// data to this object, AppendSessionData is more efficient. /// /// /// /// The data to be appended to this persistent timestamp. /// /// public void DisabledAppendData(short[] data) { try { lock (LockObject) { using (var fileWriter = new BinaryWriter(new FileStream(Filename, FileMode.Open))) { fileWriter.Seek(0, SeekOrigin.End); for (var i = 0; i < data.Length; i++) fileWriter.Write(data[i]); } } } catch (System.Exception ex) { throw new Exception("encountered problem appending data to persistent timestamp representation", ex); } } /// /// Get the CRC for the current state of the header. /// public uint Crc32 { get { try { if (!IsInitialized) throw new SliceRaw.File.PersistentChannel.NotInitializedException("object initialization has not completed"); return dasHeader.Crc32; } catch (System.Exception ex) { throw new Exception("encountered problem getting " + GetType().FullName + ".Crc32 property value", ex); } } private set { try { lock (LockObject) { { var offset = GetFileOffsetOf(Field.Crc32); using (var fileWriter = new BinaryWriter(new FileStream(Filename, FileMode.Open))) { fileWriter.Seek(offset, SeekOrigin.Begin); fileWriter.Write(value); } } } } catch (System.Exception ex) { throw new Exception("encountered problem setting " + GetType().FullName + ".Crc32 property", ex); } } } /// /// Populate the persistent CRC property with the CRC of the current state of the timestamp header. /// public void StampCrc() { try { // // Calling the "get" accessor for all properties will ensure that they // are populated, which is important if the CRC is going to be running // over them. // var header = new BinaryDasTimestampHeader(); header.MagicKey = MagicKey; header.HeaderVersionNumber = HeaderVersionNumber; header.OffsetOfSampleDataStart = OffsetOfSampleDataStart; header.NumberOfSamples = NumberOfSamples; Crc32 = header.Crc32; } catch (System.Exception ex) { throw new Exception("encountered problem stamping persistent timestamp CRC", ex); } } /// /// Dispose of this object's Disposables. /// public void Dispose() { lock (LockObject) { try { if (_ViewArray.IsInitialized) { IsMemoryMapped = false; //Is this right? } try { ViewArray?.Dispose(); } catch (System.Exception) { } } catch (System.Exception ex) { // If the ViewArray was not yet initialized, that's OK. Eat the exception if (null == ex.InnerException || ex.InnerException.GetType() != typeof(Property.NotInitializedException)) { throw new Exception("encountered problem disposing of " + GetType().FullName, ex); } } } } } } }