Skip to content

Commit

Permalink
Merge pull request #361 from haqoff/feature/321-allow-mutable-structs…
Browse files Browse the repository at this point in the history
…-as-buffer-writer

Support for mutable structs as BufferWriter.
  • Loading branch information
neuecc authored Feb 6, 2025
2 parents ca28202 + acf8c66 commit c42d4a0
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 9 deletions.
20 changes: 11 additions & 9 deletions src/MemoryPack.Core/MemoryPackSerializer.Serialize.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,12 @@ public static unsafe void Serialize<T, TBufferWriter>(in TBufferWriter bufferWri
where TBufferWriter : class, IBufferWriter<byte>
#endif
{
ref var bufferWriterRef = ref Unsafe.AsRef(in bufferWriter);
if (!RuntimeHelpers.IsReferenceOrContainsReferences<T>())
{
var buffer = bufferWriter.GetSpan(Unsafe.SizeOf<T>());
var buffer = bufferWriterRef.GetSpan(Unsafe.SizeOf<T>());
Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(buffer), value);
bufferWriter.Advance(Unsafe.SizeOf<T>());
bufferWriterRef.Advance(Unsafe.SizeOf<T>());
return;
}
#if NET7_0_OR_GREATER
Expand All @@ -105,29 +106,30 @@ public static unsafe void Serialize<T, TBufferWriter>(in TBufferWriter bufferWri
{
if (value == null)
{
var span = bufferWriter.GetSpan(4);
var span = bufferWriterRef.GetSpan(4);
MemoryPackCode.NullCollectionData.CopyTo(span);
bufferWriter.Advance(4);
bufferWriterRef.Advance(4);
return;
}

var srcArray = ((Array)(object)value!);
var length = srcArray.Length;
if (length == 0)
{
var span = bufferWriter.GetSpan(4);
var span = bufferWriterRef.GetSpan(4);
MemoryPackCode.ZeroCollectionData.CopyTo(span);
bufferWriter.Advance(4);
bufferWriterRef.Advance(4);
return;
}

var dataSize = elementSize * length;
var destSpan = bufferWriter.GetSpan(dataSize + 4);
var destSpan = bufferWriterRef.GetSpan(dataSize + 4);
ref var head = ref MemoryMarshal.GetReference(destSpan);

Unsafe.WriteUnaligned(ref head, length);
Unsafe.CopyBlockUnaligned(ref Unsafe.Add(ref head, 4), ref MemoryMarshal.GetArrayDataReference(srcArray), (uint)dataSize);

bufferWriter.Advance(dataSize + 4);
bufferWriterRef.Advance(dataSize + 4);
return;
}
#endif
Expand All @@ -141,7 +143,7 @@ public static unsafe void Serialize<T, TBufferWriter>(in TBufferWriter bufferWri

try
{
var writer = new MemoryPackWriter<TBufferWriter>(ref Unsafe.AsRef(in bufferWriter), state);
var writer = new MemoryPackWriter<TBufferWriter>(ref bufferWriterRef, state);
Serialize(ref writer, value);
}
finally
Expand Down
59 changes: 59 additions & 0 deletions tests/MemoryPack.Tests/SerializerStructBufferWriterTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System;
using System.Buffers;
using MemoryPack.Tests.Models;

namespace MemoryPack.Tests;

public class SerializerStructBufferWriterTest
{
[Fact]
public void Serialize_ShouldSupportStructAsBufferWriter_WhenValueIsNotReferenceAndNotContainsReferences()
{
var writer = new TestBufferWriter();
MemoryPackSerializer.Serialize(writer, 16);
Assert.Equal(4, writer.WrittenSize);
}

[Fact]
public void Serialize_ShouldSupportStructAsBufferWriter_WhenValueIsUnmanagedSZArray()
{
var writer = new TestBufferWriter();
MemoryPackSerializer.Serialize(writer, new UnmanagedStruct[] { new() { X = 1, Y = 2, Z = 3 } });
Assert.Equal(16, writer.WrittenSize);
}

[Fact]
public void Serialize_ShouldSupportStructAsBufferWriter_WhenFormatterRequired()
{
var writer = new TestBufferWriter();
MemoryPackSerializer.Serialize(writer, new TestData(1));
Assert.Equal(5, writer.WrittenSize);
}
}

[MemoryPackable]
public partial record TestData(int A);

public struct TestBufferWriter : IBufferWriter<byte>
{
public int WrittenSize = 0;

public TestBufferWriter()
{
}

public void Advance(int count)
{
WrittenSize += count;
}

public Memory<byte> GetMemory(int sizeHint = 0)
{
throw new InvalidOperationException();
}

public Span<byte> GetSpan(int sizeHint = 0)
{
return new byte[sizeHint];
}
}

0 comments on commit c42d4a0

Please sign in to comment.