2020-05-14 19:56:28 -04:00
using System ;
2020-05-15 08:46:55 -04:00
using System.Collections.Generic ;
2020-05-14 19:56:28 -04:00
using System.Diagnostics.CodeAnalysis ;
using System.Runtime.InteropServices ;
using Vanara.Extensions ;
using Vanara.PInvoke ;
#if ALLOWSPAN
using System.Buffers ;
# endif
namespace Vanara.InteropServices
{
/// <summary>Base abstract class for a structure handler based on <see cref="SafeMemoryHandle{TMem}"/>.</summary>
/// <typeparam name="TStruct">The type of the structure.</typeparam>
/// <typeparam name="TMem">The type of the memory.</typeparam>
/// <seealso cref="Vanara.InteropServices.SafeMemoryHandle{TMem}"/>
2020-05-15 08:46:55 -04:00
public abstract class SafeMemStruct < TStruct , TMem > : SafeMemoryHandle < TMem > , IEquatable < TStruct > where TMem : IMemoryMethods , new ( ) where TStruct : struct
2020-05-14 19:56:28 -04:00
{
2020-08-19 22:51:11 -04:00
/// <summary>The structure size, in bytes, of TStruct.</summary>
protected static readonly SizeT BaseStructSize = InteropExtensions . SizeOf < TStruct > ( ) ;
2020-05-14 19:56:28 -04:00
/// <summary>Initializes a new instance of the <see cref="SafeMemStruct{TStruct, TMem}"/> class.</summary>
/// <param name="s">The TStruct value.</param>
/// <param name="capacity">The capacity of the buffer, in bytes.</param>
2020-08-19 22:51:11 -04:00
protected SafeMemStruct ( in TStruct s , SizeT capacity = default ) : base ( Math . Max ( BaseStructSize , ( ulong ) capacity ) ) = > handle . Write ( s ) ;
2020-05-14 19:56:28 -04:00
/// <summary>Initializes a new instance of the <see cref="SafeMemStruct{TStruct, TMem}"/> class.</summary>
/// <param name="capacity">The capacity of the buffer, in bytes.</param>
2020-08-19 22:51:11 -04:00
protected SafeMemStruct ( SizeT capacity = default ) : base ( Math . Max ( BaseStructSize , ( ulong ) capacity ) ) { }
2020-05-14 19:56:28 -04:00
/// <summary>Initializes a new instance of the <see cref="SafeMemStruct{TStruct, TMem}"/> class.</summary>
/// <param name="ptr">The PTR.</param>
/// <param name="ownsHandle"><c>true</c> to reliably release the handle during finalization; <c>false</c> to prevent it.</param>
/// <param name="allocatedBytes">The number of bytes allocated to <paramref name="ptr"/>.</param>
[ExcludeFromCodeCoverage]
protected SafeMemStruct ( IntPtr ptr , bool ownsHandle = true , SizeT allocatedBytes = default ) : base ( ptr , allocatedBytes , ownsHandle ) { }
/// <summary>Gets a value indicating whether the current memory has a valid value of its underlying type.</summary>
/// <value><see langword="true"/> if this instance has a value; otherwise, <see langword="false"/>.</value>
public bool HasValue = > ! IsClosed & & ! IsInvalid ;
/// <summary>
/// Gets or sets the value of the current <see cref="SafeMemStruct{TStruct, TMem}"/> object if it has been assigned a valid
/// underlying value.
/// </summary>
/// <value>
/// The value of the current <see cref="SafeMemStruct{TStruct, TMem}"/> object if the HasValue property is true. An exception is
/// thrown if the HasValue property is false.
/// </value>
/// <exception cref="System.InvalidOperationException">The HasValue property is false.</exception>
public TStruct Value
{
get = > HasValue ? handle . ToStructure < TStruct > ( Size ) : throw new InvalidOperationException ( "The HasValue property is false." ) ;
set = > _ = HasValue ? handle . Write ( value , 0 , Size ) : throw new InvalidOperationException ( "The HasValue property is false." ) ;
}
/// <summary>Returns the TStruct value held by a <see cref="SafeMemStruct{TStruct, TMem}"/>.</summary>
/// <param name="s">The <see cref="SafeMemStruct{TStruct, TMem}"/> instance.</param>
/// <returns>
/// A nullable value held by the <see cref="SafeMemStruct{TStruct, TMem}"/> or <c>null</c> if the handle or value is invalid.
/// </returns>
public static explicit operator TStruct ? ( SafeMemStruct < TStruct , TMem > s ) = > s is null | | ! s . HasValue ? ( TStruct ? ) null : s . Value ;
/// <summary>Performs an explicit conversion from <see cref="SafeMemStruct{TStruct, TMem}"/> to <see cref="System.Char"/>.</summary>
/// <param name="s">The <see cref="SafeMemStruct{TStruct, TMem}"/> instance.</param>
/// <returns>The result of the conversion.</returns>
/// <exception cref="InvalidCastException">Cannot convert an ANSI string to a Char pointer.</exception>
public static unsafe explicit operator void * ( SafeMemStruct < TStruct , TMem > s ) = > ( void * ) s . handle ;
/// <summary>Returns the value of the <see cref="SafeHandle.handle"/> field.</summary>
/// <param name="s">The <see cref="SafeMemStruct{TStruct, TMem}"/> instance.</param>
/// <returns>
/// An <see cref="IntPtr"/> representing the value of the handle field. If the handle has been marked invalid with <see
/// cref="SafeHandle.SetHandleAsInvalid"/>, this method still returns the original handle value, which can be a stale value.
/// </returns>
public static implicit operator IntPtr ( SafeMemStruct < TStruct , TMem > s ) = > s . DangerousGetHandle ( ) ;
/// <summary>Returns the TStruct value held by a <see cref="SafeMemStruct{TStruct, TMem}"/>.</summary>
/// <param name="s">The <see cref="SafeMemStruct{TStruct, TMem}"/> instance.</param>
/// <returns>
/// The structure value held by the <see cref="SafeMemStruct{TStruct, TMem}"/> or an <see cref="InvalidOperationException"/>
/// exception if the handle or value is invalid.
/// </returns>
public static implicit operator TStruct ( SafeMemStruct < TStruct , TMem > s ) = > ! ( s is null ) ? s . Value : throw new ArgumentNullException ( nameof ( s ) ) ;
2021-01-20 18:56:09 -05:00
/// <summary>Appends the specified bytes to the end of the allocated memory for this structure, expanding the allocation to fit the byte array.</summary>
/// <param name="bytes">The bytes.</param>
/// <returns>A pointer to the copied bytes in memory.</returns>
public virtual IntPtr Append ( byte [ ] bytes )
{
var sz = Size ;
Size + = bytes . Length ;
Marshal . Copy ( bytes , 0 , handle . Offset ( sz ) , bytes . Length ) ;
return handle . Offset ( sz ) ;
}
/// <summary>Appends the specified memory to the end of the allocated memory for this structure, expanding the allocation to fit the added memory.</summary>
/// <param name="mem">The memory to append.</param>
/// <returns>A pointer to the copied memory.</returns>
public virtual IntPtr Append ( SafeAllocatedMemoryHandleBase mem )
{
var sz = Size ;
Size + = mem . Size ;
( ( IntPtr ) mem ) . CopyTo ( handle . Offset ( sz ) , mem . Size ) ;
return handle . Offset ( sz ) ;
}
/// <summary>Appends the specified object to the end of the allocated memory for this structure, expanding the allocation to fit the added object.</summary>
/// <param name="value">The value to append.</param>
/// <returns>A pointer to the copied memory.</returns>
public virtual IntPtr Append ( object value )
{
var sz = Size ;
var vSz = InteropExtensions . SizeOf ( value ) ;
Size + = vSz ;
handle . Write ( value , sz , Size ) ;
return handle . Offset ( sz ) ;
}
2020-05-14 19:56:28 -04:00
/// <summary>Determines whether the specified <see cref="System.Object"/>, is equal to this instance.</summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
/// <returns><c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.</returns>
2020-12-30 22:47:14 -05:00
public override bool Equals ( object obj ) = > ReferenceEquals ( this , obj ) | |
( obj switch
{
null = > false ,
SafeMemStruct < TStruct , TMem > ms = > Equals ( ( TStruct ? ) this , ( TStruct ? ) ms ) ,
TStruct s = > Equals ( s ) ,
SafeAllocatedMemoryHandle m = > m . DangerousGetHandle ( ) = = handle ,
_ = > false ,
} ) ;
2020-05-14 19:56:28 -04:00
2020-05-15 08:46:55 -04:00
/// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
/// <param name="other">An object to compare with this object.</param>
/// <returns>true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.</returns>
2020-12-30 22:47:14 -05:00
public bool Equals ( TStruct other ) = > HasValue & & EqualityComparer < TStruct > . Default . Equals ( handle . ToStructure < TStruct > ( Size ) , other ) ;
2020-05-15 08:46:55 -04:00
2021-01-20 18:56:09 -05:00
/// <summary>Gets the memory address of a field within <typeparamref name="TStruct"/>.</summary>
/// <param name="fieldName">Name of the field.</param>
/// <returns>The pointer to the field in memory.</returns>
public virtual IntPtr GetFieldAddress ( string fieldName ) = > handle . Offset ( FieldOffset ( fieldName ) ) ;
2020-05-14 19:56:28 -04:00
/// <summary>Returns a hash code for this instance.</summary>
/// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.</returns>
public override int GetHashCode ( ) = > handle . ToInt32 ( ) ;
/// <summary>Retrieves the value of the current <see cref="SafeMemStruct{TStruct, TMem}"/> object, or the specified default value.</summary>
/// <param name="defaultValue">A value to return if the <see cref="HasValue"/> property is <see langword="false"/>.</param>
/// <returns>
/// The value of the <see cref="Value"/> property if the <see cref="HasValue"/> property is <see langword="true"/>; otherwise, the
/// <paramref name="defaultValue"/> parameter.
/// </returns>
public virtual TStruct GetValueOrDefault ( in TStruct defaultValue = default ) = > HasValue ? Value : defaultValue ;
2021-01-25 14:10:13 -05:00
/// <summary>Initializes the size field by the specified name or the first four bytes of the structure's memory.</summary>
/// <param name="fieldName">Name of the field.</param>
public virtual void InitializeSizeField ( string fieldName = null ) = > ( fieldName is null ? handle : GetFieldAddress ( fieldName ) ) . Write ( ( uint ) BaseStructSize ) ;
2020-05-14 19:56:28 -04:00
/// <summary>Returns the string value held by this instance.</summary>
/// <returns>A <see cref="System.String"/> value held by this instance or <c>null</c> if the handle is invalid.</returns>
public override string ToString ( ) = > ( ( TStruct ? ) this ) . ToString ( ) ;
2020-08-19 22:51:11 -04:00
/// <summary>Returns the field offset of the named field.</summary>
/// <param name="name">The field name.</param>
/// <returns>The offset, in bytes, of the field within the structure.</returns>
protected static long FieldOffset ( string name ) = > Marshal . OffsetOf ( typeof ( TStruct ) , name ) . ToInt64 ( ) ;
2020-05-14 19:56:28 -04:00
#if ALLOWSPAN
/// <summary>Gets a reference to a structure based on this allocated memory.</summary>
/// <returns>A referenced structure.</returns>
public ref TStruct AsRef ( ) = > ref MemoryMarshal . GetReference ( AsSpan ( ) ) ;
/// <summary>Creates a new span over this allocated memory.</summary>
/// <returns>The span representation of the structure.</returns>
public Span < TStruct > AsSpan ( ) = > base . AsSpan < TStruct > ( 1 ) ;
# endif
}
2020-05-26 08:41:40 -04:00
/// <summary>
/// A structure handler based on unmanaged memory allocated by AllocCoTaskMem.
/// </summary>
/// <typeparam name="TStruct">The type of the structure.</typeparam>
/// <seealso cref="Vanara.InteropServices.SafeMemStruct{TStruct, TMem}" />
public class SafeCoTaskMemStruct < TStruct > : SafeMemStruct < TStruct , CoTaskMemoryMethods > where TStruct : struct
{
/// <summary>Initializes a new instance of the <see cref="SafeCoTaskMemStruct{TStruct}"/> class.</summary>
/// <param name="s">The TStruct value.</param>
/// <param name="capacity">The capacity of the buffer, in bytes.</param>
public SafeCoTaskMemStruct ( in TStruct s , SizeT capacity = default ) : base ( s , capacity ) { }
/// <summary>Initializes a new instance of the <see cref="SafeCoTaskMemStruct{TStruct}"/> class.</summary>
/// <param name="capacity">The capacity of the buffer, in bytes.</param>
public SafeCoTaskMemStruct ( SizeT capacity = default ) : base ( capacity ) { }
/// <summary>Initializes a new instance of the <see cref="SafeCoTaskMemStruct{TStruct}"/> class.</summary>
/// <param name="ptr">The PTR.</param>
/// <param name="ownsHandle"><c>true</c> to reliably release the handle during finalization; <c>false</c> to prevent it.</param>
/// <param name="allocatedBytes">The number of bytes allocated to <paramref name="ptr"/>.</param>
[ExcludeFromCodeCoverage]
public SafeCoTaskMemStruct ( IntPtr ptr , bool ownsHandle = true , SizeT allocatedBytes = default ) : base ( ptr , ownsHandle , allocatedBytes ) { }
2020-05-28 00:14:24 -04:00
2020-12-30 22:47:14 -05:00
/// <summary>Represents the <see langword="null"/> equivalent of this class instances.</summary>
public static readonly SafeCoTaskMemStruct < TStruct > Null = new SafeCoTaskMemStruct < TStruct > ( IntPtr . Zero , false ) ;
2020-05-28 00:14:24 -04:00
/// <summary>Performs an implicit conversion from <see cref="System.Nullable{TStruct}"/> to <see cref="SafeCoTaskMemStruct{TStruct}"/>.</summary>
/// <param name="s">The value of the <typeparamref name="TStruct"/> instance or <see langword="null"/>.</param>
/// <returns>The resulting <see cref="SafeCoTaskMemStruct{TStruct}"/> instance from the conversion.</returns>
public static implicit operator SafeCoTaskMemStruct < TStruct > ( TStruct ? s ) = > s . HasValue ? new SafeCoTaskMemStruct < TStruct > ( s . Value ) : new SafeCoTaskMemStruct < TStruct > ( IntPtr . Zero ) ;
2020-05-26 08:41:40 -04:00
}
/// <summary>
/// A structure handler based on unmanaged memory allocated by AllocHGlobal.
/// </summary>
/// <typeparam name="TStruct">The type of the structure.</typeparam>
/// <seealso cref="Vanara.InteropServices.SafeMemStruct{TStruct, TMem}" />
public class SafeHGlobalStruct < TStruct > : SafeMemStruct < TStruct , HGlobalMemoryMethods > where TStruct : struct
{
/// <summary>Initializes a new instance of the <see cref="SafeHGlobalStruct{TStruct}"/> class.</summary>
/// <param name="s">The TStruct value.</param>
/// <param name="capacity">The capacity of the buffer, in bytes.</param>
public SafeHGlobalStruct ( in TStruct s , SizeT capacity = default ) : base ( s , capacity ) { }
/// <summary>Initializes a new instance of the <see cref="SafeHGlobalStruct{TStruct}"/> class.</summary>
/// <param name="capacity">The capacity of the buffer, in bytes.</param>
public SafeHGlobalStruct ( SizeT capacity = default ) : base ( capacity ) { }
/// <summary>Initializes a new instance of the <see cref="SafeHGlobalStruct{TStruct}"/> class.</summary>
/// <param name="ptr">The PTR.</param>
/// <param name="ownsHandle"><c>true</c> to reliably release the handle during finalization; <c>false</c> to prevent it.</param>
/// <param name="allocatedBytes">The number of bytes allocated to <paramref name="ptr"/>.</param>
[ExcludeFromCodeCoverage]
public SafeHGlobalStruct ( IntPtr ptr , bool ownsHandle = true , SizeT allocatedBytes = default ) : base ( ptr , ownsHandle , allocatedBytes ) { }
2020-05-28 00:14:24 -04:00
2020-12-30 22:47:14 -05:00
/// <summary>Represents the <see langword="null"/> equivalent of this class instances.</summary>
public static readonly SafeHGlobalStruct < TStruct > Null = new SafeHGlobalStruct < TStruct > ( IntPtr . Zero , false ) ;
2020-05-28 00:14:24 -04:00
/// <summary>Performs an implicit conversion from <see cref="System.Nullable{TStruct}"/> to <see cref="SafeHGlobalStruct{TStruct}"/>.</summary>
/// <param name="s">The value of the <typeparamref name="TStruct"/> instance or <see langword="null"/>.</param>
/// <returns>The resulting <see cref="SafeHGlobalStruct{TStruct}"/> instance from the conversion.</returns>
public static implicit operator SafeHGlobalStruct < TStruct > ( TStruct ? s ) = > s . HasValue ? new SafeHGlobalStruct < TStruct > ( s . Value ) : new SafeHGlobalStruct < TStruct > ( IntPtr . Zero ) ;
2020-05-26 08:41:40 -04:00
}
2020-05-14 19:56:28 -04:00
}