Skip to main content

vortex_file/footer/
segment.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::ops::Range;
5
6use vortex_buffer::Alignment;
7use vortex_error::VortexError;
8
9use crate::flatbuffers::footer as fb;
10
11/// The location of a segment within a Vortex file.
12///
13/// A segment is a contiguous block of bytes in a file that contains a part of the file's data.
14/// The `SegmentSpec` struct specifies the location and properties of a segment.
15#[derive(Clone, Copy, Debug)]
16pub struct SegmentSpec {
17    /// The byte offset of the segment from the start of the file.
18    pub offset: u64,
19    /// The length of the segment in bytes.
20    pub length: u32,
21    /// The memory alignment requirement of the segment.
22    pub alignment: Alignment,
23}
24
25impl SegmentSpec {
26    /// Returns the byte range of the segment within the file.
27    ///
28    /// The range starts at the segment's offset and extends for its length.
29    pub fn byte_range(&self) -> Range<u64> {
30        self.offset..self.offset + u64::from(self.length)
31    }
32}
33
34impl From<&SegmentSpec> for fb::SegmentSpec {
35    fn from(value: &SegmentSpec) -> Self {
36        fb::SegmentSpec::new(value.offset, value.length, value.alignment.exponent(), 0, 0)
37    }
38}
39
40impl TryFrom<&fb::SegmentSpec> for SegmentSpec {
41    type Error = VortexError;
42
43    fn try_from(value: &fb::SegmentSpec) -> Result<Self, Self::Error> {
44        Ok(Self {
45            offset: value.offset(),
46            length: value.length(),
47            // The alignment exponent comes from the file and may be corrupt, so validate it rather
48            // than panicking on a too-large shift (see issue #8819).
49            alignment: Alignment::try_from_untrusted_exponent(value.alignment_exponent())?,
50        })
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn rejects_out_of_range_alignment_exponent() {
60        // A fuzzed segment spec can declare an alignment exponent that would overflow a usize
61        // shift. Parsing it must return an error rather than panicking (see issue #8819).
62        let fb_spec = fb::SegmentSpec::new(0, 0, u8::MAX, 0, 0);
63        let err = SegmentSpec::try_from(&fb_spec).unwrap_err();
64        assert!(err.to_string().contains("too large"), "{err}");
65    }
66
67    #[test]
68    fn rejects_excessive_alignment_exponent() {
69        // A representable alignment can still cause an unreasonable allocation when a segment is
70        // copied to satisfy it.
71        let fb_spec = fb::SegmentSpec::new(0, 0, 17, 0, 0);
72        let err = SegmentSpec::try_from(&fb_spec).unwrap_err();
73        assert!(err.to_string().contains("exceeds"), "{err}");
74    }
75}