// Copyright (c) Facebook, Inc. and its affiliates // SPDX-License-Identifier: MIT OR Apache-2.0 using System; using System.IO; using System.Numerics; using System.Text; namespace Serde { public abstract class BinaryDeserializer : IDeserializer, IDisposable { protected readonly ArraySegment input; protected readonly BinaryReader reader; protected readonly Encoding utf8 = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback()); private long containerDepthBudget; public BinaryDeserializer(byte[] _input, long maxContainerDepth) : this(new ArraySegment(_input), maxContainerDepth) { } public BinaryDeserializer(ArraySegment _input, long maxContainerDepth) { input = _input; reader = new BinaryReader(new MemoryStream(input.Array, input.Offset, input.Count)); containerDepthBudget = maxContainerDepth; } public void Dispose() => reader.Dispose(); public int get_buffer_offset() => (int)reader.BaseStream.Position; public abstract long deserialize_len(); public abstract int deserialize_variant_index(); public abstract void check_that_key_slices_are_increasing(Range key1, Range key2); public char deserialize_char() => throw new DeserializationException("Not implemented: char deserialization"); public float deserialize_f32() => reader.ReadSingle(); public double deserialize_f64() => reader.ReadDouble(); public void increase_container_depth() { if (containerDepthBudget == 0) { throw new DeserializationException("Exceeded maximum container depth"); } containerDepthBudget -= 1; } public void decrease_container_depth() { containerDepthBudget += 1; } public string deserialize_str() { long len = deserialize_len(); if (len < 0 || len > int.MaxValue) { throw new DeserializationException("Incorrect length value for C# string"); } byte[] content = reader.ReadBytes((int)len); if (content.Length < len) throw new DeserializationException($"Need {len - content.Length} more bytes for string"); return utf8.GetString(content); } public ValueArray deserialize_bytes() { long len = deserialize_len(); if (len < 0 || len > int.MaxValue) { throw new DeserializationException("Incorrect length value for C# array"); } byte[] content = reader.ReadBytes((int)len); if (content.Length < len) throw new DeserializationException($"Need {len - content.Length} more bytes for byte array"); return new ValueArray(content); } public bool deserialize_bool() { byte value = reader.ReadByte(); switch (value) { case 0: return false; case 1: return true; default: throw new DeserializationException("Incorrect value for bool: " + value); } } public Unit deserialize_unit() => new Unit(); public byte deserialize_u8() => reader.ReadByte(); public ushort deserialize_u16() => reader.ReadUInt16(); public uint deserialize_u32() => reader.ReadUInt32(); public ulong deserialize_u64() => reader.ReadUInt64(); public BigInteger deserialize_u128() { BigInteger signed = deserialize_i128(); if (signed >= 0) { return signed; } else { return signed + (BigInteger.One << 128); } } public sbyte deserialize_i8() => reader.ReadSByte(); public short deserialize_i16() => reader.ReadInt16(); public int deserialize_i32() => reader.ReadInt32(); public long deserialize_i64() => reader.ReadInt64(); public BigInteger deserialize_i128() { byte[] content = reader.ReadBytes(16); if (content.Length < 16) throw new DeserializationException("Need more bytes to deserialize 128-bit integer"); return new BigInteger(content); } public bool deserialize_option_tag() { byte value = reader.ReadByte(); switch (value) { case 0: return false; case 1: return true; default: throw new DeserializationException("Incorrect value for Option tag: " + value); } } } }