This commit is contained in:
James Courtney 2022-11-26 04:38:38 -08:00 коммит произвёл GitHub
Родитель fb060c8d0f
Коммит 0bc08b9ee3
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
22 изменённых файлов: 1289 добавлений и 1007 удалений

Просмотреть файл

@ -17,7 +17,7 @@
namespace FlatSharpEndToEndTests.ClassLib.FlatBufferVectorOfUnionTests;
/// <summary>
/// Tests for the FlatBufferVector class that implements IList.
/// Tests for FlatSharp's IList of Union.
/// </summary>
public class FlatBufferVectorOfUnionTests

Просмотреть файл

@ -19,7 +19,7 @@ using System.Linq;
namespace FlatSharpEndToEndTests.ClassLib.FlatBufferVectorTests;
/// <summary>
/// Tests for the FlatBufferVector class that implements IList.
/// Tests for FlatSharp's IList implementations.
/// </summary>
public class FlatBufferVectorTests
{

Просмотреть файл

@ -0,0 +1,222 @@
/*
* 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 FlatSharp.Internal;
using System.IO;
namespace FlatSharpEndToEndTests.ClassLib.SerializerConfigurationTests;
public class SerializerConfigurationTests
{
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void UseDeserializationMode(FlatBufferDeserializationOption option)
{
var item = new Root { StringVector = new string[] { "a", "b", "c" } };
ISerializer<Root> serializer = Root.Serializer.WithSettings(opt => opt.UseDeserializationMode(option));
Root parsed = item.SerializeAndParse(serializer);
IFlatBufferDeserializedObject obj = parsed as IFlatBufferDeserializedObject;
Assert.Equal(option, obj.DeserializationContext.DeserializationOption);
}
[Fact]
public void UseLazyDeserialization()
{
var item = new Root { StringVector = new string[] { "a", "b", "c" } };
ISerializer<Root> serializer = Root.Serializer.WithSettings(opt => opt.UseLazyDeserialization());
Root parsed = item.SerializeAndParse(serializer);
IFlatBufferDeserializedObject obj = parsed as IFlatBufferDeserializedObject;
Assert.Equal(FlatBufferDeserializationOption.Lazy, obj.DeserializationContext.DeserializationOption);
}
[Fact]
public void UseProgressiveDeserialization()
{
var item = new Root { StringVector = new string[] { "a", "b", "c" } };
ISerializer<Root> serializer = Root.Serializer.WithSettings(opt => opt.UseProgressiveDeserialization());
Root parsed = item.SerializeAndParse(serializer);
IFlatBufferDeserializedObject obj = parsed as IFlatBufferDeserializedObject;
Assert.Equal(FlatBufferDeserializationOption.Progressive, obj.DeserializationContext.DeserializationOption);
}
[Fact]
public void UseGreedyDeserialization()
{
var item = new Root { StringVector = new string[] { "a", "b", "c" } };
ISerializer<Root> serializer = Root.Serializer.WithSettings(opt => opt.UseGreedyDeserialization());
Root parsed = item.SerializeAndParse(serializer);
IFlatBufferDeserializedObject obj = parsed as IFlatBufferDeserializedObject;
Assert.Equal(FlatBufferDeserializationOption.Greedy, obj.DeserializationContext.DeserializationOption);
}
[Fact]
public void UseGreedyMutableDeserialization()
{
var item = new Root { StringVector = new string[] { "a", "b", "c" } };
ISerializer<Root> serializer = Root.Serializer.WithSettings(opt => opt.UseGreedyMutableDeserialization());
Root parsed = item.SerializeAndParse(serializer);
IFlatBufferDeserializedObject obj = parsed as IFlatBufferDeserializedObject;
Assert.Equal(FlatBufferDeserializationOption.GreedyMutable, obj.DeserializationContext.DeserializationOption);
}
[Theory]
[InlineData(FlatBufferDeserializationOption.Lazy, false, false)]
[InlineData(FlatBufferDeserializationOption.Lazy, true, true)]
[InlineData(FlatBufferDeserializationOption.Progressive, false, false)]
[InlineData(FlatBufferDeserializationOption.Progressive, true, true)]
[InlineData(FlatBufferDeserializationOption.Greedy, false, false)]
[InlineData(FlatBufferDeserializationOption.Greedy, true, false)]
[InlineData(FlatBufferDeserializationOption.GreedyMutable, false, false)]
[InlineData(FlatBufferDeserializationOption.GreedyMutable, true, false)]
public void UseMemoryCopySerialization(FlatBufferDeserializationOption option, bool enableMemCopy, bool expectMemCopy)
{
Root t = new() { StringVector = new[] { "A", "b", "c", } };
var compiled = Root.Serializer.WithSettings(s => s.UseMemoryCopySerialization(enableMemCopy).UseDeserializationMode(option));
// overallocate
byte[] data = new byte[1024];
int maxBytes = compiled.GetMaxSize(t);
Assert.Equal(88, maxBytes);
int actualBytes = compiled.Write(data, t);
Assert.Equal(58, actualBytes);
// First test: Parse the array but don't trim the buffer. This causes the underlying
// buffer to be much larger than the actual data.
var parsed = compiled.Parse(data);
byte[] data2 = new byte[2048];
int bytesWritten = compiled.Write(data2, parsed);
if (expectMemCopy)
{
Assert.Equal(1024, bytesWritten); // We use mem copy serialization here, and we gave it 1024 bytes when we parsed. So we get 1024 bytes back out.
Assert.Equal(1024, compiled.GetMaxSize(parsed));
Assert.Throws<BufferTooSmallException>(() => compiled.Write(new byte[maxBytes], parsed));
// Repeat, but now using the trimmed array.
parsed = compiled.Parse(data.AsMemory().Slice(0, actualBytes));
bytesWritten = compiled.Write(data2, parsed);
Assert.Equal(58, bytesWritten);
Assert.Equal(58, compiled.GetMaxSize(parsed));
// Default still returns 88.
Assert.Equal(88, Root.Serializer.GetMaxSize(parsed));
}
else
{
Assert.Equal(58, bytesWritten);
Assert.Equal(88, compiled.GetMaxSize(parsed));
compiled.Write(new byte[maxBytes], parsed);
}
}
[Fact]
public void SharedStringWriters()
{
ISerializer<Root> noSharedStrings = Root.Serializer.WithSettings(opt => opt.DisableSharedStrings());
ISerializer<Root> smallHashSharedStrings = Root.Serializer.WithSettings(opt => opt.UseDefaultSharedStringWriter(1));
ISerializer<Root> largeHashSharedStrings = Root.Serializer.WithSettings(opt => opt.UseDefaultSharedStringWriter());
ISerializer<Root> perfectSharedStrings = Root.Serializer.WithSettings(opt => opt.UseSharedStringWriter<PerfectSharedStringWriter>());
{
Root root = new Root { StringVector = new[] { "a", "b", "a", "b", "a", "b", "a", } };
byte[] notShared = root.AllocateAndSerialize(noSharedStrings);
byte[] smallShared = root.AllocateAndSerialize(smallHashSharedStrings);
byte[] largeShared = root.AllocateAndSerialize(largeHashSharedStrings);
byte[] perfectShared = root.AllocateAndSerialize(perfectSharedStrings);
// Small shared doesn't accomplish anything since it alternates.
Assert.Equal(notShared.Length, smallShared.Length);
Assert.True(largeShared.Length < smallShared.Length);
Assert.Equal(largeShared.Length, perfectShared.Length);
}
{
Root root = new Root { StringVector = new[] { "a", "a", "a", "a", "b", "b", "b", "b" } };
byte[] notShared = root.AllocateAndSerialize(noSharedStrings);
byte[] smallShared = root.AllocateAndSerialize(smallHashSharedStrings);
byte[] largeShared = root.AllocateAndSerialize(largeHashSharedStrings);
byte[] perfectShared = root.AllocateAndSerialize(perfectSharedStrings);
// Small shared doesn't accomplish anything since it alternates.
Assert.True(smallShared.Length < notShared.Length);
Assert.Equal(largeShared.Length, smallShared.Length);
Assert.Equal(largeShared.Length, perfectShared.Length);
}
}
private class PerfectSharedStringWriter : ISharedStringWriter
{
private readonly Dictionary<string, List<int>> offsets = new();
public PerfectSharedStringWriter()
{
}
public bool IsDirty => this.offsets.Count > 0;
public void FlushWrites<TSpanWriter>(TSpanWriter writer, Span<byte> data, SerializationContext context) where TSpanWriter : ISpanWriter
{
foreach (var kvp in this.offsets)
{
string value = kvp.Key;
int stringOffset = writer.WriteAndProvisionString(data, value, context);
for (int i = 0; i < kvp.Value.Count; ++i)
{
writer.WriteUOffset(data, kvp.Value[i], stringOffset);
}
}
this.offsets.Clear();
}
public void Reset()
{
this.offsets.Clear();
}
public void WriteSharedString<TSpanWriter>(
TSpanWriter spanWriter,
Span<byte> data,
int offset,
string value,
SerializationContext context) where TSpanWriter : ISpanWriter
{
if (!this.offsets.TryGetValue(value, out var list))
{
list = new();
this.offsets[value] = list;
}
list.Add(offset);
}
}
}

Просмотреть файл

@ -0,0 +1,14 @@

attribute "fs_serializer";
attribute "fs_unsafeStructVector";
attribute "fs_valueStruct";
attribute "fs_writeThrough";
attribute "fs_vector";
attribute "fs_sharedString";
namespace FlatSharpEndToEndTests.ClassLib.SerializerConfigurationTests;
table Root (fs_serializer)
{
StringVector : [ string ] (fs_sharedString);
}

Просмотреть файл

@ -15,8 +15,9 @@
*/
using FlatSharp.Internal;
using System.IO;
namespace FlatSharpEndToEndTests.VTables;
namespace FlatSharpEndToEndTests.ClassLib.VTables;
public class VTableTests
{
@ -43,6 +44,83 @@ public class VTableTests
[Fact]
public void Test_VTableGeneric() => this.RunTests<VTableGeneric>(255, VTableGeneric.Create);
[Fact]
public void InitializeVTable_NormalTable()
{
byte[] buffer =
{
8, 0, // vtable length
12, 0, // table length
4, 0, // index 0 offset
8, 0, // index 1 offset
8, 0, 0, 0, // soffset to vtable.
1, 0, 0, 0,
2, 0, 0, 0,
};
new ArrayInputBuffer(buffer).InitializeVTable(
8,
out int vtableOffset,
out nuint fieldCount,
out ReadOnlySpan<byte> fieldData);
Assert.Equal(0, vtableOffset);
Assert.Equal((nuint)2, fieldCount);
Assert.Equal(4, fieldData.Length);
}
[Fact]
public void InitializeVTable_EmptyTable()
{
byte[] buffer =
{
4, 0, // vtable length
4, 0, // table length
4, 0, 0, 0 // soffset to vtable.
};
new ArrayInputBuffer(buffer).InitializeVTable(
4,
out int vtableOffset,
out nuint fieldCount,
out ReadOnlySpan<byte> fieldData);
Assert.Equal(0, vtableOffset);
Assert.Equal((nuint)0, fieldCount);
Assert.Equal(0, fieldData.Length);
}
[Fact]
public void InitializeVTable_InvalidLength()
{
byte[] buffer =
{
3, 0, // vtable length
4, 0, // table length
4, 0, 0, 0 // soffset to vtable.
};
var ex = Assert.Throws<InvalidDataException>(() =>
new ArrayInputBuffer(buffer).InitializeVTable(4, out _, out _, out _));
Assert.Equal(
"FlatBuffer was in an invalid format: VTable was not long enough to be valid.",
ex.Message);
}
[Fact]
public void ReadUOffset()
{
byte[] buffer = { 4, 0, 0, 0 };
Assert.Equal(4, new ArrayInputBuffer(buffer).ReadUOffset(0));
buffer = new byte[] { 3, 0, 0, 0 };
var ex = Assert.Throws<InvalidDataException>(() => new ArrayInputBuffer(buffer).ReadUOffset(0));
Assert.Equal(
"FlatBuffer was in an invalid format: Decoded uoffset_t had value less than 4. Value = 3",
ex.Message);
}
private void RunTests<TVTable>(int expectedMaxIndex, CreateCallback<TVTable> callback)
where TVTable : struct, IVTable
{

Просмотреть файл

@ -31,17 +31,37 @@ public static class Helpers
public static byte[] AllocateAndSerialize<T>(this T item) where T : class, IFlatBufferSerializable<T>
{
byte[] data = new byte[item.Serializer.GetMaxSize(item)];
int bytesWritten = item.Serializer.Write(data, item);
return item.AllocateAndSerialize(item.Serializer);
}
public static byte[] AllocateAndSerialize<T>(this T item, ISerializer<T> serializer) where T : class, IFlatBufferSerializable<T>
{
byte[] data = new byte[serializer.GetMaxSize(item)];
int bytesWritten = serializer.Write(data, item);
return data.AsMemory().Slice(0, bytesWritten).ToArray();
}
public static T SerializeAndParse<T>(this T item) where T : class, IFlatBufferSerializable<T>
{
return SerializeAndParse<T>(item, item.Serializer);
}
public static T SerializeAndParse<T>(
this T item,
FlatBufferDeserializationOption? option = null) where T : class, IFlatBufferSerializable<T>
FlatBufferDeserializationOption? option) where T : class, IFlatBufferSerializable<T>
{
byte[] buffer = item.AllocateAndSerialize();
return item.Serializer.Parse(buffer, option);
}
public static T SerializeAndParse<T>(
this T item,
ISerializer<T>? serializer) where T : class, IFlatBufferSerializable<T>
{
serializer ??= item.Serializer;
byte[] buffer = item.AllocateAndSerialize();
return serializer.Parse(buffer);
}
}

Просмотреть файл

@ -130,83 +130,6 @@ public class InputBufferTests
(s, b) => s.Parse(new ArraySegment<byte>(b)));
}
[Fact]
public void InitializeVTable_EmptyTable()
{
byte[] buffer =
{
4, 0, // vtable length
4, 0, // table length
4, 0, 0, 0 // soffset to vtable.
};
new ArrayInputBuffer(buffer).InitializeVTable(
4,
out int vtableOffset,
out nuint fieldCount,
out ReadOnlySpan<byte> fieldData);
Assert.Equal(0, vtableOffset);
Assert.Equal((nuint)0, fieldCount);
Assert.Equal(0, fieldData.Length);
}
[Fact]
public void InitializeVTable_InvalidLength()
{
byte[] buffer =
{
3, 0, // vtable length
4, 0, // table length
4, 0, 0, 0 // soffset to vtable.
};
var ex = Assert.Throws<InvalidDataException>(() =>
new ArrayInputBuffer(buffer).InitializeVTable(4, out _, out _, out _));
Assert.Equal(
"FlatBuffer was in an invalid format: VTable was not long enough to be valid.",
ex.Message);
}
[Fact]
public void InitializeVTable_NormalTable()
{
byte[] buffer =
{
8, 0, // vtable length
12, 0, // table length
4, 0, // index 0 offset
8, 0, // index 1 offset
8, 0, 0, 0, // soffset to vtable.
1, 0, 0, 0,
2, 0, 0, 0,
};
new ArrayInputBuffer(buffer).InitializeVTable(
8,
out int vtableOffset,
out nuint fieldCount,
out ReadOnlySpan<byte> fieldData);
Assert.Equal(0, vtableOffset);
Assert.Equal((nuint)2, fieldCount);
Assert.Equal(4, fieldData.Length);
}
[Fact]
public void ReadUOffset()
{
byte[] buffer = { 4, 0, 0, 0 };
Assert.Equal(4, new ArrayInputBuffer(buffer).ReadUOffset(0));
buffer = new byte[] { 3, 0, 0, 0 };
var ex = Assert.Throws<InvalidDataException>(() => new ArrayInputBuffer(buffer).ReadUOffset(0));
Assert.Equal(
"FlatBuffer was in an invalid format: Decoded uoffset_t had value less than 4. Value = 3",
ex.Message);
}
private void TableSerializationTest(
ISpanWriter writer,
Func<ISerializer<PrimitiveTypesTable>, byte[], PrimitiveTypesTable> parse)

Просмотреть файл

@ -0,0 +1,47 @@
/*
* 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.
*/
attribute "fs_vector";
attribute "fs_serializer";
attribute "fs_sortedVector";
attribute "fs_sharedString";
namespace FlatSharpEndToEndTests.RawData;
table SharedStringTable (fs_serializer)
{
a : string (fs_sharedString);
b : string (fs_sharedString);
c : string (fs_sharedString);
vector : [ string ] (fs_sharedString);
}
table EmptyTable (fs_serializer) { }
struct SimpleStruct
{
Long : long;
Byte : ubyte;
Uint : uint;
}
table SimpleTable (fs_serializer)
{
String : string;
struct : SimpleStruct;
struct_vector : [ SimpleStruct ];
inner_table : SimpleTable;
}

Просмотреть файл

@ -0,0 +1,222 @@
/*
* 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 FlatSharpEndToEndTests.RawData;
public class RawDataSharedStringTests
{
[Fact]
public void SharedStrings_Vector_Disabled()
{
var t = new SharedStringTable
{
Vector = new List<string> { "string", "string", "string" },
};
var noSharedStrings = SharedStringTable.Serializer.WithSettings(opt => opt.DisableSharedStrings());
byte[] buffer = t.AllocateAndSerialize(noSharedStrings);
byte[] expectedBytes = new byte[]
{
4, 0, 0, 0, // offset to table start
248, 255, 255, 255, // soffset to vtable.
16, 0, 0, 0, // uoffset to vector
12, 0, 8, 0, // vtable length, table length
0, 0, // vtable[0]
0, 0, // vtable[1]
0, 0, // vtable[2]
4, 0, // vtable[3]
3, 0, 0, 0, // vector length
12, 0, 0, 0, // uffset to first item
20, 0, 0, 0, // uoffset to second item
28, 0, 0, 0, // uoffset to third item
6, 0, 0, 0, // string length
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0, 0, // null terminator + padding
6, 0, 0, 0,
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0, 0, // null terminator + padding
6, 0, 0, 0,
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0, // null terminator
};
Assert.True(expectedBytes.AsSpan().SequenceEqual(buffer));
}
[Fact]
public void SharedStrings_Table_Disabled()
{
var t = new SharedStringTable
{
A = "string",
B = "string",
C = "string",
};
var noSharedStrings = SharedStringTable.Serializer.WithSettings(opt => opt.DisableSharedStrings());
byte[] buffer = t.AllocateAndSerialize(noSharedStrings);
byte[] expectedBytes = new byte[]
{
4, 0, 0, 0, // offset to table start
240, 255, 255, 255, // soffset to vtable.
24, 0, 0, 0, // uoffset to string 1
32, 0, 0 ,0, // uoffset to string 2
40, 0, 0, 0, // uoffset to string 3
10, 0, 16, 0, // vtable length, table length
12, 0, 8, 0, // vtable(2), vtable(1)
4, 0, 0, 0, // vtable(1), padding
6, 0, 0, 0, // string length
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0, 0, // null terminator.
6, 0, 0, 0,
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0, 0, // null terminator.
6, 0, 0, 0,
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0, // null terminator.
};
Assert.True(expectedBytes.AsSpan().SequenceEqual(buffer));
}
[Fact]
public void SharedStrings_Table()
{
var t = new SharedStringTable
{
A = "string",
B = "string",
C = "string",
};
byte[] buffer = t.AllocateAndSerialize();
byte[] expectedBytes = new byte[]
{
4, 0, 0, 0, // offset to table start
240, 255, 255, 255, // soffset to vtable.
24, 0, 0, 0, // uoffset to string 1
20, 0, 0 ,0, // uoffset to string 2
16, 0, 0, 0, // uoffset to string 3
10, 0, 16, 0, // vtable length, table length
12, 0, 8, 0, // vtable(2), vtable(1)
4, 0, 0, 0, // vtable(1), padding
6, 0, 0, 0, // string length
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0, // null terminator.
};
Assert.True(expectedBytes.AsSpan().SequenceEqual(buffer));
}
[Fact]
public void SharedStrings_Table_WithNull()
{
var t = new SharedStringTable
{
A = null,
B = "string",
C = null,
};
byte[] buffer = t.AllocateAndSerialize();
byte[] expectedBytes = new byte[]
{
4, 0, 0, 0, // offset to table start
248, 255, 255, 255, // soffset to vtable.
12, 0, 0 ,0, // uoffset to string
8, 0, 8, 0, // vtable length, table length
0, 0, 4, 0, // vtable(0), vtable(1)
6, 0, 0, 0, // string length
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0 // null terminator.
};
Assert.True(expectedBytes.AsSpan().SequenceEqual(buffer));
}
[Fact]
public void SharedStrings_Table_WithEviction()
{
var t = new SharedStringTable
{
A = "string",
B = "foo",
C = "string",
};
var serializer = SharedStringTable.Serializer.WithSettings(s => s.UseDefaultSharedStringWriter(1));
byte[] buffer = t.AllocateAndSerialize(serializer);
byte[] expectedBytes = new byte[]
{
4, 0, 0, 0, // offset to table start
240, 255, 255, 255, // soffset to vtable.
24, 0, 0, 0, // uoffset to string 1
32, 0, 0 ,0, // uoffset to string 2
36, 0, 0, 0, // uoffset to string 3
10, 0, 16, 0, // vtable length, table length
12, 0, 8, 0, // vtable(0), vtable(1)
4, 0, 0, 0, // vtable(2), padding
6, 0, 0, 0, // string0 length
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0, 0, // null terminator + 1 byte padding
3, 0, 0, 0, // string1 length
(byte)'f', (byte)'o', (byte)'o', 0, // string1 + null terminator
6, 0, 0, 0,
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0 // null terminator
};
Assert.True(expectedBytes.AsSpan().SequenceEqual(buffer));
}
[Fact]
public void SharedStrings_Vector()
{
var t = new SharedStringTable
{
Vector = new List<string> { "string", "string", "string" },
};
byte[] buffer = t.AllocateAndSerialize();
byte[] expectedBytes = new byte[]
{
4, 0, 0, 0, // offset to table start
248, 255, 255, 255, // soffset to vtable.
16, 0, 0, 0, // uoffset to vector
12, 0, 8, 0, // vtable length, table length
0, 0, // vtable[0]
0, 0, // vtable[1]
0, 0, // vtable[2]
4, 0, // vtable[3]
3, 0, 0, 0, // vector length
12, 0, 0, 0, // uffset to first item
8, 0, 0, 0, // uoffset to second item
4, 0, 0, 0, // uoffset to third item
6, 0, 0, 0, // string length
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0,
};
Assert.True(expectedBytes.AsSpan().SequenceEqual(buffer));
}
}

Просмотреть файл

@ -0,0 +1,133 @@
/*
* 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 FlatSharpEndToEndTests.RawData;
using StringTable = FlatSharpEndToEndTests.TableMembers.StringTable;
public class RawDataStringTests
{
[Fact]
public void EmptyString()
{
var root = new StringTable
{
ItemStandard = string.Empty,
};
byte[] data = root.AllocateAndSerialize();
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 string
6, 0, // vtable length
8, 0, // table length
4, 0, // offset of index 0 field
0, 0, // padding to 4-byte alignment
0, 0, 0, 0, // vector length
0, // null terminator (special case for strings).
};
Assert.True(expectedResult.AsSpan().SequenceEqual(data));
}
[Fact]
public void DeprecatedString()
{
var root = new StringTable
{
ItemDeprecated = string.Empty,
};
byte[] data = root.AllocateAndSerialize();
byte[] expectedResult =
{
4, 0, 0, 0, // offset to table start
252, 255, 255, 255, // soffset to vtable (-4)
4, 0, // vtable length
4, 0, // table length
};
Assert.True(expectedResult.AsSpan().SequenceEqual(data));
}
[Fact]
public void SimpleString()
{
var root = new StringTable
{
ItemStandard = new string(new char[] { (char)1, (char)2, (char)3 }),
};
byte[] target = root.AllocateAndSerialize();
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, 2, 3, 0, // data + null terminator (special case for string vectors).
};
Assert.True(expectedResult.AsSpan().SequenceEqual(target));
}
[Fact]
public void StringVector()
{
var root = new StringTable
{
ItemDeprecated = "notnull",
ItemVectorImplicit = new[] { "abc_", "def", "ghi" }
};
byte[] data = root.AllocateAndSerialize();
byte[] expectedResult =
{
4, 0, 0, 0, // offset to table start
248, 255, 255, 255, // soffset to vtable (-8)
16, 0, 0, 0, // uoffset_t to vector
10, 0, // vtable length
8, 0, // table length
0, 0, // offset of index 0 field
0, 0, // offset to index 1 field (deprecated)
4, 0, // offset to index 2 field (the vector)
0, 0, // padding to 4-byte alignment
3, 0, 0, 0, // vector length
12, 0, 0, 0, // offset to index 0
20, 0, 0, 0, // offset to index 1
24, 0, 0, 0, // offset to index 2
4, 0, 0 ,0, // length of string 0
(byte)'a', (byte)'b', (byte)'c', (byte)'_', 0, // string 0
0, 0, 0, // padding
3, 0, 0 ,0, // length of string 1
(byte)'d', (byte)'e', (byte)'f', 0, // string 1
3, 0, 0 ,0, // length of string 1
(byte)'g', (byte)'h', (byte)'i', 0, // string 2
};
Assert.True(expectedResult.AsSpan().SequenceEqual(data));
}
}

Просмотреть файл

@ -0,0 +1,115 @@
/*
* 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 FlatSharpEndToEndTests.RawData;
public class RawDataTableTests
{
[Fact]
public void AllMembersNull()
{
SimpleTable table = new SimpleTable();
byte[] buffer = table.AllocateAndSerialize();
byte[] expectedData =
{
4, 0, 0, 0,
252, 255, 255, 255,
4, 0,
4, 0,
};
Assert.True(expectedData.AsSpan().SequenceEqual(buffer));
}
[Fact]
public void TableWithStruct()
{
SimpleTable table = new SimpleTable
{
Struct = new SimpleStruct { Byte = 1, Long = 2, Uint = 3 }
};
byte[] buffer = table.AllocateAndSerialize();
byte[] expectedData =
{
4, 0, 0, 0, // uoffset to table start
236, 255, 255, 255, // soffet to vtable (4 - x = 24 => x = -20)
2, 0, 0, 0, 0, 0, 0, 0, // struct.long
1, // struct.byte
0, 0, 0, // padding
3, 0, 0, 0, // struct.uint
8, 0, // vtable length
20, 0, // table length
0, 0, // index 0 offset
4, 0, // Index 1 offset
};
Assert.True(expectedData.AsSpan().SequenceEqual(buffer));
}
[Fact]
public void TableWithStructAndString()
{
SimpleTable table = new SimpleTable
{
String = "hi",
Struct = new SimpleStruct { Byte = 1, Long = 2, Uint = 3 }
};
byte[] buffer = table.AllocateAndSerialize();
byte[] expectedData =
{
4, 0, 0, 0, // uoffset to table start
232, 255, 255, 255, // soffet to vtable (4 - x = 24 => x = -20)
2, 0, 0, 0, 0, 0, 0, 0, // struct.long
1, 0, 0, 0, // struct.byte + padding
3, 0, 0, 0, // struct.uint
12, 0, 0, 0, // uoffset to string
8, 0, // vtable length
24, 0, // table length
20, 0, // index 0 offset
4, 0, // Index 1 offset
2, 0, 0, 0, // string length
104, 105, 0, // hi + null terminator
};
Assert.True(expectedData.AsSpan().SequenceEqual(buffer));
}
[Fact]
public void EmptyTable_Serialize_MaxSize()
{
EmptyTable table = new EmptyTable();
byte[] buffer = table.AllocateAndSerialize();
byte[] expectedData =
{
4, 0, 0, 0,
252, 255, 255, 255,
4, 0,
4, 0,
};
Assert.True(expectedData.AsSpan().SequenceEqual(buffer));
int maxSize = EmptyTable.Serializer.GetMaxSize(table);
Assert.Equal(23, maxSize);
}
}

Просмотреть файл

@ -143,6 +143,14 @@ table DoubleTable (fs_serializer)
item_readonly_list : [ double ] (fs_vector:"IReadOnlyList");
}
table StringTable (fs_serializer)
{
item_standard : string;
item_deprecated : string (deprecated);
item_vector_implicit : [ string ];
item_vector_readonly : [ string ] (fs_vector:"IReadOnlyList");
}
table EmptyTable (fs_serializer)
{
}

Просмотреть файл

@ -83,6 +83,47 @@ public partial class TableMemberTests
[ClassData(typeof(DeserializationOptionClassData))]
public void Double(FlatBufferDeserializationOption option) => this.RunTest<double, DoubleTable>(3.14, option);
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void String(FlatBufferDeserializationOption option)
{
{
byte[] emptyTable = new EmptyTable().AllocateAndSerialize();
StringTable parsed = StringTable.Serializer.Parse(emptyTable, option);
Assert.Null(parsed.ItemDeprecated);
Assert.Null(parsed.ItemStandard);
Assert.Null(parsed.ItemVectorImplicit);
Assert.Null(parsed.ItemVectorReadonly);
}
{
StringTable template = new()
{
ItemDeprecated = "deprecated",
ItemStandard = "standard",
ItemVectorImplicit = new[] { "a", "b", },
ItemVectorReadonly = new[] { "c", "d", "e" },
};
StringTable parsed = template.SerializeAndParse(option);
Assert.Null(parsed.ItemDeprecated);
Assert.Equal("standard", parsed.ItemStandard);
IList<string> @implicit = parsed.ItemVectorImplicit;
Assert.Equal(2, @implicit.Count);
Assert.Equal("a", @implicit[0]);
Assert.Equal("b", @implicit[1]);
IReadOnlyList<string> rolist = parsed.ItemVectorReadonly;
Assert.Equal(3, rolist.Count);
Assert.Equal("c", rolist[0]);
Assert.Equal("d", rolist[1]);
Assert.Equal("e", rolist[2]);
}
}
private void RunTest<T, TTable>(T expectedDefault, FlatBufferDeserializationOption option)
where T : struct
where TTable : class, ITypedTable<T>, IFlatBufferSerializable<TTable>, new()

Просмотреть файл

@ -74,7 +74,7 @@ public class ValueStructTestCases
[Fact]
public void Basics()
{
Assert.Equal(148, Unsafe.SizeOf<ValueStruct>());
Assert.Equal(36, Unsafe.SizeOf<ValueStruct>());
}
[Fact]
@ -82,7 +82,7 @@ public class ValueStructTestCases
{
ValueStruct v = default;
Assert.Equal(128, v.D_Length);
Assert.Equal(16, v.D_Length);
for (int i = 0; i < v.D_Length; ++i)
{

Просмотреть файл

@ -39,7 +39,7 @@ struct ValueStruct (fs_valueStruct) {
A : ubyte;
B : int;
C : long;
D : [ubyte : 128];
D : [ ubyte : 16 ];
Inner : InnerValueStruct;
}
@ -50,36 +50,36 @@ struct RefStruct {
struct ValueUnsafeStructVector_Byte (fs_valueStruct) {
GuardLower : ulong;
Vector : [ubyte : 128] (fs_unsafeStructVector);
Vector : [ubyte : 16] (fs_unsafeStructVector);
GuardHigher : ulong;
}
struct ValueUnsafeStructVector_UShort (fs_valueStruct) {
GuardLower : ulong;
Vector : [ushort : 128] (fs_unsafeStructVector);
Vector : [ushort : 16] (fs_unsafeStructVector);
GuardHigher : ulong;
}
struct ValueUnsafeStructVector_UInt (fs_valueStruct) {
GuardLower : ulong;
Vector : [uint : 128] (fs_unsafeStructVector);
Vector : [uint : 16] (fs_unsafeStructVector);
GuardHigher : ulong;
}
struct ValueUnsafeStructVector_ULong (fs_valueStruct) {
GuardLower : ulong;
Vector : [ulong : 128] (fs_unsafeStructVector);
Vector : [ulong : 16] (fs_unsafeStructVector);
GuardHigher : ulong;
}
struct ValueUnsafeStructVector_Float (fs_valueStruct) {
GuardLower : ulong;
Vector : [float : 128] (fs_unsafeStructVector);
Vector : [float : 16] (fs_unsafeStructVector);
GuardHigher : ulong;
}
struct ValueUnsafeStructVector_Double (fs_valueStruct) {
GuardLower : ulong;
Vector : [double : 128] (fs_unsafeStructVector);
Vector : [double : 16] (fs_unsafeStructVector);
GuardHigher : ulong;
}

Просмотреть файл

@ -0,0 +1,210 @@
/*
* 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 FlatSharp.Internal;
using System.Runtime.InteropServices;
namespace FlatSharpEndToEndTests.Vectors.Sorted;
public class IndexedVectorTests
{
private static readonly Random Rng = new();
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void Bool(FlatBufferDeserializationOption opt) => this.IndexedVectorTest<bool, BoolKey>(
opt,
x => x.IndexedVectorOfBool,
(x, v) => x.IndexedVectorOfBool = v,
() => new BoolKey { Key = Rng.Next() % 2 == 0 },
k => k.Key);
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void Byte(FlatBufferDeserializationOption opt) => this.IndexedVectorTest<byte, ByteKey>(
opt,
x => x.IndexedVectorOfByte,
(x, v) => x.IndexedVectorOfByte = v,
GetValueFactory<byte, ByteKey>((k, v) => k.Key = v),
k => k.Key);
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void SByte(FlatBufferDeserializationOption opt) => this.IndexedVectorTest<sbyte, SByteKey>(
opt,
x => x.IndexedVectorOfSbyte,
(x, v) => x.IndexedVectorOfSbyte = v,
GetValueFactory<sbyte, SByteKey>((k, v) => k.Key = v),
k => k.Key);
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void Short(FlatBufferDeserializationOption opt) => this.IndexedVectorTest<short, ShortKey>(
opt,
x => x.IndexedVectorOfShort,
(x, v) => x.IndexedVectorOfShort = v,
GetValueFactory<short, ShortKey>((k, v) => k.Key = v),
k => k.Key);
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void UShort(FlatBufferDeserializationOption opt) => this.IndexedVectorTest<ushort, UShortKey>(
opt,
x => x.IndexedVectorOfUshort,
(x, v) => x.IndexedVectorOfUshort = v,
GetValueFactory<ushort, UShortKey>((k, v) => k.Key = v),
k => k.Key);
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void Int(FlatBufferDeserializationOption opt) => this.IndexedVectorTest<int, IntKey>(
opt,
x => x.IndexedVectorOfInt,
(x, v) => x.IndexedVectorOfInt = v,
GetValueFactory<int, IntKey>((k, v) => k.Key = v),
k => k.Key);
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void UInt(FlatBufferDeserializationOption opt) => this.IndexedVectorTest<uint, UIntKey>(
opt,
x => x.IndexedVectorOfUint,
(x, v) => x.IndexedVectorOfUint = v,
GetValueFactory<uint, UIntKey>((k, v) => k.Key = v),
k => k.Key);
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void Long(FlatBufferDeserializationOption opt) => this.IndexedVectorTest<long, LongKey>(
opt,
x => x.IndexedVectorOfLong,
(x, v) => x.IndexedVectorOfLong = v,
GetValueFactory<long, LongKey>((k, v) => k.Key = v),
k => k.Key);
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void ULong(FlatBufferDeserializationOption opt) => this.IndexedVectorTest<ulong, ULongKey>(
opt,
x => x.IndexedVectorOfUlong,
(x, v) => x.IndexedVectorOfUlong = v,
GetValueFactory<ulong, ULongKey>((k, v) => k.Key = v),
k => k.Key);
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void Float(FlatBufferDeserializationOption opt) => this.IndexedVectorTest<float, FloatKey>(
opt,
x => x.IndexedVectorOfFloat,
(x, v) => x.IndexedVectorOfFloat = v,
GetValueFactory<float, FloatKey>((k, v) => k.Key = v),
k => k.Key);
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void Double(FlatBufferDeserializationOption opt) => this.IndexedVectorTest<double, DoubleKey>(
opt,
x => x.IndexedVectorOfDouble,
(x, v) => x.IndexedVectorOfDouble = v,
GetValueFactory<double, DoubleKey>((k, v) => k.Key = v),
k => k.Key);
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void String(FlatBufferDeserializationOption opt) => this.IndexedVectorTest<string, StringKey>(
opt,
x => x.IndexedVectorOfString,
(x, v) => x.IndexedVectorOfString = v,
() =>
{
int length = Rng.Next(0, 2048);
byte[] data = new byte[length];
Rng.NextBytes(data);
return new StringKey { Key = Convert.ToBase64String(data) };
},
k => k.Key);
private static Func<TValue> GetValueFactory<TKey, TValue>(Action<TValue, TKey> setKey)
where TKey : struct
where TValue : class, ISortableTable<TKey>, new()
{
return () =>
{
byte[] data = new byte[8];
Rng.NextBytes(data);
TKey key = MemoryMarshal.Cast<byte, TKey>(data.AsSpan())[0];
var val = new TValue();
setKey(val, key);
return val;
};
}
private void IndexedVectorTest<TKey, TValue>(
FlatBufferDeserializationOption option,
Func<RootTableIndexed, IIndexedVector<TKey, TValue>> getVector,
Action<RootTableIndexed, IIndexedVector<TKey, TValue>> setVector,
Func<TValue> createValue,
Func<TValue, TKey> getKey)
where TValue : class, ISortableTable<TKey>
{
RootTableIndexed root = new();
HashSet<TKey> knownKeys = new();
IIndexedVector<TKey, TValue> originalVector = new IndexedVector<TKey, TValue>();
for (int i = 0; i < 100; ++i)
{
var toAdd = createValue();
// Some key types may have a small range of values
// such as bool and byte.
if (knownKeys.Add(getKey(toAdd)))
{
originalVector.Add(toAdd);
}
}
setVector(root, originalVector);
byte[] data = root.AllocateAndSerialize();
var parsed = RootTableIndexed.Serializer.Parse(data, option);
IIndexedVector<TKey, TValue> vector = getVector(parsed);
Assert.Equal(knownKeys.Count, vector.Count);
foreach (var key in knownKeys)
{
Assert.True(vector.TryGetValue(key, out TValue value));
Assert.True(vector.ContainsKey(key));
Assert.NotNull(vector[key]);
Assert.NotNull(value);
Assert.Equal(key, getKey(value));
}
for (int i = 0; i < 100; ++i)
{
TKey key = getKey(createValue());
if (!knownKeys.Contains(key))
{
Assert.False(vector.TryGetValue(key, out TValue value));
Assert.Null(value);
Assert.False(vector.ContainsKey(key));
Assert.Throws<KeyNotFoundException>(() => vector[key]);
}
}
}
}

Просмотреть файл

@ -21,8 +21,10 @@ namespace FlatSharpEndToEndTests.Vectors.Sorted;
public class SortedVectorTests
{
[Fact]
public void Bool() => this.SortedVectorTest<bool, BoolKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void Bool(FlatBufferDeserializationOption opt) => this.SortedVectorTest<bool, BoolKey>(
opt,
rng => rng.Next() % 2 == 0,
rt => rt.ListVectorOfBool,
(rt, l) => rt.ListVectorOfBool = l,
@ -31,78 +33,100 @@ public class SortedVectorTests
(k, v) => k.Key = v,
Comparer<bool>.Default);
[Fact]
public void Byte() => this.SortedVectorStructTest<byte, ByteKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void Byte(FlatBufferDeserializationOption opt) => this.SortedVectorStructTest<byte, ByteKey>(
opt,
rt => rt.ListVectorOfByte,
(rt, l) => rt.ListVectorOfByte = l,
k => k.Key,
(k, v) => k.Key = v);
[Fact]
public void SByte() => this.SortedVectorStructTest<sbyte, SByteKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void SByte(FlatBufferDeserializationOption opt) => this.SortedVectorStructTest<sbyte, SByteKey>(
opt,
rt => rt.ListVectorOfSbyte,
(rt, l) => rt.ListVectorOfSbyte = l,
k => k.Key,
(k, v) => k.Key = v);
[Fact]
public void Short() => this.SortedVectorStructTest<short, ShortKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void Short(FlatBufferDeserializationOption opt) => this.SortedVectorStructTest<short, ShortKey>(
opt,
rt => rt.ListVectorOfShort,
(rt, l) => rt.ListVectorOfShort = l,
k => k.Key,
(k, v) => k.Key = v);
[Fact]
public void UShort() => this.SortedVectorStructTest<ushort, UShortKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void UShort(FlatBufferDeserializationOption opt) => this.SortedVectorStructTest<ushort, UShortKey>(
opt,
rt => rt.ListVectorOfUshort,
(rt, l) => rt.ListVectorOfUshort = l,
k => k.Key,
(k, v) => k.Key = v);
[Fact]
public void Int() => this.SortedVectorStructTest<int, IntKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void Int(FlatBufferDeserializationOption opt) => this.SortedVectorStructTest<int, IntKey>(
opt,
rt => rt.ListVectorOfInt,
(rt, l) => rt.ListVectorOfInt = l,
k => k.Key,
(k, v) => k.Key = v);
[Fact]
public void UInt() => this.SortedVectorStructTest<uint, UIntKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void UInt(FlatBufferDeserializationOption opt) => this.SortedVectorStructTest<uint, UIntKey>(
opt,
rt => rt.ListVectorOfUint,
(rt, l) => rt.ListVectorOfUint = l,
k => k.Key,
(k, v) => k.Key = v);
[Fact]
public void Long() => this.SortedVectorStructTest<long, LongKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void Long(FlatBufferDeserializationOption opt) => this.SortedVectorStructTest<long, LongKey>(
opt,
rt => rt.ListVectorOfLong,
(rt, l) => rt.ListVectorOfLong = l,
k => k.Key,
(k, v) => k.Key = v);
[Fact]
public void ULong() => this.SortedVectorStructTest<ulong, ULongKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void ULong(FlatBufferDeserializationOption opt) => this.SortedVectorStructTest<ulong, ULongKey>(
opt,
rt => rt.ListVectorOfUlong,
(rt, l) => rt.ListVectorOfUlong = l,
k => k.Key,
(k, v) => k.Key = v);
[Fact]
public void Float() => this.SortedVectorStructTest<float, FloatKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void Float(FlatBufferDeserializationOption opt) => this.SortedVectorStructTest<float, FloatKey>(
opt,
rt => rt.ListVectorOfFloat,
(rt, l) => rt.ListVectorOfFloat = l,
k => k.Key,
(k, v) => k.Key = v);
[Fact]
public void Double() => this.SortedVectorStructTest<double, DoubleKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void Double(FlatBufferDeserializationOption opt) => this.SortedVectorStructTest<double, DoubleKey>(
opt,
rt => rt.ListVectorOfDouble,
(rt, l) => rt.ListVectorOfDouble = l,
k => k.Key,
(k, v) => k.Key = v);
[Fact]
public void String_Base64() => this.SortedVectorTest<string, StringKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void String_Base64(FlatBufferDeserializationOption opt) => this.SortedVectorTest<string, StringKey>(
opt,
rng =>
{
int length = rng.Next(0, 2048);
@ -117,8 +141,10 @@ public class SortedVectorTests
(k, v) => k.Key = v,
new Utf8StringComparer());
[Fact]
public void Bool_ReadOnly() => this.SortedVectorTestReadOnly<bool, BoolKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void Bool_ReadOnly(FlatBufferDeserializationOption opt) => this.SortedVectorTestReadOnly<bool, BoolKey>(
opt,
rng => rng.Next() % 2 == 0,
rt => rt.ListVectorOfBool,
(rt, l) => rt.ListVectorOfBool = l,
@ -127,78 +153,100 @@ public class SortedVectorTests
(k, v) => k.Key = v,
Comparer<bool>.Default);
[Fact]
public void Byte_ReadOnly() => this.SortedVectorStructTestReadOnly<byte, ByteKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void Byte_ReadOnly(FlatBufferDeserializationOption opt) => this.SortedVectorStructTestReadOnly<byte, ByteKey>(
opt,
rt => rt.ListVectorOfByte,
(rt, l) => rt.ListVectorOfByte = l,
k => k.Key,
(k, v) => k.Key = v);
[Fact]
public void SByte_ReadOnly() => this.SortedVectorStructTestReadOnly<sbyte, SByteKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void SByte_ReadOnly(FlatBufferDeserializationOption opt) => this.SortedVectorStructTestReadOnly<sbyte, SByteKey>(
opt,
rt => rt.ListVectorOfSbyte,
(rt, l) => rt.ListVectorOfSbyte = l,
k => k.Key,
(k, v) => k.Key = v);
[Fact]
public void Short_ReadOnly() => this.SortedVectorStructTestReadOnly<short, ShortKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void Short_ReadOnly(FlatBufferDeserializationOption opt) => this.SortedVectorStructTestReadOnly<short, ShortKey>(
opt,
rt => rt.ListVectorOfShort,
(rt, l) => rt.ListVectorOfShort = l,
k => k.Key,
(k, v) => k.Key = v);
[Fact]
public void UShort_ReadOnly() => this.SortedVectorStructTestReadOnly<ushort, UShortKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void UShort_ReadOnly(FlatBufferDeserializationOption opt) => this.SortedVectorStructTestReadOnly<ushort, UShortKey>(
opt,
rt => rt.ListVectorOfUshort,
(rt, l) => rt.ListVectorOfUshort = l,
k => k.Key,
(k, v) => k.Key = v);
[Fact]
public void Int_ReadOnly() => this.SortedVectorStructTestReadOnly<int, IntKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void Int_ReadOnly(FlatBufferDeserializationOption opt) => this.SortedVectorStructTestReadOnly<int, IntKey>(
opt,
rt => rt.ListVectorOfInt,
(rt, l) => rt.ListVectorOfInt = l,
k => k.Key,
(k, v) => k.Key = v);
[Fact]
public void UInt_ReadOnly() => this.SortedVectorStructTestReadOnly<uint, UIntKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void UInt_ReadOnly(FlatBufferDeserializationOption opt) => this.SortedVectorStructTestReadOnly<uint, UIntKey>(
opt,
rt => rt.ListVectorOfUint,
(rt, l) => rt.ListVectorOfUint = l,
k => k.Key,
(k, v) => k.Key = v);
[Fact]
public void Long_ReadOnly() => this.SortedVectorStructTestReadOnly<long, LongKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void Long_ReadOnly(FlatBufferDeserializationOption opt) => this.SortedVectorStructTestReadOnly<long, LongKey>(
opt,
rt => rt.ListVectorOfLong,
(rt, l) => rt.ListVectorOfLong = l,
k => k.Key,
(k, v) => k.Key = v);
[Fact]
public void ULong_ReadOnly() => this.SortedVectorStructTestReadOnly<ulong, ULongKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void ULong_ReadOnly(FlatBufferDeserializationOption opt) => this.SortedVectorStructTestReadOnly<ulong, ULongKey>(
opt,
rt => rt.ListVectorOfUlong,
(rt, l) => rt.ListVectorOfUlong = l,
k => k.Key,
(k, v) => k.Key = v);
[Fact]
public void Float_ReadOnly() => this.SortedVectorStructTestReadOnly<float, FloatKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void Float_ReadOnly(FlatBufferDeserializationOption opt) => this.SortedVectorStructTestReadOnly<float, FloatKey>(
opt,
rt => rt.ListVectorOfFloat,
(rt, l) => rt.ListVectorOfFloat = l,
k => k.Key,
(k, v) => k.Key = v);
[Fact]
public void Double_ReadOnly() => this.SortedVectorStructTestReadOnly<double, DoubleKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void Double_ReadOnly(FlatBufferDeserializationOption opt) => this.SortedVectorStructTestReadOnly<double, DoubleKey>(
opt,
rt => rt.ListVectorOfDouble,
(rt, l) => rt.ListVectorOfDouble = l,
k => k.Key,
(k, v) => k.Key = v);
[Fact]
public void String_Base64_ReadOnly() => this.SortedVectorTestReadOnly<string, StringKey>(
[Theory]
[ClassData(typeof(DeserializationOptionClassData))]
public void String_Base64_ReadOnly(FlatBufferDeserializationOption opt) => this.SortedVectorTestReadOnly<string, StringKey>(
opt,
rng =>
{
int length = rng.Next(0, 2048);
@ -214,6 +262,7 @@ public class SortedVectorTests
new Utf8StringComparer());
private void SortedVectorStructTestReadOnly<TKey, TValue>(
FlatBufferDeserializationOption option,
Func<RootTableReadOnly, IReadOnlyList<TValue>> getList,
Action<RootTableReadOnly, IReadOnlyList<TValue>> setList,
Func<TValue, TKey> getKey,
@ -223,6 +272,7 @@ public class SortedVectorTests
where TKey : struct
{
this.SortedVectorTestReadOnly(
option,
rng =>
{
byte[] data = new byte[8];
@ -239,6 +289,7 @@ public class SortedVectorTests
}
private void SortedVectorStructTest<TKey, TValue>(
FlatBufferDeserializationOption option,
Func<RootTable, IList<TValue>> getList,
Action<RootTable, IList<TValue>> setList,
Func<TValue, TKey> getKey,
@ -248,6 +299,7 @@ public class SortedVectorTests
where TKey : struct
{
this.SortedVectorTest(
option,
rng =>
{
byte[] data = new byte[8];
@ -264,6 +316,7 @@ public class SortedVectorTests
}
private void SortedVectorTestReadOnly<TKey, TValue>(
FlatBufferDeserializationOption opt,
Func<Random, TKey> createKey,
Func<RootTableReadOnly, IReadOnlyList<TValue>> getList,
Action<RootTableReadOnly, IReadOnlyList<TValue>> setList,
@ -290,11 +343,12 @@ public class SortedVectorTests
list.Add(value);
}
this.SortedVectorTestReadOnly(table, getList, getKey, comparer);
this.SortedVectorTestReadOnly(opt, table, getList, getKey, comparer);
}
}
private void SortedVectorTest<TKey, TValue>(
FlatBufferDeserializationOption option,
Func<Random, TKey> createKey,
Func<RootTable, IList<TValue>> getList,
Action<RootTable, IList<TValue>> setList,
@ -321,11 +375,12 @@ public class SortedVectorTests
list.Add(value);
}
this.SortedVectorTest(table, getList, getKey, comparer);
this.SortedVectorTest(option, table, getList, getKey, comparer);
}
}
private void SortedVectorTestReadOnly<TKey, TValue>(
FlatBufferDeserializationOption option,
RootTableReadOnly root,
Func<RootTableReadOnly, IReadOnlyList<TValue>> getList,
Func<TValue, TKey> getKey,
@ -335,9 +390,6 @@ public class SortedVectorTests
IReadOnlyList<TValue> rootList = getList(root);
byte[] data = root.AllocateAndSerialize();
void RunTest(
FlatBufferDeserializationOption option)
{
var parsed = RootTableReadOnly.Serializer.Parse(data, option);
IReadOnlyList<TValue> vector = getList(parsed);
@ -364,13 +416,8 @@ public class SortedVectorTests
}
}
foreach (FlatBufferDeserializationOption mode in Enum.GetValues(typeof(FlatBufferDeserializationOption)))
{
RunTest(mode);
}
}
private void SortedVectorTest<TKey, TValue>(
FlatBufferDeserializationOption option,
RootTable root,
Func<RootTable, IList<TValue>> getList,
Func<TValue, TKey> getKey,
@ -380,9 +427,6 @@ public class SortedVectorTests
IList<TValue> rootList = getList(root);
byte[] data = root.AllocateAndSerialize();
void RunTest(
FlatBufferDeserializationOption option)
{
var parsed = RootTable.Serializer.Parse(data, option);
IList<TValue> vector = getList(parsed);
@ -408,10 +452,4 @@ public class SortedVectorTests
}
}
}
foreach (FlatBufferDeserializationOption mode in Enum.GetValues(typeof(FlatBufferDeserializationOption)))
{
RunTest(mode);
}
}
}

Просмотреть файл

@ -52,6 +52,28 @@ table RootTableReadOnly (fs_serializer)
list_vector_of_string : [ StringKey ] (fs_sortedVector, fs_vector:"IReadOnlyList");
}
table RootTableIndexed (fs_serializer)
{
indexed_vector_of_bool : [ BoolKey ] (fs_vector:"IIndexedVector");
indexed_vector_of_byte : [ ByteKey ] (fs_vector:"IIndexedVector");
indexed_vector_of_sbyte : [ SByteKey ] (fs_vector:"IIndexedVector");
indexed_vector_of_short : [ ShortKey ] (fs_vector:"IIndexedVector");
indexed_vector_of_ushort : [ UShortKey ] (fs_vector:"IIndexedVector");
indexed_vector_of_int : [ IntKey ] (fs_vector:"IIndexedVector");
indexed_vector_of_uint : [ UIntKey ] (fs_vector:"IIndexedVector");
indexed_vector_of_long : [ LongKey ] (fs_vector:"IIndexedVector");
indexed_vector_of_ulong : [ ULongKey ] (fs_vector:"IIndexedVector");
indexed_vector_of_float : [ FloatKey ] (fs_vector:"IIndexedVector");
indexed_vector_of_double : [ DoubleKey ] (fs_vector:"IIndexedVector");
indexed_vector_of_string : [ StringKey ] (fs_vector:"IIndexedVector");
}
table BoolKey { Key : bool (key); }
table ByteKey { Key : ubyte (key); }
table SByteKey { Key : byte (key); }

Просмотреть файл

@ -1,165 +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 FlatSharp;
namespace FlatSharpTests;
/// <summary>
/// Tests serialization using the memory copy shortcut.
/// </summary>
public class MemCopySerializeTests
{
[Fact]
public void GreedyMemoryCopySerialization_NoEffect()
{
TestTable<Struct> t = new()
{
Foo = "foobar",
Struct = new Struct
{
Bar = 12,
}
};
var compiled = FlatBufferSerializer.Default.Compile<TestTable<Struct>>().WithSettings(s => s.UseMemoryCopySerialization());
byte[] data = new byte[1024];
Assert.Equal(70, compiled.GetMaxSize(t));
int actualBytes = compiled.Write(data, t);
// First test: Parse the array but don't trim the buffer. This causes the underlying
// buffer to be much larger than the actual data.
var parsed = compiled.Parse(data);
byte[] data2 = new byte[2048];
int bytesWritten = compiled.Write(data2, parsed);
Assert.Equal(35, actualBytes);
Assert.Equal(35, bytesWritten);
Assert.Equal(70, compiled.GetMaxSize(parsed));
}
[Fact]
public void LazyMemoryCopySerialization()
{
TestTable<WriteThroughStruct> t = new()
{
Foo = "foobar",
Struct = new WriteThroughStruct
{
Bar = 12,
}
};
FlatBufferSerializer serializer = FlatBufferSerializer.Default;
var compiled = serializer.Compile<TestTable<WriteThroughStruct>>().WithSettings(s => s.UseMemoryCopySerialization().UseLazyDeserialization());
byte[] data = new byte[1024];
Assert.Equal(70, compiled.GetMaxSize(t));
int actualBytes = compiled.Write(data, t);
// First test: Parse the array but don't trim the buffer. This causes the underlying
// buffer to be much larger than the actual data.
var parsed = compiled.Parse(data);
byte[] data2 = new byte[2048];
int bytesWritten = compiled.Write(data2, parsed);
Assert.Equal(35, actualBytes);
Assert.Equal(1024, bytesWritten); // We use mem copy serialization here, and we gave it 1024 bytes when we parsed. So we get 1024 bytes back out.
Assert.Equal(1024, compiled.GetMaxSize(parsed));
Assert.Throws<BufferTooSmallException>(() => compiled.Write(new byte[35], parsed));
// Repeat, but now using the trimmed array.
parsed = compiled.Parse(data.AsMemory().Slice(0, actualBytes));
bytesWritten = compiled.Write(data2, parsed);
Assert.Equal(35, actualBytes);
Assert.Equal(35, bytesWritten);
Assert.Equal(35, compiled.GetMaxSize(parsed));
// Default still returns 70.
Assert.Equal(70, serializer.GetMaxSize(parsed));
}
[Fact]
public void ProgressiveMemoryCopySerialization()
{
TestTable<Struct> t = new()
{
Foo = "foobar",
Struct = new Struct
{
Bar = 12,
}
};
FlatBufferSerializer serializer = FlatBufferSerializer.Default;
var compiled = serializer.Compile<TestTable<Struct>>().WithSettings(s => s.UseMemoryCopySerialization().UseProgressiveDeserialization());
byte[] data = new byte[1024];
Assert.Equal(70, compiled.GetMaxSize(t));
int actualBytes = compiled.Write(data, t);
// First test: Parse the array but don't trim the buffer. This causes the underlying
// buffer to be much larger than the actual data.
var parsed = compiled.Parse(data);
byte[] data2 = new byte[2048];
int bytesWritten = compiled.Write(data2, parsed);
Assert.Equal(35, actualBytes);
Assert.Equal(1024, bytesWritten); // We use mem copy serialization here, and we gave it 1024 bytes when we parsed. So we get 1024 bytes back out.
Assert.Equal(1024, compiled.GetMaxSize(parsed));
Assert.Throws<BufferTooSmallException>(() => compiled.Write(new byte[35], parsed));
// Repeat, but now using the trimmed array.
parsed = compiled.Parse(data.AsMemory().Slice(0, actualBytes));
bytesWritten = compiled.Write(data2, parsed);
Assert.Equal(35, actualBytes);
Assert.Equal(35, bytesWritten);
Assert.Equal(35, compiled.GetMaxSize(parsed));
// Default still returns 70.
Assert.Equal(70, serializer.GetMaxSize(parsed));
}
[FlatBufferTable]
public class TestTable<T> where T : IInt
{
[FlatBufferItem(0)]
public virtual string? Foo { get; set; }
[FlatBufferItem(1)]
public virtual T? Struct { get; set; }
}
public interface IInt
{
int Bar { get; set; }
}
[FlatBufferStruct]
public class WriteThroughStruct : IInt
{
[FlatBufferItem(0, WriteThrough = true)]
public virtual int Bar { get; set; }
}
[FlatBufferStruct]
public class Struct : IInt
{
[FlatBufferItem(0)]
public virtual int Bar { get; set; }
}
}

Просмотреть файл

@ -1,355 +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 FlatSharpTests;
/// <summary>
/// Tests default values on a table.
/// </summary>
public class SharedStringTests
{
[Fact]
public void Test_NonSharedStringVector()
{
var t = new RegularStringsVector
{
StringVector = new List<string> { "string", "string", "string" },
};
byte[] destination = new byte[1024];
var serializer = FlatBufferSerializer.Default.Compile<RegularStringsVector>();
int bytesWritten = serializer.Write(default(SpanWriter), destination, t);
byte[] expectedBytes = new byte[]
{
4, 0, 0, 0, // offset to table start
248, 255, 255, 255, // soffset to vtable.
12, 0, 0, 0, // uoffset to vector
6, 0, 8, 0, // vtable length, table length
4, 0, 0, 0, // offset within table to item 0 + alignment
3, 0, 0, 0, // vector length
12, 0, 0, 0, // uffset to first item
20, 0, 0, 0, // uoffset to second item
28, 0, 0, 0, // uoffset to third item
6, 0, 0, 0, // string length
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0, 0, // null terminator + padding
6, 0, 0, 0,
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0, 0, // null terminator + padding
6, 0, 0, 0,
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0, // null terminator
};
Assert.True(expectedBytes.AsSpan().SequenceEqual(destination.AsSpan().Slice(0, bytesWritten)));
}
[Fact]
public void Test_TableNonSharedStrings()
{
var t = new RegularStringsTable
{
String1 = "string",
String2 = "string",
String3 = "string",
};
byte[] destination = new byte[1024];
var serializer = FlatBufferSerializer.Default.Compile<RegularStringsTable>();
int bytesWritten = serializer.Write(default(SpanWriter), destination, t);
byte[] expectedBytes = new byte[]
{
4, 0, 0, 0, // offset to table start
240, 255, 255, 255, // soffset to vtable.
24, 0, 0, 0, // uoffset to string 1
32, 0, 0 ,0, // uoffset to string 2
40, 0, 0, 0, // uoffset to string 3
10, 0, 16, 0, // vtable length, table length
12, 0, 8, 0, // vtable(2), vtable(1)
4, 0, 0, 0, // vtable(1), padding
6, 0, 0, 0, // string length
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0, 0, // null terminator.
6, 0, 0, 0,
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0, 0, // null terminator.
6, 0, 0, 0,
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0, // null terminator.
};
Assert.True(expectedBytes.AsSpan().SequenceEqual(destination.AsSpan().Slice(0, bytesWritten)));
}
[Fact]
public void Test_VectorSharedStrings()
{
var t = new SharedStringsVector
{
StringVector = new List<string> { "string", "string", "string" },
};
byte[] destination = new byte[1024];
int maxBytes = FlatBufferSerializer.Default.GetMaxSize(t);
var serializer = FlatBufferSerializer.Default.Compile<SharedStringsVector>();
int bytesWritten = serializer.Write(default(SpanWriter), destination, t);
Assert.True(bytesWritten <= maxBytes);
byte[] expectedBytes = new byte[]
{
4, 0, 0, 0, // offset to table start
248, 255, 255, 255, // soffset to vtable.
12, 0, 0, 0, // uoffset to vector
6, 0, 8, 0, // vtable length, table length
4, 0, 0, 0, // offset within table to item 0 + alignment
3, 0, 0, 0, // vector length
12, 0, 0, 0, // uffset to first item
8, 0, 0, 0, // uoffset to second item
4, 0, 0, 0, // uoffset to third item
6, 0, 0, 0, // string length
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0 // null terminator.
};
Assert.True(expectedBytes.AsSpan().SequenceEqual(destination.AsSpan().Slice(0, bytesWritten)));
}
[Fact]
public void Test_ISerializerSharedStringSettings()
{
var t = new SharedStringsVector
{
StringVector = new List<string> { "string", "string", "string" },
};
byte[] destination = new byte[1024];
int maxBytes = FlatBufferSerializer.Default.GetMaxSize(t);
var serializer = FlatBufferSerializer.Default.Compile<SharedStringsVector>();
int bytesWritten = serializer.Write(default(SpanWriter), destination, t);
Assert.True(bytesWritten <= maxBytes);
byte[] expectedBytes = new byte[]
{
4, 0, 0, 0, // offset to table start
248, 255, 255, 255, // soffset to vtable.
12, 0, 0, 0, // uoffset to vector
6, 0, 8, 0, // vtable length, table length
4, 0, 0, 0, // offset within table to item 0 + alignment
3, 0, 0, 0, // vector length
12, 0, 0, 0, // uffset to first item
8, 0, 0, 0, // uoffset to second item
4, 0, 0, 0, // uoffset to third item
6, 0, 0, 0, // string length
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0 // null terminator.
};
Assert.True(expectedBytes.AsSpan().SequenceEqual(destination.AsSpan().Slice(0, bytesWritten)));
// Set SharedStringWriter to null. Shared strings remain turned on.
serializer = serializer.WithSettings(s => s.UseSharedStringWriter<SharedStringWriter>());
bytesWritten = serializer.Write(default(SpanWriter), destination, t);
Assert.True(expectedBytes.AsSpan().SequenceEqual(destination.AsSpan().Slice(0, bytesWritten)));
// Set SharedStringWriter to return null. Shared strings turn off.
serializer = serializer.WithSettings(s => s.DisableSharedStrings());
int bytesWrittenBig = serializer.Write(default(SpanWriter), destination, t);
Assert.True(bytesWrittenBig > bytesWritten);
}
[Fact]
public void Test_TableSharedStrings()
{
var t = new SharedStringsTable
{
String1 = "string",
String2 = "string",
String3 = "string",
};
byte[] destination = new byte[1024];
var serializer = FlatBufferSerializer.Default.Compile<SharedStringsTable>();
int bytesWritten = serializer.Write(default(SpanWriter), destination, t);
byte[] expectedBytes = new byte[]
{
4, 0, 0, 0, // offset to table start
240, 255, 255, 255, // soffset to vtable.
24, 0, 0, 0, // uoffset to string 1
20, 0, 0 ,0, // uoffset to string 2
16, 0, 0, 0, // uoffset to string 3
10, 0, 16, 0, // vtable length, table length
12, 0, 8, 0, // vtable(0), vtable(1)
4, 0, 0, 0, // vtable(2), padding
6, 0, 0, 0, // string length
(byte)'s', (byte)'t', (byte)'r', (byte)'i', (byte)'n', (byte)'g',
0 // null terminator.
};
Assert.True(expectedBytes.AsSpan().SequenceEqual(destination.AsSpan().Slice(0, bytesWritten)));
}
[Fact]
public void Test_TableSharedStringsWithNull()
{
var t = new SharedStringsTable
{
String1 = null,
String2 = "string",
String3 = (string)null,
};
byte[] destination = new byte[1024];
var serializer = FlatBufferSerializer.Default.Compile<SharedStringsTable>();
int bytesWritten = serializer.Write(default(SpanWriter), destination, t);
byte[] expectedBytes = new byte[]
{
4, 0, 0, 0, // offset to table start
248, 255, 255, 255, // soffset to vtable.
12, 0, 0 ,0, // uoffset to string
8, 0, 8, 0, // vtable length, table length
0, 0, 4, 0, // vtable(0), vtable(1)
6, 0, 0, 0, // string length
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0 // null terminator.
};
Assert.True(expectedBytes.AsSpan().SequenceEqual(destination.AsSpan().Slice(0, bytesWritten)));
}
/// <summary>
/// Tests with an LRU string writer that can only hold onto one item at a time. Each new string it sees evicts the old one.
/// </summary>
[Fact]
public void Test_TableSharedStringsWithEviction()
{
var t = new SharedStringsTable
{
String1 = "string",
String2 = "foo",
String3 = "string",
};
byte[] destination = new byte[1024];
var serializer = FlatBufferSerializer.Default.Compile<SharedStringsTable>()
.WithSettings(s => s.UseDefaultSharedStringWriter(1));
int bytesWritten = serializer.Write(default(SpanWriter), destination, t);
byte[] expectedBytes = new byte[]
{
4, 0, 0, 0, // offset to table start
240, 255, 255, 255, // soffset to vtable.
24, 0, 0, 0, // uoffset to string 1
32, 0, 0 ,0, // uoffset to string 2
36, 0, 0, 0, // uoffset to string 3
10, 0, 16, 0, // vtable length, table length
12, 0, 8, 0, // vtable(0), vtable(1)
4, 0, 0, 0, // vtable(2), padding
6, 0, 0, 0, // string0 length
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0, 0, // null terminator + 1 byte padding
3, 0, 0, 0, // string1 length
(byte)'f', (byte)'o', (byte)'o', 0, // string1 + null terminator
6, 0, 0, 0,
(byte)'s', (byte)'t', (byte)'r', (byte)'i',
(byte)'n', (byte)'g', 0 // null terminator
};
Assert.True(expectedBytes.AsSpan().SequenceEqual(destination.AsSpan().Slice(0, bytesWritten)));
}
/// <summary>
/// Identical to the above test but with a large enough LRU cache to handle both strings.
/// </summary>
[Fact]
public void Test_TableSharedStringsWithoutEviction()
{
var t = new SharedStringsTable
{
String1 = "string",
String2 = "foo",
String3 = "string",
};
byte[] destination = new byte[1024];
var serializer = FlatBufferSerializer.Default.Compile<SharedStringsTable>();
int bytesWritten = serializer.Write(default(SpanWriter), destination, t);
byte[] stringBytes = Encoding.UTF8.GetBytes("string");
// We can't predict ordering since there is hashing under the hood.
int firstIndex = destination.AsSpan().IndexOf(stringBytes);
int secondIndex = destination.AsSpan().Slice(0, firstIndex + 1).IndexOf(stringBytes);
Assert.Equal(-1, secondIndex);
}
[FlatBufferTable]
public class SharedStringsVector : object
{
[FlatBufferItem(0, SharedString = true)]
public virtual IList<string>? StringVector { get; set; }
}
[FlatBufferTable]
public class RegularStringsVector : object
{
[FlatBufferItem(0)]
public virtual IList<string>? StringVector { get; set; }
}
[FlatBufferTable]
public class SharedStringsTable : object
{
[FlatBufferItem(0, SharedString = true)]
public virtual string? String1 { get; set; }
[FlatBufferItem(1, SharedString = true)]
public virtual string? String2 { get; set; }
[FlatBufferItem(2, SharedString = true)]
public virtual string? String3 { get; set; }
}
[FlatBufferTable]
public class RegularStringsTable : object
{
[FlatBufferItem(0)]
public virtual string? String1 { get; set; }
[FlatBufferItem(1)]
public virtual string? String2 { get; set; }
[FlatBufferItem(2)]
public virtual string? String3 { get; set; }
}
}

Просмотреть файл

@ -25,112 +25,12 @@ namespace FlatSharpTests;
public class TableSerializationTests
{
[Fact]
public void AllMembersNull()
{
SimpleTable table = new SimpleTable();
byte[] buffer = new byte[1024];
byte[] expectedData =
{
4, 0, 0, 0,
252, 255, 255, 255,
4, 0,
4, 0,
};
int bytesWritten = FlatBufferSerializer.Default.Serialize(table, buffer);
Assert.True(expectedData.AsSpan().SequenceEqual(buffer.AsSpan().Slice(0, bytesWritten)));
}
[Fact]
public void RootTableNull()
{
Assert.Throws<ArgumentNullException>(() => FlatBufferSerializer.Default.Serialize<SimpleTable>(null, new byte[1024]));
}
[Fact]
public void TableWithStruct()
{
SimpleTable table = new SimpleTable
{
Struct = new SimpleStruct { Byte = 1, Long = 2, Uint = 3 }
};
byte[] buffer = new byte[1024];
byte[] expectedData =
{
4, 0, 0, 0, // uoffset to table start
236, 255, 255, 255, // soffet to vtable (4 - x = 24 => x = -20)
2, 0, 0, 0, 0, 0, 0, 0, // struct.long
1, // struct.byte
0, 0, 0, // padding
3, 0, 0, 0, // struct.uint
8, 0, // vtable length
20, 0, // table length
0, 0, // index 0 offset
4, 0, // Index 1 offset
};
int bytesWritten = FlatBufferSerializer.Default.Serialize(table, buffer);
Assert.True(expectedData.AsSpan().SequenceEqual(buffer.AsSpan().Slice(0, bytesWritten)));
}
[Fact]
public void TableWithStructAndString()
{
SimpleTable table = new SimpleTable
{
String = "hi",
Struct = new SimpleStruct { Byte = 1, Long = 2, Uint = 3 }
};
byte[] buffer = new byte[1024];
byte[] expectedData =
{
4, 0, 0, 0, // uoffset to table start
232, 255, 255, 255, // soffet to vtable (4 - x = 24 => x = -20)
2, 0, 0, 0, 0, 0, 0, 0, // struct.long
1, 0, 0, 0, // struct.byte + padding
3, 0, 0, 0, // struct.uint
12, 0, 0, 0, // uoffset to string
8, 0, // vtable length
24, 0, // table length
20, 0, // index 0 offset
4, 0, // Index 1 offset
2, 0, 0, 0, // string length
104, 105, 0, // hi + null terminator
};
int bytesWritten = FlatBufferSerializer.Default.Serialize(table, buffer);
Assert.True(expectedData.AsSpan().SequenceEqual(buffer.AsSpan().Slice(0, bytesWritten)));
}
[Fact]
public void EmptyTableSerialize()
{
EmptyTable table = new EmptyTable();
byte[] buffer = new byte[1024];
byte[] expectedData =
{
4, 0, 0, 0,
252, 255, 255, 255,
4, 0,
4, 0,
};
int bytesWritten = FlatBufferSerializer.Default.Serialize(table, buffer);
Assert.True(expectedData.AsSpan().SequenceEqual(buffer.AsSpan().Slice(0, bytesWritten)));
int maxSize = FlatBufferSerializer.Default.GetMaxSize(table);
Assert.Equal(23, maxSize);
}
[Fact]
public void TableParse_NotMutable()
{

Просмотреть файл

@ -29,61 +29,6 @@ public class VectorSerializationTests
{
public class SimpleTests
{
[Fact]
public void EmptyString()
{
var root = new RootTable<string>
{
Vector = string.Empty,
};
Span<byte> target = new byte[10240];
int offset = FlatBufferSerializer.Default.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 string
6, 0, // vtable length
8, 0, // table length
4, 0, // offset of index 0 field
0, 0, // padding to 4-byte alignment
0, 0, 0, 0, // vector length
0, // null terminator (special case for strings).
};
Assert.True(expectedResult.AsSpan().SequenceEqual(target));
}
[Fact]
public void SimpleString()
{
var root = new RootTable<string>
{
Vector = new string(new char[] { (char)1, (char)2, (char)3 }),
};
Span<byte> target = new byte[10240];
int offset = FlatBufferSerializer.Default.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, 2, 3, 0, // data + null terminator (special case for string vectors).
};
Assert.True(expectedResult.AsSpan().SequenceEqual(target));
}
[Fact]
public void Simple_Scalar_Vectors()
@ -536,142 +481,6 @@ public class VectorSerializationTests
}
}
public class IndexedVectorTests
{
[Fact]
public void IndexedVector_Simple()
{
var table = new RootTableSorted<IIndexedVector<string, TableWithKey<string>>>
{
Vector = new IndexedVector<string, TableWithKey<string>>
{
{ new TableWithKey<string> { Key = "a", Value = "AAA" } },
{ new TableWithKey<string> { Key = "b", Value = "BBB" } },
{ new TableWithKey<string> { Key = "c", Value = "CCC" } },
}
};
var serializer = FlatBufferSerializer.Default.Compile<RootTableSorted<IIndexedVector<string, TableWithKey<string>>>>()
.WithSettings(s => s.UseLazyDeserialization());
byte[] data = new byte[1024 * 1024];
serializer.Write(data, table);
var parsed = serializer.Parse(data);
Assert.Equal("AAA", parsed.Vector["a"].Value);
Assert.Equal("BBB", parsed.Vector["b"].Value);
Assert.Equal("CCC", parsed.Vector["c"].Value);
Assert.True(parsed.Vector.TryGetValue("a", out var value) && value.Value == "AAA");
Assert.True(parsed.Vector.TryGetValue("b", out value) && value.Value == "BBB");
Assert.True(parsed.Vector.TryGetValue("c", out value) && value.Value == "CCC");
}
[Fact]
public void IndexedVector_RandomString()
{
var table = new RootTable<IIndexedVector<string, TableWithKey<string>>>
{
Vector = new IndexedVector<string, TableWithKey<string>>()
};
List<string> keys = new List<string>();
for (int i = 0; i < 1000; ++i)
{
string key = Guid.NewGuid().ToString();
keys.Add(key);
table.Vector.AddOrReplace(new TableWithKey<string> { Key = key, Value = Guid.NewGuid().ToString() });
}
byte[] data = new byte[10 * 1024 * 1024];
var serializer = FlatBufferSerializer.Default.Compile<RootTable<IIndexedVector<string, TableWithKey<string>>>>().WithSettings(s => s.UseLazyDeserialization());
int bytesWritten = serializer.Write(data, table);
var parsed = serializer.Parse(data);
foreach (var key in keys)
{
Assert.Equal(table.Vector[key].Value, parsed.Vector[key].Value);
}
}
[Fact]
public void IndexedVector_RandomByte() => IndexedVectorScalarTest<byte>();
[Fact]
public void IndexedVector_RandomSByte() => IndexedVectorScalarTest<sbyte>();
[Fact]
public void IndexedVector_RandomUShort() => IndexedVectorScalarTest<ushort>();
[Fact]
public void IndexedVector_RandomShort() => IndexedVectorScalarTest<short>();
[Fact]
public void IndexedVector_RandomUInt() => IndexedVectorScalarTest<uint>();
[Fact]
public void IndexedVector_RandomInt() => IndexedVectorScalarTest<int>();
[Fact]
public void IndexedVector_RandomULong() => IndexedVectorScalarTest<ulong>();
[Fact]
public void IndexedVector_RandomLong() => IndexedVectorScalarTest<long>();
private void IndexedVectorScalarTest<T>() where T : struct
{
foreach (FlatBufferDeserializationOption option in Enum.GetValues(typeof(FlatBufferDeserializationOption)))
{
var table = new RootTable<IIndexedVector<T, TableWithKey<T>>>
{
Vector = new IndexedVector<T, TableWithKey<T>>()
};
Random r = new Random();
byte[] keyBuffer = new byte[8];
List<T> keys = new List<T>();
for (int i = 0; i < 1000; ++i)
{
r.NextBytes(keyBuffer);
T key = MemoryMarshal.Cast<byte, T>(keyBuffer)[0];
keys.Add(key);
table.Vector.AddOrReplace(new TableWithKey<T> { Key = key, Value = Guid.NewGuid().ToString() });
}
byte[] data = new byte[1024 * 1024];
var serializer = FlatBufferSerializer.Default.Compile<RootTable<IIndexedVector<T, TableWithKey<T>>>>().WithSettings(s => s.UseDeserializationMode(option));
int bytesWritten = serializer.Write(data, table);
var parsed = serializer.Parse<RootTable<IIndexedVector<T, TableWithKey<T>>>>(data);
foreach (var key in keys)
{
Assert.Equal(table.Vector[key].Value, parsed.Vector[key].Value);
}
// verify sorted and that we can read it when it's from a normal vector.
var listSerializer = FlatBufferSerializer.Default.Compile<RootTable<IList<TableWithKey<T>>>>().WithSettings(s => s.UseDeserializationMode(option));
var parsedList = listSerializer.Parse(data);
Assert.Equal(parsed.Vector.Count, parsedList.Vector.Count);
var previous = parsedList.Vector[0];
for (int i = 1; i < parsedList.Vector.Count; ++i)
{
var item = parsedList.Vector[i];
Assert.True(Comparer<T>.Default.Compare(previous.Key, item.Key) <= 0);
Assert.True(parsed.Vector.TryGetValue(item.Key, out var fromDict));
Assert.Equal(item.Key, fromDict.Key);
Assert.Equal(item.Value, fromDict.Value);
previous = item;
}
}
}
}
public class VectorOfUnionTests
{
[Fact]