public override T Read <T>(ReadOnlySpan <byte> data, ValueConverter <T> converter = null)
        {
            // Strip version
            if (data.Length == 0)
            {
                throw new SerializationException("No version byte found");
            }
            data = data.Slice(1);

            if (data.Length != 0 && EtfReader.GetTokenType(ref data) == EtfTokenType.DistributionHeader)
            {
                throw new SerializationException("Distribution header is unsupported");
            }

            return(base.Read(data, converter));
        }
Esempio n. 2
0
        public static bool TryRead <T>(EtfSerializer serializer, ref ReadOnlySpan <byte> remaining, out T[] result,
                                       PropertyMap propMap, ValueConverter <T> innerConverter, ArrayPool <T> itemPool, ArrayPool <byte> bytePool)
        {
            result = default;
            int resultCount = 0;

            switch (EtfReader.GetTokenType(ref remaining))
            {
            case EtfTokenType.SmallTuple:
            {
                if (remaining.Length < 2)
                {
                    return(false);
                }

                // remaining = remaining.Slice(1);
                byte count = remaining[1];
                remaining = remaining.Slice(2);

                result = new T[count];
                for (int i = 0; i < count; i++, resultCount++)
                {
                    var restore = remaining;
                    if (!innerConverter.TryRead(ref remaining, out result[resultCount], propMap))
                    {
                        if (propMap?.IgnoreErrors == true)
                        {
                            remaining = restore;
                            if (!EtfReader.Skip(ref remaining, out _))
                            {
                                return(false);
                            }
                            serializer.RaiseFailedProperty(propMap, i);
                            resultCount--;
                        }
                        else
                        {
                            return(false);
                        }
                    }
                }
                break;
            }

            case EtfTokenType.LargeTuple:
            {
                if (remaining.Length < 5)
                {
                    return(false);
                }

                remaining = remaining.Slice(1);
                uint count = BinaryPrimitives.ReadUInt32BigEndian(remaining);
                remaining = remaining.Slice(4);

                if (count > int.MaxValue)
                {
                    return(false);
                }

                result = new T[count];
                for (int i = 0; i < count; i++, resultCount++)
                {
                    var restore = remaining;
                    if (!innerConverter.TryRead(ref remaining, out result[resultCount], propMap))
                    {
                        if (propMap?.IgnoreErrors == true)
                        {
                            remaining = restore;
                            if (!EtfReader.Skip(ref remaining, out _))
                            {
                                return(false);
                            }
                            serializer.RaiseFailedProperty(propMap, i);
                            resultCount--;
                        }
                        else
                        {
                            return(false);
                        }
                    }
                }
                break;
            }

            case EtfTokenType.List:
            {
                if (remaining.Length < 5)
                {
                    return(false);
                }

                remaining = remaining.Slice(1);
                uint count = BinaryPrimitives.ReadUInt32BigEndian(remaining);
                remaining = remaining.Slice(4);

                if (count > int.MaxValue)
                {
                    return(false);
                }

                result = new T[count];
                for (int i = 0; i < count; i++, resultCount++)
                {
                    var restore = remaining;
                    if (!innerConverter.TryRead(ref remaining, out result[resultCount], propMap))
                    {
                        if (propMap?.IgnoreErrors == true)
                        {
                            remaining = restore;
                            if (!EtfReader.Skip(ref remaining, out _))
                            {
                                return(false);
                            }
                            serializer.RaiseFailedProperty(propMap, i);
                            resultCount--;
                        }
                        else
                        {
                            return(false);
                        }
                    }
                }

                // Tail element
                if (EtfReader.GetTokenType(ref remaining) != EtfTokenType.Nil)
                {
                    return(false);
                }
                remaining = remaining.Slice(1);
                break;
            }

            case EtfTokenType.String:
            {
                if (remaining.Length < 3)
                {
                    return(false);
                }

                remaining = remaining.Slice(1);
                ushort count = BinaryPrimitives.ReadUInt16BigEndian(remaining);
                remaining = remaining.Slice(2);

                if (remaining.Length < count)
                {
                    return(false);
                }

                // String optimizes away the per-item header, but our item readers depend on them.
                // Unfortunately this means we need to inject a header
                // In the case of reading a byte[] however, we can do a direct copy
                if (typeof(T) == typeof(byte))
                {
                    result = new T[count];
                    remaining.Slice(0, count).CopyTo((result as byte[]).AsSpan());
                    remaining = remaining.Slice(count);
                }
                else
                {
                    var tmpArr = bytePool.Rent(2);
                    try
                    {
                        tmpArr[0] = (byte)EtfTokenType.SmallInteger;
                        result    = new T[count];
                        for (int i = 0; i < count; i++, resultCount++)
                        {
                            tmpArr[1] = remaining[i];
                            var span = new ReadOnlySpan <byte>(tmpArr, 0, 2);
                            if (!innerConverter.TryRead(ref span, out result[resultCount], propMap))
                            {
                                if (propMap?.IgnoreErrors == true)
                                {
                                    serializer.RaiseFailedProperty(propMap, i);
                                    resultCount--;
                                }
                                else
                                {
                                    return(false);
                                }
                            }
                        }
                        remaining = remaining.Slice(count);
                    }
                    finally { bytePool.Return(tmpArr); }
                }
                break;
            }

            case EtfTokenType.Binary:
            {
                if (remaining.Length < 5)
                {
                    return(false);
                }

                remaining = remaining.Slice(1);
                uint count = BinaryPrimitives.ReadUInt32BigEndian(remaining);
                if (count > int.MaxValue || remaining.Length < count + 4U)
                {
                    return(false);
                }
                remaining = remaining.Slice(4);

                if (remaining.Length < count)
                {
                    return(false);
                }

                // String optimizes away the per-item header, but our item readers depend on them.
                // Unfortunately this means we need to inject a header
                // In the case of reading a byte[] however, we can do a direct copy
                if (typeof(T) == typeof(byte))
                {
                    result = new T[count];
                    remaining.Slice(0, (int)count).CopyTo((result as byte[]).AsSpan());
                    remaining = remaining.Slice((int)count);
                }
                else
                {
                    var tmpArr = bytePool.Rent(2);
                    try
                    {
                        tmpArr[0] = (byte)EtfTokenType.SmallInteger;
                        result    = new T[count];
                        for (int i = 0; i < count; i++, resultCount++)
                        {
                            tmpArr[1] = remaining[i];
                            var span = new ReadOnlySpan <byte>(tmpArr, 0, 2);
                            if (!innerConverter.TryRead(ref span, out result[resultCount], propMap))
                            {
                                if (propMap?.IgnoreErrors == true)
                                {
                                    serializer.RaiseFailedProperty(propMap, i);
                                    resultCount--;
                                }
                                else
                                {
                                    return(false);
                                }
                            }
                        }
                        remaining = remaining.Slice((int)count);
                    }
                    finally { bytePool.Return(tmpArr); }
                }
                break;
            }

            default:
                return(false);
            }

            // Resize array if any elements failed
            if (resultCount != result.Length)
            {
                Array.Resize(ref result, resultCount);
            }
            return(true);
        }
        public override bool TryRead(ref ReadOnlySpan <byte> remaining, out T result, PropertyMap propMap = null)
        {
            if (EtfReader.TryReadNullSafe(ref remaining))
            {
                result = null;
                return(true);
            }

            result = default;

            switch (EtfReader.GetTokenType(ref remaining))
            {
            case EtfTokenType.Map:
                if (remaining.Length < 5)
                {
                    return(false);
                }
                remaining = remaining.Slice(1);

                uint arity = BinaryPrimitives.ReadUInt32BigEndian(remaining);     // count
                remaining = remaining.Slice(4);

                result = _map.CreateUninitialized();
                if (arity == 0)
                {
                    return(true);
                }

                uint dependencies = 0;
                var  deferred     = new DeferredPropertyList <byte, byte>();

                for (int i = 0; i < arity; i++)
                {
                    if (!EtfReader.TryReadUtf8String(ref remaining, out var key))
                    {
                        return(false);
                    }

                    // Unknown Property
                    if (!_map.TryGetProperty(key, out var innerPropMap, out bool isIgnored))
                    {
                        if (!isIgnored)
                        {
                            _serializer.RaiseUnknownProperty(_map, key);
                        }
                        if (!EtfReader.Skip(ref remaining, out _))
                        {
                            return(false);
                        }
                        continue;
                    }

                    if (!innerPropMap.CanRead)
                    {
                        return(false);
                    }

                    // Property depends on another that hasn't been deserialized yet
                    if (!innerPropMap.HasReadConverter(result, dependencies))
                    {
                        if (!EtfReader.Skip(ref remaining, out var skipped))
                        {
                            return(false);
                        }
                        if (!deferred.Add(key, skipped))
                        {
                            return(false);
                        }
                        continue;
                    }

                    var restore = remaining;
                    if (!innerPropMap.TryRead(result, ref remaining, dependencies))
                    {
                        if (innerPropMap.IgnoreErrors)
                        {
                            remaining = restore;
                            if (!EtfReader.Skip(ref remaining, out var skipped))
                            {
                                return(false);
                            }
                            _serializer.RaiseFailedProperty(_map, innerPropMap);
                            continue;
                        }
                        else
                        {
                            return(false);
                        }
                    }

                    dependencies |= innerPropMap.IndexMask;
                }

                // Process all deferred properties
                for (int i = 0; i < deferred.Count; i++)
                {
                    if (!_map.TryGetProperty(deferred.GetKey(i), out var innerPropMap, out _))
                    {
                        return(false);
                    }
                    var value = deferred.GetValue(i);

                    if (!innerPropMap.TryRead(result, ref value, dependencies))
                    {
                        if (innerPropMap.IgnoreErrors)
                        {
                            _serializer.RaiseFailedProperty(_map, innerPropMap);
                        }
                        else
                        {
                            return(false);
                        }
                    }
                }
                return(true);

            default:
                return(false);
            }
        }