Skip to content

Commit 8861abd

Browse files
pranavkmAArnott
authored andcommitted
Fix MessagePackReader for multi-segment strings (MessagePack-CSharp#430)
* Fix MessagePackReader for multi-segment strings * Add failing unit test to show the bug * Fix issues in ReadStringSlow in NS1.6 code path
1 parent d5d6b6e commit 8861abd

File tree

4 files changed

+72
-3
lines changed

4 files changed

+72
-3
lines changed

src/MessagePack/MessagePackReader.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -779,8 +779,7 @@ private string ReadStringSlow(int byteLength)
779779
// We need to concatenate all the bytes together into one array.
780780
byte[] byteArray = ArrayPool<byte>.Shared.Rent(byteLength);
781781
ThrowInsufficientBufferUnless(this.reader.TryCopyTo(byteArray));
782-
this.reader.Advance(byteArray.Length);
783-
string value = StringEncoding.UTF8.GetString(byteArray);
782+
string value = StringEncoding.UTF8.GetString(byteArray, 0, byteLength);
784783
ArrayPool<byte>.Shared.Return(byteArray);
785784
return value;
786785
#else
@@ -808,6 +807,7 @@ private string ReadStringSlow(int byteLength)
808807
}
809808
}
810809
#endif
810+
this.reader.Advance(bytesRead);
811811
}
812812

813813
string value = new string(charArray, 0, initializedChars);

src/MessagePack/SequenceReader.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ public bool TryCopyTo(Span<T> destination)
438438

439439
internal bool TryCopyMultisegment(Span<T> destination)
440440
{
441-
if (Remaining < destination.Length)
441+
if (destination.Length < Remaining)
442442
return false;
443443

444444
ReadOnlySpan<T> firstSpan = UnreadSpan;

tests/MessagePack.Internal.Tests/MessagePack.Internal.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
<Compile Include="..\MessagePack.Tests\MessagePackBinaryTest.cs" />
2020
<Compile Include="..\MessagePack.Tests\MessagePackReaderTests.cs" />
2121
<Compile Include="..\MessagePack.Tests\MessagePackReaderTests.ReadInt.cs" />
22+
<Compile Include="..\MessagePack.Tests\MessagePackReaderTests.ReadString.cs" />
2223
<Compile Include="..\MessagePack.Tests\Utils\ChainingAssertion.Xunit.cs" />
2324
</ItemGroup>
2425
</Project>
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using System;
2+
using System.Buffers;
3+
using Xunit;
4+
5+
namespace MessagePack.Tests
6+
{
7+
partial class MessagePackReaderTests
8+
{
9+
[Fact]
10+
public void ReadString_HandlesSingleSegment()
11+
{
12+
var seq = BuildSequence(new[] {
13+
(byte)(MessagePackCode.MinFixStr + 2),
14+
(byte)'A', (byte)'B' });
15+
16+
var reader = new MessagePackReader(seq);
17+
var result = reader.ReadString();
18+
Assert.Equal("AB", result);
19+
}
20+
21+
[Fact]
22+
public void ReadString_HandlesMultipleSegments()
23+
{
24+
var seq = BuildSequence(
25+
new[] { (byte)(MessagePackCode.MinFixStr + 2), (byte)'A' },
26+
new[] { (byte)'B' });
27+
28+
var reader = new MessagePackReader(seq);
29+
var result = reader.ReadString();
30+
Assert.Equal("AB", result);
31+
}
32+
33+
ReadOnlySequence<T> BuildSequence<T>(params T[][] segmentContents)
34+
{
35+
if (segmentContents.Length == 1)
36+
{
37+
return new ReadOnlySequence<T>(segmentContents[0].AsMemory());
38+
}
39+
40+
var bufferSegment = new BufferSegment<T>(segmentContents[0].AsMemory());
41+
BufferSegment<T> last = default;
42+
for (var i = 1; i < segmentContents.Length; i++)
43+
{
44+
last = bufferSegment.Append(segmentContents[i]);
45+
}
46+
47+
return new ReadOnlySequence<T>(bufferSegment, 0, last, last.Memory.Length);
48+
}
49+
50+
internal class BufferSegment<T> : ReadOnlySequenceSegment<T>
51+
{
52+
public BufferSegment(ReadOnlyMemory<T> memory)
53+
{
54+
Memory = memory;
55+
}
56+
57+
public BufferSegment<T> Append(ReadOnlyMemory<T> memory)
58+
{
59+
var segment = new BufferSegment<T>(memory)
60+
{
61+
RunningIndex = RunningIndex + Memory.Length
62+
};
63+
Next = segment;
64+
return segment;
65+
}
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)