diff --git a/src/Benchmarks/Benchmark/Benchmark.csproj b/src/Benchmarks/Benchmark/Benchmark.csproj
index dd3af888..bd93e92a 100644
--- a/src/Benchmarks/Benchmark/Benchmark.csproj
+++ b/src/Benchmarks/Benchmark/Benchmark.csproj
@@ -4,7 +4,7 @@
net8.0
false
false
- $(DefineContants);CURRENT_VERSION_ONLY;RUN_COMPARISON_BENCHMARKS;FLATSHARP_7_0_0_OR_GREATER
+ $(DefineContants);CURRENT_VERSION_ONLY;FLATSHARP_7_0_0_OR_GREATER
False
true
diff --git a/src/Benchmarks/Benchmark/Program.cs b/src/Benchmarks/Benchmark/Program.cs
index 5f9849e9..8ada96a7 100644
--- a/src/Benchmarks/Benchmark/Program.cs
+++ b/src/Benchmarks/Benchmark/Program.cs
@@ -48,7 +48,7 @@ public static void Main(string[] args)
Job job = Job.ShortRun
.WithAnalyzeLaunchVariance(true)
- .WithLaunchCount(7)
+ .WithLaunchCount(3)
.WithWarmupCount(3)
.WithIterationCount(5)
#if AOT
@@ -58,14 +58,14 @@ public static void Main(string[] args)
#else
.WithRuntime(CoreRuntime.Core80)
#endif
- ;
+ ;
// job = job.WithEnvironmentVariable(new EnvironmentVariable("DOTNET_TieredPGO", "0"));
var config = DefaultConfig.Instance
.AddColumn(new[] { StatisticColumn.P25, StatisticColumn.P95 })
.AddDiagnoser(MemoryDiagnoser.Default)
- .AddJob(job);
+ .AddJob(job.DontEnforcePowerPlan());
summaries.Add(BenchmarkRunner.Run(typeof(FBBench.FBSerializeBench), config));
summaries.Add(BenchmarkRunner.Run(typeof(FBBench.FBDeserializeBench), config));
diff --git a/src/Benchmarks/Benchmark/Serializers/FlatSharpHelper.cs b/src/Benchmarks/Benchmark/Serializers/FlatSharpHelper.cs
index 7e50f971..247c953d 100644
--- a/src/Benchmarks/Benchmark/Serializers/FlatSharpHelper.cs
+++ b/src/Benchmarks/Benchmark/Serializers/FlatSharpHelper.cs
@@ -16,11 +16,11 @@ internal class FlatSharpHelper
private static FS.SortedVectorContainer sortedInts;
private static FS.SortedVectorContainer unsortedInts;
- public static int Prepare(int length)
+ public static long Prepare(int length)
{
BenchmarkUtilities.Prepare(length, out container);
buffer = new byte[GetMaxSize()];
- int bytesWritten = Serialize();
+ long bytesWritten = Serialize();
sortedStrings = new() { SortedStrings = new List() };
unsortedStrings = new() { UnsortedStrings = new List() };
@@ -41,14 +41,14 @@ public static int Prepare(int length)
return bytesWritten;
}
- public static int GetMaxSize()
+ public static long GetMaxSize()
{
return FS.FooBarContainer.Serializer.GetMaxSize(container);
}
- public static int Serialize()
+ public static long Serialize()
{
- return FS.FooBarContainer.Serializer.Write(new SpanWriter(), buffer, container);
+ return FS.FooBarContainer.Serializer.Write(buffer, container);
}
public static int ParseAndTraverse(int iterations, FlatBufferDeserializationOption option)
@@ -63,15 +63,15 @@ public static int ParseAndTraversePartial(int iterations, FlatBufferDeserializat
return BenchmarkUtilities.TraverseFooBarContainerPartial(parsed, iterations);
}
- public static int SerializeSortedStrings()
- => FS.SortedVectorContainer.Serializer.Write(new SpanWriter(), buffer, sortedStrings);
+ public static long SerializeSortedStrings()
+ => FS.SortedVectorContainer.Serializer.Write(buffer, sortedStrings);
- public static int SerializeUnsortedStrings()
- => FS.SortedVectorContainer.Serializer.Write(new SpanWriter(), buffer, unsortedStrings);
+ public static long SerializeUnsortedStrings()
+ => FS.SortedVectorContainer.Serializer.Write(buffer, unsortedStrings);
- public static int SerializeSortedInts()
- => FS.SortedVectorContainer.Serializer.Write(new SpanWriter(), buffer, sortedInts);
+ public static long SerializeSortedInts()
+ => FS.SortedVectorContainer.Serializer.Write(buffer, sortedInts);
- public static int SerializeUnsortedInts()
- => FS.SortedVectorContainer.Serializer.Write(new SpanWriter(), buffer, unsortedInts);
+ public static long SerializeUnsortedInts()
+ => FS.SortedVectorContainer.Serializer.Write(buffer, unsortedInts);
}
diff --git a/src/Benchmarks/Benchmark/Serializers/FlatSharpValueStructs.cs b/src/Benchmarks/Benchmark/Serializers/FlatSharpValueStructs.cs
index e62dd4e9..90122446 100644
--- a/src/Benchmarks/Benchmark/Serializers/FlatSharpValueStructs.cs
+++ b/src/Benchmarks/Benchmark/Serializers/FlatSharpValueStructs.cs
@@ -9,21 +9,21 @@ internal class FlatSharpValueStructs
private static byte[] buffer;
private static FS.FooBarContainerValue container;
- public static int Prepare(int length)
+ public static long Prepare(int length)
{
BenchmarkUtilities.Prepare(length, out container);
buffer = new byte[GetMaxSize()];
return Serialize();
}
- public static int GetMaxSize()
+ public static long GetMaxSize()
{
return FS.FooBarContainerValue.Serializer.GetMaxSize(container);
}
- public static int Serialize()
+ public static long Serialize()
{
- return FS.FooBarContainerValue.Serializer.Write(new SpanWriter(), buffer, container);
+ return FS.FooBarContainerValue.Serializer.Write(buffer, container);
}
public static int ParseAndTraverse(int iterations, FlatBufferDeserializationOption option)
diff --git a/src/Benchmarks/MicroBench.Current/Constants.cs b/src/Benchmarks/MicroBench.Current/Constants.cs
index 2a542f00..8ec51970 100644
--- a/src/Benchmarks/MicroBench.Current/Constants.cs
+++ b/src/Benchmarks/MicroBench.Current/Constants.cs
@@ -22,6 +22,7 @@ namespace Microbench
using System.Linq;
using FlatSharp;
using FlatSharp.Internal;
+ using Microsoft.Diagnostics.Tracing.Parsers.FrameworkEventSource;
public static class Constants
{
@@ -61,6 +62,22 @@ public static class PrimitiveTables
Float = 1,
Double = 1,
};
+
+ public static NestedTable RandomEntries = new()
+ {
+ Tables = Enumerable.Range(0, 1000).Select(i => new PrimitivesTable
+ {
+ Bool = Random.Shared.Next() % 2 == 0,
+ Byte = (byte)(Random.Shared.Next() % 2),
+ SByte = (sbyte)(Random.Shared.Next() % 2),
+ Short = (short)(Random.Shared.Next() % 2),
+ UShort = (ushort)(Random.Shared.Next() % 2),
+ Int = (int)(Random.Shared.Next() % 2),
+ UInt = (uint)(Random.Shared.Next() % 2),
+ Long = (long)(Random.Shared.Next() % 2),
+ ULong = (ulong)(Random.Shared.Next() % 2),
+ }).ToArray(),
+ };
}
public static class StructTables
@@ -155,6 +172,7 @@ public static class Buffers
public static readonly byte[] PrimitivesTable_Empty = AllocateAndSerialize(PrimitiveTables.Empty);
public static readonly byte[] PrimitivesTable_Full = AllocateAndSerialize(PrimitiveTables.Full);
+ public static readonly byte[] PrimitivesTable_RandomEntries = AllocateAndSerialize(PrimitiveTables.RandomEntries);
public static readonly byte[] StructTable_SingleRef = AllocateAndSerialize(StructTables.SingleRef);
public static readonly byte[] StructTable_SingleValue = AllocateAndSerialize(StructTables.SingleValue);
@@ -169,7 +187,7 @@ public static class Buffers
private static byte[] AllocateAndSerialize(T value) where T : class, IFlatBufferSerializable
{
byte[] buffer = new byte[value.Serializer.GetMaxSize(value)];
- int length = value.Serializer.Write(buffer, value);
+ long length = value.Serializer.Write(buffer, value);
return buffer;
}
diff --git a/src/Benchmarks/MicroBench.Current/Program.cs b/src/Benchmarks/MicroBench.Current/Program.cs
index e4495c8c..2b2ca8d6 100644
--- a/src/Benchmarks/MicroBench.Current/Program.cs
+++ b/src/Benchmarks/MicroBench.Current/Program.cs
@@ -40,7 +40,9 @@ public static void Main(string[] args)
.WithLaunchCount(1)
.WithWarmupCount(3)
.WithIterationCount(6)
- .WithRuntime(CoreRuntime.Core70);
+ .WithRuntime(CoreRuntime.Core80)
+ .DontEnforcePowerPlan();
+
//.WithEnvironmentVariable(new EnvironmentVariable("DOTNET_TieredPGO", "1"));
var config = DefaultConfig.Instance
diff --git a/src/Benchmarks/MicroBench.Current/SerializeBenchmarks.cs b/src/Benchmarks/MicroBench.Current/SerializeBenchmarks.cs
index adf5052f..81192b8e 100644
--- a/src/Benchmarks/MicroBench.Current/SerializeBenchmarks.cs
+++ b/src/Benchmarks/MicroBench.Current/SerializeBenchmarks.cs
@@ -24,73 +24,79 @@ namespace Microbench
public class SerializeBenchmarks
{
[Benchmark]
- public int Serialize_StringTable_SingleString()
+ public long Serialize_StringTable_SingleString()
{
return StringTable.Serializer.Write(Constants.Buffers.StringTable_WithString, Constants.StringTables.WithString);
}
[Benchmark]
- public int Serialize_StringTable_Vector()
+ public long Serialize_StringTable_Vector()
{
return StringTable.Serializer.Write(Constants.Buffers.StringTable_WithVector, Constants.StringTables.WithVector);
}
[Benchmark]
- public int Serialize_StringTable_EmptyTable()
+ public long Serialize_StringTable_EmptyTable()
{
return StringTable.Serializer.Write(Constants.Buffers.Stringtable_Empty, Constants.StringTables.Empty);
}
[Benchmark]
- public int Serialize_PrimitivesTable_Empty()
+ public long Serialize_PrimitivesTable_Empty()
{
return PrimitivesTable.Serializer.Write(Constants.Buffers.PrimitivesTable_Empty, Constants.PrimitiveTables.Empty);
}
[Benchmark]
- public int Serialize_PrimitivesTable_Full()
+ public long Serialize_PrimitivesTable_Full()
{
return PrimitivesTable.Serializer.Write(Constants.Buffers.PrimitivesTable_Full, Constants.PrimitiveTables.Full);
}
[Benchmark]
- public int Serialize_StructTable_SingleRef()
+ public long Serialize_PrimitivesTable_RandomlyPopulated()
+ {
+ return NestedTable.Serializer.Write(Constants.Buffers.PrimitivesTable_RandomEntries, Constants.PrimitiveTables.RandomEntries);
+ }
+
+ [Benchmark]
+ public long Serialize_StructTable_SingleRef()
{
return StructsTable.Serializer.Write(Constants.Buffers.StructTable_SingleRef, Constants.StructTables.SingleRef);
}
[Benchmark]
- public int Serialize_StructTable_SingleValue()
+ public long Serialize_StructTable_SingleValue()
{
return StructsTable.Serializer.Write(Constants.Buffers.StructTable_SingleValue, Constants.StructTables.SingleValue);
}
[Benchmark]
- public int Serialize_StructTable_VecRef()
+ public long Serialize_StructTable_VecRef()
{
return StructsTable.Serializer.Write(Constants.Buffers.StructTable_VecRef, Constants.StructTables.VectorRef);
}
[Benchmark]
- public int Serialize_StructTable_VecValue()
+ public long Serialize_StructTable_VecValue()
{
return StructsTable.Serializer.Write(Constants.Buffers.StructTable_VecValue, Constants.StructTables.VectorValue);
}
[Benchmark]
- public int Serialize_SafeUnion()
+ public long Serialize_SafeUnion()
{
return UnionTable.Serializer.Write(Constants.Buffers.UnionTable_Safe, Constants.UnionTables.Safe);
}
[Benchmark]
- public int Serialize_UnsafeUnion()
+ public long Serialize_UnsafeUnion()
{
return UnionTable.Serializer.Write(Constants.Buffers.UnionTable_Unsafe, Constants.UnionTables.Unsafe);
}
[Benchmark]
- public int Serialize_MixedUnion()
+ public long Serialize_MixedUnion()
{
return UnionTable.Serializer.Write(Constants.Buffers.UnionTable_Mixed, Constants.UnionTables.Mixed);
}
diff --git a/src/Benchmarks/Microbench.fbs b/src/Benchmarks/Microbench.fbs
index 79ac4f89..e193e151 100644
--- a/src/Benchmarks/Microbench.fbs
+++ b/src/Benchmarks/Microbench.fbs
@@ -31,6 +31,10 @@ table PrimitivesTable (fs_serializer:"Lazy") {
Float : float;
}
+table NestedTable (fs_serializer:"Lazy") {
+ Tables : [ PrimitivesTable ];
+}
+
struct RefStruct (fs_writeThrough) { Value : int; }
struct ValueStruct (fs_valueStruct) { Value : int; }
@@ -51,7 +55,6 @@ table SortedTable (fs_serializer:"Lazy")
Ints : [IntKey] (fs_vector:"IIndexedVector");
}
-
struct ValueStructA (fs_valueStruct) { x : int; }
struct ValueStructB (fs_valueStruct) { y : long; }
struct ValueStructC (fs_valueStruct) { a : ValueStructA; b : ValueStructB; }
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 896562bf..4852125f 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -3,7 +3,7 @@
true
false
$(MSBuildThisFileDirectory)\..\misc\strongname.snk
- 12.0
+ preview
true
$(NoWarn);CS8032
True
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index 2546a6f7..a4e55699 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -13,9 +13,12 @@
-
+
-
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/src/FlatSharp.Runtime/BufferTooSmallException.cs b/src/FlatSharp.Runtime/BufferTooSmallException.cs
index 6af02ea9..2ca529eb 100644
--- a/src/FlatSharp.Runtime/BufferTooSmallException.cs
+++ b/src/FlatSharp.Runtime/BufferTooSmallException.cs
@@ -28,5 +28,5 @@ public BufferTooSmallException() : base($"The provided buffer was too small to h
///
/// The maximum amount of size needed for this message.
///
- public int SizeNeeded { get; internal set; }
+ public long SizeNeeded { get; internal set; }
}
diff --git a/src/FlatSharp.Runtime/FSThrow.cs b/src/FlatSharp.Runtime/FSThrow.cs
index c082cea5..bde337ae 100644
--- a/src/FlatSharp.Runtime/FSThrow.cs
+++ b/src/FlatSharp.Runtime/FSThrow.cs
@@ -135,7 +135,7 @@ public static T Argument(string message)
#region BufferTooSmall
[DoesNotReturn]
- public static void BufferTooSmall(int sizeNeeded)
+ public static void BufferTooSmall(long sizeNeeded)
{
throw new BufferTooSmallException
{
diff --git a/src/FlatSharp.Runtime/FlatSharp.Runtime.csproj b/src/FlatSharp.Runtime/FlatSharp.Runtime.csproj
index 55ff7475..0cf6e92e 100644
--- a/src/FlatSharp.Runtime/FlatSharp.Runtime.csproj
+++ b/src/FlatSharp.Runtime/FlatSharp.Runtime.csproj
@@ -9,6 +9,7 @@
embedded
true
$(NoWarn);CS1591
+ true
@@ -17,6 +18,10 @@
+
+ BigSpanHelpers.cs
+ TextTemplatingFileGenerator
+
SpanComparers.cs
TextTemplatingFileGenerator
@@ -32,6 +37,11 @@
+
+ True
+ True
+ BigSpanHelpers.tt
+
True
True
@@ -43,4 +53,8 @@
UnionTypes.tt
+
+
+
+
diff --git a/src/FlatSharp.Runtime/GeneratedSerializerWrapper.cs b/src/FlatSharp.Runtime/GeneratedSerializerWrapper.cs
index ff867d1c..bff73f56 100644
--- a/src/FlatSharp.Runtime/GeneratedSerializerWrapper.cs
+++ b/src/FlatSharp.Runtime/GeneratedSerializerWrapper.cs
@@ -60,7 +60,7 @@ private GeneratedSerializerWrapper(GeneratedSerializerWrapper template)
public FlatBufferDeserializationOption DeserializationOption => this.option;
- public int GetMaxSize(T item)
+ public long GetMaxSize(T item)
{
if (item is null)
{
@@ -83,9 +83,8 @@ item is IFlatBufferDeserializedObject deserializedObj &&
// than to introduce an 'if'.
}
- int ISerializer.GetMaxSize(object item)
+ long ISerializer.GetMaxSize(object item)
{
-
return item switch
{
T t => this.GetMaxSize(t),
@@ -144,8 +143,7 @@ public T Parse(TInputBuffer buffer, FlatBufferDeserializationOptio
object ISerializer.Parse(TInputBuffer buffer, FlatBufferDeserializationOption? option) => this.Parse(buffer, option);
- public int Write(TSpanWriter writer, Span destination, T item)
- where TSpanWriter : ISpanWriter
+ public long Write(BigSpan destination, T item)
{
if (item is null)
{
@@ -163,14 +161,7 @@ item is IFlatBufferDeserializedObject deserializedObj &&
{
IInputBuffer? inputBuffer = deserializedObj.InputBuffer;
FlatSharpInternal.Assert(inputBuffer is not null, "Input buffer was null");
-
- if (destination.Length < inputBuffer.Length)
- {
- FSThrow.BufferTooSmall(inputBuffer.Length);
- }
-
- inputBuffer.GetReadOnlySpan().CopyTo(destination);
- return inputBuffer.Length;
+ return inputBuffer.CopyTo(destination);
}
var serializationContext = SerializationContext.ThreadLocalContext.Value!;
@@ -187,11 +178,11 @@ item is IFlatBufferDeserializedObject deserializedObj &&
Debug.Assert(!sharedStringWriter.IsDirty);
}
- this.innerSerializer.Write(writer, destination, item, serializationContext);
+ this.innerSerializer.Write(destination, item, serializationContext);
if (sharedStringWriter?.IsDirty == true)
{
- writer.FlushSharedStrings(sharedStringWriter, destination, serializationContext);
+ sharedStringWriter.FlushWrites(destination, serializationContext);
Debug.Assert(!sharedStringWriter.IsDirty);
}
@@ -206,11 +197,11 @@ item is IFlatBufferDeserializedObject deserializedObj &&
return serializationContext.Offset;
}
- int ISerializer.Write(TSpanWriter writer, Span destination, object item)
+ long ISerializer.Write(BigSpan target, object item)
{
return item switch
{
- T t => this.Write(writer, destination, t),
+ T t => this.Write(target, t),
null => FSThrow.ArgumentNull(nameof(item)),
_ => FSThrow.Argument($"Argument was not of the correct type. Type = {item.GetType().FullName}, Expected Type = {typeof(T).FullName}")
};
diff --git a/src/FlatSharp.Runtime/IFlatBufferDeserializedVector.cs b/src/FlatSharp.Runtime/IFlatBufferDeserializedVector.cs
index 9746ebe6..302c9504 100644
--- a/src/FlatSharp.Runtime/IFlatBufferDeserializedVector.cs
+++ b/src/FlatSharp.Runtime/IFlatBufferDeserializedVector.cs
@@ -40,12 +40,12 @@ public interface IFlatBufferDeserializedVector
///
/// Gets the base offset of the vector.
///
- int OffsetBase { get; }
+ long OffsetBase { get; }
///
/// Returns the absolute position in the Input Buffer of the given index in the vector.
///
- int OffsetOf(int index);
+ long OffsetOf(int index);
///
/// Gets the item at the given index.
diff --git a/src/FlatSharp.Runtime/IGeneratedSerializer.cs b/src/FlatSharp.Runtime/IGeneratedSerializer.cs
index cb92646e..e4f07c63 100644
--- a/src/FlatSharp.Runtime/IGeneratedSerializer.cs
+++ b/src/FlatSharp.Runtime/IGeneratedSerializer.cs
@@ -38,17 +38,15 @@ public GeneratedSerializerParseArguments(int offset, short depthLimit)
public interface IGeneratedSerializer
{
///
- /// Writes the given item to the buffer using the given spanwriter.
+ /// Writes the given item to the given target.
///
- /// The span writer.
- /// The span to write to.
- /// The object to serialize.
+ /// The target.
+ /// The item.
/// The serialization context.
- void Write(
- TSpanWriter writer,
- Span destination,
+ void Write(
+ BigSpan target,
T item,
- SerializationContext context) where TSpanWriter : ISpanWriter;
+ SerializationContext context);
///
/// Computes the maximum size necessary to serialize the given instance of .
diff --git a/src/FlatSharp.Runtime/IO/ArrayInputBuffer.cs b/src/FlatSharp.Runtime/IO/ArrayInputBuffer.cs
deleted file mode 100644
index 11692f89..00000000
--- a/src/FlatSharp.Runtime/IO/ArrayInputBuffer.cs
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright 2018 James Courtney
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-using System.Text;
-
-namespace FlatSharp;
-
-///
-/// An implementation of for managed arrays.
-///
-public struct ArrayInputBuffer : IInputBuffer
-{
- private readonly byte[] memory;
-
- public ArrayInputBuffer(byte[] buffer)
- {
- this.memory = buffer;
- }
-
- public bool IsPinned => false;
-
- public bool IsReadOnly => false;
-
- public int Length => this.memory.Length;
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public byte ReadByte(int offset)
- {
- return ScalarSpanReader.ReadByte(this.memory.AsSpan().Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public sbyte ReadSByte(int offset)
- {
- return ScalarSpanReader.ReadSByte(this.memory.AsSpan().Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ushort ReadUShort(int offset)
- {
- this.CheckAlignment(offset, sizeof(ushort));
- return ScalarSpanReader.ReadUShort(this.memory.AsSpan().Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public short ReadShort(int offset)
- {
- this.CheckAlignment(offset, sizeof(short));
- return ScalarSpanReader.ReadShort(this.memory.AsSpan().Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public uint ReadUInt(int offset)
- {
- this.CheckAlignment(offset, sizeof(uint));
- return ScalarSpanReader.ReadUInt(this.memory.AsSpan().Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public int ReadInt(int offset)
- {
- this.CheckAlignment(offset, sizeof(int));
- return ScalarSpanReader.ReadInt(this.memory.AsSpan().Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ulong ReadULong(int offset)
- {
- this.CheckAlignment(offset, sizeof(ulong));
- return ScalarSpanReader.ReadULong(this.memory.AsSpan().Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public long ReadLong(int offset)
- {
- this.CheckAlignment(offset, sizeof(long));
- return ScalarSpanReader.ReadLong(this.memory.AsSpan().Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public float ReadFloat(int offset)
- {
- this.CheckAlignment(offset, sizeof(float));
- return ScalarSpanReader.ReadFloat(this.memory.AsSpan().Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public double ReadDouble(int offset)
- {
- this.CheckAlignment(offset, sizeof(double));
- return ScalarSpanReader.ReadDouble(this.memory.AsSpan().Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public string ReadString(int offset, int byteLength, Encoding encoding)
- {
- return ScalarSpanReader.ReadString(this.memory.AsSpan().Slice(offset, byteLength), encoding);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ReadOnlySpan GetReadOnlySpan()
- {
- return this.memory;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Span GetSpan()
- {
- return this.memory;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Memory GetMemory()
- {
- return this.memory;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ReadOnlyMemory GetReadOnlyMemory()
- {
- return this.memory;
- }
-}
diff --git a/src/FlatSharp.Runtime/IO/ArraySegmentInputBuffer.cs b/src/FlatSharp.Runtime/IO/ArraySegmentInputBuffer.cs
deleted file mode 100644
index e3de0e77..00000000
--- a/src/FlatSharp.Runtime/IO/ArraySegmentInputBuffer.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright 2021 James Courtney
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-using System.Text;
-
-namespace FlatSharp;
-
-///
-/// An implementation of for array segments.
-///
-public struct ArraySegmentInputBuffer : IInputBuffer
-{
- private readonly ArraySegmentPointer pointer;
-
- public ArraySegmentInputBuffer(ArraySegment memory)
- {
- this.pointer = new ArraySegmentPointer { segment = memory };
- }
-
- public bool IsPinned => false;
-
- public bool IsReadOnly => false;
-
- public int Length => this.pointer.segment.Count;
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public byte ReadByte(int offset)
- {
- return ScalarSpanReader.ReadByte(this.pointer.segment.AsSpan().Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public sbyte ReadSByte(int offset)
- {
- return ScalarSpanReader.ReadSByte(this.pointer.segment.AsSpan().Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ushort ReadUShort(int offset)
- {
- this.CheckAlignment(offset, sizeof(ushort));
- return ScalarSpanReader.ReadUShort(this.pointer.segment.AsSpan().Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public short ReadShort(int offset)
- {
- this.CheckAlignment(offset, sizeof(short));
- return ScalarSpanReader.ReadShort(this.pointer.segment.AsSpan().Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public uint ReadUInt(int offset)
- {
- this.CheckAlignment(offset, sizeof(uint));
- return ScalarSpanReader.ReadUInt(this.pointer.segment.AsSpan().Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public int ReadInt(int offset)
- {
- this.CheckAlignment(offset, sizeof(int));
- return ScalarSpanReader.ReadInt(this.pointer.segment.AsSpan().Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ulong ReadULong(int offset)
- {
- this.CheckAlignment(offset, sizeof(ulong));
- return ScalarSpanReader.ReadULong(this.pointer.segment.AsSpan().Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public long ReadLong(int offset)
- {
- this.CheckAlignment(offset, sizeof(long));
- return ScalarSpanReader.ReadLong(this.pointer.segment.AsSpan().Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public float ReadFloat(int offset)
- {
- this.CheckAlignment(offset, sizeof(float));
- return ScalarSpanReader.ReadFloat(this.pointer.segment.AsSpan().Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public double ReadDouble(int offset)
- {
- this.CheckAlignment(offset, sizeof(double));
- return ScalarSpanReader.ReadDouble(this.pointer.segment.AsSpan().Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public string ReadString(int offset, int byteLength, Encoding encoding)
- {
- return ScalarSpanReader.ReadString(this.pointer.segment.AsSpan().Slice(offset, byteLength), encoding);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ReadOnlySpan GetReadOnlySpan()
- {
- return this.pointer.segment;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Span GetSpan()
- {
- return this.pointer.segment;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Memory GetMemory()
- {
- return this.pointer.segment;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ReadOnlyMemory GetReadOnlyMemory()
- {
- return this.pointer.segment;
- }
-
- // Array Segment is a relatively heavy struct. It contains an array pointer, an int offset, and and int length.
- // Copying this by value for each method call is actually slower than having a little private pointer to a single item.
- private class ArraySegmentPointer
- {
- public ArraySegment segment;
- }
-}
diff --git a/src/FlatSharp.Runtime/IO/IInputBuffer.cs b/src/FlatSharp.Runtime/IO/IInputBuffer.cs
deleted file mode 100644
index 9563a864..00000000
--- a/src/FlatSharp.Runtime/IO/IInputBuffer.cs
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2020 James Courtney
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-using System.Text;
-
-namespace FlatSharp;
-
-///
-/// Defines a buffer that FlatSharp can parse from. Implementations will be fastest when using a struct.
-///
-public interface IInputBuffer
-{
- ///
- /// Indicates if this instance is read only.
- ///
- bool IsReadOnly { get; }
-
- ///
- /// Indicates if this instance represents pinned (non-movable) memory.
- ///
- bool IsPinned { get; }
-
- ///
- /// Gets the length of this input buffer.
- ///
- int Length { get; }
-
- ///
- /// Reads the byte at the given offset.
- ///
- byte ReadByte(int offset);
-
- ///
- /// Reads the sbyte at the given offset.
- ///
- sbyte ReadSByte(int offset);
-
- ///
- /// Reads the ushort at the given offset.
- ///
- ushort ReadUShort(int offset);
-
- ///
- /// Reads the short at the given offset.
- ///
- short ReadShort(int offset);
-
- ///
- /// Reads the uint at the given offset.
- ///
- uint ReadUInt(int offset);
-
- ///
- /// Reads the int at the given offset.
- ///
- int ReadInt(int offset);
-
- ///
- /// Reads the ulong at the given offset.
- ///
- ulong ReadULong(int offset);
-
- ///
- /// Reads the long at the given offset.
- ///
- long ReadLong(int offset);
-
- ///
- /// Reads the float at the given offset.
- ///
- float ReadFloat(int offset);
-
- ///
- /// Reads the double at the given offset.
- ///
- double ReadDouble(int offset);
-
- ///
- /// Reads the string of the given length at the given offset with the given encoding.
- ///
- string ReadString(int offset, int byteLength, Encoding encoding);
-
- ///
- /// Gets a read only span covering the entire input buffer.
- ///
- ReadOnlySpan GetReadOnlySpan();
-
- ///
- /// Gets a read only memory covering the entire input buffer.
- ///
- ReadOnlyMemory GetReadOnlyMemory();
-
- ///
- /// Gets a span covering the entire input buffer.
- ///
- Span GetSpan();
-
- ///
- /// Gets a memory covering the entire input buffer.
- ///
- Memory GetMemory();
-}
\ No newline at end of file
diff --git a/src/FlatSharp.Runtime/IO/ISharedStringWriter.cs b/src/FlatSharp.Runtime/IO/ISharedStringWriter.cs
index fad0bda2..b562af78 100644
--- a/src/FlatSharp.Runtime/IO/ISharedStringWriter.cs
+++ b/src/FlatSharp.Runtime/IO/ISharedStringWriter.cs
@@ -38,16 +38,13 @@ public interface ISharedStringWriter
/// Writes the given string to the span.
///
/// The spanwriter.
- /// The span.
/// The location in the buffer of the uoffset to the string.
/// The string to write.
/// The serialization context.
- void WriteSharedString(TSpanWriter spanWriter, Span data, int offset, string value, SerializationContext context)
- where TSpanWriter : ISpanWriter;
+ void WriteSharedString(BigSpan spanWriter, long offset, string value, SerializationContext context);
///
/// Flushes any pending writes. Invoked at the end of a serialization operation.
///
- void FlushWrites(TSpanWriter writer, Span data, SerializationContext context)
- where TSpanWriter : ISpanWriter;
+ void FlushWrites(BigSpan writer, SerializationContext context);
}
diff --git a/src/FlatSharp.Runtime/IO/ISpanWriter.cs b/src/FlatSharp.Runtime/IO/ISpanWriter.cs
deleted file mode 100644
index beb8a93f..00000000
--- a/src/FlatSharp.Runtime/IO/ISpanWriter.cs
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2020 James Courtney
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-using System.Text;
-
-namespace FlatSharp;
-
-///
-/// Defines a span writer.
-///
-public interface ISpanWriter
-{
- ///
- /// Writes the given byte to the span at the given offset.
- ///
- void WriteByte(Span span, byte value, int offset);
-
- ///
- /// Writes the given double to the span at the given offset.
- ///
- void WriteDouble(Span span, double value, int offset);
-
- ///
- /// Writes the given float to the span at the given offset.
- ///
- void WriteFloat(Span span, float value, int offset);
-
- ///
- /// Writes the given int to the span at the given offset.
- ///
- void WriteInt(Span span, int value, int offset);
-
- ///
- /// Writes the given long to the span at the given offset.
- ///
- void WriteLong(Span span, long value, int offset);
-
- ///
- /// Writes the given sbyte to the span at the given offset.
- ///
- void WriteSByte(Span span, sbyte value, int offset);
-
- ///
- /// Writes the given short to the span at the given offset.
- ///
- void WriteShort(Span span, short value, int offset);
-
- ///
- /// Writes the given uint to the span at the given offset.
- ///
- void WriteUInt(Span span, uint value, int offset);
-
- ///
- /// Writes the given ulong to the span at the given offset.
- ///
- void WriteULong(Span span, ulong value, int offset);
-
- ///
- /// Writes the given ushort to the span at the given offset.
- ///
- void WriteUShort(Span span, ushort value, int offset);
-
- ///
- /// Writes the bytes of the given string to the destination span according to the given encoding.
- ///
- int GetStringBytes(Span destination, string value, Encoding encoding);
-
- ///
- /// Invokes the method.
- ///
- void FlushSharedStrings(
- ISharedStringWriter writer,
- Span destination,
- SerializationContext context);
-}
diff --git a/src/FlatSharp.Runtime/IO/InputBuffer/ArrayInputBuffer.cs b/src/FlatSharp.Runtime/IO/InputBuffer/ArrayInputBuffer.cs
new file mode 100644
index 00000000..8ba548db
--- /dev/null
+++ b/src/FlatSharp.Runtime/IO/InputBuffer/ArrayInputBuffer.cs
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2018 James Courtney
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Text;
+
+namespace FlatSharp;
+
+///
+/// An implementation of for managed arrays.
+///
+public struct ArrayInputBuffer : IInputBuffer
+{
+ private readonly byte[] memory;
+
+ public ArrayInputBuffer(byte[] buffer)
+ {
+ this.memory = buffer;
+ }
+
+ public bool IsPinned => false;
+
+ public bool IsReadOnly => false;
+
+ public long Length => this.memory.Length;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public BigReadOnlySpan GetReadOnlySpan()
+ {
+ return new(this.GetSpan());
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public BigSpan GetSpan()
+ {
+ return new(this.memory.AsSpan());
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Memory GetMemory(long offset, int length)
+ {
+ checked
+ {
+ return this.memory.AsMemory().Slice((int)offset, length);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlyMemory GetReadOnlyMemory(long offset, int length)
+ {
+ return this.GetMemory(offset, length);
+ }
+}
diff --git a/src/FlatSharp.Runtime/IO/InputBuffer/ArraySegmentInputBuffer.cs b/src/FlatSharp.Runtime/IO/InputBuffer/ArraySegmentInputBuffer.cs
new file mode 100644
index 00000000..430ec745
--- /dev/null
+++ b/src/FlatSharp.Runtime/IO/InputBuffer/ArraySegmentInputBuffer.cs
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2021 James Courtney
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Text;
+
+namespace FlatSharp;
+
+///
+/// An implementation of for array segments.
+///
+public struct ArraySegmentInputBuffer : IInputBuffer
+{
+ private readonly ArraySegmentPointer pointer;
+
+ public ArraySegmentInputBuffer(ArraySegment memory)
+ {
+ this.pointer = new ArraySegmentPointer { segment = memory };
+ }
+
+ public bool IsPinned => false;
+
+ public bool IsReadOnly => false;
+
+ public long Length => this.pointer.segment.Count;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public BigReadOnlySpan GetReadOnlySpan()
+ {
+ return new(this.GetSpan());
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public BigSpan GetSpan()
+ {
+ return new(this.pointer.segment.AsSpan());
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Memory GetMemory(long offset, int length)
+ {
+ checked
+ {
+ return this.pointer.segment.AsMemory((int)offset, length);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlyMemory GetReadOnlyMemory(long offset, int length)
+ {
+ return this.GetMemory(offset, length);
+ }
+
+ // Array Segment is a relatively heavy struct. It contains an array pointer, an int offset, and and int length.
+ // Copying this by value for each method call is actually slower than having a little private pointer to a single item.
+ private class ArraySegmentPointer
+ {
+ public ArraySegment segment;
+ }
+}
diff --git a/src/FlatSharp.Runtime/IO/InputBuffer/IInputBuffer.cs b/src/FlatSharp.Runtime/IO/InputBuffer/IInputBuffer.cs
new file mode 100644
index 00000000..852033cd
--- /dev/null
+++ b/src/FlatSharp.Runtime/IO/InputBuffer/IInputBuffer.cs
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2020 James Courtney
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace FlatSharp;
+
+///
+/// Defines a buffer that FlatSharp can parse from. Implementations will be fastest when using a struct.
+///
+public interface IInputBuffer
+{
+ ///
+ /// Indicates if this instance is read only.
+ ///
+ bool IsReadOnly { get; }
+
+ ///
+ /// Indicates if this instance represents pinned (non-movable) memory.
+ ///
+ bool IsPinned { get; }
+
+ ///
+ /// Gets the length of this input buffer.
+ ///
+ long Length { get; }
+
+ ///
+ /// Gets a read only span covering the entire input buffer.
+ ///
+ BigReadOnlySpan GetReadOnlySpan();
+
+ ///
+ /// Gets a read only memory covering the entire input buffer.
+ ///
+ ReadOnlyMemory GetReadOnlyMemory(long offset, int length);
+
+ ///
+ /// Gets a span covering the entire input buffer.
+ ///
+ BigSpan GetSpan();
+
+ ///
+ /// Gets a memory covering the entire input buffer.
+ ///
+ Memory GetMemory(long offset, int length);
+}
diff --git a/src/FlatSharp.Runtime/IO/InputBuffer/InputBufferExtensions.cs b/src/FlatSharp.Runtime/IO/InputBuffer/InputBufferExtensions.cs
new file mode 100644
index 00000000..198b7b9b
--- /dev/null
+++ b/src/FlatSharp.Runtime/IO/InputBuffer/InputBufferExtensions.cs
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2020 James Courtney
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Buffers;
+using System.Buffers.Binary;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace FlatSharp.Internal;
+
+///
+/// Extensions for input buffers.
+///
+public static class InputBufferExtensions
+{
+ ///
+ /// Reads a bool.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool ReadBool(this TBuffer buffer, long offset)
+ where TBuffer : IInputBuffer
+ {
+ return buffer.ReadByte(offset) != SerializationHelpers.False;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static byte ReadByte(this TBuffer buffer, long offset)
+ where TBuffer : IInputBuffer
+ {
+ return buffer.GetReadOnlySpan()[offset];
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static sbyte ReadSByte(this TBuffer buffer, long offset)
+ where TBuffer : IInputBuffer
+ {
+ return buffer.GetReadOnlySpan().ReadSByte(offset);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ushort ReadUShort(this TBuffer buffer, long offset)
+ where TBuffer : IInputBuffer
+ {
+ return buffer.GetReadOnlySpan().ReadUShort(offset);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static short ReadShort(this TBuffer buffer, long offset)
+ where TBuffer : IInputBuffer
+ {
+ return buffer.GetReadOnlySpan().ReadShort(offset);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint ReadUInt(this TBuffer buffer, long offset)
+ where TBuffer : IInputBuffer
+ {
+ return buffer.GetReadOnlySpan().ReadUInt(offset);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int ReadInt(this TBuffer buffer, long offset)
+ where TBuffer : IInputBuffer
+ {
+ return buffer.GetReadOnlySpan().ReadInt(offset);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong ReadULong(this TBuffer buffer, long offset)
+ where TBuffer : IInputBuffer
+ {
+ return buffer.GetReadOnlySpan().ReadULong(offset);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static long ReadLong(this TBuffer buffer, long offset)
+ where TBuffer : IInputBuffer
+ {
+ return buffer.GetReadOnlySpan().ReadLong(offset);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float ReadFloat(this TBuffer buffer, long offset)
+ where TBuffer : IInputBuffer
+ {
+ return buffer.GetReadOnlySpan().ReadFloat(offset);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double ReadDouble(this TBuffer buffer, long offset)
+ where TBuffer : IInputBuffer
+ {
+ return buffer.GetReadOnlySpan().ReadDouble(offset);
+ }
+
+ ///
+ /// Reads a string at the given offset.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string ReadString(this TBuffer buffer, long offset) where TBuffer : IInputBuffer
+ {
+ checked
+ {
+ // Strings are stored by reference.
+ offset += buffer.ReadUOffset(offset);
+ return buffer.ReadStringFromUOffset(offset);
+ }
+ }
+
+ ///
+ /// Reads a string from the given uoffset.
+ ///
+ public static string ReadStringFromUOffset(this TBuffer buffer, long stringStart)
+ where TBuffer : IInputBuffer
+ {
+ int numberOfBytes = (int)buffer.ReadUInt(stringStart);
+ ReadOnlySpan stringValue = buffer.GetReadOnlySpan().ToSpan(stringStart + sizeof(int), numberOfBytes);
+
+#if NETSTANDARD2_0
+ byte[] temp = ArrayPool.Shared.Rent(numberOfBytes);
+ stringValue.CopyTo(temp);
+ string result = SerializationHelpers.Encoding.GetString(temp, 0, numberOfBytes);
+ ArrayPool.Shared.Return(temp);
+ return result;
+#else
+ return SerializationHelpers.Encoding.GetString(stringValue);
+#endif
+ }
+
+ ///
+ /// Reads the given uoffset.
+ ///
+ public static int ReadUOffset(this TBuffer buffer, long offset) where TBuffer : IInputBuffer
+ {
+ int uoffset = buffer.ReadInt(offset);
+ if (uoffset < sizeof(uint))
+ {
+ FSThrow.InvalidData_UOffsetTooSmall((uint)uoffset);
+ }
+
+ return uoffset;
+ }
+
+ ///
+ /// Validates a vtable and reads the initial bytes of a vtable.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void InitializeVTable(
+ this TBuffer buffer,
+ long tableOffset,
+ out long vtableOffset,
+ out ulong vtableFieldCount,
+ out ReadOnlySpan fieldData) where TBuffer : IInputBuffer
+ {
+ BigReadOnlySpan span = buffer.GetReadOnlySpan();
+ vtableOffset = tableOffset - span.ReadInt(tableOffset);
+
+ ushort vtableLength = span.ReadUShort(vtableOffset);
+ if (vtableLength < 4)
+ {
+ FSThrow.InvalidData_VTableTooShort();
+ }
+
+ fieldData = span.ToSpan(vtableOffset, vtableLength).Slice(4);
+ vtableFieldCount = (ulong)(fieldData.Length / 2);
+ }
+
+ // Seems to break JIT in .NET Core 2.1. Framework 4.7 and Core 3.1 work as expected.
+ // [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Memory ReadByteMemoryBlock(this TBuffer buffer, long uoffset)
+ where TBuffer : IInputBuffer
+ {
+ // The local value stores a uoffset_t, so follow that now.
+ uoffset += buffer.ReadUOffset(uoffset);
+ return buffer.GetMemory(uoffset + sizeof(int), buffer.ReadInt(uoffset));
+ }
+
+ // Seems to break JIT in .NET Core 2.1. Framework 4.7 and Core 3.1 work as expected.
+ // [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlyMemory ReadByteReadOnlyMemoryBlock(this TBuffer buffer, long uoffset)
+ where TBuffer : IInputBuffer
+ {
+ // The local value stores a uoffset_t, so follow that now.
+ uoffset += buffer.ReadUOffset(uoffset);
+ return buffer.GetReadOnlyMemory(uoffset + sizeof(uint), buffer.ReadInt(uoffset));
+ }
+
+ ///
+ /// Reads a sequence of TElement items from the buffer at the given offset using the equivalent of reinterpret_cast.
+ ///
+ public static Span UnsafeReadSpan(this TBuffer buffer, long uoffset)
+ where TBuffer : IInputBuffer
+ where TElement : unmanaged
+ {
+ // The local value stores a uoffset_t, so follow that now.
+ uoffset = uoffset + buffer.ReadUOffset(uoffset);
+
+ // We need to construct a Span from byte buffer that:
+ // 1. starts at correct offset for vector data
+ // 2. has a length based on *TElement* count not *byte* count
+ var byteSpanAtDataOffset = buffer
+ .GetSpan()
+ .ToSpan(
+ uoffset + sizeof(uint),
+ checked(Unsafe.SizeOf() * (int)buffer.ReadUInt(uoffset)));
+
+ var sourceSpan = MemoryMarshal.Cast(byteSpanAtDataOffset);
+
+ return sourceSpan;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static long CopyTo(this TBuffer buffer, BigSpan target)
+ where TBuffer : IInputBuffer
+ {
+ if (target.Length < buffer.Length)
+ {
+ FSThrow.BufferTooSmall(buffer.Length);
+ return 0;
+ }
+
+ long offset = 0;
+ while (offset < buffer.Length)
+ {
+ long remaining = buffer.Length - offset;
+ var chunk = buffer.GetReadOnlySpan().ToSpan(offset, (int)Math.Min(int.MaxValue, remaining));
+ chunk.CopyTo(target.ToSpan(offset, chunk.Length));
+
+ offset += chunk.Length;
+ }
+
+ return offset;
+ }
+
+ [ExcludeFromCodeCoverage] // Not currently used.
+ [Conditional("DEBUG")]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void CheckAlignment(this TBuffer buffer, long offset, int size)
+ where TBuffer : IInputBuffer
+#if NET9_0_OR_GREATER
+ , allows ref struct
+#endif
+ {
+#if DEBUG
+ if (offset % size != 0)
+ {
+ FSThrow.InvalidOperation(
+ $"BugCheck: attempted to read unaligned data at index: {offset}, expected alignment: {size}");
+ }
+#endif
+ }
+}
\ No newline at end of file
diff --git a/src/FlatSharp.Runtime/IO/InputBuffer/MemoryInputBuffer.cs b/src/FlatSharp.Runtime/IO/InputBuffer/MemoryInputBuffer.cs
new file mode 100644
index 00000000..d69168e3
--- /dev/null
+++ b/src/FlatSharp.Runtime/IO/InputBuffer/MemoryInputBuffer.cs
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2018 James Courtney
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace FlatSharp;
+
+///
+/// An implementation of InputBuffer for writable memory segments.
+///
+public readonly struct MemoryInputBuffer : IInputBuffer
+{
+ private readonly MemoryPointer pointer;
+
+ public MemoryInputBuffer(Memory memory, bool isPinned = false)
+ {
+ this.pointer = new MemoryPointer { memory = memory, isPinned = isPinned };
+ }
+
+ public bool IsPinned => this.pointer.isPinned;
+
+ public bool IsReadOnly => false;
+
+ public long Length
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => this.pointer.memory.Length;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public BigReadOnlySpan GetReadOnlySpan()
+ {
+ return new(this.GetSpan());
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public BigSpan GetSpan()
+ {
+ return new(this.pointer.memory.Span);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlyMemory GetReadOnlyMemory(long offset, int length)
+ {
+ return this.GetMemory(offset, length);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Memory GetMemory(long offset, int length)
+ {
+ checked
+ {
+ return this.pointer.memory.Slice((int)offset, length);
+ }
+ }
+
+ // Memory is a relatively heavy struct. It's cheaper to wrap it in a
+ // a reference that will be collected ephemerally in Gen0 than it is to
+ // copy it around.
+ private class MemoryPointer
+ {
+ public Memory memory;
+ public bool isPinned;
+ }
+}
diff --git a/src/FlatSharp.Runtime/IO/InputBuffer/ReadOnlyMemoryInputBuffer.cs b/src/FlatSharp.Runtime/IO/InputBuffer/ReadOnlyMemoryInputBuffer.cs
new file mode 100644
index 00000000..7e9d03bf
--- /dev/null
+++ b/src/FlatSharp.Runtime/IO/InputBuffer/ReadOnlyMemoryInputBuffer.cs
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2020 James Courtney
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace FlatSharp;
+
+///
+/// An implemenation of InputBuffer that accepts ReadOnlyMemory. ReadOnlyMemoryInputBuffer
+/// behaves identically to MemoryInputBuffer with one exception, which is that it will refuse
+/// to deserialize any mutable memory (Memory{T}) instances. These will result in an exception
+/// being thrown. ReadOnlyMemoryInputBuffer guarantees that the objects returned will
+/// not modify in the input buffer (unless unsafe operations / MemoryMarshal) are used.
+///
+public struct ReadOnlyMemoryInputBuffer : IInputBuffer
+{
+ private const string ErrorMessage = "ReadOnlyMemory inputs may not deserialize writable memory.";
+
+ private readonly MemoryPointer pointer;
+
+ public ReadOnlyMemoryInputBuffer(ReadOnlyMemory memory, bool isPinned = false)
+ {
+ this.pointer = new MemoryPointer { memory = memory, isPinned = isPinned };
+ }
+
+ public bool IsPinned => this.pointer.isPinned;
+
+ public bool IsReadOnly => true;
+
+ public long Length
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => this.pointer.memory.Length;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public BigReadOnlySpan GetReadOnlySpan()
+ {
+ var rwMemory = MemoryMarshal.AsMemory(this.pointer.memory);
+ return new(new BigSpan(rwMemory.Span));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlyMemory GetReadOnlyMemory(long offset, int length)
+ {
+ checked
+ {
+ return this.pointer.memory.Slice((int)offset, length);
+ }
+ }
+
+ public BigSpan GetSpan()
+ {
+ FSThrow.InvalidOperation(ErrorMessage);
+ return default;
+ }
+
+ public Memory GetMemory(long offset, int length)
+ {
+ return FSThrow.InvalidOperation>(ErrorMessage);
+ }
+
+ // Memory is a relatively heavy struct. It's cheaper to wrap it in a
+ // a reference that will be collected ephemerally in Gen0 than is is to
+ // copy it around.
+ private class MemoryPointer
+ {
+ public ReadOnlyMemory memory;
+ public bool isPinned;
+ }
+}
diff --git a/src/FlatSharp.Runtime/IO/InputBufferExtensions.cs b/src/FlatSharp.Runtime/IO/InputBufferExtensions.cs
deleted file mode 100644
index 29f365fe..00000000
--- a/src/FlatSharp.Runtime/IO/InputBufferExtensions.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright 2020 James Courtney
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-using System.IO;
-using System.Runtime.InteropServices;
-
-namespace FlatSharp.Internal;
-
-///
-/// Extensions for input buffers.
-///
-public static class InputBufferExtensions
-{
- ///
- /// Reads a bool.
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool ReadBool(this TBuffer buffer, int offset) where TBuffer : IInputBuffer
- {
- return buffer.ReadByte(offset) != SerializationHelpers.False;
- }
-
- ///
- /// Reads a string at the given offset.
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static string ReadString(this TBuffer buffer, int offset) where TBuffer : IInputBuffer
- {
- checked
- {
- // Strings are stored by reference.
- offset += buffer.ReadUOffset(offset);
- return buffer.ReadStringFromUOffset(offset);
- }
- }
-
- ///
- /// Reads a string from the given uoffset.
- ///
- public static string ReadStringFromUOffset(this TBuffer buffer, int uoffset) where TBuffer : IInputBuffer
- {
- int numberOfBytes = (int)buffer.ReadUInt(uoffset);
- return buffer.ReadString(uoffset + sizeof(int), numberOfBytes, SerializationHelpers.Encoding);
- }
-
- ///
- /// Reads the given uoffset.
- ///
- public static int ReadUOffset(this TBuffer buffer, int offset) where TBuffer : IInputBuffer
- {
- int uoffset = buffer.ReadInt(offset);
- if (uoffset < sizeof(uint))
- {
- FSThrow.InvalidData_UOffsetTooSmall((uint)uoffset);
- }
-
- return uoffset;
- }
-
- ///
- /// Validates a vtable and reads the initial bytes of a vtable.
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void InitializeVTable(
- this TBuffer buffer,
- int tableOffset,
- out int vtableOffset,
- out nuint vtableFieldCount,
- out ReadOnlySpan fieldData) where TBuffer : IInputBuffer
- {
- vtableOffset = tableOffset - buffer.ReadInt(tableOffset);
- ushort vtableLength = buffer.ReadUShort(vtableOffset);
-
- if (vtableLength < 4)
- {
- FSThrow.InvalidData_VTableTooShort();
- }
-
- fieldData = buffer.GetReadOnlySpan().Slice(vtableOffset, vtableLength).Slice(4);
- vtableFieldCount = (nuint)fieldData.Length / 2;
- }
-
- // Seems to break JIT in .NET Core 2.1. Framework 4.7 and Core 3.1 work as expected.
- // [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Memory ReadByteMemoryBlock(this TBuffer buffer, int uoffset) where TBuffer : IInputBuffer
- {
- // The local value stores a uoffset_t, so follow that now.
- uoffset = uoffset + buffer.ReadUOffset(uoffset);
- return buffer.GetMemory().Slice(uoffset + sizeof(uint), (int)buffer.ReadUInt(uoffset));
- }
-
- // Seems to break JIT in .NET Core 2.1. Framework 4.7 and Core 3.1 work as expected.
- // [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ReadOnlyMemory ReadByteReadOnlyMemoryBlock(this TBuffer buffer, int uoffset) where TBuffer : IInputBuffer
- {
- // The local value stores a uoffset_t, so follow that now.
- uoffset = uoffset + buffer.ReadUOffset(uoffset);
- return buffer.GetReadOnlyMemory().Slice(uoffset + sizeof(uint), (int)buffer.ReadUInt(uoffset));
- }
-
- ///
- /// Reads a sequence of TElement items from the buffer at the given offset using the equivalent of reinterpret_cast.
- ///
- public static Span UnsafeReadSpan(this TBuffer buffer, int uoffset) where TBuffer : IInputBuffer where TElement : struct
- {
- // The local value stores a uoffset_t, so follow that now.
- uoffset = uoffset + buffer.ReadUOffset(uoffset);
-
- // We need to construct a Span from byte buffer that:
- // 1. starts at correct offset for vector data
- // 2. has a length based on *TElement* count not *byte* count
- var byteSpanAtDataOffset = buffer.GetSpan().Slice(uoffset + sizeof(uint));
- var sourceSpan = MemoryMarshal.Cast(byteSpanAtDataOffset).Slice(0, (int)buffer.ReadUInt(uoffset));
-
- return sourceSpan;
- }
-
- [ExcludeFromCodeCoverage] // Not currently used.
- [Conditional("DEBUG")]
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void CheckAlignment(this TBuffer buffer, int offset, int size) where TBuffer : IInputBuffer
- {
-#if DEBUG
- if (offset % size != 0)
- {
- FSThrow.InvalidOperation($"BugCheck: attempted to read unaligned data at index: {offset}, expected alignment: {size}");
- }
-#endif
- }
-}
diff --git a/src/FlatSharp.Runtime/IO/MemoryInputBuffer.cs b/src/FlatSharp.Runtime/IO/MemoryInputBuffer.cs
deleted file mode 100644
index dedeb7ee..00000000
--- a/src/FlatSharp.Runtime/IO/MemoryInputBuffer.cs
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright 2018 James Courtney
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-using System.Text;
-
-namespace FlatSharp;
-
-///
-/// An implementation of InputBuffer for writable memory segments.
-///
-public struct MemoryInputBuffer : IInputBuffer
-{
- private readonly MemoryPointer pointer;
-
- public MemoryInputBuffer(Memory memory, bool isPinned = false)
- {
- this.pointer = new MemoryPointer { memory = memory, isPinned = isPinned };
- }
-
- public bool IsPinned => this.pointer.isPinned;
-
- public bool IsReadOnly => false;
-
- public int Length
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => this.pointer.memory.Length;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public byte ReadByte(int offset)
- {
- return ScalarSpanReader.ReadByte(this.pointer.memory.Span.Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public sbyte ReadSByte(int offset)
- {
- return ScalarSpanReader.ReadSByte(this.pointer.memory.Span.Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ushort ReadUShort(int offset)
- {
- this.CheckAlignment(offset, sizeof(ushort));
- return ScalarSpanReader.ReadUShort(this.pointer.memory.Span.Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public short ReadShort(int offset)
- {
- this.CheckAlignment(offset, sizeof(short));
- return ScalarSpanReader.ReadShort(this.pointer.memory.Span.Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public uint ReadUInt(int offset)
- {
- this.CheckAlignment(offset, sizeof(uint));
- return ScalarSpanReader.ReadUInt(this.pointer.memory.Span.Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public int ReadInt(int offset)
- {
- this.CheckAlignment(offset, sizeof(int));
- return ScalarSpanReader.ReadInt(this.pointer.memory.Span.Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ulong ReadULong(int offset)
- {
- this.CheckAlignment(offset, sizeof(ulong));
- return ScalarSpanReader.ReadULong(this.pointer.memory.Span.Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public long ReadLong(int offset)
- {
- this.CheckAlignment(offset, sizeof(long));
- return ScalarSpanReader.ReadLong(this.pointer.memory.Span.Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public float ReadFloat(int offset)
- {
- this.CheckAlignment(offset, sizeof(float));
- return ScalarSpanReader.ReadFloat(this.pointer.memory.Span.Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public double ReadDouble(int offset)
- {
- this.CheckAlignment(offset, sizeof(double));
- return ScalarSpanReader.ReadDouble(this.pointer.memory.Span.Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public string ReadString(int offset, int byteLength, Encoding encoding)
- {
- return ScalarSpanReader.ReadString(this.pointer.memory.Span.Slice(offset, byteLength), encoding);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ReadOnlySpan GetReadOnlySpan()
- {
- return this.pointer.memory.Span;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Span GetSpan()
- {
- return this.pointer.memory.Span;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ReadOnlyMemory GetReadOnlyMemory()
- {
- return this.pointer.memory;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Memory GetMemory()
- {
- return this.pointer.memory;
- }
-
- // Memory is a relatively heavy struct. It's cheaper to wrap it in a
- // a reference that will be collected ephemerally in Gen0 than it is to
- // copy it around.
- private class MemoryPointer
- {
- public Memory memory;
- public bool isPinned;
- }
-}
diff --git a/src/FlatSharp.Runtime/IO/ReadOnlyMemoryInputBuffer.cs b/src/FlatSharp.Runtime/IO/ReadOnlyMemoryInputBuffer.cs
deleted file mode 100644
index 84df4e78..00000000
--- a/src/FlatSharp.Runtime/IO/ReadOnlyMemoryInputBuffer.cs
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright 2020 James Courtney
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-using System.Text;
-
-namespace FlatSharp;
-
-///
-/// An implemenation of InputBuffer that accepts ReadOnlyMemory. ReadOnlyMemoryInputBuffer
-/// behaves identically to MemoryInputBuffer with one exception, which is that it will refuse
-/// to deserialize any mutable memory (Memory{T}) instances. These will result in an exception
-/// being thrown. ReadOnlyMemoryInputBuffer guarantees that the objects returned will
-/// not modify in the input buffer (unless unsafe operations / MemoryMarshal) are used.
-///
-public struct ReadOnlyMemoryInputBuffer : IInputBuffer
-{
- private const string ErrorMessage = "ReadOnlyMemory inputs may not deserialize writable memory.";
-
- private readonly MemoryPointer pointer;
-
- public ReadOnlyMemoryInputBuffer(ReadOnlyMemory memory, bool isPinned = false)
- {
- this.pointer = new MemoryPointer { memory = memory, isPinned = isPinned };
- }
-
- public bool IsPinned => this.pointer.isPinned;
-
- public bool IsReadOnly => true;
-
- public int Length
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => this.pointer.memory.Length;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public byte ReadByte(int offset)
- {
- return ScalarSpanReader.ReadByte(this.pointer.memory.Span.Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public sbyte ReadSByte(int offset)
- {
- return ScalarSpanReader.ReadSByte(this.pointer.memory.Span.Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ushort ReadUShort(int offset)
- {
- this.CheckAlignment(offset, sizeof(ushort));
- return ScalarSpanReader.ReadUShort(this.pointer.memory.Span.Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public short ReadShort(int offset)
- {
- this.CheckAlignment(offset, sizeof(short));
- return ScalarSpanReader.ReadShort(this.pointer.memory.Span.Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public uint ReadUInt(int offset)
- {
- this.CheckAlignment(offset, sizeof(uint));
- return ScalarSpanReader.ReadUInt(this.pointer.memory.Span.Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public int ReadInt(int offset)
- {
- this.CheckAlignment(offset, sizeof(int));
- return ScalarSpanReader.ReadInt(this.pointer.memory.Span.Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ulong ReadULong(int offset)
- {
- this.CheckAlignment(offset, sizeof(ulong));
- return ScalarSpanReader.ReadULong(this.pointer.memory.Span.Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public long ReadLong(int offset)
- {
- this.CheckAlignment(offset, sizeof(long));
- return ScalarSpanReader.ReadLong(this.pointer.memory.Span.Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public float ReadFloat(int offset)
- {
- this.CheckAlignment(offset, sizeof(float));
- return ScalarSpanReader.ReadFloat(this.pointer.memory.Span.Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public double ReadDouble(int offset)
- {
- this.CheckAlignment(offset, sizeof(double));
- return ScalarSpanReader.ReadDouble(this.pointer.memory.Span.Slice(offset));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public string ReadString(int offset, int byteLength, Encoding encoding)
- {
- return ScalarSpanReader.ReadString(this.pointer.memory.Span.Slice(offset, byteLength), encoding);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ReadOnlySpan GetReadOnlySpan()
- {
- return this.pointer.memory.Span;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ReadOnlyMemory GetReadOnlyMemory()
- {
- return this.pointer.memory;
- }
-
- public Span GetSpan()
- {
- FSThrow.InvalidOperation(ErrorMessage);
- return default;
- }
-
- public Memory GetMemory()
- {
- return FSThrow.InvalidOperation>(ErrorMessage);
- }
-
- // Memory is a relatively heavy struct. It's cheaper to wrap it in a
- // a reference that will be collected ephemerally in Gen0 than is is to
- // copy it around.
- private class MemoryPointer
- {
- public ReadOnlyMemory memory;
- public bool isPinned;
- }
-}
diff --git a/src/FlatSharp.Runtime/IO/SerializationTarget/BigReadOnlySpan.cs b/src/FlatSharp.Runtime/IO/SerializationTarget/BigReadOnlySpan.cs
new file mode 100644
index 00000000..f29b36c4
--- /dev/null
+++ b/src/FlatSharp.Runtime/IO/SerializationTarget/BigReadOnlySpan.cs
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2024 James Courtney
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace FlatSharp;
+
+using System.Buffers;
+using System.Buffers.Binary;
+using System.Runtime.InteropServices;
+
+
+public readonly ref partial struct BigReadOnlySpan
+{
+ private readonly BigSpan span;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public BigReadOnlySpan(BigSpan span)
+ {
+ this.span = span;
+ }
+
+ internal BigSpan Span
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => this.span;
+ }
+
+ public long Length
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => this.span.Length;
+ }
+
+ public readonly ref byte this[long index]
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => ref this.span[index];
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public BigReadOnlySpan Slice(long start, long length)
+ {
+ return new(this.span.Slice(start, length));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public BigReadOnlySpan Slice(long start)
+ {
+ return new(this.span.Slice(start));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ReadOnlySpan ToSpan(long start, int length)
+ {
+ return this.span.ToSpan(start, length);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool ReadBool(long offset) => this.span.ReadBool(offset);
+
+ private static void ThrowOutOfRange()
+ {
+ throw new IndexOutOfRangeException();
+ }
+}
\ No newline at end of file
diff --git a/src/FlatSharp.Runtime/IO/SerializationTarget/BigSpan.cs b/src/FlatSharp.Runtime/IO/SerializationTarget/BigSpan.cs
new file mode 100644
index 00000000..1f5a0ba1
--- /dev/null
+++ b/src/FlatSharp.Runtime/IO/SerializationTarget/BigSpan.cs
@@ -0,0 +1,343 @@
+/*
+ * Copyright 2024 James Courtney
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace FlatSharp;
+
+using System.Buffers;
+using System.Buffers.Binary;
+using System.Runtime.InteropServices;
+
+public readonly ref partial struct BigSpan
+{
+#if NET7_0_OR_GREATER
+ private readonly long length;
+ private readonly ref byte value;
+#else
+ private readonly Span span;
+#endif
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public BigSpan(Span span)
+ {
+#if NET7_0_OR_GREATER
+ this.length = span.Length;
+ this.value = ref span[0];
+#else
+ this.span = span;
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private BigSpan(ref byte value, long length)
+ {
+#if NET7_0_OR_GREATER
+ this.value = ref value;
+ this.length = length;
+#else
+ unsafe
+ {
+ fixed (byte* pByte = &value)
+ {
+ this.span = new(pByte, checked((int)length));
+ }
+ }
+#endif
+ }
+
+ public long Length
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#if NET7_0_OR_GREATER
+ get => this.length;
+#else
+ get => this.span.Length;
+#endif
+ }
+
+ public ref byte this[long index]
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ if ((ulong)index >= (ulong)this.Length)
+ {
+ ThrowOutOfRange();
+ }
+
+#if NET7_0_OR_GREATER
+ return ref Unsafe.Add(ref this.value, (IntPtr)index);
+#else
+ return ref this.span[(int)index];
+#endif
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public BigSpan Slice(long start, long length)
+ {
+ bool isOutOfRange = (length | start) < 0;
+ isOutOfRange |= (ulong)(start + length) > (ulong)this.Length;
+
+ if (isOutOfRange)
+ {
+ ThrowOutOfRange();
+ }
+
+#if NET7_0_OR_GREATER
+ return new BigSpan(ref Unsafe.Add(ref this.value, (IntPtr)start), length);
+#else
+ return new(this.span.Slice((int)start, (int)length));
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public BigSpan Slice(long start)
+ {
+ if ((ulong)start > (ulong)this.Length)
+ {
+ ThrowOutOfRange();
+ }
+
+#if NET7_0_OR_GREATER
+ return new BigSpan(ref Unsafe.Add(ref this.value, (IntPtr)start), this.Length - start);
+#else
+ return new(this.span.Slice((int)start));
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Span ToSpan(long start, int length)
+ {
+ this.CheckRange(start, length);
+
+#if NET7_0_OR_GREATER
+ return MemoryMarshal.CreateSpan(
+ ref Unsafe.Add(ref this.value, (IntPtr)start),
+ length);
+#else
+ return this.span.Slice((int)start, length);
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteBool(long offset, bool value)
+ {
+ this[offset] = value ? SerializationHelpers.True : SerializationHelpers.False;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool ReadBool(long offset) => this[offset] != SerializationHelpers.False;
+
+ public void WriteReadOnlyByteMemoryBlock(
+ ReadOnlyMemory memory,
+ long offset,
+ SerializationContext ctx)
+ {
+ int numberOfItems = memory.Length;
+ long vectorStartOffset = ctx.AllocateVector(itemAlignment: sizeof(byte), numberOfItems, sizePerItem: sizeof(byte));
+
+ this.WriteUOffset(offset, vectorStartOffset);
+ this.WriteInt(vectorStartOffset, numberOfItems);
+
+ memory.Span.CopyTo(this.ToSpan(vectorStartOffset + sizeof(uint), numberOfItems));
+ }
+
+ public void UnsafeWriteSpan(
+ Span buffer,
+ long offset,
+ int alignment,
+ SerializationContext ctx)
+ where TElement : unmanaged
+ {
+ // Since we are copying bytes here, only LE is supported.
+ FlatSharpInternal.AssertLittleEndian();
+ FlatSharpInternal.AssertWellAligned(alignment);
+
+ int numberOfItems = buffer.Length;
+ long vectorStartOffset = ctx.AllocateVector(
+ itemAlignment: alignment,
+ numberOfItems,
+ sizePerItem: Unsafe.SizeOf());
+
+ this.WriteUOffset(offset, vectorStartOffset);
+ this.WriteInt(vectorStartOffset, numberOfItems);
+
+ Span destination = this.ToSpan(
+ vectorStartOffset + sizeof(uint),
+ checked(numberOfItems * Unsafe.SizeOf()));
+
+ MemoryMarshal.Cast(buffer).CopyTo(destination);
+ }
+
+ ///
+ /// Writes the given string.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteString(
+ string value,
+ long offset,
+ SerializationContext context)
+ {
+ long stringOffset = this.WriteAndProvisionString(value, context);
+ this.WriteUOffset(offset, stringOffset);
+ }
+
+ ///
+ /// Writes the string to the buffer, returning the absolute offset of the string.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public long WriteAndProvisionString(
+ string value,
+ SerializationContext context)
+ {
+ var encoding = SerializationHelpers.Encoding;
+
+ // Allocate more than we need and then give back what we don't use.
+ int maxItems = encoding.GetMaxByteCount(value.Length) + 1;
+ long stringStartOffset = context.AllocateVector(sizeof(byte), maxItems, sizeof(byte));
+
+ BigSpan scopedThis = this.Slice(stringStartOffset, maxItems + sizeof(uint));
+ Span destination = scopedThis.ToSpan(sizeof(uint), maxItems);
+
+#if NETSTANDARD2_0
+ int length = value.Length;
+ byte[] buffer = ArrayPool.Shared.Rent(encoding.GetMaxByteCount(length));
+ int bytesWritten = encoding.GetBytes(value, 0, length, buffer, 0);
+ buffer.AsSpan().Slice(0, bytesWritten).CopyTo(destination);
+ ArrayPool.Shared.Return(buffer);
+#else
+ int bytesWritten = encoding.GetBytes(value, destination);
+#endif
+
+ // null teriminator
+ scopedThis.UnsafeWriteByte(sizeof(uint) + bytesWritten, 0);
+
+ // write length
+ scopedThis.UnsafeWriteInt(0, bytesWritten);
+
+ // give back unused space. Account for null terminator.
+ context.Offset -= maxItems - (bytesWritten + 1);
+
+ return stringStartOffset;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteUOffset(
+ long offset,
+ long secondOffset)
+ {
+ long difference = secondOffset - offset;
+ uint uoffset = checked((uint)difference);
+ this.WriteUInt(offset, uoffset);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private T ReadUnaligned(long offset)
+ where T : unmanaged
+ {
+ this.CheckRange(offset, Unsafe.SizeOf());
+ return this.ReadUnalignedUnsafe(offset);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void WriteUnaligned(long offset, T value)
+ where T : unmanaged
+ {
+ this.CheckRange(offset, Unsafe.SizeOf());
+ this.WriteUnalignedUnsafe(offset, value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal T ReadUnalignedUnsafe(long offset)
+ where T : unmanaged
+ {
+#if DEBUG
+ CheckAlignment(offset, Unsafe.SizeOf());
+ CheckRange(offset, Unsafe.SizeOf());
+#endif
+
+#if NET7_0_OR_GREATER
+ return Unsafe.ReadUnaligned(ref Unsafe.Add(ref this.value, (IntPtr)offset));
+#else
+ var slice = this.ToSpan(offset, Unsafe.SizeOf());
+ return Unsafe.ReadUnaligned(ref slice[0]);
+#endif
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal void WriteUnalignedUnsafe(long offset, T value)
+ where T : unmanaged
+ {
+#if DEBUG
+ CheckAlignment(offset, Unsafe.SizeOf());
+ CheckRange(offset, Unsafe.SizeOf());
+#endif
+
+#if NET7_0_OR_GREATER
+ Unsafe.WriteUnaligned(ref Unsafe.Add(ref this.value, (IntPtr)offset), value);
+#else
+ var slice = this.ToSpan(offset, Unsafe.SizeOf());
+ Unsafe.WriteUnaligned(ref slice[0], value);
+#endif
+ }
+
+ internal static double ReverseEndianness(double value)
+ {
+ long longValue = Unsafe.As(ref value);
+ longValue = BinaryPrimitives.ReverseEndianness(longValue);
+ return BitConverter.Int64BitsToDouble(longValue);
+ }
+
+ internal static float ReverseEndianness(float value)
+ {
+ uint intValue = Unsafe.As(ref value);
+ intValue = BinaryPrimitives.ReverseEndianness(intValue);
+
+ ScalarSpanReader.FloatLayout f = new()
+ {
+ bytes = intValue
+ };
+
+ return f.value;
+ }
+
+ private static void ThrowOutOfRange()
+ {
+ throw new IndexOutOfRangeException();
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void CheckRange(long start, int length)
+ {
+ long sum = start + (long)length;
+ if ((ulong)sum > (ulong)this.Length)
+ {
+ ThrowOutOfRange();
+ }
+ }
+
+ [ExcludeFromCodeCoverage]
+ [Conditional("DEBUG")]
+ private static void CheckAlignment(long offset, int size)
+ {
+#if DEBUG
+ if (offset % size != 0)
+ {
+ FSThrow.InvalidOperation($"BugCheck: attempted to read unaligned data at index: {offset}, expected alignment: {size}");
+ }
+#endif
+ }
+}
\ No newline at end of file
diff --git a/src/FlatSharp.Runtime/IO/SerializationTarget/BigSpanExtensions.cs b/src/FlatSharp.Runtime/IO/SerializationTarget/BigSpanExtensions.cs
new file mode 100644
index 00000000..f42438ae
--- /dev/null
+++ b/src/FlatSharp.Runtime/IO/SerializationTarget/BigSpanExtensions.cs
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2024 James Courtney
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Buffers;
+using System.Buffers.Binary;
+using System.Runtime.InteropServices;
+
+namespace FlatSharp.Internal;
+
+public static partial class BigSpanExtensions
+{
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool UnsafeReadBool(this BigSpan span, long offset)
+ {
+ return span.UnsafeReadByte(offset) != SerializationHelpers.False;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool UnsafeReadBool(this BigReadOnlySpan span, long offset)
+ {
+ return span.Span.UnsafeReadBool(offset);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void UnsafeWriteBool(this BigSpan span, long offset, bool value)
+ {
+ span.WriteByte(offset, value ? SerializationHelpers.True : SerializationHelpers.False);
+ }
+}
\ No newline at end of file
diff --git a/src/FlatSharp.Runtime/IO/SerializationTarget/BigSpanHelpers.cs b/src/FlatSharp.Runtime/IO/SerializationTarget/BigSpanHelpers.cs
new file mode 100644
index 00000000..04086fb0
--- /dev/null
+++ b/src/FlatSharp.Runtime/IO/SerializationTarget/BigSpanHelpers.cs
@@ -0,0 +1,610 @@
+/*
+ * Copyright 2024 James Courtney
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+using System.Buffers.Binary;
+
+namespace FlatSharp
+{
+
+ public readonly ref partial struct BigSpan
+ {
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public byte ReadByte(long offset)
+ {
+ var value = this.ReadUnaligned(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteByte(long offset, byte value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ this.WriteUnaligned(offset, value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public sbyte ReadSByte(long offset)
+ {
+ var value = this.ReadUnaligned(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteSByte(long offset, sbyte value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ this.WriteUnaligned(offset, value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ushort ReadUShort(long offset)
+ {
+ var value = this.ReadUnaligned(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteUShort(long offset, ushort value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ this.WriteUnaligned(offset, value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public short ReadShort(long offset)
+ {
+ var value = this.ReadUnaligned(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteShort(long offset, short value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ this.WriteUnaligned(offset, value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int ReadInt(long offset)
+ {
+ var value = this.ReadUnaligned(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteInt(long offset, int value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ this.WriteUnaligned(offset, value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public uint ReadUInt(long offset)
+ {
+ var value = this.ReadUnaligned(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteUInt(long offset, uint value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ this.WriteUnaligned(offset, value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public long ReadLong(long offset)
+ {
+ var value = this.ReadUnaligned(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteLong(long offset, long value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ this.WriteUnaligned(offset, value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ulong ReadULong(long offset)
+ {
+ var value = this.ReadUnaligned(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteULong(long offset, ulong value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ this.WriteUnaligned(offset, value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public float ReadFloat(long offset)
+ {
+ var value = this.ReadUnaligned(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteFloat(long offset, float value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = ReverseEndianness(value);
+ }
+
+ this.WriteUnaligned(offset, value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public double ReadDouble(long offset)
+ {
+ var value = this.ReadUnaligned(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteDouble(long offset, double value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = ReverseEndianness(value);
+ }
+
+ this.WriteUnaligned(offset, value);
+ }
+ }
+
+ public readonly ref partial struct BigReadOnlySpan
+ {
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public byte ReadByte(long offset) => this.span.ReadByte(offset);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public sbyte ReadSByte(long offset) => this.span.ReadSByte(offset);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ushort ReadUShort(long offset) => this.span.ReadUShort(offset);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public short ReadShort(long offset) => this.span.ReadShort(offset);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int ReadInt(long offset) => this.span.ReadInt(offset);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public uint ReadUInt(long offset) => this.span.ReadUInt(offset);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public long ReadLong(long offset) => this.span.ReadLong(offset);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ulong ReadULong(long offset) => this.span.ReadULong(offset);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public float ReadFloat(long offset) => this.span.ReadFloat(offset);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public double ReadDouble(long offset) => this.span.ReadDouble(offset);
+ }
+}
+
+namespace FlatSharp.Internal
+{
+ public static partial class BigSpanExtensions
+ {
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static byte UnsafeReadByte(this BigSpan span, long offset)
+ {
+ var value = span.ReadUnalignedUnsafe(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void UnsafeWriteByte(this BigSpan span, long offset, byte value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ span.WriteUnalignedUnsafe(offset, value);
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static byte UnsafeReadByte(this BigReadOnlySpan span, long offset)
+ {
+ return span.Span.UnsafeReadByte(offset);
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static sbyte UnsafeReadSByte(this BigSpan span, long offset)
+ {
+ var value = span.ReadUnalignedUnsafe(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void UnsafeWriteSByte(this BigSpan span, long offset, sbyte value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ span.WriteUnalignedUnsafe(offset, value);
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static sbyte UnsafeReadSByte(this BigReadOnlySpan span, long offset)
+ {
+ return span.Span.UnsafeReadSByte(offset);
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ushort UnsafeReadUShort(this BigSpan span, long offset)
+ {
+ var value = span.ReadUnalignedUnsafe(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void UnsafeWriteUShort(this BigSpan span, long offset, ushort value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ span.WriteUnalignedUnsafe(offset, value);
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ushort UnsafeReadUShort(this BigReadOnlySpan span, long offset)
+ {
+ return span.Span.UnsafeReadUShort(offset);
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static short UnsafeReadShort(this BigSpan span, long offset)
+ {
+ var value = span.ReadUnalignedUnsafe(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void UnsafeWriteShort(this BigSpan span, long offset, short value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ span.WriteUnalignedUnsafe(offset, value);
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static short UnsafeReadShort(this BigReadOnlySpan span, long offset)
+ {
+ return span.Span.UnsafeReadShort(offset);
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int UnsafeReadInt(this BigSpan span, long offset)
+ {
+ var value = span.ReadUnalignedUnsafe(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void UnsafeWriteInt(this BigSpan span, long offset, int value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ span.WriteUnalignedUnsafe(offset, value);
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int UnsafeReadInt(this BigReadOnlySpan span, long offset)
+ {
+ return span.Span.UnsafeReadInt(offset);
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint UnsafeReadUInt(this BigSpan span, long offset)
+ {
+ var value = span.ReadUnalignedUnsafe(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void UnsafeWriteUInt(this BigSpan span, long offset, uint value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ span.WriteUnalignedUnsafe(offset, value);
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint UnsafeReadUInt(this BigReadOnlySpan span, long offset)
+ {
+ return span.Span.UnsafeReadUInt(offset);
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static long UnsafeReadLong(this BigSpan span, long offset)
+ {
+ var value = span.ReadUnalignedUnsafe(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void UnsafeWriteLong(this BigSpan span, long offset, long value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ span.WriteUnalignedUnsafe(offset, value);
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static long UnsafeReadLong(this BigReadOnlySpan span, long offset)
+ {
+ return span.Span.UnsafeReadLong(offset);
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong UnsafeReadULong(this BigSpan span, long offset)
+ {
+ var value = span.ReadUnalignedUnsafe(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void UnsafeWriteULong(this BigSpan span, long offset, ulong value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BinaryPrimitives.ReverseEndianness(value);
+ }
+
+ span.WriteUnalignedUnsafe(offset, value);
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong UnsafeReadULong(this BigReadOnlySpan span, long offset)
+ {
+ return span.Span.UnsafeReadULong(offset);
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float UnsafeReadFloat(this BigSpan span, long offset)
+ {
+ var value = span.ReadUnalignedUnsafe(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BigSpan.ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void UnsafeWriteFloat(this BigSpan span, long offset, float value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BigSpan.ReverseEndianness(value);
+ }
+
+ span.WriteUnalignedUnsafe(offset, value);
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float UnsafeReadFloat(this BigReadOnlySpan span, long offset)
+ {
+ return span.Span.UnsafeReadFloat(offset);
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double UnsafeReadDouble(this BigSpan span, long offset)
+ {
+ var value = span.ReadUnalignedUnsafe(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BigSpan.ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void UnsafeWriteDouble(this BigSpan span, long offset, double value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = BigSpan.ReverseEndianness(value);
+ }
+
+ span.WriteUnalignedUnsafe(offset, value);
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double UnsafeReadDouble(this BigReadOnlySpan span, long offset)
+ {
+ return span.Span.UnsafeReadDouble(offset);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/FlatSharp.Runtime/IO/SerializationTarget/BigSpanHelpers.tt b/src/FlatSharp.Runtime/IO/SerializationTarget/BigSpanHelpers.tt
new file mode 100644
index 00000000..26a8bc5f
--- /dev/null
+++ b/src/FlatSharp.Runtime/IO/SerializationTarget/BigSpanHelpers.tt
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2024 James Courtney
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+<#@ template debug="false" hostspecific="false" language="C#" #>
+<#@ assembly name="System.Core" #>
+<#@ import namespace="System" #>
+<#@ import namespace="System.Linq" #>
+<#@ import namespace="System.Text" #>
+<#@ import namespace="System.Collections.Generic" #>
+<#@ output extension=".cs" #>
+
+<#
+ (string casedName, string typeName, bool useBinaryPrimitives)[] types =
+ {
+ ("Byte", "byte", true),
+ ("SByte", "sbyte", true),
+ ("UShort", "ushort", true),
+ ("Short", "short", true),
+ ("Int", "int", true),
+ ("UInt", "uint", true),
+ ("Long", "long", true),
+ ("ULong", "ulong", true),
+ ("Float", "float", false),
+ ("Double", "double", false),
+ };
+#>
+
+using System.Buffers.Binary;
+
+namespace FlatSharp
+{
+
+ public readonly ref partial struct BigSpan
+ {
+
+ <#
+ foreach (var tuple in types)
+ {
+ string casedName = tuple.casedName;
+ string typeName = tuple.typeName;
+ string bp = tuple.useBinaryPrimitives ? "BinaryPrimitives." : "";
+ #>
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public <#= typeName #> Read<#=casedName#>(long offset)
+ {
+ var value = this.ReadUnaligned<<#=typeName#>>(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = <#= bp #>ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Write<#=casedName#>(long offset, <#= typeName #> value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = <#=bp#>ReverseEndianness(value);
+ }
+
+ this.WriteUnaligned(offset, value);
+ }
+ <#
+ }
+ #>
+ }
+
+ public readonly ref partial struct BigReadOnlySpan
+ {
+
+ <#
+ foreach (var tuple in types)
+ {
+ string casedName = tuple.casedName;
+ string typeName = tuple.typeName;
+ string bp = tuple.useBinaryPrimitives ? "BinaryPrimitives." : "";
+ #>
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public <#= typeName #> Read<#=casedName#>(long offset) => this.span.Read<#=casedName#>(offset);
+ <#
+ }
+ #>
+ }
+}
+
+namespace FlatSharp.Internal
+{
+ public static partial class BigSpanExtensions
+ {
+ <#
+ foreach (var tuple in types)
+ {
+ string casedName = tuple.casedName;
+ string typeName = tuple.typeName;
+ string bp = tuple.useBinaryPrimitives ? "BinaryPrimitives." : "BigSpan.";
+ #>
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static <#=typeName#> UnsafeRead<#=casedName#>(this BigSpan span, long offset)
+ {
+ var value = span.ReadUnalignedUnsafe<<#=typeName#>>(offset);
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = <#=bp#>ReverseEndianness(value);
+ }
+
+ return value;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void UnsafeWrite<#=casedName#>(this BigSpan span, long offset, <#=typeName#> value)
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ value = <#=bp#>ReverseEndianness(value);
+ }
+
+ span.WriteUnalignedUnsafe<<#=typeName#>>(offset, value);
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static <#=typeName#> UnsafeRead<#=casedName#>(this BigReadOnlySpan span, long offset)
+ {
+ return span.Span.UnsafeRead<#=casedName#>(offset);
+ }
+
+ <#
+ }
+ #>
+ }
+}
\ No newline at end of file
diff --git a/src/FlatSharp.Runtime/IO/SharedStringWriter.cs b/src/FlatSharp.Runtime/IO/SharedStringWriter.cs
index 03f326a4..58636755 100644
--- a/src/FlatSharp.Runtime/IO/SharedStringWriter.cs
+++ b/src/FlatSharp.Runtime/IO/SharedStringWriter.cs
@@ -61,7 +61,7 @@ public void Reset()
if (entry.Offsets == null)
{
- entry.Offsets = new List();
+ entry.Offsets = new List();
}
entry.Offsets.Clear();
@@ -73,12 +73,11 @@ public void Reset()
///
/// Writes a shared string.
///
- public void WriteSharedString(
- TSpanWriter spanWriter,
- Span data,
- int offset,
+ public void WriteSharedString(
+ BigSpan target,
+ long offset,
string value,
- SerializationContext context) where TSpanWriter : ISpanWriter
+ SerializationContext context)
{
// Find the associative set that must contain our key.
var cache = this.sharedStringOffsetCache;
@@ -95,7 +94,7 @@ public void WriteSharedString(
string? sharedString = line.String;
if (sharedString is not null)
{
- FlushSharedString(spanWriter, data, sharedString, offsets, context);
+ FlushSharedString(target, sharedString, offsets, context);
}
line.String = value;
@@ -107,7 +106,9 @@ public void WriteSharedString(
///
/// Flush any pending writes.
///
- public void FlushWrites(TSpanWriter writer, Span data, SerializationContext context) where TSpanWriter : ISpanWriter
+ public void FlushWrites(
+ BigSpan target,
+ SerializationContext context)
{
var cache = this.sharedStringOffsetCache;
for (int i = 0; i < cache.Length; ++i)
@@ -117,7 +118,7 @@ public void FlushWrites(TSpanWriter writer, Span data, Serial
if (str is not null)
{
- FlushSharedString(writer, data, str, item.Offsets, context);
+ FlushSharedString(target, str, item.Offsets, context);
item.String = null;
}
@@ -128,18 +129,17 @@ public void FlushWrites(TSpanWriter writer, Span data, Serial
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void FlushSharedString(
- TSpanWriter spanWriter,
- Span span,
+ private static void FlushSharedString(
+ BigSpan target,
string value,
- List offsets,
- SerializationContext context) where TSpanWriter : ISpanWriter
+ List offsets,
+ SerializationContext context)
{
- int stringOffset = spanWriter.WriteAndProvisionString(span, value, context);
+ long stringOffset = target.WriteAndProvisionString(value, context);
int count = offsets.Count;
for (int i = 0; i < count; ++i)
{
- spanWriter.WriteUOffset(span, offsets[i], stringOffset);
+ target.WriteUOffset(offsets[i], stringOffset);
}
offsets.Clear();
@@ -151,6 +151,6 @@ private struct WriteCacheEntry
// The string
public string? String;
- public List Offsets;
+ public List Offsets;
}
}
diff --git a/src/FlatSharp.Runtime/IO/SpanWriter.cs b/src/FlatSharp.Runtime/IO/SpanWriter.cs
deleted file mode 100644
index 304a6596..00000000
--- a/src/FlatSharp.Runtime/IO/SpanWriter.cs
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright 2020 James Courtney
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-using System.Buffers;
-using System.Buffers.Binary;
-using System.Text;
-
-namespace FlatSharp;
-
-///
-/// Utility class for writing items to spans.
-///
-public struct SpanWriter : ISpanWriter
-{
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteByte(Span span, byte value, int offset)
- {
- span[offset] = value;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteSByte(Span span, sbyte value, int offset)
- {
- span[offset] = (byte)value;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteUShort(Span span, ushort value, int offset)
- {
- this.CheckAlignment(offset, sizeof(ushort));
- BinaryPrimitives.WriteUInt16LittleEndian(span.Slice(offset), value);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteShort(Span span, short value, int offset)
- {
- this.CheckAlignment(offset, sizeof(short));
- BinaryPrimitives.WriteInt16LittleEndian(span.Slice(offset), value);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteUInt(Span span, uint value, int offset)
- {
- this.CheckAlignment(offset, sizeof(uint));
- BinaryPrimitives.WriteUInt32LittleEndian(span.Slice(offset), value);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteInt(Span span, int value, int offset)
- {
- this.CheckAlignment(offset, sizeof(int));
- BinaryPrimitives.WriteInt32LittleEndian(span.Slice(offset), value);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteULong(Span span, ulong value, int offset)
- {
- this.CheckAlignment(offset, sizeof(ulong));
- BinaryPrimitives.WriteUInt64LittleEndian(span.Slice(offset), value);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteLong(Span span, long value, int offset)
- {
- this.CheckAlignment(offset, sizeof(long));
- BinaryPrimitives.WriteInt64LittleEndian(span.Slice(offset), value);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteFloat(Span span, float value, int offset)
- {
- ScalarSpanReader.FloatLayout floatLayout = new ScalarSpanReader.FloatLayout
- {
- value = value
- };
-
- this.WriteUInt(span, floatLayout.bytes, offset);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void WriteDouble(Span span, double value, int offset)
- {
- this.WriteLong(span, BitConverter.DoubleToInt64Bits(value), offset);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public int GetStringBytes(Span destination, string value, Encoding encoding)
- {
-#if NETSTANDARD2_0
- int length = value.Length;
- byte[] buffer = ArrayPool.Shared.Rent(encoding.GetMaxByteCount(length));
- int bytesWritten = encoding.GetBytes(value, 0, length, buffer, 0);
- buffer.AsSpan().Slice(0, bytesWritten).CopyTo(destination);
- ArrayPool.Shared.Return(buffer);
-#else
- int bytesWritten = encoding.GetBytes(value, destination);
-#endif
-
- return bytesWritten;
- }
-
- public void FlushSharedStrings(ISharedStringWriter writer, Span destination, SerializationContext context)
- {
- writer.FlushWrites(this, destination, context);
- }
-}
diff --git a/src/FlatSharp.Runtime/IO/SpanWriterExtensions.cs b/src/FlatSharp.Runtime/IO/SpanWriterExtensions.cs
deleted file mode 100644
index ac506470..00000000
--- a/src/FlatSharp.Runtime/IO/SpanWriterExtensions.cs
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright 2024 James Courtney
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-using System.Runtime.InteropServices;
-
-namespace FlatSharp.Internal;
-
-///
-/// Extension methods that apply to all implementations.
-///
-public static class SpanWriterExtensions
-{
- public static void WriteReadOnlyByteMemoryBlock(
- this TSpanWriter spanWriter,
- Span span,
- ReadOnlyMemory memory,
- int offset,
- SerializationContext ctx) where TSpanWriter : ISpanWriter
- {
- int numberOfItems = memory.Length;
- int vectorStartOffset = ctx.AllocateVector(itemAlignment: sizeof(byte), numberOfItems, sizePerItem: sizeof(byte));
-
- spanWriter.WriteUOffset(span, offset, vectorStartOffset);
- spanWriter.WriteInt(span, numberOfItems, vectorStartOffset);
-
- memory.Span.CopyTo(span.Slice(vectorStartOffset + sizeof(uint)));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void UnsafeWriteSpan(
- this TSpanWriter spanWriter,
- Span span,
- Span buffer,
- int offset,
- int alignment,
- SerializationContext ctx) where TSpanWriter : ISpanWriter where TElement : unmanaged
- {
- // Since we are copying bytes here, only LE is supported.
- FlatSharpInternal.AssertLittleEndian();
- FlatSharpInternal.AssertWellAligned(alignment);
-
- int numberOfItems = buffer.Length;
- int vectorStartOffset = ctx.AllocateVector(
- itemAlignment: alignment,
- numberOfItems,
- sizePerItem: Unsafe.SizeOf());
-
- spanWriter.WriteUOffset(span, offset, vectorStartOffset);
- spanWriter.WriteInt(span, numberOfItems, vectorStartOffset);
-
- var start = span.Slice(vectorStartOffset + sizeof(uint), checked(numberOfItems * Unsafe.SizeOf()));
-
- MemoryMarshal.Cast(buffer).CopyTo(start);
- }
-
- ///
- /// Writes the given string.
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void WriteString(
- this TSpanWriter spanWriter,
- Span span,
- string value,
- int offset,
- SerializationContext context) where TSpanWriter : ISpanWriter
- {
- int stringOffset = spanWriter.WriteAndProvisionString(span, value, context);
- spanWriter.WriteUOffset(span, offset, stringOffset);
- }
-
- ///
- /// Writes the string to the buffer, returning the absolute offset of the string.
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int WriteAndProvisionString(this TSpanWriter spanWriter, Span span, string value, SerializationContext context)
- where TSpanWriter : ISpanWriter
- {
- var encoding = SerializationHelpers.Encoding;
-
- // Allocate more than we need and then give back what we don't use.
- int maxItems = encoding.GetMaxByteCount(value.Length) + 1;
- int stringStartOffset = context.AllocateVector(sizeof(byte), maxItems, sizeof(byte));
-
- int bytesWritten = spanWriter.GetStringBytes(span.Slice(stringStartOffset + sizeof(uint), maxItems), value, encoding);
-
- // null teriminator
- span[stringStartOffset + bytesWritten + sizeof(uint)] = 0;
-
- // write length
- spanWriter.WriteInt(span, bytesWritten, stringStartOffset);
-
- // give back unused space. Account for null terminator.
- context.Offset -= maxItems - (bytesWritten + 1);
-
- return stringStartOffset;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void WriteUOffset(this TSpanWriter spanWriter, Span span, int offset, int secondOffset)
- where TSpanWriter : ISpanWriter
- {
- checked
- {
- uint uoffset = (uint)(secondOffset - offset);
- spanWriter.WriteUInt(span, uoffset, offset);
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void WriteBool(this TSpanWriter spanWriter, Span span, bool b, int offset)
- where TSpanWriter : ISpanWriter
- {
- spanWriter.WriteByte(span, b ? SerializationHelpers.True : SerializationHelpers.False, offset);
- }
-
- [ExcludeFromCodeCoverage]
- [Conditional("DEBUG")]
- public static void CheckAlignment(this TSpanWriter spanWriter, int offset, int size) where TSpanWriter : ISpanWriter
- {
-#if DEBUG
- if (offset % size != 0)
- {
- FSThrow.InvalidOperation($"BugCheck: attempted to read unaligned data at index: {offset}, expected alignment: {size}");
- }
-#endif
- }
-}
diff --git a/src/FlatSharp.Runtime/IPostSerializeAction.cs b/src/FlatSharp.Runtime/IPostSerializeAction.cs
new file mode 100644
index 00000000..3c1b0346
--- /dev/null
+++ b/src/FlatSharp.Runtime/IPostSerializeAction.cs
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2024 James Courtney
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace FlatSharp.Internal;
+
+///
+/// Describes an action that can be invoked after a serialize operation.
+///
+public interface IPostSerializeAction
+{
+ void Invoke(BigSpan destination, SerializationContext context);
+}
\ No newline at end of file
diff --git a/src/FlatSharp.Runtime/ISerializer.cs b/src/FlatSharp.Runtime/ISerializer.cs
index a86b45aa..e2918bf8 100644
--- a/src/FlatSharp.Runtime/ISerializer.cs
+++ b/src/FlatSharp.Runtime/ISerializer.cs
@@ -36,16 +36,15 @@ public interface ISerializer
///
/// Writes the given item to the buffer using the given spanwriter.
///
- /// The span writer.
- /// The span to write to.
+ /// The destination.
/// The object to serialize.
/// The number of bytes written.
- int Write(TSpanWriter writer, Span destination, object item) where TSpanWriter : ISpanWriter;
+ long Write(BigSpan target, object item);
///
/// Computes the maximum size necessary to serialize the given instance.
///
- int GetMaxSize(object item);
+ long GetMaxSize(object item);
///
/// Parses the given buffer as an instance of this ISerializer's type.
@@ -74,16 +73,15 @@ public interface ISerializer
///
/// Writes the given item to the buffer using the given spanwriter.
///
- /// The span writer.
- /// The span to write to.
+ /// The destination.
/// The object to serialize.
/// The number of bytes written.
- int Write(TSpanWriter writer, Span destination, T item) where TSpanWriter : ISpanWriter;
+ long Write(BigSpan destination, T item);
///
/// Computes the maximum size necessary to serialize the given instance of .
///
- int GetMaxSize(T item);
+ long GetMaxSize(T item);
///
/// Parses the given buffer as an instance of .
diff --git a/src/FlatSharp.Runtime/ISerializerExtensions.cs b/src/FlatSharp.Runtime/ISerializerExtensions.cs
index 39b9ba4b..fe5bb823 100644
--- a/src/FlatSharp.Runtime/ISerializerExtensions.cs
+++ b/src/FlatSharp.Runtime/ISerializerExtensions.cs
@@ -116,9 +116,9 @@ public static object Parse(this ISerializer serializer, ReadOnlyMemory dat
///
/// The number of bytes written.
[ExcludeFromCodeCoverage] // Just a helper
- public static int Write(this ISerializer serializer, byte[] buffer, T item) where T : class
+ public static long Write(this ISerializer serializer, byte[] buffer, T item) where T : class
{
- return Write(serializer, buffer.AsSpan(), item);
+ return serializer.Write(new BigSpan(buffer.AsSpan()), item);
}
///
@@ -126,9 +126,9 @@ public static int Write(this ISerializer serializer, byte[] buffer, T item
///
/// The number of bytes written.
[ExcludeFromCodeCoverage] // Just a helper
- public static int Write(this ISerializer serializer, byte[] buffer, object item)
+ public static long Write(this ISerializer serializer, byte[] buffer, object item)
{
- return Write(serializer, buffer.AsSpan(), item);
+ return serializer.Write(new BigSpan(buffer.AsSpan()), item);
}
///
@@ -136,9 +136,9 @@ public static int Write(this ISerializer serializer, byte[] buffer, object item)
///
/// The number of bytes written.
[ExcludeFromCodeCoverage] // Just a helper
- public static int Write(this ISerializer serializer, ArraySegment buffer, T item) where T : class
+ public static long Write(this ISerializer serializer, ArraySegment buffer, T item) where T : class
{
- return Write(serializer, buffer.AsSpan(), item);
+ return serializer.Write(new BigSpan(buffer.AsSpan()), item);
}
///
@@ -146,9 +146,9 @@ public static int Write(this ISerializer serializer, ArraySegment bu
///
/// The number of bytes written.
[ExcludeFromCodeCoverage] // Just a helper
- public static int Write(this ISerializer serializer, ArraySegment buffer, object item)
+ public static long Write(this ISerializer serializer, ArraySegment buffer, object item)
{
- return Write(serializer, buffer.AsSpan(), item);
+ return serializer.Write(new BigSpan(buffer.AsSpan()), item);
}
///
@@ -156,9 +156,9 @@ public static int Write(this ISerializer serializer, ArraySegment buffer,
///
/// The number of bytes written.
[ExcludeFromCodeCoverage] // Just a helper
- public static int Write(this ISerializer serializer, Memory buffer, T item) where T : class
+ public static long Write(this ISerializer serializer, Memory buffer, T item) where T : class
{
- return Write(serializer, buffer.Span, item);
+ return serializer.Write(new BigSpan(buffer.Span), item);
}
///
@@ -166,9 +166,9 @@ public static int Write(this ISerializer serializer, Memory buffer,
///
/// The number of bytes written.
[ExcludeFromCodeCoverage] // Just a helper
- public static int Write(this ISerializer serializer, Memory buffer, object item)
+ public static long Write(this ISerializer serializer, Memory buffer, object item)
{
- return Write(serializer, buffer.Span, item);
+ return serializer.Write(new BigSpan(buffer.Span), item);
}
///
@@ -176,9 +176,9 @@ public static int Write(this ISerializer serializer, Memory buffer, object
///
/// The number of bytes written.
[ExcludeFromCodeCoverage] // Just a helper
- public static int Write(this ISerializer serializer, Span