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 980ff2542..23d41d9fa 100644
--- a/LiteDB/Engine/Disk/Serializer/BufferReader.cs
+++ b/LiteDB/Engine/Disk/Serializer/BufferReader.cs
@@ -1,5 +1,6 @@
-using System;
+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);
- // fill buffer
- if (buffer != null)
+ 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;
+
+ 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(count == bufferPosition, "current value must fit inside defined buffer");
+ ENSURE(remaining == 0, "current value must fit inside defined buffer");
+ }
- return bufferPosition;
+ ///
+ /// 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;
+ }
+
+ this.ReadTo(buffer.AsSpan(offset, count));
+
+ 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
@@ -173,39 +235,137 @@ private bool TryReadCStringCurrentSegment(out string value)
#endregion
- #region Read Numbers
-
- private T ReadNumber(Func convert, int size)
+ #region Read Numbers
+
+ 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 value = BinaryPrimitives.ReadInt64LittleEndian(_current.AsSpan(_currentPosition, size));
+
+ this.MoveForward(size);
+
+ return value;
+ }
+
+ Span buffer = stackalloc byte[size];
+
+ this.ReadTo(buffer);
+
+ return BinaryPrimitives.ReadInt64LittleEndian(buffer);
+ }
+
+ public UInt16 ReadUInt16()
+ {
+ const int size = 2;
+
+ if (_currentPosition + size <= _current.Count)
{
- var buffer = _bufferPool.Rent(size);
+ var value = BinaryPrimitives.ReadUInt16LittleEndian(_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.ReadUInt16LittleEndian(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 UInt16 ReadUInt16() => this.ReadNumber(BitConverter.ToUInt16, 2);
- public UInt32 ReadUInt32() => this.ReadNumber(BitConverter.ToUInt32, 4);
- public Single ReadSingle() => this.ReadNumber(BitConverter.ToSingle, 4);
- public Double ReadDouble() => this.ReadNumber(BitConverter.ToDouble, 8);
+ public Single ReadSingle()
+ {
+ const int size = 4;
+
+ if (_currentPosition + size <= _current.Count)
+ {
+ var bits = BinaryPrimitives.ReadInt32LittleEndian(_current.AsSpan(_currentPosition, size));
+
+ this.MoveForward(size);
+
+ return Int32BitsToSingle(bits);
+ }
+
+ Span buffer = stackalloc byte[size];
+
+ this.ReadTo(buffer);
+
+ var value = BinaryPrimitives.ReadInt32LittleEndian(buffer);
+
+ return Int32BitsToSingle(value);
+ }
+
+ public Double ReadDouble()
+ {
+ const int size = 8;
+
+ if (_currentPosition + size <= _current.Count)
+ {
+ var bits = BinaryPrimitives.ReadInt64LittleEndian(_current.AsSpan(_currentPosition, size));
+
+ this.MoveForward(size);
+
+ return BitConverter.Int64BitsToDouble(bits);
+ }
+
+ Span buffer = stackalloc byte[size];
+
+ this.ReadTo(buffer);
+
+ var value = BinaryPrimitives.ReadInt64LittleEndian(buffer);
+
+ return BitConverter.Int64BitsToDouble(value);
+ }
+
+ private static unsafe float Int32BitsToSingle(int value)
+ {
+ // Unsafe re-interpretation keeps Engine serializer span-based without extra allocations.
+ return *(float*)&value;
+ }
public Decimal ReadDecimal()
{
@@ -562,4 +722,5 @@ public void Dispose()
_source?.Dispose();
}
}
-}
\ No newline at end of file
+}
+
diff --git a/LiteDB/Engine/Disk/Serializer/BufferWriter.cs b/LiteDB/Engine/Disk/Serializer/BufferWriter.cs
index 83c6c26f8..6129404d4 100644
--- a/LiteDB/Engine/Disk/Serializer/BufferWriter.cs
+++ b/LiteDB/Engine/Disk/Serializer/BufferWriter.cs
@@ -1,6 +1,8 @@
-using System;
+using System;
using System.Buffers;
+using System.Buffers.Binary;
using System.Collections.Generic;
+using System.Runtime.InteropServices;
using static LiteDB.Constants;
namespace LiteDB.Engine
@@ -85,39 +87,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);
- // fill buffer
- if (buffer != null)
+ remaining -= bytesToSkip;
+ }
+
+ ENSURE(remaining == 0, "current value must fit inside defined buffer");
+ }
+
+ private void Write(ReadOnlySpan source)
+ {
+ var remaining = source.Length;
+ var sourceOffset = 0;
+
+ 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(remaining == 0, "current value must fit inside defined buffer");
+ }
+
+ ///
+ /// 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;
}
- ENSURE(count == bufferPosition, "current value must fit inside defined buffer");
+ if (buffer == null)
+ {
+ this.Advance(count);
+ return count;
+ }
- return bufferPosition;
+ this.Write(buffer.AsSpan(offset, count));
+
+ return count;
}
///
@@ -128,7 +182,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
@@ -160,34 +223,110 @@ public void Consume()
#endregion
- #region Numbers
+ #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];
+ BinaryPrimitives.WriteInt64LittleEndian(buffer, value);
+ this.Write(buffer);
+ }
- _bufferPool.Return(buffer, true);
+ public void Write(UInt16 value)
+ {
+ const int size = 2;
+
+ if (_currentPosition + size <= _current.Count)
+ {
+ BinaryPrimitives.WriteUInt16LittleEndian(_current.AsWritableSpan(_currentPosition, size), value);
+
+ this.MoveForward(size);
+ return;
}
+
+ Span buffer = stackalloc byte[size];
+ BinaryPrimitives.WriteUInt16LittleEndian(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(UInt16 value) => this.WriteNumber(value, BufferExtensions.ToBytes, 2);
- public void Write(UInt32 value) => this.WriteNumber(value, BufferExtensions.ToBytes, 4);
- public void Write(Single value) => this.WriteNumber(value, BufferExtensions.ToBytes, 4);
- public void Write(Double value) => this.WriteNumber(value, BufferExtensions.ToBytes, 8);
+ 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(Single value)
+ {
+ const int size = 4;
+
+ if (_currentPosition + size <= _current.Count)
+ {
+ MemoryMarshal.Write(_current.AsWritableSpan(_currentPosition, size), ref value);
+
+ this.MoveForward(size);
+ return;
+ }
+
+ Span buffer = stackalloc byte[size];
+ MemoryMarshal.Write(buffer, ref value);
+ this.Write(buffer);
+ }
+
+ 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)
{
@@ -217,10 +356,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);
+ }
}
///
@@ -444,3 +595,5 @@ public void Dispose()
}
}
}
+
+
diff --git a/LiteDB/LiteDB.csproj b/LiteDB/LiteDB.csproj
index c1a6f2070..07e04de7e 100644
--- a/LiteDB/LiteDB.csproj
+++ b/LiteDB/LiteDB.csproj
@@ -1,4 +1,4 @@
-
+
netstandard2.0;net8.0
@@ -63,9 +63,10 @@
-
+
+
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 8f0d0eeb4..a949a5227 100644
--- a/LiteDB/Utils/Constants.cs
+++ b/LiteDB/Utils/Constants.cs
@@ -1,4 +1,4 @@
-using LiteDB.Engine;
+using LiteDB.Engine;
using System;
using System.Diagnostics;
@@ -6,6 +6,7 @@
using System.Threading;
[assembly: InternalsVisibleTo("LiteDB.Tests")]
+[assembly: InternalsVisibleTo("LiteDB.Benchmarks")]
#if DEBUG || TESTING
[assembly: InternalsVisibleTo("ConsoleApp1")]
#endif
@@ -198,4 +199,4 @@ public static void DEBUG(bool conditional, string message = null)
}
}
}
-}
\ No newline at end of file
+}
diff --git a/LiteDB/Utils/Extensions/BufferSliceExtensions.cs b/LiteDB/Utils/Extensions/BufferSliceExtensions.cs
index 049ce931f..684d8e2f1 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,22 +22,22 @@ 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 float ReadSingle(this BufferSlice buffer, int offset)
@@ -47,12 +47,13 @@ public static float ReadSingle(this BufferSlice buffer, int offset)
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)
@@ -201,22 +202,22 @@ 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, float value, int offset)
@@ -226,12 +227,13 @@ public static void Write(this BufferSlice buffer, float value, int offset)
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)
@@ -245,12 +247,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;
}
@@ -277,7 +280,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)