From 589d47c3b6d6caf356a4237d696d00f1c7b37cb5 Mon Sep 17 00:00:00 2001 From: dahall Date: Fri, 15 May 2020 16:37:15 -0600 Subject: [PATCH] Added classes to enumerate on pointer elements (NativeMemoryEnumerator and UntypedNativeMemoryEnumerator). Changed IntPtr.ToIEnum methods to use new classes and added IntPtrGetEnumerator extension methods. --- Core/Collections/NativeMemoryEnumerator.cs | 115 +++++++++++++++++++++++++++++ Core/Extensions/InteropExtensions.cs | 39 ++++++---- 2 files changed, 139 insertions(+), 15 deletions(-) create mode 100644 Core/Collections/NativeMemoryEnumerator.cs diff --git a/Core/Collections/NativeMemoryEnumerator.cs b/Core/Collections/NativeMemoryEnumerator.cs new file mode 100644 index 00000000..ed679fa8 --- /dev/null +++ b/Core/Collections/NativeMemoryEnumerator.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Vanara.Extensions; +using Vanara.InteropServices; + +namespace Vanara.PInvoke.Collections +{ + /// Provides a generic enumerator over native memory. + /// The type of the element to extract from memory. + /// + /// + public class NativeMemoryEnumerator : UntypedNativeMemoryEnumerator, IEnumerator, IEnumerable + { + /// Initializes a new instance of the class. + /// A pointer to the starting address of a specified number of elements in memory. + /// The number of elements to be included in the enumeration. + /// Bytes to skip before reading the first element. + /// If known, the total number of bytes allocated to the native memory in . + /// count + /// + public NativeMemoryEnumerator(IntPtr ptr, int length, int prefixBytes = 0, SizeT allocatedBytes = default) : base(ptr, typeof(T), length, prefixBytes, allocatedBytes) { } + + /// Gets the element in the collection at the current position of the enumerator. + public new T Current => (T)base.Current; + + /// Returns an enumerator that iterates through the collection. + /// A that can be used to iterate through the collection. + public new IEnumerator GetEnumerator() => this; + } + + /// Provides an enumerator over native memory. + /// + /// + public class UntypedNativeMemoryEnumerator : IEnumerator, IEnumerable + { + /// The number of allocated bytes. + protected SizeT allocated; + + /// The number of elements in the enumeration. + protected int count; + + /// The index of the current item. (-2) signifies an error. (-1) means MoveNext has not been called.. + protected int index = -1; + + /// The number of bytes to skip before reading the first element. + protected int prefix; + + /// A pointer to the starting address of a specified number of elements in memory. + protected IntPtr ptr; + + /// The size of . + protected SizeT stSize; + + /// The type of the element to extract from memory. + protected Type type; + + /// Initializes a new instance of the class. + /// A pointer to the starting address of a specified number of elements in memory. + /// The type of the element to extract from memory. + /// The number of elements to be included in the enumeration. + /// Bytes to skip before reading the first element. + /// If known, the total number of bytes allocated to the native memory in . + /// count + /// type + /// + 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(); + } + + /// Gets the element in the collection at the current position of the enumerator. + 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); + } + } + + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + public virtual void Dispose() { } + + /// Returns an enumerator that iterates through a collection. + /// An object that can be used to iterate through the collection. + public IEnumerator GetEnumerator() => this; + + /// Advances the enumerator to the next element of the collection. + /// + /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. + /// + public bool MoveNext() + { + if (++index >= count) + index = -2; + return index >= 0; + } + + /// Sets the enumerator to its initial position, which is before the first element in the collection. + public void Reset() => index = -1; + } +} \ No newline at end of file diff --git a/Core/Extensions/InteropExtensions.cs b/Core/Extensions/InteropExtensions.cs index 5c2d39c7..9b2e4f9f 100644 --- a/Core/Extensions/InteropExtensions.cs +++ b/Core/Extensions/InteropExtensions.cs @@ -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); } + /// Converts an that points to a C-style array into an . + /// Type of native structure used by the C-style array. + /// The pointing to the native array. + /// The number of items in the native array. + /// Bytes to skip before reading the array. + /// If known, the total number of bytes allocated to the native memory in . + /// An exposing the elements of the native array. + public static IEnumerator GetEnumerator(this IntPtr ptr, int count, int prefixBytes = 0, SizeT allocatedBytes = default) => + new NativeMemoryEnumerator(ptr, count, prefixBytes, allocatedBytes); + + /// Converts an that points to a C-style array into an . + /// The pointing to the native array. + /// Type of native structure used by the C-style array. + /// The number of items in the native array. + /// Bytes to skip before reading the array. + /// If known, the total number of bytes allocated to the native memory in . + /// An exposing the elements of the native array. + 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); + /// /// Gets the length of a null terminated array of pointers. This is a very dangerous function and can result in /// memory access errors if the does not point to a null-terminated array of pointers. @@ -471,7 +492,7 @@ namespace Vanara.Extensions /// If known, the total number of bytes allocated to the native memory in . /// An exposing the elements of the native array. public static IEnumerable ToIEnum(this IntPtr ptr, int count, int prefixBytes = 0, SizeT allocatedBytes = default) => - ToIEnum(ptr, typeof(T), count, prefixBytes, allocatedBytes).Cast(); + new NativeMemoryEnumerator(ptr, count, prefixBytes, allocatedBytes); /// Converts an that points to a C-style array into an . /// The pointing to the native array. @@ -480,20 +501,8 @@ namespace Vanara.Extensions /// Bytes to skip before reading the array. /// If known, the total number of bytes allocated to the native memory in . /// An exposing the elements of the native array. - public static IEnumerable 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); /// Converts a to a string. /// The value.