summaryrefslogtreecommitdiff
path: root/src/mscorlib/corefx/System/Runtime
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/corefx/System/Runtime')
-rw-r--r--src/mscorlib/corefx/System/Runtime/InteropServices/NativeBuffer.cs157
-rw-r--r--src/mscorlib/corefx/System/Runtime/InteropServices/SafeHeapHandle.cs109
-rw-r--r--src/mscorlib/corefx/System/Runtime/InteropServices/SafeHeapHandleCache.cs97
-rw-r--r--src/mscorlib/corefx/System/Runtime/InteropServices/StringBuffer.cs199
4 files changed, 73 insertions, 489 deletions
diff --git a/src/mscorlib/corefx/System/Runtime/InteropServices/NativeBuffer.cs b/src/mscorlib/corefx/System/Runtime/InteropServices/NativeBuffer.cs
deleted file mode 100644
index 875009aee2..0000000000
--- a/src/mscorlib/corefx/System/Runtime/InteropServices/NativeBuffer.cs
+++ /dev/null
@@ -1,157 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Runtime.CompilerServices;
-
-namespace System.Runtime.InteropServices
-{
- /// <summary>
- /// Wrapper for access to the native heap. Dispose to free the memory. Try to use with using statements.
- /// Does not allocate zero size buffers, and will free the existing native buffer if capacity is dropped to zero.
- ///
- /// NativeBuffer utilizes a cache of heap buffers.
- /// </summary>
- /// <remarks>
- /// Suggested use through P/Invoke: define DllImport arguments that take a byte buffer as SafeHandle.
- ///
- /// Using SafeHandle will ensure that the buffer will not get collected during a P/Invoke.
- /// (Notably AddRef and ReleaseRef will be called by the interop layer.)
- ///
- /// This class is not threadsafe, changing the capacity or disposing on multiple threads risks duplicate heap
- /// handles or worse.
- /// </remarks>
- internal class NativeBuffer : IDisposable
- {
- private readonly static SafeHeapHandleCache s_handleCache = new SafeHeapHandleCache();
- private readonly static SafeHandle s_emptyHandle = new EmptySafeHandle();
- private SafeHeapHandle _handle;
- private ulong _capacity;
-
- /// <summary>
- /// Create a buffer with at least the specified initial capacity in bytes.
- /// </summary>
- public NativeBuffer(ulong initialMinCapacity = 0)
- {
- EnsureByteCapacity(initialMinCapacity);
- }
-
- protected unsafe void* VoidPointer
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- return _handle == null ? null : _handle.DangerousGetHandle().ToPointer();
- }
- }
-
- protected unsafe byte* BytePointer
- {
- get
- {
- return (byte*)VoidPointer;
- }
- }
-
- /// <summary>
- /// Get the handle for the buffer.
- /// </summary>
- public SafeHandle GetHandle()
- {
- // Marshalling code will throw on null for SafeHandle
- return _handle ?? s_emptyHandle;
- }
-
- /// <summary>
- /// The capacity of the buffer in bytes.
- /// </summary>
- public ulong ByteCapacity
- {
- get { return _capacity; }
- }
-
- /// <summary>
- /// Ensure capacity in bytes is at least the given minimum.
- /// </summary>
- /// <exception cref="OutOfMemoryException">Thrown if unable to allocate memory when setting.</exception>
- /// <exception cref="ArgumentOutOfRangeException">Thrown if attempting to set <paramref name="nameof(minCapacity)"/> to a value that is larger than the maximum addressable memory.</exception>
- public void EnsureByteCapacity(ulong minCapacity)
- {
- if (_capacity < minCapacity)
- {
- Resize(minCapacity);
- _capacity = minCapacity;
- }
- }
-
- public unsafe byte this[ulong index]
- {
- get
- {
- if (index >= _capacity) throw new ArgumentOutOfRangeException();
- return BytePointer[index];
- }
- set
- {
- if (index >= _capacity) throw new ArgumentOutOfRangeException();
- BytePointer[index] = value;
- }
- }
-
- private unsafe void Resize(ulong byteLength)
- {
- if (byteLength == 0)
- {
- ReleaseHandle();
- return;
- }
-
- if (_handle == null)
- {
- _handle = s_handleCache.Acquire(byteLength);
- }
- else
- {
- _handle.Resize(byteLength);
- }
- }
-
- private void ReleaseHandle()
- {
- if (_handle != null)
- {
- s_handleCache.Release(_handle);
- _capacity = 0;
- _handle = null;
- }
- }
-
- /// <summary>
- /// Release the backing buffer
- /// </summary>
- public virtual void Free()
- {
- ReleaseHandle();
- }
-
- public void Dispose()
- {
- Free();
- }
-
- private sealed class EmptySafeHandle : SafeHandle
- {
- public EmptySafeHandle() : base(IntPtr.Zero, true) { }
-
- public override bool IsInvalid
- {
- get { return true; }
- }
-
- protected override bool ReleaseHandle()
- {
- return true;
- }
- }
- }
-}
diff --git a/src/mscorlib/corefx/System/Runtime/InteropServices/SafeHeapHandle.cs b/src/mscorlib/corefx/System/Runtime/InteropServices/SafeHeapHandle.cs
deleted file mode 100644
index 92b3d980db..0000000000
--- a/src/mscorlib/corefx/System/Runtime/InteropServices/SafeHeapHandle.cs
+++ /dev/null
@@ -1,109 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-namespace System.Runtime.InteropServices
-{
- /// <summary>
- /// Handle for heap memory that allows tracking of capacity and reallocating.
- /// </summary>
- internal sealed class SafeHeapHandle : SafeBuffer
- {
- /// <summary>
- /// Allocate a buffer of the given size if requested.
- /// </summary>
- /// <param name="byteLength">Required size in bytes. Must be less than UInt32.MaxValue for 32 bit or UInt64.MaxValue for 64 bit.</param>
- /// <exception cref="OutOfMemoryException">Thrown if the requested memory size cannot be allocated.</exception>
- /// <exception cref="ArgumentOutOfRangeException">Thrown if size is greater than the maximum memory size.</exception>
- public SafeHeapHandle(ulong byteLength) : base(ownsHandle: true)
- {
- Resize(byteLength);
- }
-
- public override bool IsInvalid
- {
- get { return handle == IntPtr.Zero; }
- }
-
- /// <summary>
- /// Resize the buffer to the given size if requested.
- /// </summary>
- /// <param name="byteLength">Required size in bytes. Must be less than UInt32.MaxValue for 32 bit or UInt64.MaxValue for 64 bit.</param>
- /// <exception cref="OutOfMemoryException">Thrown if the requested memory size cannot be allocated.</exception>
- /// <exception cref="ArgumentOutOfRangeException">Thrown if size is greater than the maximum memory size.</exception>
- public void Resize(ulong byteLength)
- {
- if (IsClosed) throw new ObjectDisposedException(nameof(SafeHeapHandle));
-
- ulong originalLength = 0;
- if (handle == IntPtr.Zero)
- {
- handle = Marshal.AllocHGlobal((IntPtr)byteLength);
- }
- else
- {
- originalLength = ByteLength;
-
- // This may or may not be the same handle, may realloc in place. If the
- // handle changes Windows will deal with the old handle, trying to free it will
- // cause an error.
- handle = Marshal.ReAllocHGlobal(pv: handle, cb: (IntPtr)byteLength);
- }
-
- if (handle == IntPtr.Zero)
- {
- // Only real plausible answer
- throw new OutOfMemoryException();
- }
-
- if (byteLength > originalLength)
- {
- // Add pressure
- ulong addedBytes = byteLength - originalLength;
- if (addedBytes > long.MaxValue)
- {
- GC.AddMemoryPressure(long.MaxValue);
- GC.AddMemoryPressure((long)(addedBytes - long.MaxValue));
- }
- else
- {
- GC.AddMemoryPressure((long)addedBytes);
- }
- }
- else
- {
- // Shrank or did nothing, release pressure if needed
- RemoveMemoryPressure(originalLength - byteLength);
- }
-
- Initialize(byteLength);
- }
-
- private void RemoveMemoryPressure(ulong removedBytes)
- {
- if (removedBytes == 0) return;
-
- if (removedBytes > long.MaxValue)
- {
- GC.RemoveMemoryPressure(long.MaxValue);
- GC.RemoveMemoryPressure((long)(removedBytes - long.MaxValue));
- }
- else
- {
- GC.RemoveMemoryPressure((long)removedBytes);
- }
- }
-
- protected override bool ReleaseHandle()
- {
- if (handle != IntPtr.Zero)
- {
- RemoveMemoryPressure(ByteLength);
- Marshal.FreeHGlobal(handle);
- }
-
- handle = IntPtr.Zero;
- return true;
- }
- }
-}
diff --git a/src/mscorlib/corefx/System/Runtime/InteropServices/SafeHeapHandleCache.cs b/src/mscorlib/corefx/System/Runtime/InteropServices/SafeHeapHandleCache.cs
deleted file mode 100644
index 725076ed66..0000000000
--- a/src/mscorlib/corefx/System/Runtime/InteropServices/SafeHeapHandleCache.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Threading;
-
-namespace System.Runtime.InteropServices
-{
- /// <summary>
- /// Allows limited thread safe reuse of heap buffers to limit memory pressure.
- ///
- /// This cache does not ensure that multiple copies of handles are not released back into the cache.
- /// </summary>
- internal sealed class SafeHeapHandleCache : IDisposable
- {
- private readonly ulong _minSize;
- private readonly ulong _maxSize;
-
- // internal for testing
- internal readonly SafeHeapHandle[] _handleCache;
-
- /// <param name="minSize">Smallest buffer size to allocate in bytes.</param>
- /// <param name="maxSize">The largest buffer size to cache in bytes.</param>
- /// <param name="maxHandles">The maximum number of handles to cache.</param>
- public SafeHeapHandleCache(ulong minSize = 64, ulong maxSize = 1024 * 2, int maxHandles = 0)
- {
- _minSize = minSize;
- _maxSize = maxSize;
- _handleCache = new SafeHeapHandle[maxHandles > 0 ? maxHandles : Environment.ProcessorCount * 4];
- }
-
- /// <summary>
- /// Get a HeapHandle
- /// </summary>
- public SafeHeapHandle Acquire(ulong minSize = 0)
- {
- if (minSize < _minSize) minSize = _minSize;
-
- SafeHeapHandle handle = null;
-
- for (int i = 0; i < _handleCache.Length; i++)
- {
- handle = Interlocked.Exchange(ref _handleCache[i], null);
- if (handle != null) break;
- }
-
- if (handle != null)
- {
- // One possible future consideration is to attempt cycling through to
- // find one that might already have sufficient capacity
- if (handle.ByteLength < minSize)
- handle.Resize(minSize);
- }
- else
- {
- handle = new SafeHeapHandle(minSize);
- }
-
- return handle;
- }
-
- /// <summary>
- /// Give a HeapHandle back for potential reuse
- /// </summary>
- public void Release(SafeHeapHandle handle)
- {
- if (handle.ByteLength <= _maxSize)
- {
- for (int i = 0; i < _handleCache.Length; i++)
- {
- // Push the handles down, walking the last one off the end to keep
- // the top of the "stack" fresh
- handle = Interlocked.Exchange(ref _handleCache[i], handle);
- if (handle == null) return;
- }
- }
-
- handle.Dispose();
- }
-
- public void Dispose()
- {
- Dispose(disposing: true);
- }
-
- private void Dispose(bool disposing)
- {
- if (disposing && _handleCache != null)
- {
- foreach (SafeHeapHandle handle in _handleCache)
- {
- if (handle != null) handle.Dispose();
- }
- }
- }
- }
-}
diff --git a/src/mscorlib/corefx/System/Runtime/InteropServices/StringBuffer.cs b/src/mscorlib/corefx/System/Runtime/InteropServices/StringBuffer.cs
index 29cef08b6c..fdd0b95590 100644
--- a/src/mscorlib/corefx/System/Runtime/InteropServices/StringBuffer.cs
+++ b/src/mscorlib/corefx/System/Runtime/InteropServices/StringBuffer.cs
@@ -2,69 +2,81 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Buffers;
+using System.Runtime.CompilerServices;
+
namespace System.Runtime.InteropServices
{
/// <summary>
- /// Native buffer that deals in char size increments. Dispose to free memory. Allows buffers larger
- /// than a maximum size string to enable working with very large string arrays. Always makes ordinal
- /// comparisons.
+ /// Buffer that deals in char size increments. Dispose to free memory. Always makes ordinal
+ /// comparisons. Not thread safe.
///
/// A more performant replacement for StringBuilder when performing native interop.
+ ///
+ /// "No copy" valuetype. Has to be passed as "ref".
+ ///
/// </summary>
/// <remarks>
/// Suggested use through P/Invoke: define DllImport arguments that take a character buffer as SafeHandle and pass StringBuffer.GetHandle().
/// </remarks>
- internal class StringBuffer : NativeBuffer
+ internal struct StringBuffer
{
- private uint _length;
+ private char[] _buffer;
+ private int _length;
/// <summary>
/// Instantiate the buffer with capacity for at least the specified number of characters. Capacity
/// includes the trailing null character.
/// </summary>
- public StringBuffer(uint initialCapacity = 0)
- : base(initialCapacity * (ulong)sizeof(char))
+ public StringBuffer(int initialCapacity)
{
+ _buffer = ArrayPool<char>.Shared.Rent(initialCapacity);
+ _length = 0;
}
/// <summary>
/// Get/set the character at the given index.
/// </summary>
/// <exception cref="ArgumentOutOfRangeException">Thrown if attempting to index outside of the buffer length.</exception>
- public unsafe char this[uint index]
+ public char this[int index]
{
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (index >= _length) throw new ArgumentOutOfRangeException(nameof(index));
- return CharPointer[index];
+ return _buffer[index];
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
if (index >= _length) throw new ArgumentOutOfRangeException(nameof(index));
- CharPointer[index] = value;
+ _buffer[index] = value;
}
}
/// <summary>
+ /// Underlying storage of the buffer. Used for interop.
+ /// </summary>
+ public char[] UnderlyingArray => _buffer;
+
+ /// <summary>
/// Character capacity of the buffer. Includes the count for the trailing null character.
/// </summary>
- public uint CharCapacity
- {
- get
- {
- ulong byteCapacity = ByteCapacity;
- ulong charCapacity = byteCapacity == 0 ? 0 : byteCapacity / sizeof(char);
- return charCapacity > uint.MaxValue ? uint.MaxValue : (uint)charCapacity;
- }
- }
+ public int Capacity => _buffer.Length;
/// <summary>
/// Ensure capacity in characters is at least the given minimum.
/// </summary>
/// <exception cref="OutOfMemoryException">Thrown if unable to allocate memory when setting.</exception>
- public void EnsureCharCapacity(uint minCapacity)
+ public void EnsureCapacity(int minCapacity)
{
- EnsureByteCapacity(minCapacity * (ulong)sizeof(char));
+ if (minCapacity > Capacity)
+ {
+ char[] oldBuffer = _buffer;
+ _buffer = ArrayPool<char>.Shared.Rent(minCapacity);
+ Array.Copy(oldBuffer, 0, _buffer, 0, oldBuffer.Length);
+ ArrayPool<char>.Shared.Return(oldBuffer);
+ }
}
/// <summary>
@@ -72,59 +84,32 @@ namespace System.Runtime.InteropServices
/// This is where the usable data ends.
/// </summary>
/// <exception cref="OutOfMemoryException">Thrown if unable to allocate memory when setting.</exception>
- /// <exception cref="ArgumentOutOfRangeException">Thrown if the set size in bytes is uint.MaxValue (as space is implicitly reserved for the trailing null).</exception>
- public unsafe uint Length
+ /// <exception cref="ArgumentOutOfRangeException">Thrown if the set size in bytes is int.MaxValue (as space is implicitly reserved for the trailing null).</exception>
+ public int Length
{
get { return _length; }
set
{
- if (value == uint.MaxValue) throw new ArgumentOutOfRangeException(nameof(Length));
-
// Null terminate
- EnsureCharCapacity(value + 1);
- CharPointer[value] = '\0';
+ EnsureCapacity(checked(value + 1));
+ _buffer[value] = '\0';
_length = value;
}
}
/// <summary>
- /// For use when the native api null terminates but doesn't return a length.
- /// If no null is found, the length will not be changed.
- /// </summary>
- public unsafe void SetLengthToFirstNull()
- {
- char* buffer = CharPointer;
- uint capacity = CharCapacity;
- for (uint i = 0; i < capacity; i++)
- {
- if (buffer[i] == '\0')
- {
- _length = i;
- break;
- }
- }
- }
-
- internal unsafe char* CharPointer
- {
- get
- {
- return (char*)VoidPointer;
- }
- }
-
- /// <summary>
/// True if the buffer contains the given character.
/// </summary>
public unsafe bool Contains(char value)
{
- char* start = CharPointer;
- uint length = _length;
-
- for (uint i = 0; i < length; i++)
+ fixed (char* start = _buffer)
{
- if (*start++ == value) return true;
+ int length = _length;
+ for (int i = 0; i < length; i++)
+ {
+ if (start[i] == value) return true;
+ }
}
return false;
@@ -136,7 +121,7 @@ namespace System.Runtime.InteropServices
public bool StartsWith(string value)
{
if (value == null) throw new ArgumentNullException(nameof(value));
- if (_length < (uint)value.Length) return false;
+ if (_length < value.Length) return false;
return SubstringEquals(value, startIndex: 0, count: value.Length);
}
@@ -150,27 +135,28 @@ namespace System.Runtime.InteropServices
/// Thrown if <paramref name="startIndex"/> or <paramref name="count"/> are outside the range
/// of the buffer's length.
/// </exception>
- public unsafe bool SubstringEquals(string value, uint startIndex = 0, int count = -1)
+ public unsafe bool SubstringEquals(string value, int startIndex = 0, int count = -1)
{
if (value == null) return false;
if (count < -1) throw new ArgumentOutOfRangeException(nameof(count));
if (startIndex > _length) throw new ArgumentOutOfRangeException(nameof(startIndex));
- uint realCount = count == -1 ? _length - startIndex : (uint)count;
+ int realCount = count == -1 ? _length - startIndex : (int)count;
if (checked(startIndex + realCount) > _length) throw new ArgumentOutOfRangeException(nameof(count));
int length = value.Length;
// Check the substring length against the input length
- if (realCount != (uint)length) return false;
+ if (realCount != length) return false;
fixed (char* valueStart = value)
+ fixed (char* bufferStart = _buffer)
{
- char* bufferStart = CharPointer + startIndex;
+ char* subStringStart = bufferStart + startIndex;
+
for (int i = 0; i < length; i++)
{
- // Note that indexing in this case generates faster code than trying to copy the pointer and increment it
- if (*bufferStart++ != valueStart[i]) return false;
+ if (subStringStart[i] != valueStart[i]) return false;
}
}
@@ -178,26 +164,6 @@ namespace System.Runtime.InteropServices
}
/// <summary>
- /// Append the given string.
- /// </summary>
- /// <param name="value">The string to append.</param>
- /// <param name="startIndex">The index in the input string to start appending from.</param>
- /// <param name="count">The count of characters to copy from the input string, or -1 for all remaining.</param>
- /// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is null.</exception>
- /// <exception cref="ArgumentOutOfRangeException">
- /// Thrown if <paramref name="startIndex"/> or <paramref name="count"/> are outside the range
- /// of <paramref name="value"/> characters.
- /// </exception>
- public void Append(string value, int startIndex = 0, int count = -1)
- {
- CopyFrom(
- bufferIndex: _length,
- source: value,
- sourceIndex: startIndex,
- count: count);
- }
-
- /// <summary>
/// Append the given buffer.
/// </summary>
/// <param name="value">The buffer to append.</param>
@@ -208,14 +174,13 @@ namespace System.Runtime.InteropServices
/// Thrown if <paramref name="startIndex"/> or <paramref name="count"/> are outside the range
/// of <paramref name="value"/> characters.
/// </exception>
- public void Append(StringBuffer value, uint startIndex = 0)
+ public void Append(ref StringBuffer value, int startIndex = 0)
{
- if (value == null) throw new ArgumentNullException(nameof(value));
if (value.Length == 0) return;
value.CopyTo(
bufferIndex: startIndex,
- destination: this,
+ destination: ref this,
destinationIndex: _length,
count: value.Length);
}
@@ -231,14 +196,13 @@ namespace System.Runtime.InteropServices
/// Thrown if <paramref name="startIndex"/> or <paramref name="count"/> are outside the range
/// of <paramref name="value"/> characters.
/// </exception>
- public void Append(StringBuffer value, uint startIndex, uint count)
+ public void Append(ref StringBuffer value, int startIndex, int count)
{
- if (value == null) throw new ArgumentNullException(nameof(value));
if (count == 0) return;
value.CopyTo(
bufferIndex: startIndex,
- destination: this,
+ destination: ref this,
destinationIndex: _length,
count: count);
}
@@ -252,29 +216,24 @@ namespace System.Runtime.InteropServices
/// of <paramref name="value"/> characters.
/// </exception>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="destination"/> is null.</exception>
- public unsafe void CopyTo(uint bufferIndex, StringBuffer destination, uint destinationIndex, uint count)
+ public void CopyTo(int bufferIndex, ref StringBuffer destination, int destinationIndex, int count)
{
- if (destination == null) throw new ArgumentNullException(nameof(destination));
if (destinationIndex > destination._length) throw new ArgumentOutOfRangeException(nameof(destinationIndex));
if (bufferIndex >= _length) throw new ArgumentOutOfRangeException(nameof(bufferIndex));
if (_length < checked(bufferIndex + count)) throw new ArgumentOutOfRangeException(nameof(count));
if (count == 0) return;
- uint lastIndex = checked(destinationIndex + count);
- if (destination._length < lastIndex) destination.Length = lastIndex;
-
- Buffer.MemoryCopy(
- source: CharPointer + bufferIndex,
- destination: destination.CharPointer + destinationIndex,
- destinationSizeInBytes: checked((long)(destination.ByteCapacity - (destinationIndex * sizeof(char)))),
- sourceBytesToCopy: checked((long)count * sizeof(char)));
+ int lastIndex = checked(destinationIndex + count);
+ if (destination.Length < lastIndex) destination.Length = lastIndex;
+
+ Array.Copy(UnderlyingArray, bufferIndex, destination.UnderlyingArray, destinationIndex, count);
}
/// <summary>
/// Copy contents from the specified string into the buffer at the given index. Start index must be within the current length of
/// the buffer, will grow as necessary.
/// </summary>
- public unsafe void CopyFrom(uint bufferIndex, string source, int sourceIndex = 0, int count = -1)
+ public void CopyFrom(int bufferIndex, string source, int sourceIndex = 0, int count = -1)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (bufferIndex > _length) throw new ArgumentOutOfRangeException(nameof(bufferIndex));
@@ -283,32 +242,22 @@ namespace System.Runtime.InteropServices
if (count < 0 || source.Length - count < sourceIndex) throw new ArgumentOutOfRangeException(nameof(count));
if (count == 0) return;
- uint lastIndex = bufferIndex + (uint)count;
+ int lastIndex = bufferIndex + (int)count;
if (_length < lastIndex) Length = lastIndex;
- fixed (char* content = source)
- {
- Buffer.MemoryCopy(
- source: content + sourceIndex,
- destination: CharPointer + bufferIndex,
- destinationSizeInBytes: checked((long)(ByteCapacity - (bufferIndex * sizeof(char)))),
- sourceBytesToCopy: (long)count * sizeof(char));
- }
+ source.CopyTo(sourceIndex, UnderlyingArray, bufferIndex, count);
}
/// <summary>
/// Trim the specified values from the end of the buffer. If nothing is specified, nothing is trimmed.
/// </summary>
- public unsafe void TrimEnd(char[] values)
+ public void TrimEnd(char[] values)
{
if (values == null || values.Length == 0 || _length == 0) return;
- char* end = CharPointer + _length - 1;
-
- while (_length > 0 && Array.IndexOf(values, *end) >= 0)
+ while (_length > 0 && Array.IndexOf(values, _buffer[_length - 1]) >= 0)
{
Length = _length - 1;
- end--;
}
}
@@ -316,11 +265,9 @@ namespace System.Runtime.InteropServices
/// String representation of the entire buffer. If the buffer is larger than the maximum size string (int.MaxValue) this will throw.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if the buffer is too big to fit into a string.</exception>
- public unsafe override string ToString()
+ public override string ToString()
{
- if (_length == 0) return string.Empty;
- if (_length > int.MaxValue) throw new InvalidOperationException();
- return new string(CharPointer, startIndex: 0, length: (int)_length);
+ return new string(_buffer, startIndex: 0, length: _length);
}
/// <summary>
@@ -331,23 +278,23 @@ namespace System.Runtime.InteropServices
/// Thrown if <paramref name="startIndex"/> or <paramref name="count"/> are outside the range of the buffer's length
/// or count is greater than the maximum string size (int.MaxValue).
/// </exception>
- public unsafe string Substring(uint startIndex, int count = -1)
+ public string Substring(int startIndex, int count = -1)
{
if (startIndex > (_length == 0 ? 0 : _length - 1)) throw new ArgumentOutOfRangeException(nameof(startIndex));
if (count < -1) throw new ArgumentOutOfRangeException(nameof(count));
- uint realCount = count == -1 ? _length - startIndex : (uint)count;
+ int realCount = count == -1 ? _length - startIndex : (int)count;
if (realCount > int.MaxValue || checked(startIndex + realCount) > _length) throw new ArgumentOutOfRangeException(nameof(count));
- if (realCount == 0) return string.Empty;
// The buffer could be bigger than will fit into a string, but the substring might fit. As the starting
// index might be bigger than int we need to index ourselves.
- return new string(value: CharPointer + startIndex, startIndex: 0, length: (int)realCount);
+ return new string(_buffer, startIndex: startIndex, length: realCount);
}
- public override void Free()
+ public void Free()
{
- base.Free();
+ ArrayPool<char>.Shared.Return(_buffer);
+ _buffer = null;
_length = 0;
}
}