Skip to main content

gsym/
endian.rs

1use std::num::NonZeroUsize;
2
3use crate::error::{Error, Result};
4
5/// Byte order used for fixed-width integers in a GSYM file.
6///
7/// A file records its own byte order, and both orders can be read on any host.
8///
9/// Conversion writes the byte order of the ELF image it read. Everything else
10/// defaults to [`Little`](Self::Little) and can be changed with
11/// [`GsymBuilder::endian`](crate::GsymBuilder::endian) or
12/// [`TranscodeOptions`](crate::TranscodeOptions).
13#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14pub enum Endian {
15    /// Least-significant byte first.
16    Little,
17    /// Most-significant byte first.
18    Big,
19}
20
21impl Endian {
22    /// Returns the byte order of the compilation target.
23    #[must_use]
24    pub const fn native() -> Self {
25        if cfg!(target_endian = "little") {
26            Self::Little
27        } else {
28            Self::Big
29        }
30    }
31}
32
33/// Bounds-checked cursor over untrusted binary input.
34#[derive(Clone, Debug)]
35pub(crate) struct Cursor<'data> {
36    bytes: &'data [u8],
37    position: usize,
38    endian: Endian,
39}
40
41impl<'data> Cursor<'data> {
42    pub(crate) const fn new(bytes: &'data [u8], endian: Endian) -> Self {
43        Self {
44            bytes,
45            position: 0,
46            endian,
47        }
48    }
49
50    pub(crate) const fn at(bytes: &'data [u8], endian: Endian, position: usize) -> Result<Self> {
51        if position > bytes.len() {
52            return Err(Error::InvalidOffset {
53                offset: position as u64,
54                input_len: bytes.len(),
55            });
56        }
57        Ok(Self {
58            bytes,
59            position,
60            endian,
61        })
62    }
63
64    pub(crate) const fn endian(&self) -> Endian {
65        self.endian
66    }
67
68    pub(crate) const fn position(&self) -> usize {
69        self.position
70    }
71
72    pub(crate) const fn remaining(&self) -> usize {
73        self.bytes.len().saturating_sub(self.position)
74    }
75
76    pub(crate) const fn is_empty(&self) -> bool {
77        self.remaining() == 0
78    }
79
80    pub(crate) fn take(&mut self, count: usize) -> Result<&'data [u8]> {
81        let end = self
82            .position
83            .checked_add(count)
84            .ok_or(Error::Overflow("cursor end offset"))?;
85        let Some(bytes) = self.bytes.get(self.position..end) else {
86            return Err(Error::UnexpectedEof {
87                offset: self.position,
88                needed: count,
89                remaining: self.remaining(),
90            });
91        };
92        self.position = end;
93        Ok(bytes)
94    }
95
96    pub(crate) fn take_array<const N: usize>(&mut self) -> Result<[u8; N]> {
97        let offset = self.position;
98        let remaining = self.remaining();
99        let bytes = self
100            .bytes
101            .get(offset..)
102            .and_then(<[u8]>::first_chunk::<N>)
103            .ok_or(Error::UnexpectedEof {
104                offset,
105                needed: N,
106                remaining,
107            })?;
108        self.position = offset.saturating_add(N);
109        Ok(*bytes)
110    }
111
112    pub(crate) fn read_u8(&mut self) -> Result<u8> {
113        let [byte] = self.take_array()?;
114        Ok(byte)
115    }
116
117    pub(crate) fn read_u16(&mut self) -> Result<u16> {
118        let bytes = self.take_array()?;
119        Ok(match self.endian {
120            Endian::Little => u16::from_le_bytes(bytes),
121            Endian::Big => u16::from_be_bytes(bytes),
122        })
123    }
124
125    pub(crate) fn read_u32(&mut self) -> Result<u32> {
126        let bytes = self.take_array()?;
127        Ok(match self.endian {
128            Endian::Little => u32::from_le_bytes(bytes),
129            Endian::Big => u32::from_be_bytes(bytes),
130        })
131    }
132
133    pub(crate) fn read_u64(&mut self) -> Result<u64> {
134        let bytes = self.take_array()?;
135        Ok(match self.endian {
136            Endian::Little => u64::from_le_bytes(bytes),
137            Endian::Big => u64::from_be_bytes(bytes),
138        })
139    }
140
141    pub(crate) fn read_uint(&mut self, width: u8) -> Result<u64> {
142        match width {
143            1 => return self.read_u8().map(u64::from),
144            2 => return self.read_u16().map(u64::from),
145            4 => return self.read_u32().map(u64::from),
146            8 => return self.read_u64(),
147            3 | 5..=7 => {}
148            _ => {
149                return Err(Error::OutOfRange {
150                    field: "integer width",
151                    value: u64::from(width),
152                    max: 8,
153                });
154            }
155        }
156        let count = usize::from(width);
157        let bytes = self.take(count)?;
158        let mut buffer = [0_u8; 8];
159        let window = match self.endian {
160            Endian::Little => buffer.get_mut(..count),
161            Endian::Big => buffer.get_mut(size_of::<u64>().saturating_sub(count)..),
162        };
163        window
164            .ok_or_else(|| Error::OutOfRange {
165                field: "integer width",
166                value: u64::from(width),
167                max: 8,
168            })?
169            .copy_from_slice(bytes);
170        Ok(match self.endian {
171            Endian::Little => u64::from_le_bytes(buffer),
172            Endian::Big => u64::from_be_bytes(buffer),
173        })
174    }
175}
176
177/// Checked binary output buffer with endian-aware fixed-width writes.
178#[derive(Clone, Debug)]
179pub(crate) struct Encoder {
180    bytes: Vec<u8>,
181    endian: Endian,
182}
183
184impl Encoder {
185    #[cfg(test)]
186    pub(crate) const fn new(endian: Endian) -> Self {
187        Self {
188            bytes: Vec::new(),
189            endian,
190        }
191    }
192
193    pub(crate) fn with_capacity(endian: Endian, capacity: usize) -> Self {
194        Self {
195            bytes: Vec::with_capacity(capacity),
196            endian,
197        }
198    }
199
200    pub(crate) const fn len(&self) -> usize {
201        self.bytes.len()
202    }
203
204    #[cfg(test)]
205    pub(crate) fn as_slice(&self) -> &[u8] {
206        &self.bytes
207    }
208
209    pub(crate) fn into_inner(self) -> Vec<u8> {
210        self.bytes
211    }
212
213    pub(crate) fn write_u8(&mut self, value: u8) {
214        self.bytes.push(value);
215    }
216
217    pub(crate) fn write_u16(&mut self, value: u16) {
218        self.bytes.extend_from_slice(&match self.endian {
219            Endian::Little => value.to_le_bytes(),
220            Endian::Big => value.to_be_bytes(),
221        });
222    }
223
224    pub(crate) fn write_u32(&mut self, value: u32) {
225        self.bytes.extend_from_slice(&match self.endian {
226            Endian::Little => value.to_le_bytes(),
227            Endian::Big => value.to_be_bytes(),
228        });
229    }
230
231    pub(crate) fn write_u64(&mut self, value: u64) {
232        self.bytes.extend_from_slice(&match self.endian {
233            Endian::Little => value.to_le_bytes(),
234            Endian::Big => value.to_be_bytes(),
235        });
236    }
237
238    pub(crate) fn write_uint(&mut self, value: u64, width: u8) -> Result<()> {
239        if !(1..=8).contains(&width) {
240            return Err(Error::OutOfRange {
241                field: "integer width",
242                value: u64::from(width),
243                max: 8,
244            });
245        }
246        let count = usize::from(width);
247        if width < 8 {
248            let max = u64::MAX
249                .checked_shr(u32::from(8_u8.saturating_sub(width)).saturating_mul(8))
250                .unwrap_or(u64::MAX);
251            if value > max {
252                return Err(Error::OutOfRange {
253                    field: "fixed-width integer",
254                    value,
255                    max,
256                });
257            }
258        }
259        let bytes = match self.endian {
260            Endian::Little => value.to_le_bytes(),
261            Endian::Big => value.to_be_bytes(),
262        };
263        let window = match self.endian {
264            Endian::Little => bytes.get(..count),
265            Endian::Big => bytes.get(size_of::<u64>().saturating_sub(count)..),
266        };
267        self.bytes
268            .extend_from_slice(window.ok_or_else(|| Error::OutOfRange {
269                field: "integer width",
270                value: u64::from(width),
271                max: 8,
272            })?);
273        Ok(())
274    }
275
276    pub(crate) fn write_bytes(&mut self, bytes: &[u8]) {
277        self.bytes.extend_from_slice(bytes);
278    }
279
280    pub(crate) fn align_to(&mut self, alignment: usize) -> Result<()> {
281        let alignment = NonZeroUsize::new(alignment).ok_or(Error::InvalidAlignment(alignment))?;
282        let remainder = self.bytes.len() % alignment;
283        if remainder != 0 {
284            let padding = alignment.get().saturating_sub(remainder);
285            let new_len = self
286                .bytes
287                .len()
288                .checked_add(padding)
289                .ok_or(Error::Overflow("aligned output length"))?;
290            self.bytes.resize(new_len, 0);
291        }
292        Ok(())
293    }
294
295    pub(crate) fn patch_u32(&mut self, offset: usize, value: u32) -> Result<()> {
296        let bytes = match self.endian {
297            Endian::Little => value.to_le_bytes(),
298            Endian::Big => value.to_be_bytes(),
299        };
300        let end = offset
301            .checked_add(bytes.len())
302            .ok_or(Error::Overflow("patch end offset"))?;
303        let remaining = self.bytes.len().saturating_sub(offset);
304        let destination = self
305            .bytes
306            .get_mut(offset..end)
307            .ok_or(Error::UnexpectedEof {
308                offset,
309                needed: bytes.len(),
310                remaining,
311            })?;
312        destination.copy_from_slice(&bytes);
313        Ok(())
314    }
315
316    #[cfg(test)]
317    pub(crate) fn patch_uint(&mut self, offset: usize, value: u64, width: u8) -> Result<()> {
318        let mut encoded = Self::new(self.endian);
319        encoded.write_uint(value, width)?;
320        let end = offset
321            .checked_add(usize::from(width))
322            .ok_or(Error::Overflow("patch end offset"))?;
323        let remaining = self.bytes.len().saturating_sub(offset);
324        let destination = self
325            .bytes
326            .get_mut(offset..end)
327            .ok_or_else(|| Error::UnexpectedEof {
328                offset,
329                needed: usize::from(width),
330                remaining,
331            })?;
332        destination.copy_from_slice(encoded.as_slice());
333        Ok(())
334    }
335}