예제 #1
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);
        }
예제 #2
0
 public ListEtfConverter(EtfSerializer serializer, ValueConverter <T> innerConverter, ArrayPool <T> pool = null)
 {
     _serializer     = serializer;
     _innerConverter = innerConverter;
     _pool           = pool;
 }
 public ObjectEtfConverter(EtfSerializer serializer)
 {
     _serializer = serializer;
     _map        = serializer.GetMap <T>();
 }