diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs index d8e0576fc1bf2d..5522fa8bb55edb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs @@ -13,8 +13,8 @@ namespace System.Runtime.InteropServices.Marshalling Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.CallerAllocatedBuffer)] public unsafe ref struct AnsiStringMarshaller { - private byte* _allocated; - private readonly Span _span; + private byte* _nativeValue; + private bool _allocated; /// /// Initializes a new instance of the . @@ -36,25 +36,29 @@ public AnsiStringMarshaller(string? str) /// public AnsiStringMarshaller(string? str, Span buffer) { - _allocated = null; + _allocated = false; + if (str is null) { - _span = default; + _nativeValue = null; return; } - // + 1 for null terminator - int maxByteCount = (str.Length + 1) * Marshal.SystemMaxDBCSCharSize + 1; - if (buffer.Length >= maxByteCount) - { - Marshal.StringToAnsiString(str, (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)), buffer.Length); - _span = buffer; - } - else + // >= for null terminator + if ((long)Marshal.SystemMaxDBCSCharSize * str.Length >= buffer.Length) { - _allocated = (byte*)Marshal.StringToCoTaskMemAnsi(str); - _span = default; + // Calculate accurate byte count when the provided stack-allocated buffer is not sufficient + int exactByteCount = Marshal.GetAnsiStringByteCount(str); // Includes null terminator + if (exactByteCount > buffer.Length) + { + buffer = new Span((byte*)Marshal.AllocCoTaskMem(exactByteCount), exactByteCount); + _allocated = true; + } } + + _nativeValue = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); + + Marshal.GetAnsiStringBytes(str, buffer); // Includes null terminator } /// @@ -63,7 +67,7 @@ public AnsiStringMarshaller(string? str, Span buffer) /// /// /// - public byte* ToNativeValue() => _allocated != null ? _allocated : (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(_span)); + public byte* ToNativeValue() => _nativeValue; /// /// Sets the native value representing the string. @@ -72,7 +76,11 @@ public AnsiStringMarshaller(string? str, Span buffer) /// /// /// - public void FromNativeValue(byte* value) => _allocated = value; + public void FromNativeValue(byte* value) + { + _nativeValue = value; + _allocated = true; + } /// /// Returns the managed string. @@ -80,7 +88,7 @@ public AnsiStringMarshaller(string? str, Span buffer) /// /// /// - public string? ToManaged() => _allocated == null ? null : new string((sbyte*)_allocated); + public string? ToManaged() => Marshal.PtrToStringAnsi((IntPtr)_nativeValue); /// /// Frees native resources. @@ -90,8 +98,8 @@ public AnsiStringMarshaller(string? str, Span buffer) /// public void FreeNative() { - if (_allocated != null) - Marshal.FreeCoTaskMem((IntPtr)_allocated); + if (_allocated) + Marshal.FreeCoTaskMem((IntPtr)_nativeValue); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf16StringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf16StringMarshaller.cs index e5220a1571f7c1..7d77bedc19ff51 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf16StringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf16StringMarshaller.cs @@ -9,64 +9,33 @@ namespace System.Runtime.InteropServices.Marshalling /// Marshaller for UTF-16 strings /// [CLSCompliant(false)] - [CustomTypeMarshaller(typeof(string), BufferSize = 0x100, - Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.CallerAllocatedBuffer)] + [CustomTypeMarshaller(typeof(string), + Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling)] public unsafe ref struct Utf16StringMarshaller { - private ushort* _allocated; - private readonly Span _span; + private ushort* _nativeValue; /// /// Initializes a new instance of the . /// - /// The string to marshal. - public Utf16StringMarshaller(string? str) - : this(str, default) - { - } - - /// - /// Initializes a new instance of the . - /// - /// The string to marshal. - /// Buffer that may be used for marshalling. /// - /// The must not be movable - that is, it should not be - /// on the managed heap or it should be pinned. - /// + /// The caller allocated constructor option is not provided because + /// pinning should be preferred for UTF-16 scenarios. /// - public Utf16StringMarshaller(string? str, Span buffer) + /// The string to marshal. + public Utf16StringMarshaller(string? str) { - _allocated = null; if (str is null) { - _span = default; + _nativeValue = null; return; } // + 1 for null terminator - if (buffer.Length >= str.Length + 1) - { - _span = buffer; - str.CopyTo(MemoryMarshal.Cast(buffer)); - _span[str.Length] = '\0'; // null-terminate - } - else - { - _allocated = (ushort*)Marshal.StringToCoTaskMemUni(str); - _span = default; - } - } - - /// - /// Returns a reference to the marshalled string. - /// - public ref ushort GetPinnableReference() - { - if (_allocated != null) - return ref Unsafe.AsRef(_allocated); + _nativeValue = (ushort*)Marshal.AllocCoTaskMem((str.Length + 1) * sizeof(ushort)); - return ref _span.GetPinnableReference(); + str.CopyTo(new Span(_nativeValue, str.Length)); + _nativeValue[str.Length] = '\0'; // null-terminate } /// @@ -75,7 +44,7 @@ public ref ushort GetPinnableReference() /// /// /// - public ushort* ToNativeValue() => _allocated != null ? _allocated : (ushort*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(_span)); + public ushort* ToNativeValue() => _nativeValue; /// /// Sets the native value representing the string. @@ -84,7 +53,7 @@ public ref ushort GetPinnableReference() /// /// /// - public void FromNativeValue(ushort* value) => _allocated = value; + public void FromNativeValue(ushort* value) => _nativeValue = value; /// /// Returns the managed string. @@ -92,7 +61,7 @@ public ref ushort GetPinnableReference() /// /// /// - public string? ToManaged() => _allocated == null ? null : new string((char*)_allocated); + public string? ToManaged() => Marshal.PtrToStringUni((IntPtr)_nativeValue); /// /// Frees native resources. @@ -102,8 +71,7 @@ public ref ushort GetPinnableReference() /// public void FreeNative() { - if (_allocated != null) - Marshal.FreeCoTaskMem((IntPtr)_allocated); + Marshal.FreeCoTaskMem((IntPtr)_nativeValue); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs index 8737d0de3928be..be37a40586f926 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs @@ -14,8 +14,8 @@ namespace System.Runtime.InteropServices.Marshalling Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.TwoStageMarshalling | CustomTypeMarshallerFeatures.CallerAllocatedBuffer)] public unsafe ref struct Utf8StringMarshaller { - private byte* _allocated; - private readonly Span _span; + private byte* _nativeValue; + private bool _allocated; /// /// Initializes a new instance of the . @@ -37,32 +37,32 @@ public Utf8StringMarshaller(string? str) /// public Utf8StringMarshaller(string? str, Span buffer) { - _allocated = null; + _allocated = false; + if (str is null) { - _span = default; + _nativeValue = null; return; } - // + 1 for null terminator - int maxByteCount = Encoding.UTF8.GetMaxByteCount(str.Length) + 1; - if (buffer.Length >= maxByteCount) - { - int byteCount = Encoding.UTF8.GetBytes(str, buffer); - buffer[byteCount] = 0; // null-terminate - _span = buffer; - } - else + const int MaxUtf8BytesPerChar = 3; + + // >= for null terminator + if ((long)MaxUtf8BytesPerChar * str.Length >= buffer.Length) { - _allocated = (byte*)Marshal.AllocCoTaskMem(maxByteCount); - int byteCount; - fixed (char* ptr = str) + // Calculate accurate byte count when the provided stack-allocated buffer is not sufficient + int exactByteCount = checked(Encoding.UTF8.GetByteCount(str) + 1); // + 1 for null terminator + if (exactByteCount > buffer.Length) { - byteCount = Encoding.UTF8.GetBytes(ptr, str.Length, _allocated, maxByteCount); + buffer = new Span((byte*)Marshal.AllocCoTaskMem(exactByteCount), exactByteCount); + _allocated = true; } - _allocated[byteCount] = 0; // null-terminate - _span = default; } + + _nativeValue = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); + + int byteCount = Encoding.UTF8.GetBytes(str, buffer); + buffer[byteCount] = 0; // null-terminate } /// @@ -71,7 +71,7 @@ public Utf8StringMarshaller(string? str, Span buffer) /// /// /// - public byte* ToNativeValue() => _allocated != null ? _allocated : (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(_span)); + public byte* ToNativeValue() => _nativeValue; /// /// Sets the native value representing the string. @@ -80,7 +80,11 @@ public Utf8StringMarshaller(string? str, Span buffer) /// /// /// - public void FromNativeValue(byte* value) => _allocated = value; + public void FromNativeValue(byte* value) + { + _nativeValue = value; + _allocated = true; + } /// /// Returns the managed string. @@ -88,7 +92,7 @@ public Utf8StringMarshaller(string? str, Span buffer) /// /// /// - public string? ToManaged() => _allocated == null ? null : Marshal.PtrToStringUTF8((IntPtr)_allocated); + public string? ToManaged() => Marshal.PtrToStringUTF8((IntPtr)_nativeValue); /// /// Frees native resources. @@ -98,8 +102,8 @@ public Utf8StringMarshaller(string? str, Span buffer) /// public void FreeNative() { - if (_allocated != null) - Marshal.FreeCoTaskMem((IntPtr)_allocated); + if (_allocated) + Marshal.FreeCoTaskMem((IntPtr)_nativeValue); } } } diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index 898b96ba95ea81..6c196e7f100621 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -2187,15 +2187,12 @@ public void FromNativeValue(byte* value) { } public void FreeNative() { } } [System.CLSCompliant(false)] - [System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerAttribute(typeof(string), BufferSize = 0x100, + [System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerAttribute(typeof(string), Features = System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerFeatures.UnmanagedResources - | System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerFeatures.CallerAllocatedBuffer | System.Runtime.InteropServices.Marshalling.CustomTypeMarshallerFeatures.TwoStageMarshalling )] public unsafe ref struct Utf16StringMarshaller { public Utf16StringMarshaller(string? str) { } - public Utf16StringMarshaller(string? str, System.Span buffer) { } - public ref ushort GetPinnableReference() { throw null; } public ushort* ToNativeValue() { throw null; } public void FromNativeValue(ushort* value) { } public string? ToManaged() { throw null; }