Skip to content

Commit

Permalink
Ensure NativeArray serialization occurs only on aligned types.
Browse files Browse the repository at this point in the history
  • Loading branch information
joncham committed Dec 1, 2022
1 parent 4484b89 commit c2e8ca5
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 4 deletions.
12 changes: 9 additions & 3 deletions src/FlatSharp.Runtime/IO/SpanWriterExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,19 @@ public static void UnsafeWriteSpan<TSpanWriter, TElement>(
Span<byte> span,
Span<TElement> buffer,
int offset,
SerializationContext ctx) where TSpanWriter : ISpanWriter where TElement : struct
int alignment,
SerializationContext ctx) where TSpanWriter : ISpanWriter where TElement : unmanaged
{
var size = Unsafe.SizeOf<TElement>();

if (size % alignment != 0)
throw new InvalidOperationException($"Unsafe Span serialization does not support types with size {size} that is not a multiple of alignment {alignment}.");

int numberOfItems = buffer.Length;
int vectorStartOffset = ctx.AllocateVector(
itemAlignment: Unsafe.SizeOf<TElement>(),
itemAlignment: alignment,
numberOfItems,
sizePerItem: Unsafe.SizeOf<TElement>());
sizePerItem: size);

spanWriter.WriteUOffset(span, offset, vectorStartOffset);
spanWriter.WriteInt(span, numberOfItems, vectorStartOffset);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public override CodeGeneratedMethod CreateParseMethodBody(ParserCodeGenContext c
public override CodeGeneratedMethod CreateSerializeMethodBody(SerializationCodeGenContext context)
{
var writeNativeArray = $"{context.SpanWriterVariableName}.UnsafeWriteSpan({context.SpanVariableName}, {context.ValueVariableName}.AsSpan(), {context.OffsetVariableName}, " +
$"{context.SerializationContextVariableName});";
$"{ItemTypeModel.PhysicalLayout[0].Alignment}, {context.SerializationContextVariableName});";

return new CodeGeneratedMethod(writeNativeArray);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,67 @@ public void UnalignedStruct_Value5Byte()
Assert.True(expectedResult.AsSpan().SequenceEqual(target));
}

[Fact]
public void UnalignedStruct_Value5Byte_NativeArray()
{
var root = new RootTable<NativeArray<ValueFiveByteStruct>>
{
Vector = new NativeArray<ValueFiveByteStruct>(new[]
{
new ValueFiveByteStruct { Byte = 1, Int = 1 },
new ValueFiveByteStruct { Byte = 2, Int = 2 },
new ValueFiveByteStruct { Byte = 3, Int = 3 },
}, Allocator.Temp)
};

Assert.Throws<InvalidOperationException>(() =>
{
Span<byte> target = new byte[10240];
FlatBufferSerializer.DefaultWithUnitySupport.Serialize(root, target);
});
}

[Fact]
public void AlignedStruct_Value5Byte_NativeArray()
{
var root = new RootTable<NativeArray<ValueFiveByteStructWithPadding>>
{
Vector = new NativeArray<ValueFiveByteStructWithPadding>(new[]
{
new ValueFiveByteStructWithPadding { Byte = 1, Int = 1 },
new ValueFiveByteStructWithPadding { Byte = 2, Int = 2 },
new ValueFiveByteStructWithPadding { Byte = 3, Int = 3 },
}, Allocator.Temp)
};

Span<byte> target = new byte[10240];
int offset = FlatBufferSerializer.DefaultWithUnitySupport.Serialize(root, target);
target = target.Slice(0, offset);

byte[] expectedResult =
{
4, 0, 0, 0, // offset to table start
248, 255, 255, 255, // soffset to vtable (-8)
12, 0, 0, 0, // uoffset_t to vector
6, 0, // vtable length
8, 0, // table length
4, 0, // offset of index 0 field
0, 0, // padding to 4-byte alignment
3, 0, 0, 0, // vector length
1, 0, 0, 0, // index 0.Int
1, // index 0.Byte
0, 0, 0, // padding
2, 0, 0, 0, // index 1.Int
2, // index 1.Byte
0, 0, 0, // padding
3, 0, 0, 0, // index2.Int
3, // Index2.byte
0, 0, 0, // padding
};

Assert.True(expectedResult.AsSpan().SequenceEqual(target));
}

[Fact]
public void UnalignedStruct_9Byte()
{
Expand Down Expand Up @@ -840,4 +901,12 @@ public struct ValueFiveByteStruct

[FieldOffset(4)] public byte Byte;
}

[FlatBufferStruct, StructLayout(LayoutKind.Explicit)]
public struct ValueFiveByteStructWithPadding
{
[FieldOffset(0)] public int Int;

[FieldOffset(4)] public byte Byte;
}
}

0 comments on commit c2e8ca5

Please sign in to comment.