297 lines
10 KiB
C#
297 lines
10 KiB
C#
// 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()
|
|
{
|
|
lock (_syncObj)
|
|
{
|
|
Dispose(true);
|
|
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
}
|
|
|
|
/// <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)
|
|
{
|
|
if (disposing)
|
|
{
|
|
_pinnedArray.Free();
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
}
|