Skip to content

Commit 926c842

Browse files
committed
Return pinnable reference
1 parent 7b61fce commit 926c842

File tree

4 files changed

+38
-0
lines changed

4 files changed

+38
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ All notable changes to **ValueStringBuilder** will be documented in this file. T
88

99
### Added
1010
- Added `Replace` methods which also tries to have the least amount of allocations.
11+
- Added `GetPinnableReference` which allows to get the content via the `fixed` keyword.
1112

1213
## [0.9.2] - 2022-04-06
1314

src/LinkDotNet.StringBuilder/ValueStringBuilder.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Buffers;
2+
using System.Runtime.InteropServices;
23

34
namespace LinkDotNet.StringBuilder;
45

@@ -49,6 +50,24 @@ public ValueStringBuilder()
4950
/// <returns>The filled array as <see cref="ReadOnlySpan{T}"/>.</returns>
5051
public ReadOnlySpan<char> AsSpan() => buffer[..bufferPosition];
5152

53+
/// <summary>
54+
/// Get a pinnable reference to the represented string from this builder.
55+
/// The content after <see cref="Length"/> is not guaranteed to be null terminated.
56+
/// </summary>
57+
/// <returns>The pointer to the first instance of the string represented by this builder.</returns>
58+
/// <remarks>
59+
/// This method is used for use-cases where the user wants to use "fixed" calls like the following:
60+
/// <code>
61+
/// var stringBuilder = new ValueStringBuilder();
62+
/// stringBuilder.Append("Hello World");
63+
/// fixed (var* buffer = stringBuilder) { ... }
64+
/// </code>
65+
/// </remarks>
66+
public ref char GetPinnableReference()
67+
{
68+
return ref MemoryMarshal.GetReference(buffer);
69+
}
70+
5271
/// <summary>
5372
/// Tries to copy the represented string into the given <see cref="Span{T}"/>.
5473
/// </summary>
@@ -105,6 +124,9 @@ public void Remove(int startIndex, int length)
105124
private void Grow(int capacity = 0)
106125
{
107126
var currentSize = buffer.Length;
127+
128+
// This could lead to the potential problem that an user sets the capacity smaller than the current length
129+
// which would lead to a truncated string.
108130
var newSize = capacity > 0 ? capacity : currentSize * 2;
109131
var rented = ArrayPool<char>.Shared.Rent(newSize);
110132
buffer.CopyTo(rented);

tests/LinkDotNet.StringBuilder.UnitTests/LinkDotNet.StringBuilder.UnitTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<TargetFramework>net6.0</TargetFramework>
55
<Nullable>enable</Nullable>
66
<IsPackable>false</IsPackable>
7+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
78
</PropertyGroup>
89

910
<ItemGroup>

tests/LinkDotNet.StringBuilder.UnitTests/ValueStringBuilderTests.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,4 +185,18 @@ public void ShouldNotRemoveEntriesWhenLengthIsEqualToZero()
185185

186186
stringBuilder.ToString().Should().Be("Hello");
187187
}
188+
189+
[Fact]
190+
public unsafe void ShouldGetPinnableReference()
191+
{
192+
var stringBuilder = new ValueStringBuilder();
193+
stringBuilder.Append("Hey");
194+
195+
fixed (char* c = stringBuilder)
196+
{
197+
c[0].Should().Be('H');
198+
c[1].Should().Be('e');
199+
c[2].Should().Be('y');
200+
}
201+
}
188202
}

0 commit comments

Comments
 (0)