Cysharp / MemoryPack

Zero encoding extreme performance binary serializer for C# and Unity.
MIT License
3.29k stars 193 forks source link

Nullable Float and Double values are serialized incorrectly by the TypeScript serializer methods. #309

Open synapticnoise opened 2 months ago

synapticnoise commented 2 months ago

This can be demonstrated with a simple test class:

[MemoryPackable]
[GenerateTypeScript]
public partial class SimpleNullableTest
{
    public float FloatValue { get; set; }
    public float? NullableFloatValue { get; set; }
    public double DoubleValue { get; set; }
    public double? NullableDoubleValue { get; set; }
}

In TypeScript, when SimpleNullableTest.serialize() is called and the resultant bytes are deserialized in C# via MemoryPackSerializer.Deserialize<SimpleNullableTest>(), both NullableFloatValue and NullableDoubleValue are null regardless of their values on the TS side.

I've had a look at the bytes written by both the C# and TS sides. For nullable values, two values are written: HasValueFlag, Value. If HasValueFlag is 0, it's null, otherwise the value is Value. It looks like both HasValueFlag and Value must be the same width in bits, written little-endian.

On the C# side, for float and double, it appears to write and expect Int32 and Int64 respectively for HasValueFlag. However, the TS side writes float and double for HasValueFlag respectively. When those flags are interpreted by the C# side, their value, while non-zero, is not 1, thus the deserialized nullable value is always null.

This was observed in version 1.21.1 with a C# target framework of net8.0.

synapticnoise commented 2 months ago

I've created a pull request with a potential fix for this issue. https://github.com/Cysharp/MemoryPack/pull/313