From 909769cbe3e8d86f31ae4fc58b7092e6cb801794 Mon Sep 17 00:00:00 2001 From: Chris Eckhardt Date: Fri, 1 Nov 2024 22:24:58 -0400 Subject: [PATCH] [Issue 9200] Implement IBaseCodec on CollectionCodec (#9209) * Implement IBaseCodec in CollectionCodec * use Deserialize() in WriteField * Add tests and fix base copier --------- Co-authored-by: Reuben Bond --- .../Codecs/CollectionCodec.cs | 66 +++++++++++++++---- .../BuiltInCodecTests.cs | 50 ++++++++++++++ 2 files changed, 105 insertions(+), 11 deletions(-) diff --git a/src/Orleans.Serialization/Codecs/CollectionCodec.cs b/src/Orleans.Serialization/Codecs/CollectionCodec.cs index 0e7207034c..6265645fa0 100644 --- a/src/Orleans.Serialization/Codecs/CollectionCodec.cs +++ b/src/Orleans.Serialization/Codecs/CollectionCodec.cs @@ -7,6 +7,7 @@ using Orleans.Serialization.Cloning; using Orleans.Serialization.GeneratedCodeHelpers; using Orleans.Serialization.WireProtocol; +using Orleans.Serialization.Serializers; namespace Orleans.Serialization.Codecs; @@ -15,7 +16,7 @@ namespace Orleans.Serialization.Codecs; /// /// The element type. [RegisterSerializer] -public sealed class CollectionCodec : IFieldCodec> +public sealed class CollectionCodec : IFieldCodec>, IBaseCodec> { private readonly Type CodecElementType = typeof(T); @@ -41,16 +42,7 @@ public void WriteField(ref Writer writer, uint fie writer.WriteFieldHeader(fieldIdDelta, expectedType, value.GetType(), WireType.TagDelimited); - if (value.Count > 0) - { - UInt32Codec.WriteField(ref writer, 0, (uint)value.Count); - uint innerFieldIdDelta = 1; - foreach (var element in value) - { - _fieldCodec.WriteField(ref writer, innerFieldIdDelta, CodecElementType, element); - innerFieldIdDelta = 0; - } - } + Serialize(ref writer, value); writer.WriteEndObject(); } @@ -116,6 +108,56 @@ public Collection ReadValue(ref Reader reader, Field field) $"Declared length of {typeof(Collection)}, {length}, is greater than total length of input."); private void ThrowLengthFieldMissing() => throw new RequiredFieldMissingException("Serialized array is missing its length field."); + + public void Serialize(ref Writer writer, Collection value) where TBufferWriter : IBufferWriter + { + if (value.Count > 0) + { + UInt32Codec.WriteField(ref writer, 0, (uint)value.Count); + uint innerFieldIdDelta = 1; + foreach (var element in value) + { + _fieldCodec.WriteField(ref writer, innerFieldIdDelta, CodecElementType, element); + innerFieldIdDelta = 0; + } + } + } + + public void Deserialize(ref Reader reader, Collection value) + { + // If the value has some values added by the constructor, clear them. + // If those values are in the serialized payload, they will be added below. + value.Clear(); + + uint fieldId = 0; + while (true) + { + var header = reader.ReadFieldHeader(); + if (header.IsEndBaseOrEndObject) + { + break; + } + + fieldId += header.FieldIdDelta; + switch (fieldId) + { + case 0: + var length = (int)UInt32Codec.ReadValue(ref reader, header); + if (length > 10240 && length > reader.Length) + { + ThrowInvalidSizeException(length); + } + + break; + case 1: + value.Add(_fieldCodec.ReadValue(ref reader, header)); + break; + default: + reader.ConsumeUnknownField(header); + break; + } + } + } } /// @@ -162,6 +204,8 @@ public Collection DeepCopy(Collection input, CopyContext context) /// public void DeepCopy(Collection input, Collection output, CopyContext context) { + output.Clear(); + foreach (var item in input) { output.Add(_copier.DeepCopy(item, context)); diff --git a/test/Orleans.Serialization.UnitTests/BuiltInCodecTests.cs b/test/Orleans.Serialization.UnitTests/BuiltInCodecTests.cs index e5169a99c0..a025011c8d 100644 --- a/test/Orleans.Serialization.UnitTests/BuiltInCodecTests.cs +++ b/test/Orleans.Serialization.UnitTests/BuiltInCodecTests.cs @@ -2215,6 +2215,56 @@ protected override Collection CreateValue() protected override Collection[] TestValues => [null, new Collection(), CreateValue(), CreateValue(), CreateValue()]; } + [GenerateSerializer] + public class TypeWithCollectionBase : Collection + { + public TypeWithCollectionBase() : this(true) { } + public TypeWithCollectionBase(bool addDefaultValue) + { + if (addDefaultValue) + { + this.Add(42); + } + } + + [Id(0)] + public int OtherProperty { get; set; } + + public override string ToString() => $"[OtherProperty: {OtherProperty}, Values: [{string.Join(", ", this)}]]"; + } + + public class CollectionBaseCodecTests(ITestOutputHelper output) : FieldCodecTester>(output) + { + private static TypeWithCollectionBase AddValues(TypeWithCollectionBase value) + { + value.Add(1); + value.Add(2); + value.Add(3); + return value; + } + + protected override TypeWithCollectionBase[] TestValues => [null, new(), new(addDefaultValue: false), new() { 15 }, AddValues(new() { OtherProperty = 123 })]; + + protected override TypeWithCollectionBase CreateValue() => AddValues(new() { OtherProperty = Random.Next() }); + protected override bool Equals(TypeWithCollectionBase left, TypeWithCollectionBase right) => ReferenceEquals(left, right) || left.SequenceEqual(right) && left.OtherProperty == right.OtherProperty; + } + + public class CollectionBaseCopierTests(ITestOutputHelper output) : CopierTester>(output) + { + private static TypeWithCollectionBase AddValues(TypeWithCollectionBase value) + { + value.Add(1); + value.Add(2); + value.Add(3); + return value; + } + + protected override TypeWithCollectionBase[] TestValues => [null, new(), new(addDefaultValue: false), new() { 15 }, AddValues(new() { OtherProperty = 123 })]; + + protected override TypeWithCollectionBase CreateValue() => AddValues(new() { OtherProperty = Random.Next() }); + protected override bool Equals(TypeWithCollectionBase left, TypeWithCollectionBase right) => ReferenceEquals(left, right) || left.SequenceEqual(right) && left.OtherProperty == right.OtherProperty; + } + public class ReadOnlyCollectionCodecTests(ITestOutputHelper output) : FieldCodecTester, ReadOnlyCollectionCodec>(output) { protected override ReadOnlyCollection CreateValue()