diff --git a/src/ZeroSerializerGenerator.cs b/src/ZeroSerializerGenerator.cs index bbf1538..fb89257 100644 --- a/src/ZeroSerializerGenerator.cs +++ b/src/ZeroSerializerGenerator.cs @@ -1316,15 +1316,7 @@ var propertyReturnType "propertyDataOffset"); break; case PropertySerializationKind.BlittableStruct: - if (property.NestedSerializableType is not null) - { - sourceBuilder.AppendLine($"return new {GetQualifiedViewName(property.NestedSerializableType)}(serializedMemory.Slice(propertyDataOffset, {property.ElementByteCount}));"); - } - else - { - sourceBuilder.AppendLine("// Fallback generated unexpectedly. According to the specification, this fallback should not be reached (the view always returns the view in any case)."); - sourceBuilder.AppendLine($"return MemoryMarshal.Read<{GetSerializedPropertyType(property).ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>(serializedData.Slice(propertyDataOffset, {property.ElementByteCount}));"); - } + sourceBuilder.AppendLine($"return new {GetQualifiedViewName(property.NestedSerializableType!)}(serializedMemory.Slice(propertyDataOffset, {property.ElementByteCount}));"); break; case PropertySerializationKind.String: EmitViewCollectionHeader(sourceBuilder, "serializedData", "propertyDataOffset"); @@ -1350,6 +1342,28 @@ var propertyReturnType sourceBuilder.CloseBlock(); sourceBuilder.CloseBlock(); + + if (property.Kind == PropertySerializationKind.BlittableStruct && !property.IsNullableType) + { + sourceBuilder.AppendLine(); + string valueType = property.Symbol.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + sourceBuilder.AppendLine($"{propertyAccessibility} {valueType} {EscapeIdentifier(property.Symbol.Name)}_AsValue"); + sourceBuilder.OpenBlock(); + sourceBuilder.AppendLine("get"); + sourceBuilder.OpenBlock(); + if (containingModel.IsBlittableStruct) + { + sourceBuilder.AppendLine($"return MemoryMarshal.Read<{valueType}>(serializedMemory.Span.Slice({property.BlittableByteOffset}, {property.ElementByteCount}));"); + } + else + { + sourceBuilder.AppendLine("ReadOnlySpan serializedData = serializedMemory.Span;"); + sourceBuilder.AppendLine($"int propertyDataOffset = BinaryPrimitives.ReadInt32LittleEndian(serializedData.Slice({propertyIndex * 4}, 4));"); + sourceBuilder.AppendLine($"return MemoryMarshal.Read<{valueType}>(serializedData.Slice(propertyDataOffset, {property.ElementByteCount}));"); + } + sourceBuilder.CloseBlock(); + sourceBuilder.CloseBlock(); + } } private static void EmitViewCollectionHeader( diff --git a/tests/SerializationTests.cs b/tests/SerializationTests.cs index 927393b..d86b6eb 100644 --- a/tests/SerializationTests.cs +++ b/tests/SerializationTests.cs @@ -1387,4 +1387,45 @@ public void NullableStructViewsSerializeAndDeserializeCorrectly() TestAssert.Equal(ByteState.Ready, viewNull.NonBlittableStruct.ByteState, "NonBlittableStruct.ByteState"); Assert.Null(viewNull.NullableNonBlittableStruct); } + + [Fact] + public void ViewStructProvidesAsValuePropertyOnlyForBlittableStructProperties() + { + Type viewType = typeof(NullableStructContainerModelView); + + // Verify BlittableStruct has _AsValue property + PropertyInfo? blittableAsValueProp = viewType.GetProperty("BlittableStruct_AsValue"); + Assert.NotNull(blittableAsValueProp); + TestAssert.Equal(typeof(PackedRecord), blittableAsValueProp.PropertyType, "BlittableStruct_AsValue PropertyType"); + + // Verify NullableBlittableStruct does NOT have _AsValue property + PropertyInfo? nullableBlittableAsValueProp = viewType.GetProperty("NullableBlittableStruct_AsValue"); + Assert.Null(nullableBlittableAsValueProp); + + // Verify NonBlittableStruct does NOT have _AsValue property + PropertyInfo? nonBlittableAsValueProp = viewType.GetProperty("NonBlittableStruct_AsValue"); + Assert.Null(nonBlittableAsValueProp); + + // Verify NullableNonBlittableStruct does NOT have _AsValue property + PropertyInfo? nullableNonBlittableAsValueProp = viewType.GetProperty("NullableNonBlittableStruct_AsValue"); + Assert.Null(nullableNonBlittableAsValueProp); + + // Verify behavior on serialized instance + NullableStructContainerModel source = new NullableStructContainerModel + { + BlittableStruct = new PackedRecord { Number = 100, State = SignedState.Positive }, + NullableBlittableStruct = new PackedRecord { Number = 200, State = SignedState.Negative }, + NonBlittableStruct = new EnumStruct(ByteState.Ready, SignedState.Positive), + NullableNonBlittableStruct = null + }; + + byte[] buffer = new byte[256]; + int written = source.Serialize(buffer); + NullableStructContainerModelView view = new NullableStructContainerModelView(buffer.AsMemory(0, written)); + + PackedRecord blittableValue = view.BlittableStruct_AsValue; + TestAssert.Equal(100, blittableValue.Number, "BlittableStruct_AsValue.Number"); + TestAssert.Equal(SignedState.Positive, blittableValue.State, "BlittableStruct_AsValue.State"); + + } }