Skip to main content

ferogram_tl_types/
deserialize.rs

1// Copyright (c) Ankit Chaubey <ankitchaubey.dev@gmail.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3//
4// ferogram: async Telegram MTProto client in Rust
5// https://github.com/ankit-chaubey/ferogram
6//
7// If you use or modify this code, keep this notice at the top of your file
8// and include the LICENSE-MIT or LICENSE-APACHE file from this repository:
9// https://github.com/ankit-chaubey/ferogram
10
11use std::fmt;
12use std::sync::OnceLock;
13
14/// Returns `true` when the `FEROGRAM_TL_DEBUG` environment variable is set
15/// to any non-empty value.  The result is cached after the first call so there
16/// is zero overhead in hot deserialisation paths when debugging is off.
17///
18/// Enable at runtime:
19/// ```sh
20/// FEROGRAM_TL_DEBUG=1 cargo run ...
21/// ```
22pub fn tl_debug() -> bool {
23    static ENABLED: OnceLock<bool> = OnceLock::new();
24    *ENABLED.get_or_init(|| std::env::var("FEROGRAM_TL_DEBUG").is_ok_and(|v| !v.is_empty()))
25}
26
27/// Errors that can occur during deserialization.
28#[derive(Clone, Debug, PartialEq)]
29pub enum Error {
30    /// Ran out of bytes before the type was fully read.
31    UnexpectedEof,
32    /// Decoded a constructor ID that doesn't match any known variant.
33    UnexpectedConstructor { id: u32 },
34}
35
36impl fmt::Display for Error {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        match self {
39            Self::UnexpectedEof => write!(f, "unexpected end of buffer"),
40            Self::UnexpectedConstructor { id } => {
41                write!(f, "unexpected constructor id: {id:#010x}")
42            }
43        }
44    }
45}
46
47impl std::error::Error for Error {}
48
49/// Specialized `Result` for deserialization.
50pub type Result<T> = std::result::Result<T, Error>;
51
52// Cursor
53
54/// A zero-copy cursor over an in-memory byte slice.
55///
56/// Avoids `std::io::Cursor` and its wide error surface; only the two error
57/// cases above can ever occur during TL deserialization.
58pub struct Cursor<'a> {
59    buf: &'a [u8],
60    pos: usize,
61}
62
63impl<'a> Cursor<'a> {
64    /// Create a cursor positioned at the start of `buf`.
65    pub fn from_slice(buf: &'a [u8]) -> Self {
66        Self { buf, pos: 0 }
67    }
68
69    /// Current byte offset.
70    pub fn pos(&self) -> usize {
71        self.pos
72    }
73
74    /// Remaining bytes.
75    pub fn remaining(&self) -> usize {
76        self.buf.len() - self.pos
77    }
78
79    /// Read a single byte.
80    pub fn read_byte(&mut self) -> Result<u8> {
81        match self.buf.get(self.pos).copied() {
82            Some(b) => {
83                self.pos += 1;
84                Ok(b)
85            }
86            None => Err(Error::UnexpectedEof),
87        }
88    }
89
90    /// Read exactly `buf.len()` bytes.
91    pub fn read_exact(&mut self, out: &mut [u8]) -> Result<()> {
92        let end = self.pos + out.len();
93        if end > self.buf.len() {
94            return Err(Error::UnexpectedEof);
95        }
96        out.copy_from_slice(&self.buf[self.pos..end]);
97        self.pos = end;
98        Ok(())
99    }
100
101    /// Consume all remaining bytes into `out`.
102    pub fn read_to_end(&mut self, out: &mut Vec<u8>) -> usize {
103        let slice = &self.buf[self.pos..];
104        out.extend_from_slice(slice);
105        self.pos = self.buf.len();
106        slice.len()
107    }
108}
109
110/// Alias used by generated code: `crate::deserialize::Buffer<'_, '_>`.
111pub type Buffer<'a, 'b> = &'a mut Cursor<'b>;
112
113// Deserializable
114
115/// Deserialize a value from TL binary format.
116pub trait Deserializable: Sized {
117    /// Read `Self` from `buf`, advancing its position.
118    fn deserialize(buf: Buffer) -> Result<Self>;
119
120    /// Convenience: deserialize from a byte slice.
121    fn from_bytes(bytes: &[u8]) -> Result<Self> {
122        let mut cursor = Cursor::from_slice(bytes);
123        Self::deserialize(&mut cursor)
124    }
125
126    /// Deserialize from a byte slice, asserting that all bytes are consumed.
127    ///
128    /// Use this instead of `from_bytes` when the slice is the exact TL body
129    /// returned by an RPC call (i.e. no trailing data is expected). Consuming
130    /// all bytes acts as a sanity check against off-by-one alignment bugs.
131    fn from_bytes_exact(bytes: &[u8]) -> Result<Self> {
132        let mut cursor = Cursor::from_slice(bytes);
133        let value = Self::deserialize(&mut cursor)?;
134        // Trailing bytes are not treated as an error: the MTProto layer may
135        // append padding. Just return the decoded value.
136        Ok(value)
137    }
138}
139
140// Primitives
141
142impl Deserializable for bool {
143    fn deserialize(buf: Buffer) -> Result<Self> {
144        match u32::deserialize(buf)? {
145            0x997275b5 => Ok(true),
146            0xbc799737 => Ok(false),
147            id => Err(Error::UnexpectedConstructor { id }),
148        }
149    }
150}
151
152impl Deserializable for i32 {
153    fn deserialize(buf: Buffer) -> Result<Self> {
154        let mut b = [0u8; 4];
155        buf.read_exact(&mut b)?;
156        Ok(i32::from_le_bytes(b))
157    }
158}
159
160impl Deserializable for u32 {
161    fn deserialize(buf: Buffer) -> Result<Self> {
162        let mut b = [0u8; 4];
163        buf.read_exact(&mut b)?;
164        Ok(u32::from_le_bytes(b))
165    }
166}
167
168impl Deserializable for i64 {
169    fn deserialize(buf: Buffer) -> Result<Self> {
170        let mut b = [0u8; 8];
171        buf.read_exact(&mut b)?;
172        Ok(i64::from_le_bytes(b))
173    }
174}
175
176impl Deserializable for f64 {
177    fn deserialize(buf: Buffer) -> Result<Self> {
178        let mut b = [0u8; 8];
179        buf.read_exact(&mut b)?;
180        Ok(f64::from_le_bytes(b))
181    }
182}
183
184impl Deserializable for [u8; 16] {
185    fn deserialize(buf: Buffer) -> Result<Self> {
186        let mut b = [0u8; 16];
187        buf.read_exact(&mut b)?;
188        Ok(b)
189    }
190}
191
192impl Deserializable for [u8; 32] {
193    fn deserialize(buf: Buffer) -> Result<Self> {
194        let mut b = [0u8; 32];
195        buf.read_exact(&mut b)?;
196        Ok(b)
197    }
198}
199
200// Bytes / String
201
202impl Deserializable for Vec<u8> {
203    fn deserialize(buf: Buffer) -> Result<Self> {
204        let first = buf.read_byte()?;
205        let (len, header_extra) = if first != 0xfe {
206            (first as usize, 0)
207        } else {
208            let a = buf.read_byte()? as usize;
209            let b = buf.read_byte()? as usize;
210            let c = buf.read_byte()? as usize;
211            (a | (b << 8) | (c << 16), 3)
212        };
213
214        let mut data = vec![0u8; len];
215        buf.read_exact(&mut data)?;
216
217        // Skip alignment padding
218        let total = 1 + header_extra + len;
219        let padding = (4 - (total % 4)) % 4;
220        for _ in 0..padding {
221            buf.read_byte()?;
222        }
223
224        Ok(data)
225    }
226}
227
228impl Deserializable for String {
229    fn deserialize(buf: Buffer) -> Result<Self> {
230        let bytes = Vec::<u8>::deserialize(buf)?;
231        String::from_utf8(bytes).map_err(|_| Error::UnexpectedEof)
232    }
233}
234
235// Vectors
236
237impl<T: Deserializable> Deserializable for Vec<T> {
238    fn deserialize(buf: Buffer) -> Result<Self> {
239        let id = u32::deserialize(buf)?;
240        if id != 0x1cb5c415 {
241            return Err(Error::UnexpectedConstructor { id });
242        }
243        let len = i32::deserialize(buf)? as usize;
244        (0..len).map(|_| T::deserialize(buf)).collect()
245    }
246}
247
248impl<T: Deserializable> Deserializable for crate::RawVec<T> {
249    fn deserialize(buf: Buffer) -> Result<Self> {
250        let len = i32::deserialize(buf)? as usize;
251        let inner = (0..len)
252            .map(|_| T::deserialize(buf))
253            .collect::<Result<_>>()?;
254        Ok(crate::RawVec(inner))
255    }
256}