internal void WriteValueWithoutAdapters <TValue>(TValue value, bool isRoot) { if (RuntimeTypeInfoCache <TValue> .IsEnum) { BinarySerialization.WritePrimitiveUnsafe(m_Stream, ref value, Enum.GetUnderlyingType(typeof(TValue))); return; } if (RuntimeTypeInfoCache <TValue> .CanBeNull && null == value) { m_Stream->Add(k_TokenNull); return; } if (RuntimeTypeInfoCache <TValue> .IsMultidimensionalArray) { // No support for multidimensional arrays yet. This can be done using adapters for now. m_Stream->Add(k_TokenNull); return; } if (RuntimeTypeInfoCache <TValue> .IsNullable) { m_Stream->Add(k_TokenNone); var underlyingType = Nullable.GetUnderlyingType(typeof(TValue)); if (RuntimeTypeInfoCache.IsContainerType(underlyingType)) { // IMPORTANT: Do NOT add a token to the stream since we are triggering a re-entrance on the same object here. // Unpack Nullable<T> as T var underlyingValue = Convert.ChangeType(value, underlyingType); if (!PropertyContainer.TryAccept(this, ref underlyingValue, out var errorCode)) { switch (errorCode) { case VisitErrorCode.NullContainer: throw new ArgumentNullException(nameof(value)); case VisitErrorCode.InvalidContainerType: throw new InvalidContainerTypeException(value.GetType()); case VisitErrorCode.MissingPropertyBag: throw new MissingPropertyBagException(value.GetType()); default: throw new Exception($"Unexpected {nameof(VisitErrorCode)}=[{errorCode}]"); } } } else { BinarySerialization.WritePrimitiveBoxed(m_Stream, value, underlyingType); } return; } if (!RuntimeTypeInfoCache <TValue> .IsValueType) { #if !UNITY_DOTSPLAYER if (!(isRoot && m_DisableRootAdapters) && value is UnityEngine.Object unityEngineObject) { // Special path for polymorphic unity object references. m_Stream->Add(k_TokenUnityEngineObjectReference); (m_Adapters.Internal as Adapters.Contravariant.IBinaryAdapter <UnityEngine.Object>).Serialize(new BinarySerializationContext <TValue>(this, default, value, isRoot), unityEngineObject);
void ReadValue <TValue>(ref TValue value, bool isRoot = false) { var runAdapters = !(isRoot && m_DisableRootAdapters); if (runAdapters && m_Adapters.TryDeserialize(m_Stream, ref value)) { return; } if (RuntimeTypeInfoCache <TValue> .IsEnum) { BinarySerialization.ReadPrimitiveUnsafe(m_Stream, ref value, Enum.GetUnderlyingType(typeof(TValue))); return; } var token = default(byte); if (RuntimeTypeInfoCache <TValue> .CanBeNull) { token = m_Stream->ReadNext <byte>(); switch (token) { case k_TokenNull: value = default; return; case k_TokenSerializedReference: var id = m_Stream->ReadNext <int>(); if (null == m_SerializedReferences) { throw new Exception("Deserialization encountered a serialized object reference while running with DisableSerializedReferences."); } value = (TValue)m_SerializedReferences.GetDeserializedReference(id); return; } } if (RuntimeTypeInfoCache <TValue> .IsNullable) { BinarySerialization.ReadPrimitiveBoxed(m_Stream, ref value, Nullable.GetUnderlyingType(typeof(TValue))); return; } #if !UNITY_DOTSPLAYER if (runAdapters && token == k_TokenUnityEngineObjectReference) { var unityEngineObject = default(UnityEngine.Object); m_Adapters.TryDeserialize(m_Stream, ref unityEngineObject); value = (TValue)(object)unityEngineObject; return; } #endif if (token == k_TokenPolymorphic) { m_Stream->ReadNext(out var assemblyQualifiedTypeName); if (string.IsNullOrEmpty(assemblyQualifiedTypeName)) { throw new ArgumentException(); } var concreteType = Type.GetType(assemblyQualifiedTypeName); if (null == concreteType) { if (FormerNameAttribute.TryGetCurrentTypeName(assemblyQualifiedTypeName, out var currentAssemblyQualifiedTypeName)) { concreteType = Type.GetType(currentAssemblyQualifiedTypeName); } if (null == concreteType) { throw new ArgumentException(); } } m_SerializedTypeProviderSerializedType = concreteType; } else { // If we have a user provided root type pass it to the type construction. m_SerializedTypeProviderSerializedType = isRoot ? m_SerializedType : null; } DefaultTypeConstruction.Construct(ref value, this); if (RuntimeTypeInfoCache <TValue> .IsObjectType && !RuntimeTypeInfoCache.IsContainerType(value.GetType())) { BinarySerialization.ReadPrimitiveBoxed(m_Stream, ref value, value.GetType()); return; } if (!PropertyContainer.Visit(ref value, this, out var errorCode)) { switch (errorCode) { case VisitErrorCode.NullContainer: throw new ArgumentNullException(nameof(value)); case VisitErrorCode.InvalidContainerType: throw new InvalidContainerTypeException(value.GetType()); case VisitErrorCode.MissingPropertyBag: throw new MissingPropertyBagException(value.GetType()); default: throw new Exception($"Unexpected {nameof(VisitErrorCode)}=[{errorCode}]"); } } }
void WriteValue <TValue>(TValue value, bool isRoot = false) { var runAdapters = !(isRoot && m_DisableRootAdapters); if (runAdapters && m_Adapters.TrySerialize(m_Stream, ref value)) { return; } if (RuntimeTypeInfoCache <TValue> .IsEnum) { BinarySerialization.WritePrimitiveUnsafe(m_Stream, ref value, Enum.GetUnderlyingType(typeof(TValue))); return; } if (RuntimeTypeInfoCache <TValue> .CanBeNull && null == value) { m_Stream->Add(k_TokenNull); return; } if (RuntimeTypeInfoCache <TValue> .IsNullable) { m_Stream->Add(k_TokenNone); BinarySerialization.WritePrimitiveBoxed(m_Stream, value, Nullable.GetUnderlyingType(typeof(TValue))); return; } if (!RuntimeTypeInfoCache <TValue> .IsValueType) { #if !UNITY_DOTSPLAYER if (runAdapters && value is UnityEngine.Object unityEngineObject) { // Special path for polymorphic unity object references. m_Stream->Add(k_TokenUnityEngineObjectReference); m_Adapters.TrySerialize(m_Stream, ref unityEngineObject); return; } #endif if (null != m_SerializedReferences && !value.GetType().IsValueType) { // At this point we don't know if an object will reference this value. // To avoid a second visitation pass we always create an entry for each managed object reference. var id = m_SerializedReferences.AddSerializedReference(value); if (!m_SerializedReferences.SetSerialized(value)) { // This is the second time encountering this object during serialization. // We instead of writing out the object we simply write the id. m_Stream->Add(k_TokenSerializedReference); m_Stream->Add(id); return; } } // This is a very common case. At serialize time we are serializing something that is polymorphic or an object // However at deserialize time the user known the System.Type, we can avoid writing out the fully qualified type name in this case. var isRootAndTypeWasGiven = isRoot && null != m_SerializedType; if (typeof(TValue) != value.GetType() && !isRootAndTypeWasGiven) { m_Stream->Add(k_TokenPolymorphic); m_Stream->Add(value.GetType().AssemblyQualifiedName); } else { m_Stream->Add(k_TokenNone); } } if (RuntimeTypeInfoCache <TValue> .IsObjectType && !RuntimeTypeInfoCache.IsContainerType(value.GetType())) { BinarySerialization.WritePrimitiveBoxed(m_Stream, value, value.GetType()); return; } if (!PropertyContainer.Visit(ref value, this, out var errorCode)) { switch (errorCode) { case VisitErrorCode.NullContainer: throw new ArgumentNullException(nameof(value)); case VisitErrorCode.InvalidContainerType: throw new InvalidContainerTypeException(value.GetType()); case VisitErrorCode.MissingPropertyBag: throw new MissingPropertyBagException(value.GetType()); default: throw new Exception($"Unexpected {nameof(VisitErrorCode)}=[{errorCode}]"); } } }
internal void ReadValueWithoutAdapters <TValue>(ref TValue value, bool isRoot) { if (RuntimeTypeInfoCache <TValue> .IsEnum) { BinarySerialization.ReadPrimitiveUnsafe(m_Stream, ref value, Enum.GetUnderlyingType(typeof(TValue))); return; } var token = default(byte); if (RuntimeTypeInfoCache <TValue> .CanBeNull) { token = m_Stream->ReadNext <byte>(); switch (token) { case k_TokenNull: value = default; return; case k_TokenSerializedReference: var id = m_Stream->ReadNext <int>(); if (null == m_SerializedReferences) { throw new Exception("Deserialization encountered a serialized object reference while running with DisableSerializedReferences."); } value = (TValue)m_SerializedReferences.GetDeserializedReference(id); return; } } if (RuntimeTypeInfoCache <TValue> .IsNullable) { var underlyingType = Nullable.GetUnderlyingType(typeof(TValue)); if (RuntimeTypeInfoCache.IsContainerType(underlyingType)) { m_SerializedTypeProviderSerializedType = underlyingType; DefaultTypeConstruction.Construct(ref value, this); var underlyingValue = Convert.ChangeType(value, underlyingType); if (!PropertyContainer.TryAccept(this, ref underlyingValue, out var errorCode)) { switch (errorCode) { case VisitErrorCode.NullContainer: throw new ArgumentNullException(nameof(value)); case VisitErrorCode.InvalidContainerType: throw new InvalidContainerTypeException(value.GetType()); case VisitErrorCode.MissingPropertyBag: throw new MissingPropertyBagException(value.GetType()); default: throw new Exception($"Unexpected {nameof(VisitErrorCode)}=[{errorCode}]"); } } // Repack the T as Nullable<T> value = (TValue)underlyingValue; } else { BinarySerialization.ReadPrimitiveBoxed(m_Stream, ref value, Nullable.GetUnderlyingType(typeof(TValue))); } return; } #if !UNITY_DOTSPLAYER if (!(isRoot && m_DisableRootAdapters) && token == k_TokenUnityEngineObjectReference) { value = (TValue)(m_Adapters.Internal as Adapters.Contravariant.IBinaryAdapter <UnityEngine.Object>).Deserialize(new BinaryDeserializationContext <TValue>(this, default, isRoot));;