From 2eeb17fe1e5b5888e19711dcb2162634ca870d37 Mon Sep 17 00:00:00 2001
From: Jonas Kamsker <11245306+JKamsker@users.noreply.github.com>
Date: Thu, 2 Oct 2025 00:20:52 +0200
Subject: [PATCH] Refine buffer serialization to use spans and add benchmarks
---
LiteDB.Benchmarks/Benchmarks/Constants.cs | 1 +
.../BufferSerializationBenchmark.cs | 128 ++++++++++++
LiteDB/Engine/Disk/Serializer/BufferReader.cs | 182 +++++++++++++---
LiteDB/Engine/Disk/Serializer/BufferWriter.cs | 195 +++++++++++++++---
LiteDB/LiteDB.csproj | 1 +
LiteDB/Utils/BufferSlice.cs | 10 +
LiteDB/Utils/Constants.cs | 1 +
.../Utils/Extensions/BufferSliceExtensions.cs | 35 ++--
8 files changed, 469 insertions(+), 84 deletions(-)
create mode 100644 LiteDB.Benchmarks/Benchmarks/Serialization/BufferSerializationBenchmark.cs
diff --git a/LiteDB.Benchmarks/Benchmarks/Constants.cs b/LiteDB.Benchmarks/Benchmarks/Constants.cs
index 3de1c953e..ac2a752f0 100644
--- a/LiteDB.Benchmarks/Benchmarks/Constants.cs
+++ b/LiteDB.Benchmarks/Benchmarks/Constants.cs
@@ -10,6 +10,7 @@ internal static class Categories
internal const string QUERIES = nameof(QUERIES);
internal const string INSERTION = nameof(INSERTION);
internal const string DELETION = nameof(DELETION);
+ internal const string SERIALIZATION = nameof(SERIALIZATION);
}
}
}
\ No newline at end of file
diff --git a/LiteDB.Benchmarks/Benchmarks/Serialization/BufferSerializationBenchmark.cs b/LiteDB.Benchmarks/Benchmarks/Serialization/BufferSerializationBenchmark.cs
new file mode 100644
index 000000000..e2cc9012e
--- /dev/null
+++ b/LiteDB.Benchmarks/Benchmarks/Serialization/BufferSerializationBenchmark.cs
@@ -0,0 +1,128 @@
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Jobs;
+using LiteDB.Benchmarks.Benchmarks;
+using LiteDB.Engine;
+using System;
+
+namespace LiteDB.Benchmarks.Benchmarks.Serialization
+{
+ [BenchmarkCategory(Constants.Categories.SERIALIZATION)]
+ [SimpleJob(RuntimeMoniker.Net80)]
+ [MemoryDiagnoser]
+ public class BufferSerializationBenchmark
+ {
+ private BufferSlice _contiguousRead;
+ private BufferSlice[] _splitRead;
+ private BufferSlice _contiguousWrite;
+ private BufferSlice[] _splitWrite;
+
+ [Params(128, 4096)]
+ public int ValueCount { get; set; }
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ var byteCount = ValueCount * sizeof(int);
+
+ var contiguous = new byte[byteCount];
+ using (var writer = new BufferWriter(contiguous))
+ {
+ for (var i = 0; i < ValueCount; i++)
+ {
+ writer.Write(i);
+ }
+ }
+
+ _contiguousRead = new BufferSlice(contiguous, 0, contiguous.Length);
+
+ var firstLength = Math.Max(1, byteCount / 2);
+ var secondLength = byteCount - firstLength;
+
+ if (secondLength == 0)
+ {
+ secondLength = 1;
+ firstLength = Math.Max(1, byteCount - secondLength);
+ }
+
+ var segmentA = new BufferSlice(new byte[firstLength], 0, firstLength);
+ var segmentB = new BufferSlice(new byte[secondLength], 0, secondLength);
+
+ using (var writer = new BufferWriter(new[] { segmentA, segmentB }))
+ {
+ for (var i = 0; i < ValueCount; i++)
+ {
+ writer.Write(i);
+ }
+ }
+
+ _splitRead = new[] { segmentA, segmentB };
+
+ _contiguousWrite = new BufferSlice(new byte[byteCount], 0, byteCount);
+ _splitWrite = new[]
+ {
+ new BufferSlice(new byte[firstLength], 0, firstLength),
+ new BufferSlice(new byte[secondLength], 0, secondLength)
+ };
+ }
+
+ [Benchmark]
+ public int ReadInt32Contiguous()
+ {
+ var sum = 0;
+
+ using (var reader = new BufferReader(_contiguousRead))
+ {
+ for (var i = 0; i < ValueCount; i++)
+ {
+ sum += reader.ReadInt32();
+ }
+ }
+
+ return sum;
+ }
+
+ [Benchmark]
+ public int ReadInt32Split()
+ {
+ var sum = 0;
+
+ using (var reader = new BufferReader(_splitRead))
+ {
+ for (var i = 0; i < ValueCount; i++)
+ {
+ sum += reader.ReadInt32();
+ }
+ }
+
+ return sum;
+ }
+
+ [Benchmark]
+ public int WriteInt32Contiguous()
+ {
+ using (var writer = new BufferWriter(_contiguousWrite))
+ {
+ for (var i = 0; i < ValueCount; i++)
+ {
+ writer.Write(i);
+ }
+
+ return writer.Position;
+ }
+ }
+
+ [Benchmark]
+ public int WriteInt32Split()
+ {
+ using (var writer = new BufferWriter(_splitWrite))
+ {
+ for (var i = 0; i < ValueCount; i++)
+ {
+ writer.Write(i);
+ }
+
+ return writer.Position;
+ }
+ }
+ }
+}
diff --git a/LiteDB/Engine/Disk/Serializer/BufferReader.cs b/LiteDB/Engine/Disk/Serializer/BufferReader.cs
index 98618124f..90859ce15 100644
--- a/LiteDB/Engine/Disk/Serializer/BufferReader.cs
+++ b/LiteDB/Engine/Disk/Serializer/BufferReader.cs
@@ -1,5 +1,6 @@
using System;
using System.Buffers;
+using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
using static LiteDB.Constants;
@@ -89,45 +90,106 @@ private bool MoveForward(int count)
return false;
}
- ///
- /// Read bytes from source and copy into buffer. Return how many bytes was read
- ///
- public int Read(byte[] buffer, int offset, int count)
+ private void EnsureCurrentSegment()
+ {
+ if (_currentPosition < _current.Count)
+ {
+ return;
+ }
+
+ if (this.MoveForward(0) == false && _isEOF)
+ {
+ return;
+ }
+ }
+
+ private void Advance(int count)
{
- var bufferPosition = 0;
+ var remaining = count;
- while (bufferPosition < count)
+ while (remaining > 0)
{
+ this.EnsureCurrentSegment();
+
+ if (_isEOF)
+ {
+ break;
+ }
+
var bytesLeft = _current.Count - _currentPosition;
- var bytesToCopy = Math.Min(count - bufferPosition, bytesLeft);
+ var bytesToSkip = Math.Min(remaining, bytesLeft);
+
+ this.MoveForward(bytesToSkip);
+
+ remaining -= bytesToSkip;
+ }
+
+ ENSURE(remaining == 0, "current value must fit inside defined buffer");
+ }
+
+ private void ReadTo(Span destination)
+ {
+ var remaining = destination.Length;
+ var destOffset = 0;
- // fill buffer
- if (buffer != null)
+ while (remaining > 0)
+ {
+ this.EnsureCurrentSegment();
+
+ if (_isEOF)
{
- Buffer.BlockCopy(_current.Array,
- _current.Offset + _currentPosition,
- buffer,
- offset + bufferPosition,
- bytesToCopy);
+ break;
}
- bufferPosition += bytesToCopy;
+ var bytesLeft = _current.Count - _currentPosition;
+ var bytesToCopy = Math.Min(remaining, bytesLeft);
+
+ _current.AsSpan(_currentPosition, bytesToCopy)
+ .CopyTo(destination.Slice(destOffset, bytesToCopy));
- // move position in current segment (and go to next segment if finish)
this.MoveForward(bytesToCopy);
- if (_isEOF) break;
+ destOffset += bytesToCopy;
+ remaining -= bytesToCopy;
+ }
+
+ ENSURE(remaining == 0, "current value must fit inside defined buffer");
+ }
+
+ ///
+ /// Read bytes from source and copy into buffer. Return how many bytes was read
+ ///
+ public int Read(byte[] buffer, int offset, int count)
+ {
+ if (count == 0)
+ {
+ return 0;
+ }
+
+ if (buffer == null)
+ {
+ this.Advance(count);
+ return count;
}
- ENSURE(count == bufferPosition, "current value must fit inside defined buffer");
+ this.ReadTo(buffer.AsSpan(offset, count));
- return bufferPosition;
+ return count;
}
///
/// Skip bytes (same as Read but with no array copy)
///
- public int Skip(int count) => this.Read(null, 0, count);
+ public int Skip(int count)
+ {
+ if (count == 0)
+ {
+ return 0;
+ }
+
+ this.Advance(count);
+ return count;
+ }
///
/// Consume all data source until finish
@@ -240,35 +302,87 @@ private bool TryReadCStringCurrentSegment(out string value)
#region Read Numbers
- private T ReadNumber(Func convert, int size)
+ public Int32 ReadInt32()
{
- T value;
+ const int size = 4;
- // if fits in current segment, use inner array - otherwise copy from multiples segments
if (_currentPosition + size <= _current.Count)
{
- value = convert(_current.Array, _current.Offset + _currentPosition);
+ var value = BinaryPrimitives.ReadInt32LittleEndian(_current.AsSpan(_currentPosition, size));
this.MoveForward(size);
+
+ return value;
}
- else
+
+ Span buffer = stackalloc byte[size];
+
+ this.ReadTo(buffer);
+
+ return BinaryPrimitives.ReadInt32LittleEndian(buffer);
+ }
+
+ public Int64 ReadInt64()
+ {
+ const int size = 8;
+
+ if (_currentPosition + size <= _current.Count)
{
- var buffer = _bufferPool.Rent(size);
+ var value = BinaryPrimitives.ReadInt64LittleEndian(_current.AsSpan(_currentPosition, size));
+
+ this.MoveForward(size);
- this.Read(buffer, 0, size);
+ return value;
+ }
- value = convert(buffer, 0);
+ Span buffer = stackalloc byte[size];
- _bufferPool.Return(buffer, true);
+ this.ReadTo(buffer);
+
+ return BinaryPrimitives.ReadInt64LittleEndian(buffer);
+ }
+
+ public UInt32 ReadUInt32()
+ {
+ const int size = 4;
+
+ if (_currentPosition + size <= _current.Count)
+ {
+ var value = BinaryPrimitives.ReadUInt32LittleEndian(_current.AsSpan(_currentPosition, size));
+
+ this.MoveForward(size);
+
+ return value;
}
- return value;
+ Span buffer = stackalloc byte[size];
+
+ this.ReadTo(buffer);
+
+ return BinaryPrimitives.ReadUInt32LittleEndian(buffer);
}
- public Int32 ReadInt32() => this.ReadNumber(BitConverter.ToInt32, 4);
- public Int64 ReadInt64() => this.ReadNumber(BitConverter.ToInt64, 8);
- public UInt32 ReadUInt32() => this.ReadNumber(BitConverter.ToUInt32, 4);
- public Double ReadDouble() => this.ReadNumber(BitConverter.ToDouble, 8);
+ public Double ReadDouble()
+ {
+ const int size = 8;
+
+ if (_currentPosition + size <= _current.Count)
+ {
+ var value = BinaryPrimitives.ReadInt64LittleEndian(_current.AsSpan(_currentPosition, size));
+
+ this.MoveForward(size);
+
+ return BitConverter.Int64BitsToDouble(value);
+ }
+
+ Span buffer = stackalloc byte[size];
+
+ this.ReadTo(buffer);
+
+ var bits = BinaryPrimitives.ReadInt64LittleEndian(buffer);
+
+ return BitConverter.Int64BitsToDouble(bits);
+ }
public Decimal ReadDecimal()
{
diff --git a/LiteDB/Engine/Disk/Serializer/BufferWriter.cs b/LiteDB/Engine/Disk/Serializer/BufferWriter.cs
index 11a0fb71e..dc80e11b5 100644
--- a/LiteDB/Engine/Disk/Serializer/BufferWriter.cs
+++ b/LiteDB/Engine/Disk/Serializer/BufferWriter.cs
@@ -1,7 +1,9 @@
using System;
using System.Buffers;
+using System.Buffers.Binary;
using System.Collections.Generic;
using System.Text;
+using System.Runtime.InteropServices;
using static LiteDB.Constants;
namespace LiteDB.Engine
@@ -86,39 +88,91 @@ private bool MoveForward(int count)
return false;
}
- ///
- /// Write bytes from buffer into segmentsr. Return how many bytes was write
- ///
- public int Write(byte[] buffer, int offset, int count)
+ private void EnsureCurrentSegment()
+ {
+ if (_currentPosition < _current.Count)
+ {
+ return;
+ }
+
+ if (this.MoveForward(0) == false && _isEOF)
+ {
+ return;
+ }
+ }
+
+ private void Advance(int count)
{
- var bufferPosition = 0;
+ var remaining = count;
- while (bufferPosition < count)
+ while (remaining > 0)
{
+ this.EnsureCurrentSegment();
+
+ if (_isEOF)
+ {
+ break;
+ }
+
var bytesLeft = _current.Count - _currentPosition;
- var bytesToCopy = Math.Min(count - bufferPosition, bytesLeft);
+ var bytesToSkip = Math.Min(remaining, bytesLeft);
+
+ this.MoveForward(bytesToSkip);
+
+ remaining -= bytesToSkip;
+ }
+
+ ENSURE(remaining == 0, "current value must fit inside defined buffer");
+ }
+
+ private void Write(ReadOnlySpan source)
+ {
+ var remaining = source.Length;
+ var sourceOffset = 0;
- // fill buffer
- if (buffer != null)
+ while (remaining > 0)
+ {
+ this.EnsureCurrentSegment();
+
+ if (_isEOF)
{
- Buffer.BlockCopy(buffer,
- offset + bufferPosition,
- _current.Array,
- _current.Offset + _currentPosition,
- bytesToCopy);
+ break;
}
- bufferPosition += bytesToCopy;
+ var bytesLeft = _current.Count - _currentPosition;
+ var bytesToCopy = Math.Min(remaining, bytesLeft);
+
+ source.Slice(sourceOffset, bytesToCopy)
+ .CopyTo(_current.AsWritableSpan(_currentPosition, bytesToCopy));
- // move position in current segment (and go to next segment if finish)
this.MoveForward(bytesToCopy);
- if (_isEOF) break;
+ sourceOffset += bytesToCopy;
+ remaining -= bytesToCopy;
}
- ENSURE(count == bufferPosition, "current value must fit inside defined buffer");
+ ENSURE(remaining == 0, "current value must fit inside defined buffer");
+ }
- return bufferPosition;
+ ///
+ /// Write bytes from buffer into segmentsr. Return how many bytes was write
+ ///
+ public int Write(byte[] buffer, int offset, int count)
+ {
+ if (count == 0)
+ {
+ return 0;
+ }
+
+ if (buffer == null)
+ {
+ this.Advance(count);
+ return count;
+ }
+
+ this.Write(buffer.AsSpan(offset, count));
+
+ return count;
}
///
@@ -129,7 +183,16 @@ public int Write(byte[] buffer, int offset, int count)
///
/// Skip bytes (same as Write but with no array copy)
///
- public int Skip(int count) => this.Write(null, 0, count);
+ public int Skip(int count)
+ {
+ if (count == 0)
+ {
+ return 0;
+ }
+
+ this.Advance(count);
+ return count;
+ }
///
/// Consume all data source until finish
@@ -224,30 +287,82 @@ public void WriteString(string value, bool specs)
#region Numbers
- private void WriteNumber(T value, Action toBytes, int size)
+ public void Write(Int32 value)
{
+ const int size = 4;
+
if (_currentPosition + size <= _current.Count)
{
- toBytes(value, _current.Array, _current.Offset + _currentPosition);
+ BinaryPrimitives.WriteInt32LittleEndian(_current.AsWritableSpan(_currentPosition, size), value);
this.MoveForward(size);
+ return;
}
- else
+
+ Span buffer = stackalloc byte[size];
+
+ BinaryPrimitives.WriteInt32LittleEndian(buffer, value);
+
+ this.Write(buffer);
+ }
+
+ public void Write(Int64 value)
+ {
+ const int size = 8;
+
+ if (_currentPosition + size <= _current.Count)
{
- var buffer = _bufferPool.Rent(size);
+ BinaryPrimitives.WriteInt64LittleEndian(_current.AsWritableSpan(_currentPosition, size), value);
- toBytes(value, buffer, 0);
+ this.MoveForward(size);
+ return;
+ }
- this.Write(buffer, 0, size);
+ Span buffer = stackalloc byte[size];
- _bufferPool.Return(buffer, true);
+ BinaryPrimitives.WriteInt64LittleEndian(buffer, value);
+
+ this.Write(buffer);
+ }
+
+ public void Write(UInt32 value)
+ {
+ const int size = 4;
+
+ if (_currentPosition + size <= _current.Count)
+ {
+ BinaryPrimitives.WriteUInt32LittleEndian(_current.AsWritableSpan(_currentPosition, size), value);
+
+ this.MoveForward(size);
+ return;
}
+
+ Span buffer = stackalloc byte[size];
+
+ BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
+
+ this.Write(buffer);
}
- public void Write(Int32 value) => this.WriteNumber(value, BufferExtensions.ToBytes, 4);
- public void Write(Int64 value) => this.WriteNumber(value, BufferExtensions.ToBytes, 8);
- public void Write(UInt32 value) => this.WriteNumber(value, BufferExtensions.ToBytes, 4);
- public void Write(Double value) => this.WriteNumber(value, BufferExtensions.ToBytes, 8);
+ public void Write(Double value)
+ {
+ const int size = 8;
+ var bits = BitConverter.DoubleToInt64Bits(value);
+
+ if (_currentPosition + size <= _current.Count)
+ {
+ BinaryPrimitives.WriteInt64LittleEndian(_current.AsWritableSpan(_currentPosition, size), bits);
+
+ this.MoveForward(size);
+ return;
+ }
+
+ Span buffer = stackalloc byte[size];
+
+ BinaryPrimitives.WriteInt64LittleEndian(buffer, bits);
+
+ this.Write(buffer);
+ }
public void Write(Decimal value)
{
@@ -277,10 +392,22 @@ public void Write(DateTime value)
///
public void Write(Guid value)
{
- // there is no avaiable value.TryWriteBytes (TODO: implement conditional compile)?
- var bytes = value.ToByteArray();
+ const int size = 16;
+
+ if (_currentPosition + size <= _current.Count)
+ {
+ MemoryMarshal.Write(_current.AsWritableSpan(_currentPosition, size), ref value);
- this.Write(bytes, 0, 16);
+ this.MoveForward(size);
+ }
+ else
+ {
+ Span buffer = stackalloc byte[size];
+
+ MemoryMarshal.Write(buffer, ref value);
+
+ this.Write(buffer);
+ }
}
///
diff --git a/LiteDB/LiteDB.csproj b/LiteDB/LiteDB.csproj
index 7ebb6d4ce..f66f875c8 100644
--- a/LiteDB/LiteDB.csproj
+++ b/LiteDB/LiteDB.csproj
@@ -52,6 +52,7 @@
+
diff --git a/LiteDB/Utils/BufferSlice.cs b/LiteDB/Utils/BufferSlice.cs
index 62fc5071f..7f91d30db 100644
--- a/LiteDB/Utils/BufferSlice.cs
+++ b/LiteDB/Utils/BufferSlice.cs
@@ -34,6 +34,16 @@ public byte this[int index]
set => this.Array[this.Offset + index] = value;
}
+ public ReadOnlySpan AsSpan(int offset, int length)
+ {
+ return new ReadOnlySpan(this.Array, this.Offset + offset, length);
+ }
+
+ public Span AsWritableSpan(int offset, int length)
+ {
+ return new Span(this.Array, this.Offset + offset, length);
+ }
+
///
/// Clear all page content byte array (not controls)
///
diff --git a/LiteDB/Utils/Constants.cs b/LiteDB/Utils/Constants.cs
index e13ae9add..4b038bd3a 100644
--- a/LiteDB/Utils/Constants.cs
+++ b/LiteDB/Utils/Constants.cs
@@ -6,6 +6,7 @@
using System.Threading;
[assembly: InternalsVisibleTo("LiteDB.Tests")]
+[assembly: InternalsVisibleTo("LiteDB.Benchmarks")]
#if DEBUG
[assembly: InternalsVisibleTo("ConsoleApp1")]
#endif
diff --git a/LiteDB/Utils/Extensions/BufferSliceExtensions.cs b/LiteDB/Utils/Extensions/BufferSliceExtensions.cs
index 3e7422d01..949fc4a89 100644
--- a/LiteDB/Utils/Extensions/BufferSliceExtensions.cs
+++ b/LiteDB/Utils/Extensions/BufferSliceExtensions.cs
@@ -1,6 +1,6 @@
using LiteDB.Engine;
using System;
-using System.Linq;
+using System.Buffers.Binary;
using System.Text;
using static LiteDB.Constants;
@@ -22,32 +22,33 @@ public static byte ReadByte(this BufferSlice buffer, int offset)
public static Int16 ReadInt16(this BufferSlice buffer, int offset)
{
- return BitConverter.ToInt16(buffer.Array, buffer.Offset + offset);
+ return BinaryPrimitives.ReadInt16LittleEndian(buffer.AsSpan(offset, sizeof(short)));
}
public static UInt16 ReadUInt16(this BufferSlice buffer, int offset)
{
- return BitConverter.ToUInt16(buffer.Array, buffer.Offset + offset);
+ return BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(offset, sizeof(ushort)));
}
public static Int32 ReadInt32(this BufferSlice buffer, int offset)
{
- return BitConverter.ToInt32(buffer.Array, buffer.Offset + offset);
+ return BinaryPrimitives.ReadInt32LittleEndian(buffer.AsSpan(offset, sizeof(int)));
}
public static UInt32 ReadUInt32(this BufferSlice buffer, int offset)
{
- return BitConverter.ToUInt32(buffer.Array, buffer.Offset + offset);
+ return BinaryPrimitives.ReadUInt32LittleEndian(buffer.AsSpan(offset, sizeof(uint)));
}
public static Int64 ReadInt64(this BufferSlice buffer, int offset)
{
- return BitConverter.ToInt64(buffer.Array, buffer.Offset + offset);
+ return BinaryPrimitives.ReadInt64LittleEndian(buffer.AsSpan(offset, sizeof(long)));
}
public static double ReadDouble(this BufferSlice buffer, int offset)
{
- return BitConverter.ToDouble(buffer.Array, buffer.Offset + offset);
+ var bits = BinaryPrimitives.ReadInt64LittleEndian(buffer.AsSpan(offset, sizeof(long)));
+ return BitConverter.Int64BitsToDouble(bits);
}
public static Decimal ReadDecimal(this BufferSlice buffer, int offset)
@@ -183,32 +184,33 @@ public static void Write(this BufferSlice buffer, byte value, int offset)
public static void Write(this BufferSlice buffer, Int16 value, int offset)
{
- value.ToBytes(buffer.Array, buffer.Offset + offset);
+ BinaryPrimitives.WriteInt16LittleEndian(buffer.AsWritableSpan(offset, sizeof(short)), value);
}
public static void Write(this BufferSlice buffer, UInt16 value, int offset)
{
- value.ToBytes(buffer.Array, buffer.Offset + offset);
+ BinaryPrimitives.WriteUInt16LittleEndian(buffer.AsWritableSpan(offset, sizeof(ushort)), value);
}
public static void Write(this BufferSlice buffer, Int32 value, int offset)
{
- value.ToBytes(buffer.Array, buffer.Offset + offset);
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.AsWritableSpan(offset, sizeof(int)), value);
}
public static void Write(this BufferSlice buffer, UInt32 value, int offset)
{
- value.ToBytes(buffer.Array, buffer.Offset + offset);
+ BinaryPrimitives.WriteUInt32LittleEndian(buffer.AsWritableSpan(offset, sizeof(uint)), value);
}
public static void Write(this BufferSlice buffer, Int64 value, int offset)
{
- value.ToBytes(buffer.Array, buffer.Offset + offset);
+ BinaryPrimitives.WriteInt64LittleEndian(buffer.AsWritableSpan(offset, sizeof(long)), value);
}
public static void Write(this BufferSlice buffer, Double value, int offset)
{
- value.ToBytes(buffer.Array, buffer.Offset + offset);
+ var bits = BitConverter.DoubleToInt64Bits(value);
+ BinaryPrimitives.WriteInt64LittleEndian(buffer.AsWritableSpan(offset, sizeof(long)), bits);
}
public static void Write(this BufferSlice buffer, Decimal value, int offset)
@@ -222,12 +224,13 @@ public static void Write(this BufferSlice buffer, Decimal value, int offset)
public static void Write(this BufferSlice buffer, DateTime value, int offset)
{
- value.ToUniversalTime().Ticks.ToBytes(buffer.Array, buffer.Offset + offset);
+ var ticks = value.ToUniversalTime().Ticks;
+ BinaryPrimitives.WriteInt64LittleEndian(buffer.AsWritableSpan(offset, sizeof(long)), ticks);
}
public static void Write(this BufferSlice buffer, PageAddress value, int offset)
{
- value.PageID.ToBytes(buffer.Array, buffer.Offset + offset);
+ BinaryPrimitives.WriteUInt32LittleEndian(buffer.AsWritableSpan(offset, sizeof(uint)), value.PageID);
buffer[offset + 4] = value.Index;
}
@@ -243,7 +246,7 @@ public static void Write(this BufferSlice buffer, ObjectId value, int offset)
public static void Write(this BufferSlice buffer, byte[] value, int offset)
{
- Buffer.BlockCopy(value, 0, buffer.Array, buffer.Offset + offset, value.Length);
+ value.AsSpan().CopyTo(buffer.AsWritableSpan(offset, value.Length));
}
public static void Write(this BufferSlice buffer, string value, int offset)