Skip to content

Commit 40b4575

Browse files
authored
Fix buffer copying for HybridCachePayload. (#7343)
1 parent 0a74a5f commit 40b4575

File tree

2 files changed

+42
-3
lines changed

2 files changed

+42
-3
lines changed

src/Libraries/Microsoft.Extensions.Caching.Hybrid/Internal/HybridCachePayload.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -308,9 +308,7 @@ public static HybridCachePayloadParseResult TryParse(ArraySegment<byte> source,
308308
pendingTags = new(pendingTagBuffer[0]);
309309
break;
310310
default:
311-
string[] final = new string[pendingTagsCount];
312-
pendingTagBuffer.CopyTo(final, 0);
313-
pendingTags = new(final);
311+
pendingTags = new(pendingTagBuffer.AsSpan(0, pendingTagsCount).ToArray());
314312
break;
315313
}
316314

test/Libraries/Microsoft.Extensions.Caching.Hybrid.Tests/PayloadTests.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,4 +322,45 @@ public async Task MalformedTagDetected()
322322
collector.WriteTo(log);
323323
collector.AssertErrors([Log.IdTagInvalidUnicode]);
324324
}
325+
326+
[Theory]
327+
[InlineData("tag1,tag2", 2)]
328+
[InlineData("tag1,tag2,tag3", 3)]
329+
public void RoundTrip_WithPendingTags_WhenKnownTagsMismatch(string delimitedTags, int tagCount)
330+
{
331+
var clock = new FakeTime();
332+
using var provider = GetDefaultCache(out var cache, config =>
333+
{
334+
config.AddSingleton<TimeProvider>(clock);
335+
});
336+
337+
byte[] bytes = new byte[1024];
338+
new Random().NextBytes(bytes);
339+
340+
string key = "my key";
341+
string[] tagsArray = delimitedTags.Split(',');
342+
var writeTags = TagSet.Create(tagsArray);
343+
Assert.Equal(tagCount, writeTags.Count);
344+
345+
var maxLen = HybridCachePayload.GetMaxBytes(key, writeTags, bytes.Length);
346+
var oversized = ArrayPool<byte>.Shared.Rent(maxLen);
347+
348+
int actualLength = HybridCachePayload.Write(oversized, key, cache.CurrentTimestamp(), TimeSpan.FromMinutes(1), 0, writeTags, new(bytes));
349+
log.WriteLine($"bytes written: {actualLength}");
350+
351+
clock.Add(TimeSpan.FromSeconds(10));
352+
353+
// Inject non-completed tasks for each tag so IsTagExpired returns isPending=true
354+
foreach (string tag in tagsArray)
355+
{
356+
cache.DebugInvalidateTag(tag, new TaskCompletionSource<long>().Task);
357+
}
358+
359+
// Parse with empty knownTags to force all tags into pendingTags via the rented buffer path
360+
var result = HybridCachePayload.TryParse(new(oversized, 0, actualLength), key, TagSet.Empty, cache,
361+
out var payload, out var remaining, out var flags, out var entropy, out var pendingTags, out _);
362+
Assert.Equal(HybridCachePayload.HybridCachePayloadParseResult.Success, result);
363+
Assert.True(payload.SequenceEqual(bytes));
364+
Assert.Equal(tagCount, pendingTags.Count);
365+
}
325366
}

0 commit comments

Comments
 (0)