gix_ref/store/file/log/
iter.rs1use gix_object::bstr::ByteSlice;
2
3use crate::{
4 FullNameRef, file,
5 file::loose::reference::logiter::must_be_io_err,
6 store_impl::file::{log, log::iter::decode::LineNumber},
7};
8
9pub mod decode {
11 use crate::store_impl::file::log;
12
13 #[derive(Debug)]
15 pub struct Error {
16 inner: log::line::decode::Error,
17 line: LineNumber,
18 }
19
20 impl std::fmt::Display for Error {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 write!(f, "In line {}: {}", self.line, self.inner)
23 }
24 }
25
26 impl std::error::Error for Error {}
27
28 impl Error {
29 pub(crate) fn new(err: log::line::decode::Error, line: LineNumber) -> Self {
30 Error { line, inner: err }
31 }
32 }
33
34 #[derive(Debug)]
35 pub(crate) enum LineNumber {
36 FromStart(usize),
37 FromEnd(usize),
38 }
39
40 impl std::fmt::Display for LineNumber {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 let (line, suffix) = match self {
43 LineNumber::FromStart(line) => (line, ""),
44 LineNumber::FromEnd(line) => (line, " from the end"),
45 };
46 write!(f, "{}{}", line + 1, suffix)
47 }
48 }
49}
50
51pub fn forward(lines: &[u8]) -> Forward<'_> {
59 Forward {
60 inner: lines.as_bstr().lines().enumerate(),
61 }
62}
63
64pub struct Forward<'a> {
66 inner: std::iter::Enumerate<gix_object::bstr::Lines<'a>>,
67}
68
69impl<'a> Iterator for Forward<'a> {
70 type Item = Result<log::LineRef<'a>, decode::Error>;
71
72 fn next(&mut self) -> Option<Self::Item> {
73 self.inner.next().map(|(ln, line)| {
74 log::LineRef::from_bytes(line).map_err(|err| decode::Error::new(err, decode::LineNumber::FromStart(ln)))
75 })
76 }
77}
78
79#[must_use = "Iterators should be obtained from this platform"]
81pub struct Platform<'a, 's> {
82 pub store: &'s file::Store,
84 pub name: &'a FullNameRef,
86 pub buf: Vec<u8>,
88}
89
90impl Platform<'_, '_> {
91 pub fn rev(&mut self) -> std::io::Result<Option<log::iter::Reverse<'_, std::fs::File>>> {
93 self.buf.clear();
94 self.buf.resize(1024 * 4, 0);
95 self.store
96 .reflog_iter_rev(self.name, &mut self.buf)
97 .map_err(must_be_io_err)
98 }
99
100 pub fn all(&mut self) -> std::io::Result<Option<log::iter::Forward<'_>>> {
102 self.buf.clear();
103 self.store.reflog_iter(self.name, &mut self.buf).map_err(must_be_io_err)
104 }
105}
106
107pub struct Reverse<'a, F> {
109 buf: &'a mut [u8],
110 count: usize,
111 read_and_pos: Option<(F, u64)>,
112 last_nl_pos: Option<usize>,
113}
114
115pub fn reverse<F>(mut log: F, buf: &mut [u8]) -> std::io::Result<Reverse<'_, F>>
126where
127 F: std::io::Read + std::io::Seek,
128{
129 let pos = log.seek(std::io::SeekFrom::End(0))?;
130 if buf.is_empty() {
131 return Err(std::io::Error::other(
132 "Zero sized buffers are not allowed, use 256 bytes or more for typical logs",
133 ));
134 }
135 Ok(Reverse {
136 buf,
137 count: 0,
138 read_and_pos: Some((log, pos)),
139 last_nl_pos: None,
140 })
141}
142
143pub mod reverse {
145
146 use super::decode;
147
148 #[derive(Debug, thiserror::Error)]
150 #[allow(missing_docs)]
151 pub enum Error {
152 #[error("The buffer could not be filled to make more lines available")]
153 Io(#[from] std::io::Error),
154 #[error("Could not decode log line")]
155 Decode(#[from] decode::Error),
156 }
157}
158
159impl<F> Iterator for Reverse<'_, F>
160where
161 F: std::io::Read + std::io::Seek,
162{
163 type Item = Result<crate::log::Line, reverse::Error>;
164
165 fn next(&mut self) -> Option<Self::Item> {
166 match (self.last_nl_pos.take(), self.read_and_pos.take()) {
167 (None, Some((mut read, pos))) => {
169 let npos = pos.saturating_sub(self.buf.len() as u64);
170 if let Err(err) = read.seek(std::io::SeekFrom::Start(npos)) {
171 return Some(Err(err.into()));
172 }
173
174 let n = (pos - npos) as usize;
175 if n == 0 {
176 return None;
177 }
178 let buf = &mut self.buf[..n];
179 if let Err(err) = read.read_exact(buf) {
180 return Some(Err(err.into()));
181 }
182
183 let last_byte = *buf.last().expect("we have read non-zero bytes before");
184 self.last_nl_pos = Some(if last_byte != b'\n' { buf.len() } else { buf.len() - 1 });
185 self.read_and_pos = Some((read, npos));
186 self.next()
187 }
188 (Some(end), Some(read_and_pos)) => match self.buf[..end].rfind_byte(b'\n') {
190 Some(start) => {
191 self.read_and_pos = Some(read_and_pos);
192 self.last_nl_pos = Some(start);
193 let buf = &self.buf[start + 1..end];
194 let res = Some(
195 log::LineRef::from_bytes(buf)
196 .map_err(|err| {
197 reverse::Error::Decode(decode::Error::new(err, LineNumber::FromEnd(self.count)))
198 })
199 .map(Into::into),
200 );
201 self.count += 1;
202 res
203 }
204 None => {
205 let (mut read, last_read_pos) = read_and_pos;
206 if last_read_pos == 0 {
207 let buf = &self.buf[..end];
208 Some(
209 log::LineRef::from_bytes(buf)
210 .map_err(|err| {
211 reverse::Error::Decode(decode::Error::new(err, LineNumber::FromEnd(self.count)))
212 })
213 .map(Into::into),
214 )
215 } else {
216 let npos = last_read_pos.saturating_sub((self.buf.len() - end) as u64);
217 if npos == last_read_pos {
218 return Some(Err(std::io::Error::other(format!(
219 "buffer too small for line size, got until {:?}",
220 self.buf.as_bstr()
221 ))
222 .into()));
223 }
224 let n = (last_read_pos - npos) as usize;
225 self.buf.copy_within(0..end, n);
226 if let Err(err) = read.seek(std::io::SeekFrom::Start(npos)) {
227 return Some(Err(err.into()));
228 }
229 if let Err(err) = read.read_exact(&mut self.buf[..n]) {
230 return Some(Err(err.into()));
231 }
232 self.read_and_pos = Some((read, npos));
233 self.last_nl_pos = Some(n + end);
234 self.next()
235 }
236 }
237 },
238 (None, None) => None,
240 (Some(_), None) => unreachable!("BUG: Invalid state: we never discard only our file, always both."),
241 }
242 }
243}