using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Security;
using Vanara.Extensions;
namespace Vanara.InteropServices
{
/// Base abstract class for a string handler based on .
/// The type of the memory.
///
public abstract class SafeMemString : SafeMemoryHandle, IEquatable>, IEquatable, IComparable>, IComparable where TMem : IMemoryMethods, new()
{
/// Initializes a new instance of the class.
/// The string value.
/// The character set.
protected SafeMemString(string s, CharSet charSet = CharSet.Unicode) : this(s, s is null ? 0 : s.Length + 1, charSet)
{
}
/// Initializes a new instance of the class.
/// The string value.
/// The capacity of the buffer, in characters.
/// The character set.
protected SafeMemString(string s, int capacity, CharSet charSet = CharSet.Unicode) : this(capacity, charSet)
{
StringHelper.Write(s, handle, out _, true, charSet, Size);
}
/// Initializes a new instance of the class.
/// The string value.
/// The character set.
protected SafeMemString(SecureString s, CharSet charSet = CharSet.Unicode) : this(IntPtr.Zero, charSet)
{
SetHandle(StringHelper.AllocSecureString(s, charSet, mm.AllocMem, out var sz));
base.sz = sz;
}
/// Initializes a new instance of the class.
/// The size of the buffer in characters, including the null character terminator.
/// The character set.
protected SafeMemString(int charLen, CharSet charSet = CharSet.Unicode) : base(charLen * StringHelper.GetCharSize(charSet))
{
CharSet = charSet;
}
/// Prevents a default instance of the class from being created.
[ExcludeFromCodeCoverage]
protected SafeMemString() : base(0) { }
/// Initializes a new instance of the class.
/// The PTR.
/// The character set.
/// true to reliably release the handle during finalization; false to prevent it.
/// The number of bytes allocated to .
[ExcludeFromCodeCoverage]
protected SafeMemString(IntPtr ptr, CharSet charSet = CharSet.Unicode, bool ownsHandle = true, PInvoke.SizeT allocatedBytes = default) : base(ptr, allocatedBytes, ownsHandle)
{
CharSet = charSet;
}
/// Gets the number of allocated characters or 0 if the size is unknown (for example if it is holding a .
/// The number of allocated characters.
public int Capacity
{
get => Size / StringHelper.GetCharSize(CharSet);
set => Size = value * StringHelper.GetCharSize(CharSet);
}
/// Gets the character set of the assigned string.
/// The character set.
public CharSet CharSet { get; private set; } = CharSet.Unicode;
/// Gets the number of characters in the current object.
public int Length => ToString().Length;
/// Performs an explicit conversion from to .
/// The instance.
/// The result of the conversion.
/// Cannot convert an ANSI string to a Char pointer.
public static unsafe explicit operator char*(SafeMemString s) => s.CharSet == CharSet.Unicode ? (char*)(void*)s.handle : throw new InvalidCastException("Cannot convert an ANSI string to a Char pointer.");
/// Returns the value of the field.
/// The instance.
///
/// An representing the value of the handle field. If the handle has been marked invalid with
/// , this method still returns the original handle value, which can be a stale value.
///
public static implicit operator IntPtr(SafeMemString s) => s.DangerousGetHandle();
/// Returns the string value held by a .
/// The instance.
///
/// A value held by the or null if the handle or value is invalid.
///
public static implicit operator string(SafeMemString s) => s?.ToString();
/// Implements the operator !=.
/// The left value.
/// The right value.
/// The result of the operator.
public static bool operator !=(SafeMemString s1, SafeMemString s2) => !s1.Equals(s2);
/// Implements the operator ==.
/// The left value.
/// The right value.
/// The result of the operator.
public static bool operator ==(SafeMemString s1, SafeMemString s2) => s1.Equals(s2);
/// Implements the operator !=.
/// The left value.
/// The right value.
/// The result of the operator.
public static bool operator !=(SafeMemString s1, string s2) => !s1.Equals(s2);
/// Implements the operator ==.
/// The left value.
/// The right value.
/// The result of the operator.
public static bool operator ==(SafeMemString s1, string s2) => s1.Equals(s2);
/// Compares the current object with another object of the same type.
/// An object to compare with this object.
///
/// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has the following
/// meanings: Value Meaning Less than zero This object is less than the parameter.Zero This object is equal
/// to . Greater than zero This object is greater than .
///
public int CompareTo(SafeMemString other) => string.Compare(this, other);
/// Compares the current object with another object of the same type.
/// An object to compare with this object.
///
/// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has the following
/// meanings: Value Meaning Less than zero This object is less than the parameter.Zero This object is equal
/// to . Greater than zero This object is greater than .
///
public int CompareTo(string other) => string.Compare(this, other);
/// Indicates whether the current object is equal to another object of the same type.
/// An object to compare with this object.
/// true if the current object is equal to the parameter; otherwise, false.
public bool Equals(SafeMemString other) => string.Equals(this, other);
/// Indicates whether the current object is equal to another object of the same type.
/// An object to compare with this object.
/// true if the current object is equal to the parameter; otherwise, false.
public bool Equals(string other) => string.Equals(this, other);
/// Determines whether the specified , is equal to this instance.
/// The to compare with this instance.
/// true if the specified is equal to this instance; otherwise, false.
public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj))
return true;
switch (obj)
{
case null:
return false;
case SafeMemString ms:
return Equals(ms);
case string s:
return Equals(s);
case SafeAllocatedMemoryHandle m:
return m == handle;
default:
return false;
}
}
/// Returns a hash code for this instance.
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
public override int GetHashCode() => ToString()?.GetHashCode() ?? 0;
/// Assigns a new string to this memory.
/// The string value. This value can be , but its length cannot be greater than the current .
public virtual void Set(string value)
{
StringHelper.Write(value, handle, out _, true, CharSet, Size);
}
/// Returns the string value held by this instance.
/// A value held by this instance or null if the handle is invalid.
public override string ToString() => IsInvalid ? null : StringHelper.GetString(handle, CharSet, Size == 0 ? long.MaxValue : (long)Size);
}
}