/// <summary> /// Reads an *implementation specific* bundled .NET object, including (as options) type-metadata, identity/re-use, etc. /// </summary> public static object ReadNetObject(object value, ProtoReader source, int key, Type type, NetObjectOptions options) { SubItemToken token = ProtoReader.StartSubItem(source); int fieldNumber; int newObjectKey = -1, newTypeKey = -1, tmp; while ((fieldNumber = source.ReadFieldHeader()) > 0) { switch (fieldNumber) { case FieldExistingObjectKey: tmp = source.ReadInt32(); value = source.NetCache.GetKeyedObject(tmp); break; case FieldNewObjectKey: newObjectKey = source.ReadInt32(); break; case FieldExistingTypeKey: tmp = source.ReadInt32(); type = (Type)source.NetCache.GetKeyedObject(tmp); key = source.GetTypeKey(ref type); break; case FieldNewTypeKey: newTypeKey = source.ReadInt32(); break; case FieldTypeName: string typeName = source.ReadString(); type = source.DeserializeType(typeName); if (type == null) { throw new ProtoException("Unable to resolve type: " + typeName + " (you can use the TypeModel.DynamicTypeFormatting event to provide a custom mapping)"); } if (type == typeof(string)) { key = -1; } else { key = source.GetTypeKey(ref type); if (key < 0) { throw new InvalidOperationException("Dynamic type is not a contract-type: " + type.Name); } } break; case FieldObject: bool isString = type == typeof(string); bool wasNull = value == null; bool lateSet = wasNull && (isString || ((options & NetObjectOptions.LateSet) != 0)); if (newObjectKey >= 0 && !lateSet) { if (value == null) { source.TrapNextObject(newObjectKey); } else { source.NetCache.SetKeyedObject(newObjectKey, value); } if (newTypeKey >= 0) { source.NetCache.SetKeyedObject(newTypeKey, type); } } object oldValue = value; if (isString) { value = source.ReadString(); } else { value = ProtoReader.ReadTypedObject(oldValue, key, source, type); } if (newObjectKey >= 0) { if (wasNull && !lateSet) { // this both ensures (via exception) that it *was* set, and makes sure we don't shout // about changed references oldValue = source.NetCache.GetKeyedObject(newObjectKey); } if (lateSet) { source.NetCache.SetKeyedObject(newObjectKey, value); if (newTypeKey >= 0) { source.NetCache.SetKeyedObject(newTypeKey, type); } } } if (newObjectKey >= 0 && !lateSet && !ReferenceEquals(oldValue, value)) { throw new ProtoException("A reference-tracked object changed reference during deserialization"); } if (newObjectKey < 0 && newTypeKey >= 0) { // have a new type, but not a new object source.NetCache.SetKeyedObject(newTypeKey, type); } break; default: source.SkipField(); break; } } if (newObjectKey >= 0 && (options & NetObjectOptions.AsReference) == 0) { throw new ProtoException("Object key in input stream, but reference-tracking was not expected"); } ProtoReader.EndSubItem(token, source); return(value); }
/// <summary> /// Reads an *implementation specific* bundled .NET object, including (as options) type-metadata, identity/re-use, etc. /// </summary> public static SubItemToken ReadNetObject_Start(ref object value, ProtoReader source, ref Type type, BclHelpers.NetObjectOptions options, out bool isDynamic, out bool isLateReference, ref int typeKey, out int newObjectKey, out int newTypeRefKey, out bool shouldEnd) { #if FEAT_IKVM throw new NotSupportedException(); #else shouldEnd = false; newObjectKey = -1; newTypeRefKey = -1; isDynamic = false; isLateReference = false; SubItemToken token = ProtoReader.StartSubItem(source); int fieldNumber; if ((fieldNumber = source.ReadFieldHeader()) == 0) { // null handling value = null; return(token); } do { int tmp; switch (fieldNumber) { case FieldExistingObjectKey: tmp = source.ReadInt32(); value = source.NetCache.GetKeyedObject(tmp); break; case FieldNewObjectKey: newObjectKey = source.ReadInt32(); break; case FieldExistingTypeKey: tmp = source.ReadInt32(); type = (Type)source.NetCache.GetKeyedObject(tmp); typeKey = source.GetTypeKey(ref type); break; case FieldNewTypeKey: newTypeRefKey = source.ReadInt32(); break; case FieldTypeName: string typeName = source.ReadString(); type = source.DeserializeType(typeName); if (type == null) { throw new ProtoException("Unable to resolve type: " + typeName + " (you can use the TypeModel.DynamicTypeFormatting event to provide a custom mapping)"); } typeKey = source.GetTypeKey(ref type); isDynamic = true; break; case FieldLateReferenceObject: case FieldObject: if (fieldNumber == FieldLateReferenceObject) { isLateReference = true; } shouldEnd = true; bool wasNull = value == null; bool lateSet = wasNull && ((options & BclHelpers.NetObjectOptions.LateSet) != 0); if (newObjectKey >= 0 && !lateSet) { if (value == null) { source.TrapNextObject(newObjectKey); } else { source.NetCache.SetKeyedObject(newObjectKey, value); } if (newTypeRefKey >= 0) { source.NetCache.SetKeyedObject(newTypeRefKey, type); } } return(token); default: source.SkipField(); break; } } while ((fieldNumber = source.ReadFieldHeader()) > 0); return(token); #endif }