Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix user defined struct array serialization failure #235

Merged
merged 1 commit into from
Jul 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 83 additions & 1 deletion src/Hyperion.Tests/CollectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -616,5 +616,87 @@ public override ValueSerializer BuildSerializer(Serializer serializer, Type type
return os;
}
}
}

[Fact]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New tests look good

public void CanSerializeNullableIntArray()
{
SerializeAndAssert(new int?[]{1, 2, 3, 4, 5});
}

[Fact]
public void CanSerializeLongArray()
{
SerializeAndAssert(new []{1L, 2L, 3L, 4L, 5L});
}

[Fact]
public void CanSerializeNullableLongArray()
{
SerializeAndAssert(new long?[]{1L, 2L, 3L, 4L, 5L});
}

[Fact]
public void CanSerializeShortArray()
{
SerializeAndAssert(new short[]{1, 2, 3, 4, 5});
}

[Fact]
public void CanSerializeNullableShortArray()
{
SerializeAndAssert(new short?[]{1, 2, 3, 4, 5});
}

[Fact]
public void CanSerializeStringArray()
{
SerializeAndAssert(new []{"1", "2", "3", "4", "5"});
}

[Fact]
public void CanSerializeDateTimeOffsetArray()
{
SerializeAndAssert(new []
{
DateTimeOffset.Now,
DateTimeOffset.UtcNow,
});
}

[Fact]
public void CanSerializeStructArray()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the new spec for the failing serialization.

{
SerializeAndAssert(new IStructInterface[]
{
new PrimitiveStruct {Int = 1, Long = 1, String = "1"},
new PrimitiveStruct {Int = 2, Long = 2, String = "2"},
new PrimitiveStruct {Int = 3, Long = 3, String = "3"},
new PrimitiveStruct {Int = 4, Long = 4, String = "4"},
new PrimitiveStruct {Int = 5, Long = 5, String = "5"},
});
}

private interface IStructInterface
{
int Int { get; set; }
long Long { get; set; }
string String { get; set; }
}

private struct PrimitiveStruct : IStructInterface
{
public int Int { get; set; }
public long Long { get; set; }
public string String { get; set; }
}

private void SerializeAndAssert<T>(T[] expected)
{
Serialize(expected);
Reset();
var res = Deserialize<T[]>();
Assert.Equal(expected, res);
AssertMemoryStreamConsumed();
}
}
}
16 changes: 10 additions & 6 deletions src/Hyperion/SerializerFactories/ArraySerializerFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
#endregion

using System;
using System.Collections;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using Hyperion.Extensions;
using Hyperion.ValueSerializers;

Expand All @@ -21,7 +23,7 @@ internal sealed class ArraySerializerFactory : ValueSerializerFactory

public override bool CanDeserialize(Serializer serializer, Type type) => CanSerialize(serializer, type);

private static void WriteValues<T>(T[] array, Stream stream, Type elementType, ValueSerializer elementSerializer, SerializerSession session)
private static void WriteValues(Array array, Stream stream, Type elementType, ValueSerializer elementSerializer, SerializerSession session)
{
Int32Serializer.WriteValueImpl(stream, array.Length, session);
var preserveObjectReferences = session.Serializer.Options.PreserveObjectReferences;
Expand All @@ -30,12 +32,13 @@ private static void WriteValues<T>(T[] array, Stream stream, Type elementType, V
stream.WriteObject(value, elementType, elementSerializer, preserveObjectReferences, session);
}
}
private static void ReadValues<T>(int length, Stream stream, DeserializerSession session, T[] array)

private static void ReadValues(int length, Stream stream, DeserializerSession session, Array array)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to change generic type to untyped Array array. Generic typed array does not play nice with user defined structs.

{
for (var i = 0; i < length; i++)
{
var value = (T)stream.ReadObject(session);
array[i] = value;
var value = stream.ReadObject(session);
array.SetValue(value, i);
}
}

Expand All @@ -57,7 +60,7 @@ public override ValueSerializer BuildSerializer(Serializer serializer, Type type
session.TrackDeserializedObject(array);
}

ReadValues(length, stream, session, (dynamic)array);
ReadValues(length, stream, session, array);

return array;
};
Expand All @@ -68,7 +71,8 @@ public override ValueSerializer BuildSerializer(Serializer serializer, Type type
session.TrackSerializedObject(arr);
}

WriteValues((dynamic)arr, stream, elementType, elementSerializer, session);
// This janky way of converting array to Array is done to get around the problem of ValueType arrays
WriteValues(((IEnumerable)arr).Cast<object>().ToArray(), stream, elementType, elementSerializer, session);
Copy link
Contributor Author

@Arkatufus Arkatufus Jul 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only way to convert an array of user defined struct object into an Array is to cast it as an IEnumerable and then cast all of its elements individually into objects. This is done to convert an array of values into an array of reference objects so that it conforms to the generated IL code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is less janky that casting to a dynamic type

};
arraySerializer.Initialize(reader, writer);

Expand Down