Beispiel #1
0
        private AnyClass ReadInstance(int index)
        {
            Debug.Assert(index > 0);

            if (index > 1)
            {
                if (_instanceMap != null && _instanceMap.Count > index - 2)
                {
                    return(_instanceMap[index - 2]);
                }
                throw new InvalidDataException($"could not find index {index} in {nameof(_instanceMap)}");
            }

            if (++_classGraphDepth > Communicator.ClassGraphDepthMax)
            {
                throw new InvalidDataException("maximum class graph depth reached");
            }

            InstanceData?previousCurrent = Push(InstanceType.Class);

            Debug.Assert(_current != null);

            AnyClass?instance = null;

            do
            {
                // Read the slice header.
                string?typeIdOpt = ReadSliceHeaderIntoCurrent();

                // We cannot read the indirection table at this point as it may reference the new instance that is
                // not created yet.

                IClassFactory?factory = null;
                if (typeIdOpt is string typeId)
                {
                    Debug.Assert(_current.SliceCompactId == null);
                    factory = Communicator.FindClassFactory(typeId);
                }
                else if (_current.SliceCompactId is int compactId)
                {
                    factory = Communicator.FindClassFactory(compactId);
                }

                if (factory != null)
                {
                    instance = factory.Read(this);
                }
                else if (SkipSlice()) // Slice off what we don't understand.
                {
                    instance = new UnknownSlicedClass(this);
                }
            }while (instance == null);

            Pop(previousCurrent);
            --_classGraphDepth;
            return(instance);
        }
Beispiel #2
0
        /// <summary>Reads a class instance from the stream.</summary>
        /// <param name="index">The index of the class instance. If greater than 1, it's a reference to a previously
        /// seen class; if 1, the class's bytes are next on the stream. Cannot be 0 or less.</param>
        /// <param name="formalTypeId">The type ID of the formal type of the parameter or data member being read.
        /// </param>
        private AnyClass ReadInstance(int index, string?formalTypeId)
        {
            Debug.Assert(Communicator != null);
            Debug.Assert(index > 0);

            if (index > 1)
            {
                if (_instanceMap != null && _instanceMap.Count > index - 2)
                {
                    return(_instanceMap[index - 2]);
                }
                throw new InvalidDataException($"could not find index {index} in {nameof(_instanceMap)}");
            }

            if (++_classGraphDepth > Communicator.ClassGraphMaxDepth)
            {
                throw new InvalidDataException("maximum class graph depth reached");
            }

            // Save current in case we're reading a nested instance.
            InstanceData previousCurrent = _current;

            _current = default;
            _current.InstanceType = InstanceType.Class;

            AnyClass?instance = null;

            _instanceMap ??= new List <AnyClass>();

            if (OldEncoding)
            {
                bool readIndirectionTable = true;
                do
                {
                    // Read the slice header.
                    (string?typeId, int?compactId) = ReadSliceHeaderIntoCurrent11();

                    // We cannot read the indirection table at this point as it may reference the new instance that is
                    // not created yet.

                    Func <AnyClass>?factory = null;
                    if (typeId != null)
                    {
                        factory = Communicator.FindClassFactory(typeId);
                    }
                    else if (compactId is int compactIdValue)
                    {
                        factory = Communicator.FindClassFactory(compactIdValue);
                    }

                    if (factory != null)
                    {
                        instance = factory();
                    }
                    else if (SkipSlice(typeId, compactId)) // Slice off what we don't understand.
                    {
                        instance = new UnknownSlicedClass();
                        // Don't read the indirection table as it's the last entry in DeferredIndirectionTableList11.
                        readIndirectionTable = false;
                    }
                }while (instance == null);

                // Add the instance to the map/list of instances. This must be done before reading the instances (for
                // circular references).
                _instanceMap.Add(instance);

                // Read all the deferred indirection tables now that the instance is inserted in _instanceMap.
                if (_current.DeferredIndirectionTableList11?.Count > 0)
                {
                    int savedPos = Pos;

                    Debug.Assert(_current.Slices?.Count == _current.DeferredIndirectionTableList11.Count);
                    for (int i = 0; i < _current.DeferredIndirectionTableList11.Count; ++i)
                    {
                        int pos = _current.DeferredIndirectionTableList11[i];
                        if (pos > 0)
                        {
                            Pos = pos;
                            _current.Slices[i].Instances = Array.AsReadOnly(ReadIndirectionTable());
                        }
                        // else remains empty
                    }

                    Pos = savedPos;
                }

                if (readIndirectionTable)
                {
                    ReadIndirectionTableIntoCurrent();
                }
            }
            else
            {
                // With the 2.0 encoding, we don't need a DeferredIndirectionTableList because all the type IDs are
                // provided by the first slice (when using the sliced format).

                (string[]? allTypeIds, _, _) = ReadFirstSliceHeaderIntoCurrent20();
                if (allTypeIds != null)
                {
                    int skipCount = 0;
                    foreach (string typeId in allTypeIds)
                    {
                        if (Communicator.FindClassFactory(typeId) is Func <AnyClass> factory)
                        {
                            instance = factory();
                            break; // foreach
                        }
                        else
                        {
                            skipCount++;
                        }
                    }

                    instance ??= new UnknownSlicedClass();

                    _instanceMap.Add(instance);
                    ReadIndirectionTableIntoCurrent(); // read the indirection table immediately

                    for (int i = 0; i < skipCount; ++i)
                    {
                        // SkipSlice saves the slice data including the current indirection table, if any.
                        if (SkipSlice(allTypeIds[i]))
                        {
                            if (i != skipCount - 1)
                            {
                                throw new InvalidDataException(
                                          "class slice marked as the last slice is not the last slice");
                            }
                            break;
                        }
                        else
                        {
                            ReadNextSliceHeaderIntoCurrent();
                            ReadIndirectionTableIntoCurrent();
                        }
                    }
                }
                else if (formalTypeId != null)
                {
                    // received null and formalTypeId is not null, apply formal type optimization.
                    if (Communicator.FindClassFactory(formalTypeId) is Func <AnyClass> factory)
                    {
                        instance = factory();
                        _instanceMap.Add(instance);
                        ReadIndirectionTableIntoCurrent();
                        // Nothing to skip
                    }
                    else
                    {
                        throw new ArgumentException($"cannot find class factory for `{formalTypeId}'",
                                                    nameof(formalTypeId));
                    }
                }
                else
                {
                    throw new InvalidDataException(
                              @$ "cannot read a class instance with no type ID; did you forget to specify the {
                            nameof(formalTypeId)}?");
                }
            }