Big changes
This commit is contained in:
324
Source/TSRealLib/Common/Raytheon.Common/Lib/DataBuffer.cs
Normal file
324
Source/TSRealLib/Common/Raytheon.Common/Lib/DataBuffer.cs
Normal file
@@ -0,0 +1,324 @@
|
||||
// UNCLASSIFIED
|
||||
/*-------------------------------------------------------------------------
|
||||
RAYTHEON PROPRIETARY: THIS DOCUMENT CONTAINS DATA OR INFORMATION
|
||||
PROPRIETARY TO RAYTHEON COMPANY AND IS RESTRICTED TO USE ONLY BY PERSONS
|
||||
AUTHORIZED BY RAYTHEON COMPANY IN WRITING TO USE IT. DISCLOSURE TO
|
||||
UNAUTHORIZED PERSONS WOULD LIKELY CAUSE SUBSTANTIAL COMPETITIVE HARM TO
|
||||
RAYTHEON COMPANY'S BUSINESS POSITION. NEITHER SAID DOCUMENT NOR ITS
|
||||
CONTENTS SHALL BE FURNISHED OR DISCLOSED TO OR COPIED OR USED BY PERSONS
|
||||
OUTSIDE RAYTHEON COMPANY WITHOUT THE EXPRESS WRITTEN APPROVAL OF RAYTHEON
|
||||
COMPANY.
|
||||
|
||||
THIS PROPRIETARY NOTICE IS NOT APPLICABLE IF DELIVERED TO THE U.S.
|
||||
GOVERNMENT.
|
||||
|
||||
UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
|
||||
-------------------------------------------------------------------------*/
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Raytheon.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// this class is a container. It acts like an array that allows bytes to be removed from its
|
||||
/// front. When bytes are removed, the beginning of the buffer moves to the byte following
|
||||
/// those which were removed. A typical use of this is to AddData(), then use CheckOutStartOfData()/CheckInStartOfData to get
|
||||
/// a pointer to the data and the amount of bytes that the pointer points to.
|
||||
/// </summary>
|
||||
public class DataBuffer: IDisposable
|
||||
{
|
||||
#region PrivateClassMembers
|
||||
private unsafe byte[] _buffer;
|
||||
private uint _endOfDataIndex; // index of last used byte
|
||||
private uint _startOfDataIndex = uint.MaxValue; // index of first used byte, MaxValue means that buffer is empty
|
||||
private uint _startOfCheckedOutDataIndex = uint.MaxValue; // index of first used byte, MaxValue means that buffer is empty
|
||||
|
||||
private static object _syncObj = new Object();
|
||||
private IntPtr _pStartOfBuffer;
|
||||
private IntPtr _pStartOfCheckedOutData;
|
||||
private GCHandle _pinnedArray;
|
||||
#endregion
|
||||
|
||||
#region PublicFuctions
|
||||
|
||||
/// <summary>
|
||||
/// The constructor which allocates a byte array and pins it so we can return byte*s in StartOfData()
|
||||
/// </summary>
|
||||
/// <param name="bufferSize">The number of bytes in the data buffer</param>
|
||||
public DataBuffer(uint bufferSize)
|
||||
{
|
||||
_buffer = new byte[bufferSize];
|
||||
_pinnedArray = GCHandle.Alloc(_buffer, GCHandleType.Pinned);
|
||||
_pStartOfBuffer = _pinnedArray.AddrOfPinnedObject();
|
||||
_pStartOfCheckedOutData = IntPtr.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The finalizer
|
||||
/// </summary>
|
||||
~DataBuffer()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// adds bytes to the buffer
|
||||
/// throws exception if requesting to add more bytes than entire buffer will hold.
|
||||
/// throws an exception if data is checked out and this call attempts to overwrite the checked out data
|
||||
/// </summary>
|
||||
/// <param name="data">array from which to add bytes to buffer</param>
|
||||
/// <param name="numBytesToAdd"># if bytes from array to add to buffer</param>
|
||||
public void Add(byte[] data, uint numBytesToAdd)
|
||||
{
|
||||
// get exclusive access to array, indexes...
|
||||
lock (_syncObj)
|
||||
{
|
||||
uint numBytesCurrentlyUsed = BytesUsed;
|
||||
|
||||
// not enough room in entire buffer?
|
||||
if (numBytesToAdd > (_buffer.Length - numBytesCurrentlyUsed))
|
||||
{
|
||||
throw new Exception("DataBuffer::Add() - Not enough room in buffer for data - need to increase size of buffer");
|
||||
}
|
||||
|
||||
// empty buffer?
|
||||
if (_startOfDataIndex == UInt32.MaxValue)
|
||||
{
|
||||
Array.Copy(data, _buffer, numBytesToAdd);
|
||||
_startOfDataIndex = 0;
|
||||
_endOfDataIndex = numBytesToAdd - 1;
|
||||
}
|
||||
else // not empty
|
||||
{
|
||||
// not enough room at end?
|
||||
if (numBytesToAdd > (_buffer.Length - _endOfDataIndex - 1))
|
||||
{
|
||||
// make sure not overwritting checked out data
|
||||
ulong afterShiftEndOfDataAddy = (ulong)_pStartOfBuffer + numBytesCurrentlyUsed - 1 + numBytesToAdd;
|
||||
|
||||
if (_pStartOfCheckedOutData != IntPtr.Zero &&
|
||||
afterShiftEndOfDataAddy >= (ulong)_pStartOfCheckedOutData)
|
||||
{
|
||||
throw new Exception("DataBuffer::Add() - attempting to overwrite data that is checked out (when shifting data to beginning)");
|
||||
}
|
||||
|
||||
// shuffle to front of buffer
|
||||
Array.Copy(_buffer, _startOfDataIndex, _buffer, 0, BytesUsed);
|
||||
_endOfDataIndex = BytesUsed - 1;
|
||||
_startOfDataIndex = 0;
|
||||
}
|
||||
else if (_pStartOfCheckedOutData != IntPtr.Zero)
|
||||
{
|
||||
// make sure not trying to overwrite data when it is checked out
|
||||
ulong endOfDataAddress = (ulong)_pStartOfBuffer + _endOfDataIndex;
|
||||
|
||||
if (endOfDataAddress < (ulong)_pStartOfCheckedOutData &&
|
||||
endOfDataAddress + numBytesToAdd >= (ulong)_pStartOfCheckedOutData)
|
||||
{
|
||||
throw new Exception("DataBuffer::Add() - attempting to overwrite data that is checked out");
|
||||
}
|
||||
}
|
||||
|
||||
Array.Copy(data, 0, _buffer, _endOfDataIndex + 1, numBytesToAdd);
|
||||
_endOfDataIndex += numBytesToAdd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns # of bytes used in buffer
|
||||
/// </summary>
|
||||
public uint BytesUsed
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_syncObj)
|
||||
{
|
||||
// is buffer empty?
|
||||
if (_startOfDataIndex == uint.MaxValue)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _endOfDataIndex - _startOfDataIndex + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks in the data. This should be used after a call to CheckOutStartOfData()
|
||||
/// </summary>
|
||||
public void CheckInStartOfData()
|
||||
{
|
||||
lock (_syncObj)
|
||||
{
|
||||
_pStartOfCheckedOutData = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This returns a pointer to the start of the new data
|
||||
/// </summary>
|
||||
/// <param name="numBytesInReturnedBuffer">The number of bytes that the returned pointer contains</param>
|
||||
/// <returns>A pointer to the data</returns>
|
||||
public IntPtr CheckOutStartOfData(ref uint numBytesInReturnedBuffer)
|
||||
{
|
||||
lock (_syncObj)
|
||||
{
|
||||
// hold onto the start index of the checked out data
|
||||
_startOfCheckedOutDataIndex = _startOfDataIndex;
|
||||
|
||||
numBytesInReturnedBuffer = 0;
|
||||
|
||||
// if nothing is in the buffer, _startOfCheckedOutDataIndex will be MaxValue
|
||||
if (_startOfCheckedOutDataIndex != uint.MaxValue)
|
||||
{
|
||||
// wrap around condition
|
||||
if (_endOfDataIndex < _startOfCheckedOutDataIndex)
|
||||
{
|
||||
// set the number of bytes from start of data to the end of the buffer
|
||||
numBytesInReturnedBuffer = (uint)_buffer.Length - _startOfCheckedOutDataIndex + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
numBytesInReturnedBuffer = _endOfDataIndex - _startOfCheckedOutDataIndex + 1;
|
||||
}
|
||||
}
|
||||
|
||||
_pStartOfCheckedOutData = IntPtr.Add(_pStartOfBuffer, (int)_startOfCheckedOutDataIndex);
|
||||
|
||||
return _pStartOfCheckedOutData;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies data into specified array from buffer
|
||||
/// Throws exception if asking to copy more bytes than buffer contains
|
||||
/// </summary>
|
||||
/// <param name="data">array into which to copy data from buffer</param>
|
||||
/// <param name="numBytesToCopy"># bytes to copy from buffer to array</param>
|
||||
public void Copy(ref byte[] data, uint numBytesToCopy)
|
||||
{
|
||||
lock (_syncObj)
|
||||
{
|
||||
// asking to copy more bytes than we have?
|
||||
if (numBytesToCopy > BytesUsed)
|
||||
{
|
||||
throw new Exception("DataBuffer::Copy() - Asking to copy more bytes than exist in buffer");
|
||||
}
|
||||
|
||||
Array.Copy(_buffer, _startOfDataIndex, data, 0, numBytesToCopy);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of resources
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")]
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (_syncObj)
|
||||
{
|
||||
Dispose(true);
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
try
|
||||
{
|
||||
ErrorLogger.Instance().Write(err.Message + "\r\n" + err.StackTrace);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//Do not rethrow. Exception from error logger that has already been garbage collected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of the buffer
|
||||
/// </summary>
|
||||
/// <returns>returns the number of bytes of the buffer.</returns>
|
||||
public uint GetSize()
|
||||
{
|
||||
lock (_syncObj)
|
||||
{
|
||||
return (uint)(_buffer.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes data from the buffer
|
||||
/// </summary>
|
||||
/// <param name="numBytesToRemove">The number of bytes to remove</param>
|
||||
public void Remove(uint numBytesToRemove)
|
||||
{
|
||||
lock (_syncObj)
|
||||
{
|
||||
// asking to remove more bytes than we have?
|
||||
if (numBytesToRemove > BytesUsed)
|
||||
{
|
||||
throw new Exception("DataBuffer::Remove() - Requested to remove more bytes than exist in buffer");
|
||||
}
|
||||
|
||||
// remove all bytes?
|
||||
if (numBytesToRemove == BytesUsed)
|
||||
{
|
||||
_startOfDataIndex = UInt32.MaxValue;
|
||||
|
||||
// all of the data is gone, need to set the _pStartOfCheckedOutData back to zero to prevent a race condition between adding data and the host checking the data back in
|
||||
_pStartOfCheckedOutData = IntPtr.Zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
_startOfDataIndex += numBytesToRemove;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all data from the buffer
|
||||
/// </summary>
|
||||
public void RemoveAll()
|
||||
{
|
||||
lock (_syncObj)
|
||||
{
|
||||
Remove(BytesUsed);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes of resources
|
||||
/// </summary>
|
||||
/// <param name="disposing"></param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_pinnedArray.Free();
|
||||
}
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
try
|
||||
{
|
||||
ErrorLogger.Instance().Write(err.Message + "\r\n" + err.StackTrace);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//Do not rethrow. Exception from error logger that has already been garbage collected
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user