Added classes to enumerate on pointer elements (NativeMemoryEnumerator and UntypedNativeMemoryEnumerator). Changed IntPtr.ToIEnum methods to use new classes and added IntPtrGetEnumerator extension methods.

pull/133/head
dahall 2020-05-15 16:37:15 -06:00
parent 385fceeaf6
commit 589d47c3b6
2 changed files with 139 additions and 15 deletions

View File

@ -0,0 +1,115 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Vanara.Extensions;
using Vanara.InteropServices;
namespace Vanara.PInvoke.Collections
{
/// <summary>Provides a generic enumerator over native memory.</summary>
/// <typeparam name="T">The type of the element to extract from memory.</typeparam>
/// <seealso cref="System.Collections.Generic.IEnumerator{T}"/>
/// <seealso cref="System.Collections.Generic.IEnumerable{T}"/>
public class NativeMemoryEnumerator<T> : UntypedNativeMemoryEnumerator, IEnumerator<T>, IEnumerable<T>
{
/// <summary>Initializes a new instance of the <see cref="NativeMemoryEnumerator{T}"/> class.</summary>
/// <param name="ptr">A pointer to the starting address of a specified number of <typeparamref name="T"/> elements in memory.</param>
/// <param name="length">The number of <typeparamref name="T"/> elements to be included in the enumeration.</param>
/// <param name="prefixBytes">Bytes to skip before reading the first element.</param>
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
/// <exception cref="System.ArgumentOutOfRangeException">count</exception>
/// <exception cref="System.InsufficientMemoryException"></exception>
public NativeMemoryEnumerator(IntPtr ptr, int length, int prefixBytes = 0, SizeT allocatedBytes = default) : base(ptr, typeof(T), length, prefixBytes, allocatedBytes) { }
/// <summary>Gets the element in the collection at the current position of the enumerator.</summary>
public new T Current => (T)base.Current;
/// <summary>Returns an enumerator that iterates through the collection.</summary>
/// <returns>A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.</returns>
public new IEnumerator<T> GetEnumerator() => this;
}
/// <summary>Provides an enumerator over native memory.</summary>
/// <seealso cref="System.Collections.IEnumerator"/>
/// <seealso cref="System.Collections.IEnumerable"/>
public class UntypedNativeMemoryEnumerator : IEnumerator, IEnumerable
{
/// <summary>The number of allocated bytes.</summary>
protected SizeT allocated;
/// <summary>The number of elements in the enumeration.</summary>
protected int count;
/// <summary>The index of the current item. (-2) signifies an error. (-1) means MoveNext has not been called..</summary>
protected int index = -1;
/// <summary>The number of bytes to skip before reading the first element.</summary>
protected int prefix;
/// <summary>A pointer to the starting address of a specified number of <see cref="type"/> elements in memory.</summary>
protected IntPtr ptr;
/// <summary>The size of <see cref="type"/>.</summary>
protected SizeT stSize;
/// <summary>The type of the element to extract from memory.</summary>
protected Type type;
/// <summary>Initializes a new instance of the <see cref="NativeMemoryEnumerator{T}"/> class.</summary>
/// <param name="ptr">A pointer to the starting address of a specified number of <paramref name="type"/> elements in memory.</param>
/// <param name="type">The type of the element to extract from memory.</param>
/// <param name="length">The number of <paramref name="type"/> elements to be included in the enumeration.</param>
/// <param name="prefixBytes">Bytes to skip before reading the first element.</param>
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
/// <exception cref="System.ArgumentOutOfRangeException">count</exception>
/// <exception cref="System.ArgumentNullException">type</exception>
/// <exception cref="System.InsufficientMemoryException"></exception>
public UntypedNativeMemoryEnumerator(IntPtr ptr, Type type, int length, int prefixBytes = 0, SizeT allocatedBytes = default)
{
if (length < 0 || ptr == IntPtr.Zero && length != 0)
throw new ArgumentOutOfRangeException(nameof(length));
if (type is null)
throw new ArgumentNullException(nameof(type));
this.ptr = ptr;
count = length;
prefix = prefixBytes;
allocated = allocatedBytes == default ? (SizeT)uint.MaxValue : allocatedBytes;
stSize = InteropExtensions.SizeOf(type);
if (allocatedBytes > 0 && stSize * length + prefixBytes > allocatedBytes)
throw new InsufficientMemoryException();
}
/// <summary>Gets the element in the collection at the current position of the enumerator.</summary>
public virtual object Current
{
get
{
if (index < 0 || index >= count)
throw new ArgumentOutOfRangeException(nameof(index));
var offset = prefix + index * stSize;
return ptr.Offset(offset).Convert(allocated - (uint)offset - (uint)prefix, type);
}
}
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public virtual void Dispose() { }
/// <summary>Returns an enumerator that iterates through a collection.</summary>
/// <returns>An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.</returns>
public IEnumerator GetEnumerator() => this;
/// <summary>Advances the enumerator to the next element of the collection.</summary>
/// <returns>
/// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
/// </returns>
public bool MoveNext()
{
if (++index >= count)
index = -2;
return index >= 0;
}
/// <summary>Sets the enumerator to its initial position, which is before the first element in the collection.</summary>
public void Reset() => index = -1;
}
}

View File

@ -9,6 +9,7 @@ using System.Security.Permissions;
using Vanara.Extensions.Reflection;
using Vanara.InteropServices;
using Vanara.PInvoke;
using Vanara.PInvoke.Collections;
namespace Vanara.Extensions
{
@ -96,6 +97,26 @@ namespace Vanara.Extensions
Marshal.WriteByte(ptr.Offset(ofs), 0, value);
}
/// <summary>Converts an <see cref="IntPtr"/> that points to a C-style array into an <see cref="IEnumerator{T}"/>.</summary>
/// <typeparam name="T">Type of native structure used by the C-style array.</typeparam>
/// <param name="ptr">The <see cref="IntPtr"/> pointing to the native array.</param>
/// <param name="count">The number of items in the native array.</param>
/// <param name="prefixBytes">Bytes to skip before reading the array.</param>
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
/// <returns>An <see cref="IEnumerable{T}"/> exposing the elements of the native array.</returns>
public static IEnumerator<T> GetEnumerator<T>(this IntPtr ptr, int count, int prefixBytes = 0, SizeT allocatedBytes = default) =>
new NativeMemoryEnumerator<T>(ptr, count, prefixBytes, allocatedBytes);
/// <summary>Converts an <see cref="IntPtr"/> that points to a C-style array into an <see cref="System.Collections.IEnumerator"/>.</summary>
/// <param name="ptr">The <see cref="IntPtr"/> pointing to the native array.</param>
/// <param name="type">Type of native structure used by the C-style array.</param>
/// <param name="count">The number of items in the native array.</param>
/// <param name="prefixBytes">Bytes to skip before reading the array.</param>
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
/// <returns>An <see cref="IEnumerable{T}"/> exposing the elements of the native array.</returns>
public static System.Collections.IEnumerator GetEnumerator(this IntPtr ptr, Type type, int count, int prefixBytes = 0, SizeT allocatedBytes = default) =>
new UntypedNativeMemoryEnumerator(ptr, type, count, prefixBytes, allocatedBytes);
/// <summary>
/// Gets the length of a null terminated array of pointers. <note type="warning">This is a very dangerous function and can result in
/// memory access errors if the <paramref name="lptr"/> does not point to a null-terminated array of pointers.</note>
@ -471,7 +492,7 @@ namespace Vanara.Extensions
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
/// <returns>An <see cref="IEnumerable{T}"/> exposing the elements of the native array.</returns>
public static IEnumerable<T> ToIEnum<T>(this IntPtr ptr, int count, int prefixBytes = 0, SizeT allocatedBytes = default) =>
ToIEnum(ptr, typeof(T), count, prefixBytes, allocatedBytes).Cast<T>();
new NativeMemoryEnumerator<T>(ptr, count, prefixBytes, allocatedBytes);
/// <summary>Converts an <see cref="IntPtr"/> that points to a C-style array into an <see cref="IEnumerable{T}"/>.</summary>
/// <param name="ptr">The <see cref="IntPtr"/> pointing to the native array.</param>
@ -480,20 +501,8 @@ namespace Vanara.Extensions
/// <param name="prefixBytes">Bytes to skip before reading the array.</param>
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
/// <returns>An <see cref="IEnumerable{T}"/> exposing the elements of the native array.</returns>
public static IEnumerable<object> ToIEnum(this IntPtr ptr, Type type, int count, int prefixBytes = 0, SizeT allocatedBytes = default)
{
if (type is null) throw new ArgumentNullException(nameof(type));
if (count == 0 || ptr == IntPtr.Zero) yield break;
var stSize = SizeOf(type);
if (allocatedBytes > 0 && stSize * count + prefixBytes > allocatedBytes)
throw new InsufficientMemoryException();
if (allocatedBytes == default) allocatedBytes = uint.MaxValue;
for (var i = 0; i < count; i++)
{
var offset = prefixBytes + i * stSize;
yield return ptr.Offset(offset).Convert(allocatedBytes - (uint)offset - (uint)prefixBytes, type);
}
}
public static System.Collections.IEnumerable ToIEnum(this IntPtr ptr, Type type, int count, int prefixBytes = 0, SizeT allocatedBytes = default) =>
new UntypedNativeMemoryEnumerator(ptr, type, count, prefixBytes, allocatedBytes);
/// <summary>Converts a <see cref="SecureString"/> to a string.</summary>
/// <param name="s">The <see cref="SecureString"/> value.</param>