csharp/a1q123456/Harmonic/Harmonic/Networking/Amf/Serialization/Amf3/Amf3Reader.cs

Amf3Reader.cs
using Harmonic.Networking.Amf.Common;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using Harmonic.Networking.Utils;
using System.Buffers;
using Harmonic.Networking.Amf.Data;
using System.Reflection;
using Harmonic.Networking.Amf.Attributes;
using Harmonic.Networking.Amf.Serialization.Attributes;

namespace Harmonic.Networking.Amf.Serialization.Amf3
{
    public clast Amf3Reader
    {
        private delegate bool ReaderHandler(Span buffer, out T value, out int consumed);
        private delegate bool ReaderHandler(Span buffer, out object value, out int consumed);

        private List _objectReferenceTable = new List();
        private List _stringReferenceTable = new List();
        private List _objectTraitsReferenceTable = new List();
        private Dictionary _readerHandlers = new Dictionary();
        private Dictionary _registeredTypedObejectStates = new Dictionary();
        private List _registeredTypes = new List();
        private Dictionary _registeredExternalizable = new Dictionary();
        private readonly IReadOnlyList _supportedTypes = null;
        private MemoryPool _memoryPool = MemoryPool.Shared;

        public IReadOnlyList RegisteredTypes { get => _registeredTypes; }

        public Amf3Reader()
        {
            var supportedTypes = new List()
            {
                 Amf3Type.Undefined ,
                 Amf3Type.Null ,
                 Amf3Type.False ,
                 Amf3Type.True,
                 Amf3Type.Integer ,
                 Amf3Type.Double ,
                 Amf3Type.String ,
                 Amf3Type.Xml ,
                 Amf3Type.XmlDocameent ,
                 Amf3Type.Date ,
                 Amf3Type.Array ,
                 Amf3Type.Object ,
                 Amf3Type.ByteArray ,
                 Amf3Type.VectorObject ,
                 Amf3Type.VectorDouble ,
                 Amf3Type.VectorInt ,
                 Amf3Type.VectorUInt ,
                 Amf3Type.Dictionary
            };
            _supportedTypes = supportedTypes;

            var readerHandlers = new Dictionary
            {
                [Amf3Type.Undefined] = ReaderHandlerWrapper(TryGetUndefined),
                [Amf3Type.Null] = ReaderHandlerWrapper(TryGetNull),
                [Amf3Type.True] = ReaderHandlerWrapper(TryGetTrue),
                [Amf3Type.False] = ReaderHandlerWrapper(TryGetFalse),
                [Amf3Type.Double] = ReaderHandlerWrapper(TryGetDouble),
                [Amf3Type.Integer] = ReaderHandlerWrapper(TryGetUInt29),
                [Amf3Type.String] = ReaderHandlerWrapper(TryGetString),
                [Amf3Type.Xml] = ReaderHandlerWrapper(TryGetXml),
                [Amf3Type.XmlDocameent] = ReaderHandlerWrapper(TryGetXmlDocameent),
                [Amf3Type.Date] = ReaderHandlerWrapper(TryGetDate),
                [Amf3Type.ByteArray] = ReaderHandlerWrapper(TryGetByteArray),
                [Amf3Type.VectorDouble] = ReaderHandlerWrapper(TryGetVectorDouble),
                [Amf3Type.VectorInt] = ReaderHandlerWrapper(TryGetVectorInt),
                [Amf3Type.VectorUInt] = ReaderHandlerWrapper(TryGetVectorUint),
                [Amf3Type.VectorObject] = ReaderHandlerWrapper(TryGetVectorObject),
                [Amf3Type.Array] = ReaderHandlerWrapper(TryGetArray),
                [Amf3Type.Object] = ReaderHandlerWrapper(TryGetObject),
                [Amf3Type.Dictionary] = ReaderHandlerWrapper(TryGetDictionary)
            };
            _readerHandlers = readerHandlers;
        }

        public void ResetReference()
        {
            _objectReferenceTable.Clear();
            _objectTraitsReferenceTable.Clear();
            _stringReferenceTable.Clear();
        }

        private ReaderHandler ReaderHandlerWrapper(ReaderHandler handler)
        {
            return (Span b, out object value, out int consumed) =>
            {
                value = default;
                consumed = default;

                if (handler(b, out var data, out consumed))
                {
                    value = data;
                    return true;
                }
                return false;
            };
        }

        internal void RegisterTypedObject(string mappedName, TypeRegisterState state)
        {
            _registeredTypedObejectStates.Add(mappedName, state);
        }

        public void RegisterTypedObject() where T: new()
        {
            var type = typeof(T);
            var props = type.GetProperties();
            var fields = props.Where(p => p.CanWrite && Attribute.GetCustomAttribute(p, typeof(ClastFieldAttribute)) != null).ToList();
            var members = fields.ToDictionary(p => ((ClastFieldAttribute)Attribute.GetCustomAttribute(p, typeof(ClastFieldAttribute))).Name ?? p.Name, p => new Action(p.SetValue));
            if (members.Keys.Where(s => string.IsNullOrEmpty(s)).Any())
            {
                throw new InvalidOperationException("Field name cannot be empty or null");
            }
            string mapedName = null;
            var attr = type.GetCustomAttribute();
            if (attr != null)
            {
                mapedName = attr.Name;
            }

            var typeName = mapedName == null ? type.Name : mapedName;
            var state = new TypeRegisterState()
            {
                Members = members,
                Type = type
            };
            _registeredTypes.Add(type);
            _registeredTypedObejectStates.Add(typeName, state);
        }

        public void RegisterExternalizable() where T : IExternalizable, new()
        {
            var type = typeof(T);
            string mapedName = null;
            var attr = type.GetCustomAttribute();
            if (attr != null)
            {
                mapedName = attr.Name;
            }
            var typeName = mapedName == null ? type.Name : mapedName;
            _registeredExternalizable.Add(typeName, type);
        }

        public bool TryDescribeData(Span buffer, out Amf3Type type)
        {
            type = default;
            if (buffer.Length < Amf3CommonValues.MARKER_LENGTH)
            {
                return false;
            }

            var typeMark = (Amf3Type)buffer[0];
            if (!_supportedTypes.Contains(typeMark))
            {
                return false;
            }

            type = typeMark;
            return true;
        }

        public bool DataIsType(Span buffer, Amf3Type type)
        {
            if (!TryDescribeData(buffer, out var dataType))
            {
                return false;
            }
            return dataType == type;
        }

        public bool TryGetUndefined(Span buffer, out Undefined value, out int consumed)
        {
            value = default;
            consumed = default;
            if (!DataIsType(buffer, Amf3Type.Undefined))
            {
                return false;
            }

            value = new Undefined();
            consumed = Amf3CommonValues.MARKER_LENGTH;
            return true;
        }

        public bool TryGetNull(Span buffer, out object value, out int consumed)
        {
            value = default;
            consumed = default;
            if (!DataIsType(buffer, Amf3Type.Null))
            {
                return false;
            }

            value = null;
            consumed = Amf3CommonValues.MARKER_LENGTH;
            return true;
        }

        public bool TryGetTrue(Span buffer, out bool value, out int consumed)
        {
            value = default;
            consumed = default;
            if (!DataIsType(buffer, Amf3Type.True))
            {
                return false;
            }

            value = true;
            consumed = Amf3CommonValues.MARKER_LENGTH;
            return true;
        }

        public bool TryGetBoolean(Span buffer, out bool value, out int consumed)
        {
            if (DataIsType(buffer, Amf3Type.True))
            {
                consumed = Amf3CommonValues.MARKER_LENGTH;
                value = true;
                return true;
            }
            else if (DataIsType(buffer, Amf3Type.False))
            {
                consumed = Amf3CommonValues.MARKER_LENGTH;
                value = false;
                return true;
            }
            value = default;
            consumed = default;
            return false;
        }

        public bool TryGetFalse(Span buffer, out bool value, out int consumed)
        {
            value = default;
            consumed = default;
            if (!DataIsType(buffer, Amf3Type.False))
            {
                return false;
            }

            value = false;
            consumed = Amf3CommonValues.MARKER_LENGTH;
            return true;
        }

        public bool TryGetUInt29(Span buffer, out uint value, out int consumed)
        {
            value = default;
            consumed = default;
            if (!DataIsType(buffer, Amf3Type.Integer))
            {
                return false;
            }

            var dataBuffer = buffer.Slice(Amf3CommonValues.MARKER_LENGTH);

            if (!TryGetU29Impl(dataBuffer, out value, out var dataConsumed))
            {
                return false;
            }

            consumed = Amf3CommonValues.MARKER_LENGTH + dataConsumed;

            return true;
        }

        private bool TryGetU29Impl(Span dataBuffer, out uint value, out int consumed)
        {
            value = default;
            consumed = default;
            var bytesNeed = 0;
            for (int i = 0; i < 4; i++)
            {
                bytesNeed++;
                if (dataBuffer.Length < bytesNeed)
                {
                    return false;
                }
                var hasBytes = ((dataBuffer[i] >> 7 & 0x01) == 0x01);
                if (!hasBytes)
                {
                    break;
                }
            }

            switch (bytesNeed)
            {
                case 3:
                case 4:
                    dataBuffer[2] = (byte)(0x7F & dataBuffer[2]);
                    dataBuffer[1] = (byte)(0x7F & dataBuffer[1]);
                    dataBuffer[0] = (byte)(0x7F & dataBuffer[0]);
                    dataBuffer[2] = (byte)(dataBuffer[1]  1));
                    dataBuffer[0] = (byte)(dataBuffer[0] >> 2);
                    break;
                case 2:
                    dataBuffer[1] = (byte)(0x7F & dataBuffer[1]);
                    dataBuffer[1] = (byte)(dataBuffer[0] > 1);
                    break;
            }

            using (var mem = _memoryPool.Rent(sizeof(uint)))
            {
                var buffer = mem.Memory.Span;
                buffer.Clear();
                dataBuffer.Slice(0, bytesNeed).CopyTo(buffer.Slice(sizeof(uint) - bytesNeed));
                value = NetworkBitConverter.ToUInt32(buffer);
                consumed = bytesNeed;
                return true;
            }


        }

        public bool TryGetDouble(Span buffer, out double value, out int consumed)
        {
            value = default;
            consumed = default;
            if (!DataIsType(buffer, Amf3Type.Double))
            {
                return false;
            }

            value = NetworkBitConverter.ToDouble(buffer.Slice(Amf3CommonValues.MARKER_LENGTH));
            consumed = Amf3CommonValues.MARKER_LENGTH + sizeof(double);
            return true;
        }
        public bool TryGetString(Span buffer, out string value, out int consumed)
        {
            value = default;
            consumed = default;
            if (!DataIsType(buffer, Amf3Type.String))
            {
                return false;
            }

            var objectBuffer = buffer.Slice(Amf3CommonValues.MARKER_LENGTH);
            TryGetStringImpl(objectBuffer, _stringReferenceTable, out var str, out var strConsumed);
            value = str;
            consumed = Amf3CommonValues.MARKER_LENGTH + strConsumed;
            return true;
        }

        private bool TryGetStringImpl(Span objectBuffer, List referenceTable, out string value, out int consumed) where T : clast
        {
            value = default;
            consumed = default;
            if (!TryGetU29Impl(objectBuffer, out var header, out int headerLen))
            {
                return false;
            }
            if (!TryGetReference(header, _stringReferenceTable, out var headerData, out string refValue, out var isRef))
            {
                return false;
            }
            if (isRef)
            {
                value = refValue;
                consumed = headerLen;
                return true;
            }

            var strLen = (int)headerData;
            if (objectBuffer.Length - headerLen < strLen)
            {
                return false;
            }

            value = Encoding.UTF8.GetString(objectBuffer.Slice(headerLen, strLen));
            consumed = headerLen + strLen;
            if (value.Any())
            {
                referenceTable.Add(value as T);
            }
            
            return true;
        }

        public bool TryGetXmlDocameent(Span buffer, out XmlDocameent value, out int consumed)
        {
            value = default;
            consumed = default;
            if (!DataIsType(buffer, Amf3Type.XmlDocameent))
            {
                return false;
            }

            var objectBuffer = buffer.Slice(Amf3CommonValues.MARKER_LENGTH);
            TryGetStringImpl(objectBuffer, _objectReferenceTable, out var str, out var strConsumed);
            var xml = new XmlDocameent();
            xml.LoadXml(str);
            value = xml;
            consumed = Amf3CommonValues.MARKER_LENGTH + strConsumed;
            return true;
        }

        public bool TryGetDate(Span buffer, out DateTime value, out int consumed)
        {
            value = default;
            consumed = default;
            if (!DataIsType(buffer, Amf3Type.Date))
            {
                return false;
            }

            var objectBuffer = buffer.Slice(Amf3CommonValues.MARKER_LENGTH);

            if (!TryGetU29Impl(objectBuffer, out var header, out var headerLength))
            {
                return false;
            }
            if (!TryGetReference(header, _objectReferenceTable, out var headerData, out DateTime refValue, out var isRef))
            {
                return false;
            }
            if (isRef)
            {
                value = refValue;
                consumed = Amf3CommonValues.MARKER_LENGTH + headerLength;
                return true;
            }

            var timestamp = NetworkBitConverter.ToDouble(objectBuffer.Slice(headerLength));
            value = DateTimeOffset.FromUnixTimeMilliseconds((long)(timestamp)).LocalDateTime;
            consumed = Amf3CommonValues.MARKER_LENGTH + headerLength + sizeof(double);
            _objectReferenceTable.Add(value);
            return true;
        }

        public bool TryGetArray(Span buffer, out Amf3Array value, out int consumed)
        {
            value = default;
            consumed = default;
            if (!DataIsType(buffer, Amf3Type.Array))
            {
                return false;
            }

            if (!TryGetU29Impl(buffer.Slice(Amf3CommonValues.MARKER_LENGTH), out var header, out var headerConsumed))
            {
                return false;
            }

            if (!TryGetReference(header, _objectReferenceTable, out var headerData, out Amf3Array refValue, out var isRef))
            {
                return false;
            }
            if (isRef)
            {
                value = refValue;
                consumed = Amf3CommonValues.MARKER_LENGTH + headerConsumed;
                return true;
            }

            var arrayConsumed = 0;
            var arrayBuffer = buffer.Slice(Amf3CommonValues.MARKER_LENGTH + headerConsumed);
            var denseItemCount = (int)headerData;

            if (!TryGetStringImpl(arrayBuffer, _stringReferenceTable, out var key, out var keyConsumed))
            {
                return false;
            }
            var array = new Amf3Array();
            _objectReferenceTable.Add(array);
            if (key.Any())
            {
                do
                {
                    arrayBuffer = arrayBuffer.Slice(keyConsumed);
                    arrayConsumed += keyConsumed;
                    if (!TryGetValue(arrayBuffer, out var item, out var itemConsumed))
                    {
                        return false;
                    }

                    arrayConsumed += itemConsumed;
                    arrayBuffer = arrayBuffer.Slice(itemConsumed);
                    array.SparsePart.Add(key, item);
                    if (!TryGetStringImpl(arrayBuffer, _stringReferenceTable, out key, out keyConsumed))
                    {
                        return false;
                    }
                }
                while (key.Any());
            }
            arrayConsumed += keyConsumed;
            arrayBuffer = arrayBuffer.Slice(keyConsumed);

            for (int i = 0; i < denseItemCount; i++)
            {
                if (!TryGetValue(arrayBuffer, out var item, out var itemConsumed))
                {
                    return false;
                }
                array.DensePart.Add(item);
                arrayConsumed += itemConsumed;
                arrayBuffer = arrayBuffer.Slice(itemConsumed);
            }

            value = array;
            consumed = Amf3CommonValues.MARKER_LENGTH + headerConsumed + arrayConsumed;
            return true;
        }

        public bool TryGetObject(Span buffer, out object value, out int consumed)
        {
            value = default;
            consumed = 0;
            if (!DataIsType(buffer, Amf3Type.Object))
            {
                return false;
            }
            consumed = Amf3CommonValues.MARKER_LENGTH;
            if (!TryGetU29Impl(buffer.Slice(Amf3CommonValues.MARKER_LENGTH), out var header, out var headerLength))
            {
                return false;
            }
            consumed += headerLength;

            if (!TryGetReference(header, _objectReferenceTable, out var headerData, out object refValue, out var isRef))
            {
                return false;
            }

            if (isRef)
            {
                value = refValue;
                return true;
            }
            Amf3ClastTraits traits = null;
            if ((header & 0x02) == 0x00)
            {
                var referenceIndex = (int)((header >> 2) & 0x3FFFFFFF);
                if (_objectTraitsReferenceTable.Count > 4;
                for (int i = 0; i < memberCount; i++)
                {
                    if (!TryGetStringImpl(dataBuffer, _stringReferenceTable, out var key, out var keyConsumed))
                    {
                        return false;
                    }
                    traits.Members.Add(key);
                    dataBuffer = dataBuffer.Slice(keyConsumed);
                    consumed += keyConsumed;
                }
                _objectTraitsReferenceTable.Add(traits);
            }

            object deserailziedObject = null;
            var valueBuffer = buffer.Slice(consumed);
            if (traits.ClastType == Amf3ClastType.Typed)
            {
                if (!_registeredTypedObejectStates.TryGetValue(traits.ClastName, out var state))
                {
                    return false;
                }

                var clastType = state.Type;
                if (!traits.Members.OrderBy(m => m).SequenceEqual(state.Members.Keys.OrderBy(p => p)))
                {
                    return false;
                }

                deserailziedObject = Activator.CreateInstance(clastType);
                _objectReferenceTable.Add(deserailziedObject);
                foreach (var member in traits.Members)
                {
                    if (!TryGetValue(valueBuffer, out var data, out var dataConsumed))
                    {
                        return false;
                    }
                    valueBuffer = valueBuffer.Slice(dataConsumed);
                    consumed += dataConsumed;
                    state.Members[member](deserailziedObject, data);
                }
            }
            else
            {
                var obj = new AmfObject();
                _objectReferenceTable.Add(obj);
                foreach (var member in traits.Members)
                {
                    if (!TryGetValue(valueBuffer, out var data, out var dataConsumed))
                    {
                        return false;
                    }
                    valueBuffer = valueBuffer.Slice(dataConsumed);
                    consumed += dataConsumed;
                    obj.Add(member, data);
                }

                deserailziedObject = obj;
            }
            if (traits.IsDynamic)
            {
                var dynamicObject = deserailziedObject as IDynamicObject;
                if (dynamicObject == null)
                {
                    return false;
                }
                if (!TryGetStringImpl(valueBuffer, _stringReferenceTable, out var key, out var keyConsumed))
                {
                    return false;
                }
                consumed += keyConsumed;
                valueBuffer = valueBuffer.Slice(keyConsumed);
                while (key.Any())
                {
                    if (!TryGetValue(valueBuffer, out var data, out var dataConsumed))
                    {
                        return false;
                    }
                    valueBuffer = valueBuffer.Slice(dataConsumed);
                    consumed += dataConsumed;

                    dynamicObject.AddDynamic(key, data);

                    if (!TryGetStringImpl(valueBuffer, _stringReferenceTable, out key, out keyConsumed))
                    {
                        return false;
                    }
                    valueBuffer = valueBuffer.Slice(keyConsumed);
                    consumed += keyConsumed;
                }
            }

            value = deserailziedObject;

            return true;
        }

        public bool TryGetXml(Span buffer, out Amf3Xml value, out int consumed)
        {
            value = default;
            consumed = default;
            if (!DataIsType(buffer, Amf3Type.Xml))
            {
                return false;
            }

            var objectBuffer = buffer.Slice(Amf3CommonValues.MARKER_LENGTH);
            TryGetStringImpl(objectBuffer, _objectReferenceTable, out var str, out var strConsumed);
            var xml = new Amf3Xml();
            xml.LoadXml(str);

            value = xml;
            consumed = Amf3CommonValues.MARKER_LENGTH + strConsumed;
            return true;
        }

        public bool TryGetByteArray(Span buffer, out byte[] value, out int consumed)
        {
            value = default;
            consumed = default;
            if (!DataIsType(buffer, Amf3Type.ByteArray))
            {
                return false;
            }

            var objectBuffer = buffer.Slice(Amf3CommonValues.MARKER_LENGTH);

            if (!TryGetU29Impl(objectBuffer, out var header, out int headerLen))
            {
                return false;
            }

            if (!TryGetReference(header, _objectReferenceTable, out var headerData, out byte[] refValue, out var isRef))
            {
                return false;
            }
            if (isRef)
            {
                value = refValue;
                consumed = Amf3CommonValues.MARKER_LENGTH + headerLen;
                return true;
            }

            var arrayLen = (int)headerData;
            if (objectBuffer.Length - headerLen < arrayLen)
            {
                return false;
            }

            value = new byte[arrayLen];

            objectBuffer.Slice(headerLen, arrayLen).CopyTo(value);
            _objectReferenceTable.Add(value);
            consumed = Amf3CommonValues.MARKER_LENGTH + headerLen + arrayLen;
            return true;
        }

        public bool TryGetValue(Span buffer, out object value, out int consumed)
        {
            value = default;
            consumed = default;

            if (!TryDescribeData(buffer, out var type))
            {
                return false;
            }

            if (!_readerHandlers.TryGetValue(type, out var handler))
            {
                return false;
            }

            if (!handler(buffer, out value, out consumed))
            {
                return false;
            }
            return true;
        }

        public bool TryGetVectorInt(Span buffer, out Vector value, out int consumed)
        {
            value = default;
            consumed = Amf3CommonValues.MARKER_LENGTH;
            if (!DataIsType(buffer, Amf3Type.VectorInt))
            {
                return false;
            }

            buffer = buffer.Slice(Amf3CommonValues.MARKER_LENGTH);
            if (!ReadVectorHeader(ref buffer, ref value, ref consumed, out var itemCount, out var isFixedSize, out var isRef))
            {
                return false;
            }

            if (isRef)
            {
                return true;
            }

            var vector = new Vector { IsFixedSize = isFixedSize };
            _objectReferenceTable.Add(vector);
            for (int i = 0; i < itemCount; i++)
            {
                if (!TryGetIntVectorData(ref buffer, vector, ref consumed))
                {
                    return false;
                }
            }
            value = vector;
            return true;
        }

        public bool TryGetVectorUint(Span buffer, out Vector value, out int consumed)
        {
            value = default;
            consumed = Amf3CommonValues.MARKER_LENGTH;
            if (!DataIsType(buffer, Amf3Type.VectorUInt))
            {
                return false;
            }

            buffer = buffer.Slice(Amf3CommonValues.MARKER_LENGTH);
            if (!ReadVectorHeader(ref buffer, ref value, ref consumed, out var itemCount, out var isFixedSize, out var isRef))
            {
                return false;
            }

            if (isRef)
            {
                return true;
            }

            var vector = new Vector { IsFixedSize = isFixedSize };
            _objectReferenceTable.Add(vector);
            for (int i = 0; i < itemCount; i++)
            {
                if (!TryGetUIntVectorData(ref buffer, vector, ref consumed))
                {
                    return false;
                }
            }

            value = vector;
            return true;
        }

        public bool TryGetVectorDouble(Span buffer, out Vector value, out int consumed)
        {
            value = default;
            consumed = default;
            if (!DataIsType(buffer, Amf3Type.VectorDouble))
            {
                return false;
            }

            buffer = buffer.Slice(Amf3CommonValues.MARKER_LENGTH);
            if (!ReadVectorHeader(ref buffer, ref value, ref consumed, out var itemCount, out var isFixedSize, out var isRef))
            {
                return false;
            }

            if (isRef)
            {
                return true;
            }

            var vector = new Vector() { IsFixedSize = isFixedSize };
            _objectReferenceTable.Add(vector);
            for (int i = 0; i < itemCount; i++)
            {
                if (!TryGetDoubleVectorData(ref buffer, vector, ref consumed))
                {
                    return false;
                }
            }

            value = vector;
            return true;
        }

        private bool TryGetIntVectorData(ref Span buffer, Vector vector, ref int consumed)
        {
            var value = NetworkBitConverter.ToInt32(buffer);
            vector.Add(value);
            consumed += sizeof(int);
            buffer = buffer.Slice(sizeof(int));
            return true;

        }

        private bool TryGetUIntVectorData(ref Span buffer, Vector vector, ref int consumed)
        {
            var value = NetworkBitConverter.ToUInt32(buffer);
            vector.Add(value);
            consumed += sizeof(uint);
            buffer = buffer.Slice(sizeof(uint));
            return true;
        }

        private bool TryGetDoubleVectorData(ref Span buffer, Vector vector, ref int consumed)
        {
            var value = NetworkBitConverter.ToDouble(buffer);
            vector.Add(value);
            consumed += sizeof(double);
            buffer = buffer.Slice(sizeof(double));
            return true;

        }

        private bool TryGetReference(uint header, List referenceTable, out uint headerData, out T value, out bool isRef)
        {
            isRef = default;
            value = default;
            headerData = header >> 1;
            if ((header & 0x01) == 0x00)
            {
                var referenceIndex = (int)headerData;
                if (referenceTable.Count  addMethod.Invoke(resultVector, new object[] { o });
            }
            for (int i = 0; i < itemCount; i++)
            {
                if (!TryGetValue(arrayBodyBuffer, out var item, out var itemConsumed))
                {
                    return false;
                }
                addAction(item);

                arrayBodyBuffer = arrayBodyBuffer.Slice(itemConsumed);
                arrayConsumed += itemConsumed;
            }
            value = resultVector;
            consumed = typeNameConsumed + arrayConsumed;
            return true;
        }

        public bool TryGetDictionary(Span buffer, out Amf3Dictionary value, out int consumed)
        {
            value = default;
            consumed = default;
            if (!DataIsType(buffer, Amf3Type.Dictionary))
            {
                return false;
            }

            if (!TryGetU29Impl(buffer.Slice(Amf3CommonValues.MARKER_LENGTH), out var header, out var headerLength))
            {
                return false;
            }

            if (!TryGetReference(header, _objectReferenceTable, out var headerData, out Amf3Dictionary refValue, out var isRef))
            {
                return false;
            }
            if (isRef)
            {
                value = refValue;
                consumed = Amf3CommonValues.MARKER_LENGTH + headerLength;
                return true;
            }

            var itemCount = (int)headerData;
            var dictConsumed = 0;
            if (buffer.Length - Amf3CommonValues.MARKER_LENGTH - headerLength < sizeof(byte))
            {
                return false;
            }
            var weakKeys = buffer[Amf3CommonValues.MARKER_LENGTH + headerLength] == 0x01;

            var dictBuffer = buffer.Slice(Amf3CommonValues.MARKER_LENGTH + headerLength + /* weak key flag */ sizeof(byte));
            var dict = new Amf3Dictionary()
            {
                WeakKeys = weakKeys
            };
            _objectReferenceTable.Add(dict);
            for (int i = 0; i < itemCount; i++)
            {
                if (!TryGetValue(dictBuffer, out var key, out var keyConsumed))
                {
                    return false;
                }
                dictBuffer = dictBuffer.Slice(keyConsumed);
                dictConsumed += keyConsumed;
                if (!TryGetValue(dictBuffer, out var data, out var dataConsumed))
                {
                    return false;
                }
                dictBuffer = dictBuffer.Slice(dataConsumed);
                dict.Add(key, data);
                dictConsumed += dataConsumed;
            }
            value = dict;
            consumed = Amf3CommonValues.MARKER_LENGTH + headerLength + dictConsumed + /* weak key flag */ sizeof(byte);
            return true;
        }
    }
}