Skip to content

Commit b0d8ae7

Browse files
committed
StringBuilder serialization support
1 parent 5a6cffa commit b0d8ae7

File tree

4 files changed

+92
-7
lines changed

4 files changed

+92
-7
lines changed

src/Backdash/Serialization/BinaryBufferReader.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Numerics;
44
using System.Runtime.CompilerServices;
55
using System.Runtime.InteropServices;
6+
using System.Text;
67
using Backdash.Core;
78
using Backdash.Data;
89
using Backdash.Network;
@@ -75,6 +76,15 @@ Span<T> GetListSpan<T>(in List<T> values, in IObjectPool<T> pool) where T : clas
7576
return CollectionsMarshal.AsSpan(values);
7677
}
7778

79+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
80+
ReadOnlySpan<T> AllocSpan<T>(int size) where T : struct
81+
{
82+
var span = CurrentBuffer[..(size * Unsafe.SizeOf<T>())];
83+
Advance(size);
84+
return MemoryMarshal.Cast<byte, T>(span);
85+
}
86+
87+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
7888
void ReadSpan<T>(in Span<T> data) where T : struct => Read(MemoryMarshal.AsBytes(data));
7989

8090
/// <summary>Reads single <see cref="byte"/> from buffer.</summary>
@@ -450,6 +460,17 @@ public void Read<T>(in List<T> values, IObjectPool<T> pool) where T : class, IBi
450460
public void Read<T>(List<T> values) where T : class, IBinarySerializable, new() =>
451461
Read(GetListSpan(in values, in DefaultObjectPool<T>.Instance));
452462

463+
/// <summary>
464+
/// Reads a StringBuilder into <paramref name="values"/> from buffer.
465+
/// </summary>
466+
public void Read(in StringBuilder values)
467+
{
468+
var size = ReadInt32();
469+
var chars = AllocSpan<char>(size);
470+
values.Clear();
471+
values.Append(chars);
472+
}
473+
453474
/// <inheritdoc cref="ReadByte()"/>
454475
public void Read(ref byte value) => value = ReadByte();
455476

src/Backdash/Serialization/BinaryBufferWriter.cs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Numerics;
55
using System.Runtime.CompilerServices;
66
using System.Runtime.InteropServices;
7+
using System.Text;
78
using Backdash.Core;
89
using Backdash.Network;
910

@@ -51,6 +52,15 @@ Span<T> AllocSpan<T>(in ReadOnlySpan<T> value) where T : unmanaged
5152
return result;
5253
}
5354

55+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
56+
Span<T> AllocSpan<T>(int size) where T : unmanaged
57+
{
58+
var sizeBytes = Unsafe.SizeOf<T>() * size;
59+
var result = MemoryMarshal.Cast<byte, T>(buffer.GetSpan(sizeBytes));
60+
Advance(sizeBytes);
61+
return result;
62+
}
63+
5464
[MethodImpl(MethodImplOptions.AggressiveInlining)]
5565
Span<T> GetListSpan<T>(in List<T> values)
5666
{
@@ -525,6 +535,14 @@ public void Write<T>(in T[] values) where T : IBinarySerializable =>
525535
/// <typeparam name="T">A type that implements <see cref="IBinarySerializable"/>.</typeparam>
526536
public void Write<T>(in List<T> values) where T : IBinarySerializable => Write<T>(GetListSpan(in values));
527537

538+
/// <summary>Writes an <see cref="StringBuilder"/> <paramref name="value"/> into buffer.</summary>
539+
public void Write(in StringBuilder value)
540+
{
541+
var len = value.Length;
542+
Write(len);
543+
value.CopyTo(0, AllocSpan<char>(len), len);
544+
}
545+
528546
/// <summary>Writes an unmanaged struct into buffer.</summary>
529547
public void WriteStruct<T>(in T value) where T : unmanaged => Write(Mem.AsBytes(in value));
530548

@@ -547,7 +565,21 @@ public void WriteStruct<T>(in T? value) where T : unmanaged
547565
}
548566

549567
/// <summary>Writes an <see cref="string"/> <paramref name="value"/> into buffer.</summary>
550-
public void WriteString(in string value) => Write(value.AsSpan());
568+
public void WriteString(in string value, int size)
569+
{
570+
var chars = value.AsSpan();
571+
if (chars.Length >= size)
572+
{
573+
Write(chars[..size]);
574+
}
575+
else
576+
{
577+
Write(chars);
578+
Span<char> nullChars = stackalloc char[size - chars.Length];
579+
nullChars.Fill(' ');
580+
Write(nullChars);
581+
}
582+
}
551583

552584
/// <summary>Writes an <see cref="string"/> <paramref name="value"/> into buffer as UTF8.</summary>
553585
public void WriteUtf8String(in ReadOnlySpan<char> value)

tests/Backdash.Tests/Specs/Unit/Serialization/BinaryBufferReadWriteSpanTests.cs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Buffers;
22
using System.Runtime.CompilerServices;
3+
using System.Text;
34
using Backdash.Network;
45
using Backdash.Serialization;
56
using Backdash.Tests.TestUtils;
@@ -225,18 +226,33 @@ public bool SpanOfUtf8Bytes(NonEmptyString input, Endianness endianness)
225226
}
226227

227228
[PropertyTest]
228-
public bool String(NonEmptyString input, Endianness endianness)
229+
public bool String(NonEmptyString input, PositiveInt stringSize)
229230
{
230231
var value = input.Item;
232+
var size = stringSize.Item;
231233

232-
var size = Setup(value.ToCharArray(), endianness, out var writer);
233-
writer.WriteString(value);
234+
Setup(Endianness.LittleEndian, out var writer);
235+
writer.WriteString(value, size);
234236
var reader = GetReader(writer);
235237

236-
var result = reader.ReadString(value.Length);
238+
var result = reader.ReadString(size);
237239

238-
reader.ReadCount.Should().Be(size);
239-
return string.Equals(value, result);
240+
if (value.Length > size)
241+
value = value[..size];
242+
243+
return string.Equals(value.Trim(), result.Trim());
244+
}
245+
246+
[PropertyTest]
247+
public bool StringBuilder(StringBuilder value, StringBuilder read)
248+
{
249+
Setup(Endianness.LittleEndian, out var writer);
250+
writer.Write(in value);
251+
252+
var reader = GetReader(writer);
253+
reader.Read(in read);
254+
255+
return string.Equals(value.ToString(), read.ToString());
240256
}
241257

242258
[PropertyTest]
@@ -457,6 +473,16 @@ out BinaryBufferWriter writer
457473
return size;
458474
}
459475

476+
static void Setup(
477+
Endianness endianness,
478+
out BinaryBufferWriter writer
479+
)
480+
{
481+
readOffset = 0;
482+
ArrayBufferWriter<byte> buffer = new();
483+
writer = new(buffer, endianness);
484+
}
485+
460486
static BinaryBufferReader GetReader(in BinaryBufferWriter writer)
461487
{
462488
var buffer = (ArrayBufferWriter<byte>)writer.Buffer;

tests/Backdash.Tests/TestUtils/TestGenerators.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Drawing;
22
using System.Numerics;
3+
using System.Text;
34
using Backdash.Core;
45
using Backdash.Data;
56
using Backdash.Network.Messages;
@@ -77,6 +78,11 @@ from y in Generate<int>()
7778
select new Point(x, y)
7879
);
7980

81+
public static Arbitrary<StringBuilder> StringBuilderGenerator() => Arb.From(
82+
from s in Generate<string>()
83+
select new StringBuilder(s)
84+
);
85+
8086
public static Arbitrary<SimpleStructData> SimpleStructDataGenerator(
8187
Arbitrary<Point> pointGenerator
8288
) =>

0 commit comments

Comments
 (0)