Move more tests! (#351)
This commit is contained in:
Родитель
fb060c8d0f
Коммит
0bc08b9ee3
|
@ -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,42 +390,34 @@ 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);
|
||||
|
||||
Assert.Equal(rootList.Count, vector.Count);
|
||||
|
||||
if (rootList.Count > 0)
|
||||
{
|
||||
var parsed = RootTableReadOnly.Serializer.Parse(data, option);
|
||||
TValue previous = vector[0];
|
||||
|
||||
IReadOnlyList<TValue> vector = getList(parsed);
|
||||
|
||||
Assert.Equal(rootList.Count, vector.Count);
|
||||
|
||||
if (rootList.Count > 0)
|
||||
for (int i = 0; i < rootList.Count; ++i)
|
||||
{
|
||||
TValue previous = vector[0];
|
||||
|
||||
for (int i = 0; i < rootList.Count; ++i)
|
||||
{
|
||||
var item = vector[i];
|
||||
Assert.True(comparer.Compare(getKey(previous), getKey(item)) <= 0);
|
||||
previous = item;
|
||||
}
|
||||
|
||||
foreach (var originalItem in rootList)
|
||||
{
|
||||
var item = vector.BinarySearchByFlatBufferKey(getKey(originalItem));
|
||||
Assert.NotNull(item);
|
||||
Assert.Equal(getKey(originalItem).ToString(), getKey(item).ToString());
|
||||
}
|
||||
var item = vector[i];
|
||||
Assert.True(comparer.Compare(getKey(previous), getKey(item)) <= 0);
|
||||
previous = item;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (FlatBufferDeserializationOption mode in Enum.GetValues(typeof(FlatBufferDeserializationOption)))
|
||||
{
|
||||
RunTest(mode);
|
||||
foreach (var originalItem in rootList)
|
||||
{
|
||||
var item = vector.BinarySearchByFlatBufferKey(getKey(originalItem));
|
||||
Assert.NotNull(item);
|
||||
Assert.Equal(getKey(originalItem).ToString(), getKey(item).ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SortedVectorTest<TKey, TValue>(
|
||||
FlatBufferDeserializationOption option,
|
||||
RootTable root,
|
||||
Func<RootTable, IList<TValue>> getList,
|
||||
Func<TValue, TKey> getKey,
|
||||
|
@ -380,38 +427,29 @@ 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);
|
||||
|
||||
Assert.Equal(rootList.Count, vector.Count);
|
||||
|
||||
if (rootList.Count > 0)
|
||||
{
|
||||
var parsed = RootTable.Serializer.Parse(data, option);
|
||||
TValue previous = vector[0];
|
||||
|
||||
IList<TValue> vector = getList(parsed);
|
||||
|
||||
Assert.Equal(rootList.Count, vector.Count);
|
||||
|
||||
if (rootList.Count > 0)
|
||||
for (int i = 0; i < rootList.Count; ++i)
|
||||
{
|
||||
TValue previous = vector[0];
|
||||
|
||||
for (int i = 0; i < rootList.Count; ++i)
|
||||
{
|
||||
var item = vector[i];
|
||||
Assert.True(comparer.Compare(getKey(previous), getKey(item)) <= 0);
|
||||
previous = item;
|
||||
}
|
||||
|
||||
foreach (var originalItem in rootList)
|
||||
{
|
||||
var item = vector.BinarySearchByFlatBufferKey(getKey(originalItem));
|
||||
Assert.NotNull(item);
|
||||
Assert.Equal(getKey(originalItem).ToString(), getKey(item).ToString());
|
||||
}
|
||||
var item = vector[i];
|
||||
Assert.True(comparer.Compare(getKey(previous), getKey(item)) <= 0);
|
||||
previous = item;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (FlatBufferDeserializationOption mode in Enum.GetValues(typeof(FlatBufferDeserializationOption)))
|
||||
{
|
||||
RunTest(mode);
|
||||
foreach (var originalItem in rootList)
|
||||
{
|
||||
var item = vector.BinarySearchByFlatBufferKey(getKey(originalItem));
|
||||
Assert.NotNull(item);
|
||||
Assert.Equal(getKey(originalItem).ToString(), getKey(item).ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
Загрузка…
Ссылка в новой задаче