194 lines
6.5 KiB
C#
194 lines
6.5 KiB
C#
|
|
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
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// Allows a view of a memory mapped file
|
|||
|
|
/// to be accessed via the index[] operator.
|
|||
|
|
/// </summary>
|
|||
|
|
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
|
|||
|
|
/// <summary>
|
|||
|
|
/// Clears all buffers for the stream and causes
|
|||
|
|
/// any unbuffered data to be written to the
|
|||
|
|
/// underlying device.
|
|||
|
|
/// </summary>
|
|||
|
|
public void forceFlush()
|
|||
|
|
{
|
|||
|
|
_view.Flush();
|
|||
|
|
}
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region Public Indexor
|
|||
|
|
/// <summary>
|
|||
|
|
/// Returns a the byte at the requested index of the MemoryMap.
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="index"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Master Constructor
|
|||
|
|
/// This constructor allows for specifying privilages
|
|||
|
|
/// for the stream and mapped file.
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="fileName">File from which to create mapping</param>
|
|||
|
|
/// <param name="access">Access level for the map view in memory. Must be consisten with the file mapping access.</param>
|
|||
|
|
/// <param name="protection">Access level for the file mapping.</param>
|
|||
|
|
/// <param name="fileMapSize">The size in bytes of the file mapping. Pass 0 to map the entire (existing and length>0) file.</param>
|
|||
|
|
/// <param name="offset">Off set from start of mapped file to start the view.</param>
|
|||
|
|
/// <param name="viewSize">The size of the view in memory. This is limited by physical system resources.</param>
|
|||
|
|
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
|
|||
|
|
}
|
|||
|
|
}
|