using System; using System.IO; using System.Collections.Generic; using System.Text; using DTS.Common.Utilities; using DTS.Common.Utilities.Logging; namespace DTS.Common.Utilities.IO.MemoryMap { /// /// Allows a view of a memory mapped file /// to be accessed via the index[] operator. /// public class FileMapViewArray : IDisposable { #region privates const long systemOffsetSize = 65536; protected Stream _view; protected MemoryMappedFile _file; protected MapAccess _mapAccess; protected byte[] buffer; protected long _mapSize; protected long _offsetOfViewFromStartOfMap; protected int _viewSize; void ResetView(long newViewStart) { // Make sure we scale the last view of the file to not overrun the end. var newViewSize = _mapSize - newViewStart > _viewSize ? _viewSize : (int)(_mapSize - newViewStart); if (newViewSize < 1) newViewSize = 0x100; // Moves the view if we reach the end. _view.Close(); _view = _file.MapView(_mapAccess, newViewStart, newViewSize); _offsetOfViewFromStartOfMap = newViewStart; } #endregion #region public /// /// Clears all buffers for the stream and causes /// any unbuffered data to be written to the /// underlying device. /// public void forceFlush() { _view.Flush(); } #endregion #region Public Indexor /// /// Returns a the byte at the requested index of the MemoryMap. /// /// /// public byte this[long index] { get { if (index >= _mapSize || index < 0) throw new Exception("Index is out of range."); try { // Get us to the right place var position = index - _offsetOfViewFromStartOfMap; if (position < _view.Length && position >= 0) { _view.Seek(position, SeekOrigin.Begin); return (byte)_view.ReadByte(); } } catch (FileMapIOException) { } // We overran the view; move it: // Overunning means index is one (or more) greater (or less) than the greatest // 'valid' index, so we need to refocus to one back from where we are now. // Reset the view to the page containing the out-of-bounds index. //this.ResetView(index); ResetView(index / systemOffsetSize * systemOffsetSize); // Try again. return this[index]; } set { try { _view.Seek(index, SeekOrigin.Begin); _view.WriteByte(value); } catch (FileMapIOException) { ResetView(index); throw; } } } #endregion #region Constructors /// /// Master Constructor /// This constructor allows for specifying privilages /// for the stream and mapped file. /// /// File from which to create mapping /// Access level for the map view in memory. Must be consisten with the file mapping access. /// Access level for the file mapping. /// The size in bytes of the file mapping. Pass 0 to map the entire (existing and length>0) file. /// Off set from start of mapped file to start the view. /// The size of the view in memory. This is limited by physical system resources. public FileMapViewArray(string fileName, MapAccess access, MapProtection protection, ulong offset, long fileMapSize, int viewSize) { long mapSize; //Check file exists and length > 0; if (File.Exists(fileName) && new FileInfo(Path.GetFullPath(fileName)).Length != 0) //passing 0 will map the whole file mapSize = fileMapSize != 0 ? fileMapSize : Path.GetFullPath(fileName).Length; else //file didn't exist or length==0, doesn't matter, gotta create it with correct size { //can't pass 0 here if (fileMapSize == 0) throw new Exception("Cannot map an empty or non-existent file."); else { // fileMapSize used here just to get a file // with a non-trivial size. File.Create(fileName, (int)fileMapSize); //a value was passed for the size of the map mapSize = fileMapSize; } } var map = MemoryMappedFile.Create(fileName, protection, mapSize); var view = map.MapView(access, (long)(offset / systemOffsetSize * systemOffsetSize), (int)System.Math.Min(mapSize, viewSize)); buffer = new byte[1]; _mapAccess = access; _viewSize = viewSize; _mapSize = mapSize; _file = map; _view = view; } #endregion #region IDisposable Members public void Dispose() { try { //this._file.Close(); // RW 2896 - added close to dispose //CPB DM - this should happen post view flush() // flush view then close file. _view.Flush(); _file.Close(); _view.Close(); } catch (Exception ex) { APILogger.Log(ex); //the stream was probably closed by .NET } _view.Dispose(); } #endregion } }