| File: System\Resources\Extensions\BinaryFormat\Deserializer\ArrayRecordDeserializer.cs | Web Access |
| Project: src\runtime\src\libraries\System.Resources.Extensions\src\System.Resources.Extensions.csproj (System.Resources.Extensions) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Formats.Nrbf; namespace System.Resources.Extensions.BinaryFormat.Deserializer; internal sealed class ArrayRecordDeserializer : ObjectRecordDeserializer { private readonly ArrayRecord _arrayRecord; private readonly Type _elementType; private readonly Array _arrayOfClassRecords; private readonly Array _arrayOfT; private readonly int[] _lengths, _indices; private bool _hasFixups, _canIterate; [RequiresUnreferencedCode("Calls System.Windows.Forms.BinaryFormat.BinaryFormattedObject.TypeResolver.GetType(TypeName)")] internal ArrayRecordDeserializer(ArrayRecord arrayRecord, IDeserializer deserializer) : base(arrayRecord, deserializer) { // Other array types are handled directly (ArraySinglePrimitive and ArraySingleString). Debug.Assert(arrayRecord.RecordType is not (SerializationRecordType.ArraySingleString or SerializationRecordType.ArraySinglePrimitive)); _arrayRecord = arrayRecord; _elementType = deserializer.TypeResolver.GetType(arrayRecord.TypeName.GetElementType()); Type expectedArrayType = arrayRecord.Rank switch { 1 => _elementType.MakeArrayType(), _ => _elementType.MakeArrayType(arrayRecord.Rank), }; // Tricky part: for arrays of classes/structs the following record allocates and array of class records // (because the payload reader can not load types, instantiate objects and rehydrate them) _arrayOfClassRecords = arrayRecord.GetArray(expectedArrayType); // Now we need to create an array of the same length, but of a different, exact type Type elementType = _arrayOfClassRecords.GetType(); while (elementType.IsArray) { elementType = elementType.GetElementType()!; } _lengths = arrayRecord.Lengths.ToArray(); Object = _arrayOfT = Array.CreateInstance(_elementType, _lengths); _indices = new int[_lengths.Length]; _canIterate = _arrayOfT.Length > 0; } internal override SerializationRecordId Continue() { int[] indices = _indices; int[] lengths = _lengths; while (_canIterate) { (object? memberValue, SerializationRecordId reference) = UnwrapMemberValue(_arrayOfClassRecords.GetValue(indices)); if (s_missingValueSentinel == memberValue) { // Record has not been encountered yet, need to pend iteration. return reference; } if (memberValue is not null && DoesValueNeedUpdated(memberValue, reference)) { // Need to track a fixup for this index. _hasFixups = true; Deserializer.PendValueUpdater(new ArrayUpdater(_arrayRecord.Id, reference, indices.ToArray())); } _arrayOfT.SetValue(memberValue, indices); int dimension = indices.Length - 1; while (dimension >= 0) { indices[dimension]++; if (indices[dimension] < lengths[dimension]) { break; } indices[dimension] = 0; dimension--; } if (dimension < 0) { _canIterate = false; } } // No more missing member refs. if (!_hasFixups) { Deserializer.CompleteObject(_arrayRecord.Id); } return default(SerializationRecordId); } internal static Array GetArraySinglePrimitive(SerializationRecord record) => record switch { SZArrayRecord<bool> primitiveArray => primitiveArray.GetArray(), SZArrayRecord<byte> primitiveArray => primitiveArray.GetArray(), SZArrayRecord<sbyte> primitiveArray => primitiveArray.GetArray(), SZArrayRecord<char> primitiveArray => primitiveArray.GetArray(), SZArrayRecord<short> primitiveArray => primitiveArray.GetArray(), SZArrayRecord<ushort> primitiveArray => primitiveArray.GetArray(), SZArrayRecord<int> primitiveArray => primitiveArray.GetArray(), SZArrayRecord<uint> primitiveArray => primitiveArray.GetArray(), SZArrayRecord<long> primitiveArray => primitiveArray.GetArray(), SZArrayRecord<ulong> primitiveArray => primitiveArray.GetArray(), SZArrayRecord<float> primitiveArray => primitiveArray.GetArray(), SZArrayRecord<double> primitiveArray => primitiveArray.GetArray(), SZArrayRecord<decimal> primitiveArray => primitiveArray.GetArray(), SZArrayRecord<DateTime> primitiveArray => primitiveArray.GetArray(), SZArrayRecord<TimeSpan> primitiveArray => primitiveArray.GetArray(), _ => throw new NotSupportedException(), }; [RequiresUnreferencedCode("Calls System.Windows.Forms.BinaryFormat.BinaryFormattedObject.TypeResolver.GetType(TypeName)")] internal static Array? GetRectangularArrayOfPrimitives(ArrayRecord arrayRecord, BinaryFormattedObject.ITypeResolver typeResolver) { // Only rectangular, non-jagged BinaryArrayRecord can hit the lucky path below. if (arrayRecord.Rank <= 1 || arrayRecord.TypeName.GetElementType().IsArray) { return null; } Type arrayRecordElementType = typeResolver.GetType(arrayRecord.TypeName.GetElementType()); Type elementType = arrayRecordElementType; while (elementType.IsArray) { elementType = elementType.GetElementType()!; } if (!HasBuiltInSupport(elementType)) { return null; } Type expectedArrayType = arrayRecordElementType.MakeArrayType(arrayRecord.Rank); return arrayRecord.GetArray(expectedArrayType); static bool HasBuiltInSupport(Type elementType) => elementType == typeof(string) || elementType == typeof(bool) || elementType == typeof(byte) || elementType == typeof(sbyte) || elementType == typeof(char) || elementType == typeof(short) || elementType == typeof(ushort) || elementType == typeof(int) || elementType == typeof(uint) || elementType == typeof(long) || elementType == typeof(ulong) || elementType == typeof(float) || elementType == typeof(double) || elementType == typeof(decimal) || elementType == typeof(DateTime) || elementType == typeof(TimeSpan); } }