예제 #1
0
        public void Stamp(Type type)
        {
            if (!StampHelpers.IsStampNeeded(type))
            {
                return;
            }
            if (alreadyWritten.Contains(type))
            {
                return;
            }
            alreadyWritten.Add(type);
            var fields = StampHelpers.GetFieldsInSerializationOrder(type).ToArray();

            writer.Write(fields.Length);
            var moduleGuid = type.Module.ModuleVersionId;

            writer.Write(moduleGuid);
            foreach (var field in fields)
            {
                var fieldType = field.FieldType;
                writer.Write(field.Name);
                writer.Write(fieldType.AssemblyQualifiedName);
            }
        }
예제 #2
0
        public void ReadStamp(Type type)
        {
            if (!StampHelpers.IsStampNeeded(type))
            {
                return;
            }
            if (stampCache.ContainsKey(type))
            {
                return;
            }
            var result     = new List <FieldInfoOrEntryToOmit>();
            var fieldNo    = reader.ReadInt32();
            var moduleGuid = reader.ReadGuid();

            if (moduleGuid == type.Module.ModuleVersionId)
            {
                // short path, nothing to check
                for (var i = 0; i < fieldNo * 2; i++)
                {
                    reader.ReadString();
                }
                stampCache.Add(type, StampHelpers.GetFieldsInSerializationOrder(type, true).Select(x => new FieldInfoOrEntryToOmit(x)).ToList());
                return;
            }
            if (versionToleranceLevel == VersionToleranceLevel.Guid)
            {
                throw new InvalidOperationException(string.Format("The class was serialized with different module version id {0}, current one is {1}.",
                                                                  moduleGuid, type.Module.ModuleVersionId));
            }
            var currentFields = StampHelpers.GetFieldsInSerializationOrder(type, true).ToDictionary(x => x.Name, x => x);

            for (var i = 0; i < fieldNo; i++)
            {
                var       fieldName = reader.ReadString();
                var       fieldType = Type.GetType(reader.ReadString());
                FieldInfo currentField;
                if (!currentFields.TryGetValue(fieldName, out currentField))
                {
                    // field is missing in the newer version of the class
                    // we have to read the data to properly move stream,
                    // but that's all - unless this is illegal from the
                    // version tolerance level point of view
                    if (versionToleranceLevel != VersionToleranceLevel.FieldRemoval && versionToleranceLevel != VersionToleranceLevel.FieldAdditionAndRemoval)
                    {
                        throw new InvalidOperationException(string.Format("Field {0} of type {1} was present in the old version of the class but is missing now. This is" +
                                                                          "incompatible with selected version tolerance level which is {2}.", fieldName, fieldType,
                                                                          versionToleranceLevel));
                    }
                    result.Add(new FieldInfoOrEntryToOmit(fieldType));
                    continue;
                }
                // are the types compatible?
                if (currentField.FieldType != fieldType)
                {
                    throw new InvalidOperationException(string.Format("Field {0} was of type {1} in the old version of the class, but currently is {2}. Cannot proceed.",
                                                                      fieldName, fieldType, currentField.FieldType));
                }
                // why do we remove a field from current ones? if some field is still left after our operation, then field addition occured
                // we have to check that, cause it can be illegal from the version tolerance point of view
                currentFields.Remove(fieldName);
                result.Add(new FieldInfoOrEntryToOmit(currentField));
            }
            // result should also contain transient fields, because some of them may
            // be marked with the [Constructor] attribute
            var transientFields = currentFields.Select(x => x.Value).Where(x => !Helpers.IsNotTransient(x)).Select(x => new FieldInfoOrEntryToOmit(x)).ToArray();

            if (currentFields.Count - transientFields.Length > 0 && versionToleranceLevel != VersionToleranceLevel.FieldAddition &&
                versionToleranceLevel != VersionToleranceLevel.FieldAdditionAndRemoval)
            {
                throw new InvalidOperationException(string.Format("Current version of the class {0} contains more fields than it had when it was serialized. With given" +
                                                                  "version tolerance level {1} the serializer cannot proceed.", type, versionToleranceLevel));
            }
            result.AddRange(transientFields);
            stampCache.Add(type, result);
        }