Skip to content

Commit d1b7b61

Browse files
committed
Fix the logic around buffering for large keys
1 parent e148b13 commit d1b7b61

File tree

2 files changed

+10
-5
lines changed

2 files changed

+10
-5
lines changed

src/Components/Components/src/SupplyParameterFromPersistentComponentStateValueProvider.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,12 @@ private static string ComputeFinalKey(byte[] preKey, ComponentState componentSta
114114
Span<byte> keyBuffer = stackalloc byte[1024];
115115
var currentBuffer = keyBuffer;
116116
preKey.CopyTo(keyBuffer);
117-
currentBuffer = currentBuffer[preKey.Length..];
118117
if (key is IUtf8SpanFormattable spanFormattable)
119118
{
120119
var wroteKey = false;
121120
while (!wroteKey)
122121
{
122+
currentBuffer = keyBuffer[preKey.Length..];
123123
wroteKey = spanFormattable.TryFormat(currentBuffer, out var written, "", CultureInfo.InvariantCulture);
124124
if (!wroteKey)
125125
{
@@ -139,12 +139,15 @@ private static string ComputeFinalKey(byte[] preKey, ComponentState componentSta
139139
var wroteKey = false;
140140
while (!wroteKey)
141141
{
142+
currentBuffer = keyBuffer[preKey.Length..];
142143
wroteKey = Encoding.UTF8.TryGetBytes(keySpan, currentBuffer, out var written);
143144
if (!wroteKey)
144145
{
145146
// It is really unlikely that we will enter here, but we need to handle this case
146147
Debug.Assert(written == 0);
147-
GrowBuffer(ref pool, ref keyBuffer);
148+
// Since this is utf-8, grab a buffer the size of the key * 4 + the preKey size
149+
// this guarantees we have enough space to encode the key
150+
GrowBuffer(ref pool, ref keyBuffer, keySpan.Length * 4 + preKey.Length);
148151
}
149152
else
150153
{
@@ -153,7 +156,7 @@ private static string ComputeFinalKey(byte[] preKey, ComponentState componentSta
153156
}
154157
}
155158

156-
keyBuffer = keyBuffer[..(keyBuffer.Length - currentBuffer.Length)];
159+
keyBuffer = keyBuffer[..(preKey.Length + currentBuffer.Length)];
157160

158161
var hashSucceeded = SHA256.TryHashData(keyBuffer, keyHash, out _);
159162
Debug.Assert(hashSucceeded);
@@ -183,9 +186,9 @@ private static ReadOnlySpan<char> ResolveKeySpan(object? key)
183186
return default;
184187
}
185188

186-
private static void GrowBuffer(ref byte[]? pool, ref Span<byte> keyBuffer)
189+
private static void GrowBuffer(ref byte[]? pool, ref Span<byte> keyBuffer, int? size = null)
187190
{
188-
var newPool = pool == null ? ArrayPool<byte>.Shared.Rent(2048) : ArrayPool<byte>.Shared.Rent(pool.Length * 2);
191+
var newPool = pool == null ? ArrayPool<byte>.Shared.Rent(size ?? 2048) : ArrayPool<byte>.Shared.Rent(pool.Length * 2);
189192
keyBuffer.CopyTo(newPool);
190193
keyBuffer = newPool;
191194
if (pool != null)

src/Components/Components/test/SupplyParameterFromPersistentComponentStateValueProviderTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,8 @@ public async Task PersistAsync_CanPersistMultipleComponentsOfSameType_WhenParent
229229
{ 123456.789m, -123456.789m },
230230
{ new DateTime(2023, 1, 1), new DateTime(2023, 12, 31) },
231231
{ "key1", "key2" },
232+
// Include a very long key to validate logic around growing buffers
233+
{ new string('a', 10000), new string('b', 10000) },
232234
{ Guid.NewGuid(), Guid.NewGuid() },
233235
{ new DateOnly(2023, 1, 1), new DateOnly(2023, 12, 31) },
234236
{ new TimeOnly(12, 34, 56), new TimeOnly(23, 45, 56) },

0 commit comments

Comments
 (0)