Amf3
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;
}
}
}