Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/LinkDotNet.StringBuilder/IntegerSpanList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ public void Add(int value)
{
if (count >= buffer.Length)
{
Grow();
EnsureCapacity();
}

buffer[count] = value;
count++;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Grow(int capacity = 0)
private void EnsureCapacity(int capacity = 0)
{
var currentSize = buffer.Length;
var newSize = capacity > 0 ? capacity : currentSize * 2;
Expand Down
10 changes: 5 additions & 5 deletions src/LinkDotNet.StringBuilder/ValueStringBuilder.Append.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public unsafe void Append(bool value)

if (newSize > buffer.Length)
{
Grow(newSize);
EnsureCapacity(newSize);
}

fixed (char* dest = &buffer[bufferPosition])
Expand Down Expand Up @@ -67,7 +67,7 @@ public void Append(scoped ReadOnlySpan<char> str)
var newSize = str.Length + bufferPosition;
if (newSize > buffer.Length)
{
Grow(newSize);
EnsureCapacity(newSize);
}

ref var strRef = ref MemoryMarshal.GetReference(str);
Expand Down Expand Up @@ -111,7 +111,7 @@ public void Append(char value)
var newSize = bufferPosition + 1;
if (newSize > buffer.Length)
{
Grow(newSize);
EnsureCapacity(newSize);
}

buffer[bufferPosition] = value;
Expand Down Expand Up @@ -163,7 +163,7 @@ public Span<char> AppendSpan(int length)
var origPos = bufferPosition;
if (origPos > buffer.Length - length)
{
Grow(length);
EnsureCapacity(length);
}

bufferPosition = origPos + length;
Expand All @@ -177,7 +177,7 @@ private void AppendSpanFormattable<T>(T value, ReadOnlySpan<char> format = defau
var newSize = bufferSize + bufferPosition;
if (newSize >= Capacity)
{
Grow(newSize);
EnsureCapacity(newSize);
}

if (!value.TryFormat(buffer[bufferPosition..], out var written, format, null))
Expand Down
57 changes: 57 additions & 0 deletions src/LinkDotNet.StringBuilder/ValueStringBuilder.EnsureCapacity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace LinkDotNet.StringBuilder;

public ref partial struct ValueStringBuilder
{
/// <summary>
/// Ensures the builder's buffer size is at least <paramref name="newCapacity"/>, renting a larger buffer if not.
/// </summary>
/// <param name="newCapacity">New capacity for the builder.</param>
/// <remarks>
/// If <see cref="Length"/> is already &gt;= <paramref name="newCapacity"/>, nothing is done.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnsureCapacity(int newCapacity)
{
if (Length >= newCapacity)
{
return;
}

var newSize = FindSmallestPowerOf2Above(newCapacity);

var rented = ArrayPool<char>.Shared.Rent(newSize);

if (bufferPosition > 0)
{
ref var sourceRef = ref MemoryMarshal.GetReference(buffer);
ref var destinationRef = ref MemoryMarshal.GetReference(rented.AsSpan());

Unsafe.CopyBlock(
ref Unsafe.As<char, byte>(ref destinationRef),
ref Unsafe.As<char, byte>(ref sourceRef),
(uint)bufferPosition * sizeof(char));
}

if (arrayFromPool is not null)
{
ArrayPool<char>.Shared.Return(arrayFromPool);
}

buffer = rented;
arrayFromPool = rented;
}

/// <summary>
/// Finds the smallest power of 2 which is greater than or equal to <paramref name="minimum"/>.
/// </summary>
/// <param name="minimum">The value the result should be greater than or equal to.</param>
/// <returns>The smallest power of 2 >= <paramref name="minimum"/>.</returns>
private static int FindSmallestPowerOf2Above(int minimum)
{
return 1 << (int)Math.Ceiling(Math.Log2(minimum));
}
}
4 changes: 2 additions & 2 deletions src/LinkDotNet.StringBuilder/ValueStringBuilder.Insert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public void Insert(int index, scoped ReadOnlySpan<char> value)
var newLength = bufferPosition + value.Length;
if (newLength > buffer.Length)
{
Grow(newLength);
EnsureCapacity(newLength);
}

bufferPosition = newLength;
Expand Down Expand Up @@ -79,7 +79,7 @@ private void InsertSpanFormattable<T>(int index, T value, scoped ReadOnlySpan<ch
var newLength = bufferPosition + written;
if (newLength > buffer.Length)
{
Grow(newLength);
EnsureCapacity(newLength);
}

bufferPosition = newLength;
Expand Down
59 changes: 3 additions & 56 deletions src/LinkDotNet.StringBuilder/ValueStringBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,12 @@ namespace LinkDotNet.StringBuilder;
private char[]? arrayFromPool;

/// <summary>
/// Initializes a new instance of the <see cref="ValueStringBuilder"/> struct.
/// Initializes a new instance of the <see cref="ValueStringBuilder"/> struct using a rented buffer of capacity 32.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueStringBuilder()
{
bufferPosition = 0;
buffer = default;
arrayFromPool = null;
Grow(32);
EnsureCapacity(32);
}

/// <summary>
Expand All @@ -41,9 +38,7 @@ public ValueStringBuilder()
#endif
public ValueStringBuilder(Span<char> initialBuffer)
{
bufferPosition = 0;
buffer = initialBuffer;
arrayFromPool = null;
}

/// <summary>
Expand All @@ -63,7 +58,7 @@ public ValueStringBuilder(ReadOnlySpan<char> initialText)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueStringBuilder(int initialCapacity)
{
Grow(initialCapacity);
EnsureCapacity(initialCapacity);
}

/// <summary>
Expand Down Expand Up @@ -172,22 +167,6 @@ public readonly string ToString(Range range)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear() => bufferPosition = 0;

/// <summary>
/// Ensures that the builder has at least <paramref name="newCapacity"/> amount of capacity.
/// </summary>
/// <param name="newCapacity">New capacity for the builder.</param>
/// <remarks>
/// If <paramref name="newCapacity"/> is smaller or equal to <see cref="Length"/> nothing will be done.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void EnsureCapacity(int newCapacity)
{
if (newCapacity > Length)
{
Grow(newCapacity);
}
}

/// <summary>
/// Removes a range of characters from this builder.
/// </summary>
Expand Down Expand Up @@ -301,36 +280,4 @@ public void Dispose()
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void Reverse() => buffer[..bufferPosition].Reverse();

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Grow(int capacity = 0)
{
var size = buffer.Length == 0 ? 8 : buffer.Length;

while (size < capacity)
{
size *= 2;
}

var rented = ArrayPool<char>.Shared.Rent(size);

if (bufferPosition > 0)
{
ref var sourceRef = ref MemoryMarshal.GetReference(buffer);
ref var destinationRef = ref MemoryMarshal.GetReference(rented.AsSpan());

Unsafe.CopyBlock(
ref Unsafe.As<char, byte>(ref destinationRef),
ref Unsafe.As<char, byte>(ref sourceRef),
(uint)(bufferPosition * sizeof(char)));
}

var oldBufferFromPool = arrayFromPool;
buffer = arrayFromPool = rented;

if (oldBufferFromPool is not null)
{
ArrayPool<char>.Shared.Return(oldBufferFromPool);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public static System.Text.StringBuilder ToStringBuilder(this ValueStringBuilder
/// <param name="builder">The builder from which the new instance is derived.</param>
/// <returns>A new <see cref="ValueStringBuilder"/> instance with the string represented by this builder.</returns>
/// <exception cref="ArgumentNullException">Throws if <paramref name="builder"/> is null.</exception>
public static ValueStringBuilder ToValueStringBuilder(this System.Text.StringBuilder? builder)
public static ValueStringBuilder ToValueStringBuilder(this System.Text.StringBuilder builder)
{
ArgumentNullException.ThrowIfNull(builder);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public void ShouldThrowWhenStringBuilderNull()
{
System.Text.StringBuilder? sb = null;

Action act = () => sb.ToValueStringBuilder();
Action act = () => sb!.ToValueStringBuilder();

act.ShouldThrow<ArgumentNullException>();
}
Expand Down
Loading