Ctrl K
ComunidadGuías…

Ferrite.TL.Schema/LayerWireCodec.cs

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2022-2026 Aykut Alparslan KOC

using System.IO;

namespace Ferrite.TL.Schema;

internal static class LayerWireCodec
{
    internal static LayerObjectValue ReadBoxedObject(LayerWireReader reader,
        LayerSchema schema)
    {
        int id = reader.ReadInt32();
        if (!schema.TryGetConstructor(id, out LayerConstructor constructor))
        {
            throw new LayerUncoveredException("Unknown source constructor 0x" +
                                              unchecked((uint)id).ToString("x8") + ".");
        }
        return ReadObjectBody(reader, schema, constructor);
    }

    internal static LayerObjectValue ReadObjectBody(LayerWireReader reader,
        LayerSchema schema, LayerConstructor constructor)
    {
        var fields = new Dictionary<string, object?>(StringComparer.Ordinal);
        foreach (LayerField field in constructor.Fields)
        {
            if (field.IsConditional)
            {
                if (!fields.TryGetValue(field.FlagsField!, out object? flagsValue) ||
                    flagsValue is not int flags)
                {
                    throw new LayerWireException("Conditional field references missing flags.");
                }
                if ((flags & (1 << field.FlagBit!.Value)) == 0)
                {
                    continue;
                }
                if (field.Type.Kind == LayerWireKind.True)
                {
                    fields.Add(field.Name, true);
                    continue;
                }
            }
            fields.Add(field.Name, ReadValue(reader, schema, field.Type));
        }
        return new LayerObjectValue(constructor, fields);
    }

    internal static object ReadValue(LayerWireReader reader, LayerSchema schema,
        LayerType type)
    {
        switch (type.Kind)
        {
            case LayerWireKind.Int32:
            case LayerWireKind.Flags:
                return reader.ReadInt32();
            case LayerWireKind.Int64:
                return reader.ReadInt64();
            case LayerWireKind.Double:
                return reader.ReadDouble();
            case LayerWireKind.Int128:
                return reader.ReadFixed(16);
            case LayerWireKind.Int256:
                return reader.ReadFixed(32);
            case LayerWireKind.Int512:
                return reader.ReadFixed(64);
            case LayerWireKind.Bytes:
            case LayerWireKind.String:
                return reader.ReadTlBytes();
            case LayerWireKind.True:
                return true;
            case LayerWireKind.Object:
                if (type.IsBare)
                {
                    if (type.Name == null ||
                        !schema.TryGetBareConstructor(type.Name,
                            out LayerConstructor bareConstructor))
                    {
                        throw new LayerUncoveredException("Bare type is ambiguous: " +
                                                          type.Name + ".");
                    }
                    return ReadObjectBody(reader, schema, bareConstructor);
                }
                LayerObjectValue boxed = ReadBoxedObject(reader, schema);
                if (type.Name != null &&
                    !string.Equals(type.Name, boxed.Constructor.ResultType,
                        StringComparison.Ordinal))
                {
                    throw new LayerWireException("Boxed object has the wrong result type.");
                }
                return boxed;
            case LayerWireKind.Vector:
                if (!type.IsBare)
                {
                    int vectorId = reader.ReadInt32();
                    if (vectorId != LayerSchemaReader.VectorConstructorId)
                    {
                        throw new LayerWireException("Invalid vector constructor.");
                    }
                }
                int count = reader.ReadInt32();
                if (count < 0 || count > 1_000_000)
                {
                    throw new LayerWireException("Invalid vector count.");
                }
                var values = new object?[count];
                for (int i = 0; i < count; i++)
                {
                    values[i] = ReadValue(reader, schema, type.ElementType!);
                }
                return Array.AsReadOnly(values);
            default:
                throw new LayerWireException("Unknown wire type.");
        }
    }

    internal static void WriteBoxedObject(LayerWireWriter writer,
        LayerSchema schema, LayerObjectValue value)
    {
        LayerConstructor constructor = schema.GetConstructor(value.Constructor.Name);
        writer.WriteInt32(constructor.Id);
        WriteObjectBody(writer, schema, constructor, value);
    }

    internal static void WriteObjectBody(LayerWireWriter writer,
        LayerSchema schema, LayerConstructor constructor, LayerObjectValue value)
    {
        var flags = new Dictionary<string, int>(StringComparer.Ordinal);
        foreach (LayerField field in constructor.Fields)
        {
            if (field.IsConditional && value.Fields.ContainsKey(field.Name))
            {
                flags.TryGetValue(field.FlagsField!, out int current);
                flags[field.FlagsField!] = current | (1 << field.FlagBit!.Value);
            }
        }

        foreach (LayerField field in constructor.Fields)
        {
            if (field.Type.Kind == LayerWireKind.Flags)
            {
                flags.TryGetValue(field.Name, out int flagValue);
                writer.WriteInt32(flagValue);
                continue;
            }
            if (!value.TryGetValue(field.Name, out object? fieldValue))
            {
                if (field.IsConditional)
                {
                    continue;
                }
                throw new LayerUncoveredException("Required output field is absent: " +
                                                  field.Name + ".");
            }
            if (field.IsConditional && field.Type.Kind == LayerWireKind.True)
            {
                continue;
            }
            WriteValue(writer, schema, field.Type, fieldValue);
        }
    }

    internal static void WriteValue(LayerWireWriter writer, LayerSchema schema,
        LayerType type, object? value)
    {
        if (value == null)
        {
            throw new LayerUncoveredException("Output value is null.");
        }
        switch (type.Kind)
        {
            case LayerWireKind.Int32:
            case LayerWireKind.Flags:
                writer.WriteInt32((int)value);
                return;
            case LayerWireKind.Int64:
                writer.WriteInt64((long)value);
                return;
            case LayerWireKind.Double:
                writer.WriteDouble((double)value);
                return;
            case LayerWireKind.Int128:
            case LayerWireKind.Int256:
            case LayerWireKind.Int512:
                writer.Write((byte[])value);
                return;
            case LayerWireKind.Bytes:
            case LayerWireKind.String:
                writer.WriteTlBytes((byte[])value);
                return;
            case LayerWireKind.True:
                return;
            case LayerWireKind.Object:
                if (type.IsBare)
                {
                    LayerObjectValue bare = (LayerObjectValue)value;
                    LayerConstructor constructor = schema.GetConstructor(bare.Constructor.Name);
                    if (type.Name != null &&
                        !string.Equals(type.Name, constructor.ResultType,
                            StringComparison.Ordinal))
                    {
                        throw new LayerUncoveredException("Bare object has the wrong result type.");
                    }
                    WriteObjectBody(writer, schema, constructor, bare);
                    return;
                }
                LayerObjectValue boxed = (LayerObjectValue)value;
                LayerConstructor boxedConstructor = schema.GetConstructor(
                    boxed.Constructor.Name);
                if (type.Name != null &&
                    !string.Equals(type.Name, boxedConstructor.ResultType,
                        StringComparison.Ordinal))
                {
                    throw new LayerUncoveredException("Boxed object has the wrong result type.");
                }
                WriteBoxedObject(writer, schema, boxed);
                return;
            case LayerWireKind.Vector:
                if (!type.IsBare)
                {
                    writer.WriteInt32(LayerSchemaReader.VectorConstructorId);
                }
                var values = (IReadOnlyList<object?>)value;
                writer.WriteInt32(values.Count);
                for (int i = 0; i < values.Count; i++)
                {
                    WriteValue(writer, schema, type.ElementType!, values[i]);
                }
                return;
            default:
                throw new LayerWireException("Unknown output wire type.");
        }
    }
}

internal sealed class LayerWireReader
{
    private readonly byte[] _buffer;
    private int _offset;

    public bool IsComplete => _offset == _buffer.Length;

    public LayerWireReader(byte[] buffer)
    {
        _buffer = buffer;
    }

    public int ReadInt32()
    {
        Require(4);
        int value = _buffer[_offset] | _buffer[_offset + 1] << 8 |
                    _buffer[_offset + 2] << 16 | _buffer[_offset + 3] << 24;
        _offset += 4;
        return value;
    }

    public long ReadInt64()
    {
        Require(8);
        ulong value = 0;
        for (int i = 0; i < 8; i++)
        {
            value |= (ulong)_buffer[_offset + i] << (8 * i);
        }
        _offset += 8;
        return unchecked((long)value);
    }

    public double ReadDouble()
    {
        long bits = ReadInt64();
        return BitConverter.Int64BitsToDouble(bits);
    }

    public byte[] ReadFixed(int length)
    {
        Require(length);
        var value = new byte[length];
        Buffer.BlockCopy(_buffer, _offset, value, 0, length);
        _offset += length;
        return value;
    }

    public byte[] ReadTlBytes()
    {
        Require(1);
        int first = _buffer[_offset++];
        int length;
        int headerLength;
        if (first < 254)
        {
            length = first;
            headerLength = 1;
        }
        else if (first == 254)
        {
            Require(3);
            length = _buffer[_offset] | _buffer[_offset + 1] << 8 |
                     _buffer[_offset + 2] << 16;
            _offset += 3;
            headerLength = 4;
        }
        else
        {
            throw new LayerWireException("Invalid TL bytes length prefix.");
        }

        byte[] value = ReadFixed(length);
        int padding = (4 - ((headerLength + length) % 4)) % 4;
        Require(padding);
        _offset += padding;
        return value;
    }

    private void Require(int count)
    {
        if (count < 0 || _offset > _buffer.Length - count)
        {
            throw new LayerWireException("TL payload is truncated.");
        }
    }
}

internal sealed class LayerWireWriter
{
    private readonly MemoryStream _stream = new MemoryStream();

    public void WriteInt32(int value)
    {
        Write(new[]
        {
            (byte)value,
            (byte)(value >> 8),
            (byte)(value >> 16),
            (byte)(value >> 24)
        });
    }

    public void WriteInt64(long value)
    {
        ulong bits = unchecked((ulong)value);
        var bytes = new byte[8];
        for (int i = 0; i < 8; i++)
        {
            bytes[i] = (byte)(bits >> (8 * i));
        }
        Write(bytes);
    }

    public void WriteDouble(double value)
    {
        WriteInt64(BitConverter.DoubleToInt64Bits(value));
    }

    public void WriteTlBytes(byte[] value)
    {
        int headerLength;
        if (value.Length < 254)
        {
            Write(new[] { (byte)value.Length });
            headerLength = 1;
        }
        else
        {
            if (value.Length > 0x00ffffff)
            {
                throw new LayerWireException("TL bytes value is too long.");
            }
            Write(new[]
            {
                (byte)254,
                (byte)value.Length,
                (byte)(value.Length >> 8),
                (byte)(value.Length >> 16)
            });
            headerLength = 4;
        }
        Write(value);
        int padding = (4 - ((headerLength + value.Length) % 4)) % 4;
        if (padding != 0)
        {
            Write(new byte[padding]);
        }
    }

    public void Write(byte[] value)
    {
        _stream.Write(value, 0, value.Length);
    }

    public byte[] ToArray()
    {
        return _stream.ToArray();
    }
}

internal sealed class LayerWireException : Exception
{
    public LayerWireException(string message) : base(message)
    {
    }
}

internal sealed class LayerUncoveredException : Exception
{
    public LayerUncoveredException(string message) : base(message)
    {
    }
}