Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions LiteDB.Tests/Document/Bson_Tests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using FluentAssertions;
using Xunit;
Expand Down Expand Up @@ -138,5 +139,41 @@ static T DeserializeAnonymous<T>(BsonMapper mapper, BsonDocument doc, T obj)
return mapper.Deserialize<T>(doc);
}
}

[Fact]
public void Serialize_To_ArrayBufferWriter_Matches_Byte_Array()
{
var document = CreateDoc();
var expected = BsonSerializer.Serialize(document);

var bufferWriter = new ArrayBufferWriter<byte>();
var written = BsonSerializer.Serialize(document, bufferWriter);

written.Should().Be(expected.Length);
bufferWriter.WrittenCount.Should().Be(expected.Length);
bufferWriter.WrittenSpan.ToArray().Should().Equal(expected);
}

[Fact]
public void Serialize_To_Pooled_Buffers_Preserves_Output()
{
var document = CreateDoc();
var expected = BsonSerializer.Serialize(document);

var rented = ArrayPool<byte>.Shared.Rent(expected.Length + 8);

try
{
var memory = new Memory<byte>(rented, 2, expected.Length);
var memoryWritten = BsonSerializer.Serialize(document, memory);

memoryWritten.Should().Be(expected.Length);
memory.Span.Slice(0, memoryWritten).ToArray().Should().Equal(expected);
}
finally
{
ArrayPool<byte>.Shared.Return(rented);
}
}
}
}
68 changes: 61 additions & 7 deletions LiteDB/Document/Bson/BsonSerializer.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using LiteDB.Engine;
using LiteDB.Engine;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using static LiteDB.Constants;

namespace LiteDB
Expand All @@ -13,21 +14,74 @@ namespace LiteDB
public class BsonSerializer
{
/// <summary>
/// Serialize BsonDocument into a binary array
/// Serialize <paramref name="doc"/> into a binary array.
/// </summary>
public static byte[] Serialize(BsonDocument doc)
{
if (doc == null) throw new ArgumentNullException(nameof(doc));

var buffer = new byte[doc.GetBytesCount(true)];
var bytesRequired = doc.GetBytesCount(true);
var buffer = new byte[bytesRequired];

using (var writer = new BufferWriter(buffer))
Serialize(doc, buffer.AsMemory());

return buffer;
}

/// <summary>
/// Serialize <paramref name="doc"/> into the provided <see cref="Memory{T}"/>.
/// Returns the number of bytes written to <paramref name="destination"/>.
/// </summary>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="doc"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="destination"/> is shorter than the serialized document.</exception>
/// <exception cref="NotSupportedException">Thrown when <paramref name="destination"/> is not backed by a managed array.</exception>
public static int Serialize(BsonDocument doc, Memory<byte> destination)
{
if (doc == null) throw new ArgumentNullException(nameof(doc));

var bytesRequired = doc.GetBytesCount(true);

if (destination.Length < bytesRequired)
{
throw new ArgumentException($"Destination memory must be at least {bytesRequired} bytes long", nameof(destination));
}

if (!MemoryMarshal.TryGetArray(destination, out ArraySegment<byte> segment))
{
throw new NotSupportedException("The destination memory must be backed by a managed array.");
}

using (var writer = new BufferWriter(new BufferSlice(segment.Array, segment.Offset, bytesRequired)))
{
writer.WriteDocument(doc, false);
}

return buffer;
return bytesRequired;
}

#if !NETSTANDARD2_0
/// <summary>
/// Serialize <paramref name="doc"/> into the supplied <see cref="IBufferWriter{T}"/>.
/// Returns the number of bytes written to <paramref name="writer"/>.
/// </summary>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="doc"/> or <paramref name="writer"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">Thrown when the provided <paramref name="writer"/> exposes a buffer that is shorter than the serialized document.</exception>
/// <exception cref="NotSupportedException">Thrown when the buffer provided by <paramref name="writer"/> is not backed by a managed array.</exception>
public static int Serialize(BsonDocument doc, IBufferWriter<byte> writer)
{
if (doc == null) throw new ArgumentNullException(nameof(doc));
if (writer == null) throw new ArgumentNullException(nameof(writer));

var bytesRequired = doc.GetBytesCount(true);

var memory = writer.GetMemory(bytesRequired);
var written = Serialize(doc, memory);

writer.Advance(written);

return written;
}
#endif

/// <summary>
/// Deserialize binary data into BsonDocument
Expand All @@ -42,4 +96,4 @@ public static BsonDocument Deserialize(byte[] buffer, bool utcDate = false, Hash
}
}
}
}
}
5 changes: 3 additions & 2 deletions LiteDB/LiteDB.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
Expand Down Expand Up @@ -63,9 +63,10 @@

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.Buffers" Version="4.5.1" />
<PackageReference Include="System.Memory" Version="4.5.0" />
<PackageReference Include="System.Memory" Version="4.5.5" />
</ItemGroup>

<!-- End References -->

</Project>

11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,17 @@ using(var db = new LiteDatabase(@"MyData.db"))
}
```

### BSON serialization

If you need direct access to raw BSON, `BsonSerializer` exposes overloads that write into caller-supplied `Memory<byte>` or `IBufferWriter<byte>` instances. This allows applications to reuse buffers from pools without forcing an intermediate array allocation.

```C#
var writer = new ArrayBufferWriter<byte>();
var written = BsonSerializer.Serialize(document, writer);

// written now contains the BSON length and writer.WrittenSpan holds the bytes
```

Using fluent mapper and cross document reference for more complex data models

```C#
Expand Down
Loading