Skip to main content

ferogram_tl_types/
deserialize.rs

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