public CachingTypeAnalyser(IAnalyseTypesForSerialisation reader)
 {
     _reader                  = reader ?? throw new ArgumentNullException(nameof(reader));
     _typeLookupCache         = new ConcurrentDictionary <string, Type>();
     _typeBuilderCache        = new ConcurrentDictionary <string, Func <object> >();
     _fieldAndPropertyCache   = new ConcurrentDictionary <Type, Tuple <MemberAndReader <FieldInfo>[], MemberAndReader <PropertyInfo>[]> >();
     _requiredFieldCache      = new ConcurrentDictionary <Type, FieldInfo[]>();
     _fieldNameCache          = new ConcurrentDictionary <Tuple <Type, string, string>, MemberAndWriter <FieldInfo> >();
     _deprecatedPropertyCache = new ConcurrentDictionary <Tuple <Type, string, string, Type>, DeprecatedPropertySettingDetails>();
 }
Exemple #2
0
        }                                           // internal constructor for unit testing

        internal BinarySerialisationWriter(         // internal constructor for unit testing and for FastestTreeBinarySerialisation
            Stream stream,
            ReferenceReuseOptions referenceReuseStrategy,
            IAnalyseTypesForSerialisation typeAnalyser,
            ConcurrentDictionary <Type, DeepCompiledMemberSettersGenerationResults> deepMemberSetterCacheIfEnabled)
        {
            _stream = stream ?? throw new ArgumentNullException(nameof(stream));
            ReferenceReuseStrategy          = referenceReuseStrategy;
            _typeAnalyser                   = typeAnalyser;
            _deepMemberSetterCacheIfEnabled = deepMemberSetterCacheIfEnabled;             // Only expect this to be non-null when called by FastestTreeBinarySerialisation

            _recordedTypeNames          = new Dictionary <Type, BinarySerialisationWriterCachedNames.CachedNameData>();
            _encounteredFields          = new Dictionary <Tuple <FieldInfo, Type>, BinarySerialisationWriterCachedNames.CachedNameData>();
            _encounteredProperties      = new Dictionary <PropertyInfo, BinarySerialisationWriterCachedNames.CachedNameData>();
            _shouldSerialiseMemberCache = new Dictionary <Tuple <MemberInfo, Type>, bool>();

            _haveStartedSerialising = false;
        }
Exemple #3
0
        internal BinarySerialisationReader(Stream stream, IDeserialisationTypeConverter[] typeConverters, IAnalyseTypesForSerialisation typeAnalyser)         // internal constructor may be used by unit tests
        {
            _stream         = stream ?? throw new ArgumentNullException(nameof(stream));
            _typeConverters = typeConverters ?? throw new ArgumentNullException(nameof(typeConverters));
            _typeAnalyser   = typeAnalyser ?? throw new ArgumentNullException(nameof(typeAnalyser));

            _nameReferences   = new Dictionary <int, string>();
            _objectReferences = new Dictionary <int, object>();
            _deferredInitialisationReferenceIDsAwaitingPopulation = new HashSet <int>();

            // We can take adantage of the fact that the fields for each distinct type will always appear in the same order (and there will always be the precise same number of
            // field entries for subsequent occurrences of a particular type) AND the field names will always use Name Reference IDs after the first time that a type is written
            // out. So, instead of treating the field data as dynamic content and having to try to resolve the fields each time that an object is to be deserialised, we can
            // build specialised deserialisers for each type that know what fields are going to appear and that have cached the field lookup data - this will mean that there is
            // a lot less work to do when there are many instances of a given type within a payload. Also note that after the first appearance of a type, Name Reference IDs will
            // always be used for the type name and so the lookup that is constructed to these per-type deserialisers has an int key (which is less work to match than using the
            // full type name as the key would be).
            // - This is confused if the BinarySerialisationWriter is optimised for wide circular references because it shifts object definitions around and so the type readers
            //   can't be used in that case (which means that performance per-instance deserialisation performance is reduced but it would stack overflow otherwise, so it's not
            //   really compromised since the optimised-for-tree approach might not work at all!)
            _typeReaders = new Dictionary <int, BinarySerialisationReaderTypeReader>();

            _haveEncounteredDeferredInitialisationObject = false;
        }
        /// <summary>
        /// A member setter is a delegate that takes an instance and writes the data for fields and properties to a BinarySerialisationWriter. Note that it will not write the
        /// ObjectStart and ObjectEnd data because the caller should be responsible for tracking references (if the caller is configured to track references), which is tied to
        /// the ObjectStart data. If the caller is tracking references (to either reuse references that appear multiple times in the source data or to identify any circular
        /// references while performing serialisation) then member setters should only be generated for types whose fields and properties are types that do not support reference
        /// reuse, such as primitives and strings. Fields and properties that are primitives or strings or DateTime or TimeSpan or GUIDs (or one-dimensional arrays of any of those
        /// types) can be handled entirely by code within this method but it's also possible to provide member setters for other field or property types via the valueWriterRetriever
        /// argument - again, if the caller is tracking references then it should only pass through member setters via valueWriterRetriever that are for non-reference types, such as
        /// structs) because generating nested member setters in this manner will prevent any reference tracking.
        /// </summary>
        public static MemberSetterGenerationAttemptResult GetMemberSetterAvailability(Type type, IAnalyseTypesForSerialisation typeAnalyser, ValueWriterRetriever valueWriterRetriever)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }
            if (typeAnalyser == null)
            {
                throw new ArgumentNullException(nameof(typeAnalyser));
            }
            if (valueWriterRetriever == null)
            {
                throw new ArgumentNullException(nameof(valueWriterRetriever));
            }

            var sourceParameter = Expression.Parameter(type, "source");
            var writerParameter = Expression.Parameter(_writerType, "writer");
            var statements      = new List <Expression>();
            var fieldsSet       = new List <CachedNameData>();

            var(fields, properties) = typeAnalyser.GetFieldsAndProperties(type);
            var membersToConsiderSerialising =
                fields.Select(f =>
            {
                var fieldNameBytes = GetFieldNameBytesIfWantoSerialiseField(f.Member, type);
                if (fieldNameBytes == null)
                {
                    return(null);
                }
                return(new
                {
                    FieldNameBytes = fieldNameBytes,
                    Member = (MemberInfo)f.Member,
                    ValueWriterIfPossibleToGenerate = TryToGetValueWriterForMember(f.Member, f.Member.FieldType, sourceParameter, writerParameter, valueWriterRetriever)
                });
            })
                .Concat(
                    properties.Select(p =>
            {
                var fieldNameBytes = GetFieldNameBytesIfWantoSerialiseProperty(p.Member);
                if (fieldNameBytes == null)
                {
                    return(null);
                }
                return(new
                {
                    FieldNameBytes = fieldNameBytes,
                    Member = (MemberInfo)p.Member,
                    ValueWriterIfPossibleToGenerate = TryToGetValueWriterForMember(p.Member, p.Member.PropertyType, sourceParameter, writerParameter, valueWriterRetriever)
                });
            })
                    )
                .Where(member => member != null);                 // Ignore any fields or properties that we didn't want to serialise

            foreach (var member in membersToConsiderSerialising)
            {
                // If it wasn't possible to get a member setter for this field/property type then it's not possible to generate a member setter for the type that it's on
                if (member.ValueWriterIfPossibleToGenerate == null)
                {
                    return(MemberSetterGenerationAttemptResult.ForFailure(member.Member));
                }

                // Generate the write-FieldName-to-stream method call
                statements.Add(
                    Expression.Call(
                        writerParameter,
                        _writeBytesMethod,
                        Expression.Constant(new[] { (byte)BinarySerialisationDataType.FieldName }.Concat(member.FieldNameBytes.OnlyAsReferenceID).ToArray())
                        )
                    );

                // Write out the value-serialising expression
                statements.Add(member.ValueWriterIfPossibleToGenerate);
                fieldsSet.Add(member.FieldNameBytes);
            }

            // Group all of the field setters together into one call
            Expression body;

            if (statements.Count == 0)
            {
                body = Expression.Empty();
            }
            else if (statements.Count == 1)
            {
                body = statements[0];
            }
            else
            {
                body = Expression.Block(statements);
            }
            return(MemberSetterGenerationAttemptResult.ForSuccess(
                       new MemberSetterDetails(
                           type,
                           Expression.Lambda(
                               body,
                               sourceParameter,
                               writerParameter
                               ),
                           GetTypeNameBytes(type),
                           fieldsSet.ToArray()
                           )
                       ));
        }
Exemple #5
0
 internal Serialiser(IAnalyseTypesForSerialisation typeAnalyser)         // internal constructor is intended for unit testing only
 {
     _typeAnalyser = typeAnalyser ?? throw new ArgumentNullException(nameof(typeAnalyser));
 }