📄 memoryarray.cs
字号:
using System;
using System.Runtime.InteropServices;
using System.Collections;
using System.Collections.Generic;
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> : ICloneable, ICollection, IEnumerable<T>, IEquatable<MemoryArray<T>> where T : struct
{
/// <summary>
/// Baseaddress of the wrapped memory.
/// </summary>
protected readonly byte* baseAddress;
/// <summary>
/// Number of elements being wrapped.
/// </summary>
protected readonly int length;
/// <summary>
/// Size, in bytes, of each element.
/// </summary>
protected 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>
protected readonly T[] buffer;
/// <summary>
/// Pointer to the element of <b>buffer</b>.
/// </summary>
protected readonly byte* ptr;
/// <summary>
/// Handle for pinning <b>buffer</b>.
/// </summary>
protected readonly GCHandle handle;
/// <summary>
/// Indicates whether the wrapped memory is handled like a bitfield.
/// </summary>
protected readonly bool isOneBit;
/// <summary>
/// Indicates whther the wrapped memory is handles like 4-bit blocks.
/// </summary>
protected readonly bool isFourBit;
/// <summary>
/// An object that can be used to synchronize access to the <see cref="MemoryArray<T>"/>.
/// </summary>
protected object syncRoot = null;
/// <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;
}
else
{
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 it's managed and unmanaged size in bytes are different.");
}
}
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)
{
this.size = Marshal.SizeOf(typeof(T));
// 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*)Marshal.UnsafeAddrOfPinnedArrayElement(this.buffer, 0);
}
}
/// <summary>
/// Frees the allocated <see cref="System.Runtime.InteropServices.GCHandle"/>.
/// </summary>
~MemoryArray()
{
if (handle.IsAllocated)
{
handle.Free();
}
}
/// <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)
{
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)
{
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)
{
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)
{
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>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -