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