Skip to main content

gix_ref/store/packed/
iter.rs

1use gix_error::{ErrorExt, ExnMessageResult, Message, corruption};
2
3use gix_object::bstr::{BString, ByteSlice};
4
5use crate::store_impl::{packed, packed::decode};
6
7/// packed-refs specific functionality
8impl packed::Buffer {
9    /// Return an iterator of references stored in this packed refs buffer, ordered by reference name.
10    /// Header failures include [metadata](gix_error::Exn::metadata()) `input` (bytes), the first line without its
11    /// newline.
12    ///
13    /// # Note
14    ///
15    /// There is no namespace support in packed iterators. It can be emulated using `iter_prefixed(…)`.
16    pub fn iter(&self) -> ExnMessageResult<packed::Iter<'_>> {
17        packed::Iter::new(self.as_ref(), self.object_hash)
18    }
19
20    /// Return an iterator yielding only references matching the given prefix, ordered by reference name.
21    /// Header failures include [metadata](gix_error::Exn::metadata()) `input` (bytes), the first selected line without
22    /// its newline.
23    pub fn iter_prefixed(&self, prefix: BString) -> ExnMessageResult<packed::Iter<'_>> {
24        let first_record_with_prefix = self.binary_search_by(prefix.as_bstr()).unwrap_or_else(|(_, pos)| pos);
25        packed::Iter::new_with_prefix(
26            &self.as_ref()[first_record_with_prefix..],
27            self.object_hash,
28            Some(prefix),
29        )
30    }
31}
32
33impl<'a> Iterator for packed::Iter<'a> {
34    type Item = ExnMessageResult<packed::Reference<'a>>;
35
36    /// Decode failures include [metadata](gix_error::Exn::metadata()) `line` (one-based line number within this
37    /// iterator's input) and `input` (line bytes).
38    fn next(&mut self) -> Option<Self::Item> {
39        if self.cursor.is_empty() {
40            return None;
41        }
42
43        let start = self.cursor;
44        match decode::reference(&mut self.cursor, self.object_hash) {
45            Ok(reference) => {
46                self.current_line += 1 + usize::from(reference.object.is_some());
47                if let Some(ref prefix) = self.prefix
48                    && !reference.name.as_bstr().starts_with_str(prefix)
49                {
50                    self.cursor = &[];
51                    return None;
52                }
53                Some(Ok(reference))
54            }
55            Err(err) => {
56                self.cursor = start;
57                let (failed_line, next_cursor) = self
58                    .cursor
59                    .find_byte(b'\n')
60                    .map_or((self.cursor, &[][..]), |pos| self.cursor.split_at(pos + 1));
61                self.cursor = next_cursor;
62                let line_number = self.current_line;
63                self.current_line += 1;
64
65                Some(Err(err.raise(
66                    Message::new("Invalid packed reference")
67                        .with("input", failed_line.strip_suffix(b"\n").unwrap_or(failed_line))
68                        .with("line", line_number),
69                )))
70            }
71        }
72    }
73}
74
75impl<'a> packed::Iter<'a> {
76    /// Return a new iterator after successfully parsing the possibly existing first line of the given `packed` refs buffer,
77    /// parsing object ids as `object_hash`.
78    /// Header failures include [metadata](gix_error::Exn::metadata()) `input` (bytes), the first line without its
79    /// newline.
80    pub fn new(packed: &'a [u8], object_hash: gix_hash::Kind) -> ExnMessageResult<Self> {
81        Self::new_with_prefix(packed, object_hash, None)
82    }
83
84    /// Returns an iterator whose references will only match `prefix`.
85    ///
86    /// It assumes that the underlying `packed` buffer is indeed sorted and parses object ids as `object_hash`.
87    /// Header failures include [metadata](gix_error::Exn::metadata()) `input` (bytes), the first line without its
88    /// newline.
89    pub(in crate::store_impl::packed) fn new_with_prefix(
90        packed: &'a [u8],
91        object_hash: gix_hash::Kind,
92        prefix: Option<BString>,
93    ) -> ExnMessageResult<Self> {
94        if packed.is_empty() {
95            Ok(packed::Iter {
96                cursor: packed,
97                object_hash,
98                prefix,
99                current_line: 1,
100            })
101        } else if packed[0] == b'#' {
102            let mut input = packed;
103            decode::header(&mut input).map_err(|()| {
104                corruption("Invalid packed reference header")
105                    .with("input", packed.lines().next().unwrap_or(packed))
106                    .raise()
107            })?;
108            let refs = input;
109            Ok(packed::Iter {
110                cursor: refs,
111                object_hash,
112                prefix,
113                current_line: 2,
114            })
115        } else {
116            Ok(packed::Iter {
117                cursor: packed,
118                object_hash,
119                prefix,
120                current_line: 1,
121            })
122        }
123    }
124}