Files
DP44/Common/DTS.Common.Utilities/FileMapViewArray.cs
2026-04-17 14:55:32 -04:00

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
}
}