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 int 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 or sets the at the specified index. /// The . /// The index of the character in the in-memory string. /// The character. /// public char this[int index] { get { var cs = StringHelper.GetCharSize(CharSet); if (index * cs >= Capacity || index < 0) throw new IndexOutOfRangeException(); return CharSet == CharSet.Ansi ? System.Text.Encoding.ASCII.GetChars(GetBytes(index * cs, cs))[0] : System.Text.Encoding.Unicode.GetChars(GetBytes(index * cs, cs))[0]; } set { var cs = StringHelper.GetCharSize(CharSet); if (index * cs >= Capacity || index < 0) throw new IndexOutOfRangeException(); var bytes = CharSet == CharSet.Ansi ? System.Text.Encoding.ASCII.GetBytes(new[] { value }) : System.Text.Encoding.Unicode.GetBytes(new[] { value }); handle.Write(bytes, index * cs, Size); } } /// 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) => ReferenceEquals(this, obj) || obj switch { null => false, SafeMemString ms => Equals(ms), string s => Equals(s), SafeAllocatedMemoryHandle m => m == handle, _ => 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); } }