828 lines
26 KiB
C#
828 lines
26 KiB
C#
|
|
using System;
|
||
|
|
using System.Runtime.InteropServices;
|
||
|
|
using System.Collections;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using System.Diagnostics;
|
||
|
|
|
||
|
|
namespace FreeImageAPI
|
||
|
|
{
|
||
|
|
/// <summary>
|
||
|
|
/// Represents unmanaged memory, containing an array of a given structure.
|
||
|
|
/// </summary>
|
||
|
|
/// <typeparam name="T">Structuretype represented by the instance.</typeparam>
|
||
|
|
/// <remarks>
|
||
|
|
/// <see cref="System.Boolean"/> and <see cref="System.Char"/> can not be marshalled.
|
||
|
|
/// <para/>
|
||
|
|
/// Use <see cref="System.Int32"/> instead of <see cref="System.Boolean"/> and
|
||
|
|
/// <see cref="System.Byte"/> instead of <see cref="System.Char"/>.
|
||
|
|
/// </remarks>
|
||
|
|
public unsafe class MemoryArray<T> : IDisposable, ICloneable, ICollection, IEnumerable<T>, IEquatable<MemoryArray<T>> where T : struct
|
||
|
|
{
|
||
|
|
/// <summary>
|
||
|
|
/// Baseaddress of the wrapped memory.
|
||
|
|
/// </summary>
|
||
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
||
|
|
protected byte* baseAddress;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Number of elements being wrapped.
|
||
|
|
/// </summary>
|
||
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
||
|
|
protected int length;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Size, in bytes, of each element.
|
||
|
|
/// </summary>
|
||
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
||
|
|
private static readonly int size;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Array of <b>T</b> containing a single element.
|
||
|
|
/// The array is used as a workaround, because there are no pointer for generic types.
|
||
|
|
/// </summary>
|
||
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
||
|
|
protected T[] buffer;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Pointer to the element of <b>buffer</b>.
|
||
|
|
/// </summary>
|
||
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
||
|
|
protected byte* ptr;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Handle for pinning <b>buffer</b>.
|
||
|
|
/// </summary>
|
||
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
||
|
|
protected GCHandle handle;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Indicates whether the wrapped memory is handled like a bitfield.
|
||
|
|
/// </summary>
|
||
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
||
|
|
protected readonly bool isOneBit;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Indicates whther the wrapped memory is handles like 4-bit blocks.
|
||
|
|
/// </summary>
|
||
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
||
|
|
protected readonly bool isFourBit;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// An object that can be used to synchronize access to the <see cref="MemoryArray<T>"/>.
|
||
|
|
/// </summary>
|
||
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
||
|
|
protected object syncRoot = null;
|
||
|
|
|
||
|
|
static MemoryArray()
|
||
|
|
{
|
||
|
|
T[] dummy = new T[2];
|
||
|
|
long marshalledSize = Marshal.SizeOf(typeof(T));
|
||
|
|
long structureSize =
|
||
|
|
Marshal.UnsafeAddrOfPinnedArrayElement(dummy, 1).ToInt64() -
|
||
|
|
Marshal.UnsafeAddrOfPinnedArrayElement(dummy, 0).ToInt64();
|
||
|
|
if (marshalledSize != structureSize)
|
||
|
|
{
|
||
|
|
throw new NotSupportedException(
|
||
|
|
"The desired type can not be handled, " +
|
||
|
|
"because its managed and unmanaged size in bytes are different.");
|
||
|
|
}
|
||
|
|
|
||
|
|
size = (int)marshalledSize;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Initializes a new instance.
|
||
|
|
/// </summary>
|
||
|
|
protected MemoryArray()
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Initializes a new instance of the <see cref="MemoryArray<T>"/> class.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="baseAddress">Address of the memory block.</param>
|
||
|
|
/// <param name="length">Length of the array.</param>
|
||
|
|
/// <exception cref="ArgumentNullException">
|
||
|
|
/// <paramref name="baseAddress"/> is null.</exception>
|
||
|
|
/// <exception cref="ArgumentOutOfRangeException">
|
||
|
|
/// <paramref name="length"/> is less or equal zero.</exception>
|
||
|
|
/// <exception cref="NotSupportedException">
|
||
|
|
/// The type is not supported.</exception>
|
||
|
|
public MemoryArray(IntPtr baseAddress, int length)
|
||
|
|
: this(baseAddress.ToPointer(), length)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Initializes a new instance of the <see cref="MemoryArray<T>"/> class.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="baseAddress">Address of the memory block.</param>
|
||
|
|
/// <param name="length">Length of the array.</param>
|
||
|
|
/// <exception cref="ArgumentNullException">
|
||
|
|
/// <paramref name="baseAddress"/> is null.</exception>
|
||
|
|
/// <exception cref="ArgumentOutOfRangeException">
|
||
|
|
/// <paramref name="length"/> is less or equal zero.</exception>
|
||
|
|
/// <exception cref="NotSupportedException">
|
||
|
|
/// The type is not supported.</exception>
|
||
|
|
public MemoryArray(void* baseAddress, int length)
|
||
|
|
{
|
||
|
|
if (typeof(T) == typeof(FI1BIT))
|
||
|
|
{
|
||
|
|
isOneBit = true;
|
||
|
|
}
|
||
|
|
else if (typeof(T) == typeof(FI4BIT))
|
||
|
|
{
|
||
|
|
isFourBit = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (baseAddress == null)
|
||
|
|
{
|
||
|
|
throw new ArgumentNullException("baseAddress");
|
||
|
|
}
|
||
|
|
if (length < 1)
|
||
|
|
{
|
||
|
|
throw new ArgumentOutOfRangeException("length");
|
||
|
|
}
|
||
|
|
|
||
|
|
this.baseAddress = (byte*)baseAddress;
|
||
|
|
this.length = (int)length;
|
||
|
|
|
||
|
|
if (!isOneBit && !isFourBit)
|
||
|
|
{
|
||
|
|
// Create an array containing a single element.
|
||
|
|
// Due to the fact, that it's not possible to create pointers
|
||
|
|
// of generic types, an array is used to obtain the memory
|
||
|
|
// address of an element of T.
|
||
|
|
this.buffer = new T[1];
|
||
|
|
// The array is pinned immediately to prevent the GC from
|
||
|
|
// moving it to a different position in memory.
|
||
|
|
this.handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||
|
|
// The array and its content have beed pinned, so that its address
|
||
|
|
// can be safely requested and stored for the whole lifetime
|
||
|
|
// of the instace.
|
||
|
|
this.ptr = (byte*)handle.AddrOfPinnedObject();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Frees the allocated <see cref="System.Runtime.InteropServices.GCHandle"/>.
|
||
|
|
/// </summary>
|
||
|
|
~MemoryArray()
|
||
|
|
{
|
||
|
|
Dispose(false);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Tests whether two specified <see cref="MemoryArray<T>"/> structures are equivalent.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="left">The <see cref="MemoryArray<T>"/> that is to the left of the equality operator.</param>
|
||
|
|
/// <param name="right">The <see cref="MemoryArray<T>"/> that is to the right of the equality operator.</param>
|
||
|
|
/// <returns>
|
||
|
|
/// <b>true</b> if the two <see cref="MemoryArray<T>"/> structures are equal; otherwise, <b>false</b>.
|
||
|
|
/// </returns>
|
||
|
|
public static bool operator ==(MemoryArray<T> left, MemoryArray<T> right)
|
||
|
|
{
|
||
|
|
if (object.ReferenceEquals(left, right))
|
||
|
|
{
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
if (object.ReferenceEquals(right, null) ||
|
||
|
|
object.ReferenceEquals(left, null) ||
|
||
|
|
(left.length != right.length))
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (left.baseAddress == right.baseAddress)
|
||
|
|
{
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return FreeImage.CompareMemory(left.baseAddress, right.baseAddress, (uint)left.length);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Tests whether two specified <see cref="MemoryArray<T>"/> structures are different.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="left">The <see cref="MemoryArray<T>"/> that is to the left of the inequality operator.</param>
|
||
|
|
/// <param name="right">The <see cref="MemoryArray<T>"/> that is to the right of the inequality operator.</param>
|
||
|
|
/// <returns>
|
||
|
|
/// <b>true</b> if the two <see cref="MemoryArray<T>"/> structures are different; otherwise, <b>false</b>.
|
||
|
|
/// </returns>
|
||
|
|
public static bool operator !=(MemoryArray<T> left, MemoryArray<T> right)
|
||
|
|
{
|
||
|
|
return (!(left == right));
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Gets the value at the specified position.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="index">A 32-bit integer that represents the position
|
||
|
|
/// of the array element to get.</param>
|
||
|
|
/// <returns>The value at the specified position.</returns>
|
||
|
|
/// <exception cref="ArgumentOutOfRangeException">
|
||
|
|
/// <paramref name="index"/> is outside the range of valid indexes
|
||
|
|
/// for the unmanaged array.</exception>
|
||
|
|
public T GetValue(int index)
|
||
|
|
{
|
||
|
|
if ((index >= this.length) || (index < 0))
|
||
|
|
{
|
||
|
|
throw new ArgumentOutOfRangeException("index");
|
||
|
|
}
|
||
|
|
|
||
|
|
return GetValueInternal(index);
|
||
|
|
}
|
||
|
|
|
||
|
|
private T GetValueInternal(int index)
|
||
|
|
{
|
||
|
|
EnsureNotDisposed();
|
||
|
|
if (isOneBit)
|
||
|
|
{
|
||
|
|
return (T)(object)(FI1BIT)(((baseAddress[index / 8] & ((1 << (7 - (index % 8))))) == 0) ? 0 : 1);
|
||
|
|
}
|
||
|
|
else if (isFourBit)
|
||
|
|
{
|
||
|
|
return (T)(object)(FI4BIT)(((index % 2) == 0) ? (baseAddress[index / 2] >> 4) : (baseAddress[index / 2] & 0x0F));
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
CopyMemory(ptr, baseAddress + (index * size), size);
|
||
|
|
return buffer[0];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Sets a value to the element at the specified position.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="value">The new value for the specified element.</param>
|
||
|
|
/// <param name="index">A 32-bit integer that represents the
|
||
|
|
/// position of the array element to set.</param>
|
||
|
|
/// <exception cref="ArgumentOutOfRangeException">
|
||
|
|
/// <paramref name="index"/> is outside the range of valid indexes
|
||
|
|
/// for the unmanaged array.</exception>
|
||
|
|
public void SetValue(T value, int index)
|
||
|
|
{
|
||
|
|
if ((index >= this.length) || (index < 0))
|
||
|
|
{
|
||
|
|
throw new ArgumentOutOfRangeException("index");
|
||
|
|
}
|
||
|
|
SetValueInternal(value, index);
|
||
|
|
}
|
||
|
|
|
||
|
|
private void SetValueInternal(T value, int index)
|
||
|
|
{
|
||
|
|
EnsureNotDisposed();
|
||
|
|
if (isOneBit)
|
||
|
|
{
|
||
|
|
if ((FI1BIT)(object)value != 0)
|
||
|
|
{
|
||
|
|
baseAddress[index / 8] |= (byte)(1 << (7 - (index % 8)));
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
baseAddress[index / 8] &= (byte)(~(1 << (7 - (index % 8))));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else if (isFourBit)
|
||
|
|
{
|
||
|
|
if ((index % 2) == 0)
|
||
|
|
{
|
||
|
|
baseAddress[index / 2] = (byte)((baseAddress[index / 2] & 0x0F) | ((FI4BIT)(object)value << 4));
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
baseAddress[index / 2] = (byte)((baseAddress[index / 2] & 0xF0) | ((FI4BIT)(object)value & 0x0F));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
buffer[0] = value;
|
||
|
|
CopyMemory(baseAddress + (index * size), ptr, size);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Gets the values at the specified position and length.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="index">A 32-bit integer that represents the position
|
||
|
|
/// of the array elements to get.</param>
|
||
|
|
/// <param name="length"> A 32-bit integer that represents the length
|
||
|
|
/// of the array elements to get.</param>
|
||
|
|
/// <returns>The values at the specified position and length.</returns>
|
||
|
|
/// <exception cref="ArgumentOutOfRangeException">
|
||
|
|
/// <paramref name="index"/> is outside the range of valid indexes
|
||
|
|
/// for the unmanaged array or <paramref name="length"/> is greater than the number of elements
|
||
|
|
/// from <paramref name="index"/> to the end of the unmanaged array.</exception>
|
||
|
|
public T[] GetValues(int index, int length)
|
||
|
|
{
|
||
|
|
EnsureNotDisposed();
|
||
|
|
if ((index >= this.length) || (index < 0))
|
||
|
|
{
|
||
|
|
throw new ArgumentOutOfRangeException("index");
|
||
|
|
}
|
||
|
|
if (((index + length) > this.length) || (length < 1))
|
||
|
|
{
|
||
|
|
throw new ArgumentOutOfRangeException("length");
|
||
|
|
}
|
||
|
|
|
||
|
|
T[] data = new T[length];
|
||
|
|
if (isOneBit || isFourBit)
|
||
|
|
{
|
||
|
|
for (int i = 0; i < length; i++)
|
||
|
|
{
|
||
|
|
data[i] = GetValueInternal(i);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||
|
|
byte* dst = (byte*)Marshal.UnsafeAddrOfPinnedArrayElement(data, 0);
|
||
|
|
CopyMemory(dst, baseAddress + (size * index), size * length);
|
||
|
|
handle.Free();
|
||
|
|
}
|
||
|
|
return data;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Sets the values at the specified position.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="values">An array containing the new values for the specified elements.</param>
|
||
|
|
/// <param name="index">A 32-bit integer that represents the position
|
||
|
|
/// of the array elements to set.</param>
|
||
|
|
/// <exception cref="ArgumentNullException">
|
||
|
|
/// <paramref name="values"/> is a null reference (Nothing in Visual Basic).</exception>
|
||
|
|
/// <exception cref="ArgumentOutOfRangeException">
|
||
|
|
/// <paramref name="index"/> is outside the range of valid indexes
|
||
|
|
/// for the unmanaged array or <paramref name="values.Length"/> is greater than the number of elements
|
||
|
|
/// from <paramref name="index"/> to the end of the array.</exception>
|
||
|
|
public void SetValues(T[] values, int index)
|
||
|
|
{
|
||
|
|
EnsureNotDisposed();
|
||
|
|
if (values == null)
|
||
|
|
{
|
||
|
|
throw new ArgumentNullException("values");
|
||
|
|
}
|
||
|
|
if ((index >= this.length) || (index < 0))
|
||
|
|
{
|
||
|
|
throw new ArgumentOutOfRangeException("index");
|
||
|
|
}
|
||
|
|
if ((index + values.Length) > this.length)
|
||
|
|
{
|
||
|
|
throw new ArgumentOutOfRangeException("values.Length");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (isOneBit || isFourBit)
|
||
|
|
{
|
||
|
|
for (int i = 0; i != values.Length; )
|
||
|
|
{
|
||
|
|
SetValueInternal(values[i++], index++);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
GCHandle handle = GCHandle.Alloc(values, GCHandleType.Pinned);
|
||
|
|
byte* src = (byte*)Marshal.UnsafeAddrOfPinnedArrayElement(values, 0);
|
||
|
|
CopyMemory(baseAddress + (index * size), src, size * length);
|
||
|
|
handle.Free();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Copies the entire array to a compatible one-dimensional <see cref="System.Array"/>,
|
||
|
|
/// starting at the specified index of the target array.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="array">The one-dimensional <see cref="System.Array"/> that is the destination
|
||
|
|
/// of the elements copied from <see cref="MemoryArray<T>"/>.
|
||
|
|
/// The <see cref="System.Array"/> must have zero-based indexing.</param>
|
||
|
|
/// <param name="index">The zero-based index in <paramref name="array"/>
|
||
|
|
/// at which copying begins.</param>
|
||
|
|
public void CopyTo(Array array, int index)
|
||
|
|
{
|
||
|
|
EnsureNotDisposed();
|
||
|
|
if (!(array is T[]))
|
||
|
|
{
|
||
|
|
throw new InvalidCastException("array");
|
||
|
|
}
|
||
|
|
try
|
||
|
|
{
|
||
|
|
CopyTo((T[])array, 0, index, length);
|
||
|
|
}
|
||
|
|
catch (ArgumentOutOfRangeException ex)
|
||
|
|
{
|
||
|
|
throw new ArgumentException(ex.Message, ex);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Copies a range of elements from the unmanaged array starting at the specified
|
||
|
|
/// <typeparamref name="sourceIndex"/> and pastes them to <paramref name="array"/>
|
||
|
|
/// starting at the specified <paramref name="destinationIndex"/>.
|
||
|
|
/// The length and the indexes are specified as 32-bit integers.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="array">The array that receives the data.</param>
|
||
|
|
/// <param name="sourceIndex">A 32-bit integer that represents the index
|
||
|
|
/// in the unmanaged array at which copying begins.</param>
|
||
|
|
/// <param name="destinationIndex">A 32-bit integer that represents the index in
|
||
|
|
/// the destination array at which storing begins.</param>
|
||
|
|
/// <param name="length">A 32-bit integer that represents the number of elements to copy.</param>
|
||
|
|
/// <exception cref="ArgumentNullException">
|
||
|
|
/// <paramref name="array"/> is a null reference (Nothing in Visual Basic).</exception>
|
||
|
|
/// <exception cref="ArgumentOutOfRangeException">
|
||
|
|
/// <paramref name="sourceIndex"/> is outside the range of valid indexes
|
||
|
|
/// for the unmanaged array or <paramref name="length"/> is greater than the number of elements
|
||
|
|
/// from <paramref name="index"/> to the end of the unmanaged array
|
||
|
|
/// <para>-or-</para>
|
||
|
|
/// <paramref name="destinationIndex"/> is outside the range of valid indexes
|
||
|
|
/// for the array or <paramref name="length"/> is greater than the number of elements
|
||
|
|
/// from <paramref name="index"/> to the end of the array.
|
||
|
|
/// </exception>
|
||
|
|
public void CopyTo(T[] array, int sourceIndex, int destinationIndex, int length)
|
||
|
|
{
|
||
|
|
EnsureNotDisposed();
|
||
|
|
if (array == null)
|
||
|
|
{
|
||
|
|
throw new ArgumentNullException("array");
|
||
|
|
}
|
||
|
|
if ((sourceIndex >= this.length) || (sourceIndex < 0))
|
||
|
|
{
|
||
|
|
throw new ArgumentOutOfRangeException("sourceIndex");
|
||
|
|
}
|
||
|
|
if ((destinationIndex >= array.Length) || (destinationIndex < 0))
|
||
|
|
{
|
||
|
|
throw new ArgumentOutOfRangeException("destinationIndex");
|
||
|
|
}
|
||
|
|
if ((sourceIndex + length > this.length) ||
|
||
|
|
(destinationIndex + length > array.Length) ||
|
||
|
|
(length < 1))
|
||
|
|
{
|
||
|
|
throw new ArgumentOutOfRangeException("length");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (isOneBit || isFourBit)
|
||
|
|
{
|
||
|
|
for (int i = 0; i != length; i++)
|
||
|
|
{
|
||
|
|
array[destinationIndex++] = GetValueInternal(sourceIndex++);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
GCHandle handle = GCHandle.Alloc(array, GCHandleType.Pinned);
|
||
|
|
byte* dst = (byte*)Marshal.UnsafeAddrOfPinnedArrayElement(array, destinationIndex);
|
||
|
|
CopyMemory(dst, baseAddress + (size * sourceIndex), size * length);
|
||
|
|
handle.Free();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Copies a range of elements from the array starting at the specified
|
||
|
|
/// <typeparamref name="sourceIndex"/> and pastes them to the unmanaged array
|
||
|
|
/// starting at the specified <paramref name="destinationIndex"/>.
|
||
|
|
/// The length and the indexes are specified as 32-bit integers.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="array">The array that holds the data.</param>
|
||
|
|
/// <param name="sourceIndex">A 32-bit integer that represents the index
|
||
|
|
/// in the array at which copying begins.</param>
|
||
|
|
/// <param name="destinationIndex">A 32-bit integer that represents the index in
|
||
|
|
/// the unmanaged array at which storing begins.</param>
|
||
|
|
/// <param name="length">A 32-bit integer that represents the number of elements to copy.</param>
|
||
|
|
/// <exception cref="ArgumentNullException">
|
||
|
|
/// <paramref name="array"/> is a null reference (Nothing in Visual Basic).</exception>
|
||
|
|
/// <exception cref="ArgumentOutOfRangeException">
|
||
|
|
/// <paramref name="sourceIndex"/> is outside the range of valid indexes
|
||
|
|
/// for the array or <paramref name="length"/> is greater than the number of elements
|
||
|
|
/// from <paramref name="index"/> to the end of the array
|
||
|
|
/// <para>-or-</para>
|
||
|
|
/// <paramref name="destinationIndex"/> is outside the range of valid indexes
|
||
|
|
/// for the unmanaged array or <paramref name="length"/> is greater than the number of elements
|
||
|
|
/// from <paramref name="index"/> to the end of the unmanaged array.
|
||
|
|
/// </exception>
|
||
|
|
public void CopyFrom(T[] array, int sourceIndex, int destinationIndex, int length)
|
||
|
|
{
|
||
|
|
EnsureNotDisposed();
|
||
|
|
if (array == null)
|
||
|
|
{
|
||
|
|
throw new ArgumentNullException("array");
|
||
|
|
}
|
||
|
|
if ((destinationIndex >= this.length) || (destinationIndex < 0))
|
||
|
|
{
|
||
|
|
throw new ArgumentOutOfRangeException("destinationIndex");
|
||
|
|
}
|
||
|
|
if ((sourceIndex >= array.Length) || (sourceIndex < 0))
|
||
|
|
{
|
||
|
|
throw new ArgumentOutOfRangeException("sourceIndex");
|
||
|
|
}
|
||
|
|
if ((destinationIndex + length > this.length) ||
|
||
|
|
(sourceIndex + length > array.Length) ||
|
||
|
|
(length < 1))
|
||
|
|
{
|
||
|
|
throw new ArgumentOutOfRangeException("length");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (isOneBit || isFourBit)
|
||
|
|
{
|
||
|
|
for (int i = 0; i != length; i++)
|
||
|
|
{
|
||
|
|
SetValueInternal(array[sourceIndex++], destinationIndex++);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
GCHandle handle = GCHandle.Alloc(array, GCHandleType.Pinned);
|
||
|
|
byte* src = (byte*)Marshal.UnsafeAddrOfPinnedArrayElement(array, sourceIndex);
|
||
|
|
CopyMemory(baseAddress + (size * destinationIndex), src, size * length);
|
||
|
|
handle.Free();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Returns the represented block of memory as an array of <see cref="Byte"/>.
|
||
|
|
/// </summary>
|
||
|
|
/// <returns>The represented block of memory.</returns>
|
||
|
|
public byte[] ToByteArray()
|
||
|
|
{
|
||
|
|
EnsureNotDisposed();
|
||
|
|
byte[] result;
|
||
|
|
if (isOneBit)
|
||
|
|
{
|
||
|
|
result = new byte[(length + 7) / 8];
|
||
|
|
}
|
||
|
|
else if (isFourBit)
|
||
|
|
{
|
||
|
|
result = new byte[(length + 3) / 4];
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
result = new byte[size * length];
|
||
|
|
}
|
||
|
|
fixed (byte* dst = result)
|
||
|
|
{
|
||
|
|
CopyMemory(dst, baseAddress, result.Length);
|
||
|
|
}
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Gets or sets the value at the specified position in the array.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="index">A 32-bit integer that represents the position
|
||
|
|
/// of the array element to get.</param>
|
||
|
|
/// <returns>The value at the specified position in the array.</returns>
|
||
|
|
/// <exception cref="ArgumentOutOfRangeException">
|
||
|
|
/// <paramref name="index"/> is outside the range of valid indexes
|
||
|
|
/// for the unmanaged array.</exception>
|
||
|
|
public T this[int index]
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
return GetValue(index);
|
||
|
|
}
|
||
|
|
set
|
||
|
|
{
|
||
|
|
SetValue(value, index);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Gets or sets the values of the unmanaged array.
|
||
|
|
/// </summary>
|
||
|
|
public T[] Data
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
return GetValues(0, length);
|
||
|
|
}
|
||
|
|
set
|
||
|
|
{
|
||
|
|
if (value == null)
|
||
|
|
{
|
||
|
|
throw new ArgumentNullException("value");
|
||
|
|
}
|
||
|
|
if (value.Length != length)
|
||
|
|
{
|
||
|
|
throw new ArgumentOutOfRangeException("value.Lengt");
|
||
|
|
}
|
||
|
|
SetValues(value, 0);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Gets the length of the unmanaged array.
|
||
|
|
/// </summary>
|
||
|
|
public int Length
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
EnsureNotDisposed();
|
||
|
|
return length;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Gets the base address of the represented memory block.
|
||
|
|
/// </summary>
|
||
|
|
public IntPtr BaseAddress
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
EnsureNotDisposed();
|
||
|
|
return new IntPtr(baseAddress);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Creates a shallow copy of the <see cref="MemoryArray<T>"/>.
|
||
|
|
/// </summary>
|
||
|
|
/// <returns>A shallow copy of the <see cref="MemoryArray<T>"/>.</returns>
|
||
|
|
public object Clone()
|
||
|
|
{
|
||
|
|
EnsureNotDisposed();
|
||
|
|
return new MemoryArray<T>(baseAddress, length);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Gets a 32-bit integer that represents the total number of elements
|
||
|
|
/// in the <see cref="MemoryArray<T>"/>.
|
||
|
|
/// </summary>
|
||
|
|
public int Count
|
||
|
|
{
|
||
|
|
get { EnsureNotDisposed(); return length; }
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Gets a value indicating whether access to the <see cref="MemoryArray<T>"/>
|
||
|
|
/// is synchronized (thread safe).
|
||
|
|
/// </summary>
|
||
|
|
public bool IsSynchronized
|
||
|
|
{
|
||
|
|
get { EnsureNotDisposed(); return false; }
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Gets an object that can be used to synchronize access to the <see cref="MemoryArray<T>"/>.
|
||
|
|
/// </summary>
|
||
|
|
public object SyncRoot
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
EnsureNotDisposed();
|
||
|
|
if (syncRoot == null)
|
||
|
|
{
|
||
|
|
System.Threading.Interlocked.CompareExchange(ref syncRoot, new object(), null);
|
||
|
|
}
|
||
|
|
return syncRoot;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Retrieves an object that can iterate through the individual
|
||
|
|
/// elements in this <see cref="MemoryArray<T>"/>.
|
||
|
|
/// </summary>
|
||
|
|
/// <returns>An <see cref="IEnumerator"/> for the <see cref="MemoryArray<T>"/>.</returns>
|
||
|
|
public IEnumerator GetEnumerator()
|
||
|
|
{
|
||
|
|
EnsureNotDisposed();
|
||
|
|
T[] values = GetValues(0, length);
|
||
|
|
for (int i = 0; i != values.Length; i++)
|
||
|
|
{
|
||
|
|
yield return values[i];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Retrieves an object that can iterate through the individual
|
||
|
|
/// elements in this <see cref="MemoryArray<T>"/>.
|
||
|
|
/// </summary>
|
||
|
|
/// <returns>An <see cref="IEnumerator<T>"/> for the <see cref="MemoryArray<T>"/>.</returns>
|
||
|
|
IEnumerator<T> IEnumerable<T>.GetEnumerator()
|
||
|
|
{
|
||
|
|
EnsureNotDisposed();
|
||
|
|
T[] values = GetValues(0, length);
|
||
|
|
for (int i = 0; i != values.Length; i++)
|
||
|
|
{
|
||
|
|
yield return values[i];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Releases all ressources.
|
||
|
|
/// </summary>
|
||
|
|
public void Dispose()
|
||
|
|
{
|
||
|
|
Dispose(true);
|
||
|
|
GC.SuppressFinalize(this);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Releases allocated handles associated with this instance.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="disposing"><b>true</b> to release managed resources.</param>
|
||
|
|
protected virtual void Dispose(bool disposing)
|
||
|
|
{
|
||
|
|
if (baseAddress != null)
|
||
|
|
{
|
||
|
|
if (handle.IsAllocated)
|
||
|
|
handle.Free();
|
||
|
|
baseAddress = null;
|
||
|
|
buffer = null;
|
||
|
|
length = 0;
|
||
|
|
syncRoot = null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Throws an <see cref="ObjectDisposedException"/> if
|
||
|
|
/// this instance is disposed.
|
||
|
|
/// </summary>
|
||
|
|
protected virtual void EnsureNotDisposed()
|
||
|
|
{
|
||
|
|
if (baseAddress == null)
|
||
|
|
throw new ObjectDisposedException("This instance is disposed.");
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Tests whether the specified <see cref="MemoryArray<T>"/> structure is equivalent to this
|
||
|
|
/// <see cref="MemoryArray<T>"/> structure.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="obj">The structure to test.</param>
|
||
|
|
/// <returns><b>true</b> if <paramref name="obj"/> is a <see cref="MemoryArray<T>"/>
|
||
|
|
/// instance equivalent to this <see cref="MemoryArray<T>"/> structure; otherwise,
|
||
|
|
/// <b>false</b>.</returns>
|
||
|
|
public override bool Equals(object obj)
|
||
|
|
{
|
||
|
|
EnsureNotDisposed();
|
||
|
|
return ((obj is MemoryArray<T>) && Equals((MemoryArray<T>)obj));
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Tests whether the specified <see cref="MemoryArray<T>"/> structure is equivalent to this
|
||
|
|
/// <see cref="MemoryArray<T>"/> structure.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="other">The structure to test.</param>
|
||
|
|
/// <returns><b>true</b> if <paramref name="other"/> is equivalent to this
|
||
|
|
/// <see cref="MemoryArray<T>"/> structure; otherwise,
|
||
|
|
/// <b>false</b>.</returns>
|
||
|
|
public bool Equals(MemoryArray<T> other)
|
||
|
|
{
|
||
|
|
EnsureNotDisposed();
|
||
|
|
return ((this.baseAddress == other.baseAddress) && (this.length == other.length));
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Serves as a hash function for a particular type.
|
||
|
|
/// </summary>
|
||
|
|
/// <returns>A hash code for the current <see cref="MemoryArray<T>"/>.</returns>
|
||
|
|
public override int GetHashCode()
|
||
|
|
{
|
||
|
|
EnsureNotDisposed();
|
||
|
|
return (int)baseAddress ^ length;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Copies a block of memory from one location to another.
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="dest">Pointer to the starting address of the copy destination.</param>
|
||
|
|
/// <param name="src">Pointer to the starting address of the block of memory to be copied.</param>
|
||
|
|
/// <param name="len">Size of the block of memory to copy, in bytes.</param>
|
||
|
|
protected static unsafe void CopyMemory(byte* dest, byte* src, int len)
|
||
|
|
{
|
||
|
|
if (len >= 0x10)
|
||
|
|
{
|
||
|
|
do
|
||
|
|
{
|
||
|
|
*((int*)dest) = *((int*)src);
|
||
|
|
*((int*)(dest + 4)) = *((int*)(src + 4));
|
||
|
|
*((int*)(dest + 8)) = *((int*)(src + 8));
|
||
|
|
*((int*)(dest + 12)) = *((int*)(src + 12));
|
||
|
|
dest += 0x10;
|
||
|
|
src += 0x10;
|
||
|
|
}
|
||
|
|
while ((len -= 0x10) >= 0x10);
|
||
|
|
}
|
||
|
|
if (len > 0)
|
||
|
|
{
|
||
|
|
if ((len & 8) != 0)
|
||
|
|
{
|
||
|
|
*((int*)dest) = *((int*)src);
|
||
|
|
*((int*)(dest + 4)) = *((int*)(src + 4));
|
||
|
|
dest += 8;
|
||
|
|
src += 8;
|
||
|
|
}
|
||
|
|
if ((len & 4) != 0)
|
||
|
|
{
|
||
|
|
*((int*)dest) = *((int*)src);
|
||
|
|
dest += 4;
|
||
|
|
src += 4;
|
||
|
|
}
|
||
|
|
if ((len & 2) != 0)
|
||
|
|
{
|
||
|
|
*((short*)dest) = *((short*)src);
|
||
|
|
dest += 2;
|
||
|
|
src += 2;
|
||
|
|
}
|
||
|
|
if ((len & 1) != 0)
|
||
|
|
{
|
||
|
|
*dest = *src;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|