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