diff options
Diffstat (limited to 'vendor/gix-object/src')
-rw-r--r-- | vendor/gix-object/src/blob.rs | 20 | ||||
-rw-r--r-- | vendor/gix-object/src/commit/decode.rs | 116 | ||||
-rw-r--r-- | vendor/gix-object/src/commit/message/body.rs | 43 | ||||
-rw-r--r-- | vendor/gix-object/src/commit/message/decode.rs | 70 | ||||
-rw-r--r-- | vendor/gix-object/src/commit/mod.rs | 45 | ||||
-rw-r--r-- | vendor/gix-object/src/commit/ref_iter.rs | 155 | ||||
-rw-r--r-- | vendor/gix-object/src/commit/write.rs | 4 | ||||
-rw-r--r-- | vendor/gix-object/src/data.rs | 6 | ||||
-rw-r--r-- | vendor/gix-object/src/encode.rs | 18 | ||||
-rw-r--r-- | vendor/gix-object/src/kind.rs | 24 | ||||
-rw-r--r-- | vendor/gix-object/src/lib.rs | 52 | ||||
-rw-r--r-- | vendor/gix-object/src/object/mod.rs | 20 | ||||
-rw-r--r-- | vendor/gix-object/src/parse.rs | 106 | ||||
-rw-r--r-- | vendor/gix-object/src/tag/decode.rs | 137 | ||||
-rw-r--r-- | vendor/gix-object/src/tag/mod.rs | 6 | ||||
-rw-r--r-- | vendor/gix-object/src/tag/ref_iter.rs | 55 | ||||
-rw-r--r-- | vendor/gix-object/src/tag/write.rs | 12 | ||||
-rw-r--r-- | vendor/gix-object/src/traits.rs | 12 | ||||
-rw-r--r-- | vendor/gix-object/src/tree/mod.rs | 2 | ||||
-rw-r--r-- | vendor/gix-object/src/tree/ref_iter.rs | 82 | ||||
-rw-r--r-- | vendor/gix-object/src/tree/write.rs | 4 |
21 files changed, 560 insertions, 429 deletions
diff --git a/vendor/gix-object/src/blob.rs b/vendor/gix-object/src/blob.rs index ff2eeafc8..d0a42092c 100644 --- a/vendor/gix-object/src/blob.rs +++ b/vendor/gix-object/src/blob.rs @@ -4,32 +4,32 @@ use crate::{Blob, BlobRef, Kind}; impl<'a> crate::WriteTo for BlobRef<'a> { /// Write the blobs data to `out` verbatim. - fn write_to(&self, mut out: impl io::Write) -> io::Result<()> { + fn write_to(&self, out: &mut dyn io::Write) -> io::Result<()> { out.write_all(self.data) } - fn size(&self) -> usize { - self.data.len() - } - fn kind(&self) -> Kind { Kind::Blob } + + fn size(&self) -> usize { + self.data.len() + } } impl crate::WriteTo for Blob { /// Write the blobs data to `out` verbatim. - fn write_to(&self, out: impl io::Write) -> io::Result<()> { + fn write_to(&self, out: &mut dyn io::Write) -> io::Result<()> { self.to_ref().write_to(out) } - fn size(&self) -> usize { - self.to_ref().size() - } - fn kind(&self) -> Kind { Kind::Blob } + + fn size(&self) -> usize { + self.to_ref().size() + } } impl Blob { diff --git a/vendor/gix-object/src/commit/decode.rs b/vendor/gix-object/src/commit/decode.rs index 821feaabb..0b8243ef3 100644 --- a/vendor/gix-object/src/commit/decode.rs +++ b/vendor/gix-object/src/commit/decode.rs @@ -1,71 +1,71 @@ use std::borrow::Cow; -use nom::{ - branch::alt, - bytes::complete::{is_not, tag}, - combinator::{all_consuming, opt}, - error::{context, ContextError, ParseError}, - multi::many0, - IResult, Parser, -}; use smallvec::SmallVec; +use winnow::{ + combinator::{alt, eof, opt, preceded, repeat, rest, terminated}, + error::{AddContext, ParserError, StrContext}, + prelude::*, + token::take_till1, +}; use crate::{parse, parse::NL, BStr, ByteSlice, CommitRef}; -pub fn message<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(i: &'a [u8]) -> IResult<&'a [u8], &'a BStr, E> { +pub fn message<'a, E: ParserError<&'a [u8]> + AddContext<&'a [u8], StrContext>>( + i: &mut &'a [u8], +) -> PResult<&'a BStr, E> { if i.is_empty() { // newline + [message] - return Err(nom::Err::Error(E::add_context( - i, - "newline + <message>", - E::from_error_kind(i, nom::error::ErrorKind::Eof), - ))); + return Err( + winnow::error::ErrMode::from_error_kind(i, winnow::error::ErrorKind::Eof) + .add_context(i, StrContext::Expected("newline + <message>".into())), + ); } - let (i, _) = context("a newline separates headers from the message", tag(NL))(i)?; - Ok((&[], i.as_bstr())) + preceded(NL, rest.map(ByteSlice::as_bstr)) + .context(StrContext::Expected( + "a newline separates headers from the message".into(), + )) + .parse_next(i) } -pub fn commit<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>( - i: &'a [u8], -) -> IResult<&'a [u8], CommitRef<'_>, E> { - let (i, tree) = context("tree <40 lowercase hex char>", |i| { - parse::header_field(i, b"tree", parse::hex_hash) - })(i)?; - let (i, parents) = context( - "zero or more 'parent <40 lowercase hex char>'", - many0(|i| parse::header_field(i, b"parent", parse::hex_hash)), - )(i)?; - let (i, author) = context("author <signature>", |i| { - parse::header_field(i, b"author", parse::signature) - })(i)?; - let (i, committer) = context("committer <signature>", |i| { - parse::header_field(i, b"committer", parse::signature) - })(i)?; - let (i, encoding) = context( - "encoding <encoding>", - opt(|i| parse::header_field(i, b"encoding", is_not(NL))), - )(i)?; - let (i, extra_headers) = context( - "<field> <single-line|multi-line>", - many0(alt(( - parse::any_header_field_multi_line.map(|(k, o)| (k.as_bstr(), Cow::Owned(o))), - |i| { - parse::any_header_field(i, is_not(NL)).map(|(i, (k, o))| (i, (k.as_bstr(), Cow::Borrowed(o.as_bstr())))) +pub fn commit<'a, E: ParserError<&'a [u8]> + AddContext<&'a [u8], StrContext>>( + i: &mut &'a [u8], +) -> PResult<CommitRef<'a>, E> { + ( + (|i: &mut _| parse::header_field(i, b"tree", parse::hex_hash)) + .context(StrContext::Expected("tree <40 lowercase hex char>".into())), + repeat(0.., |i: &mut _| parse::header_field(i, b"parent", parse::hex_hash)) + .map(|p: Vec<_>| p) + .context(StrContext::Expected( + "zero or more 'parent <40 lowercase hex char>'".into(), + )), + (|i: &mut _| parse::header_field(i, b"author", parse::signature)) + .context(StrContext::Expected("author <signature>".into())), + (|i: &mut _| parse::header_field(i, b"committer", parse::signature)) + .context(StrContext::Expected("committer <signature>".into())), + opt(|i: &mut _| parse::header_field(i, b"encoding", take_till1(NL))) + .context(StrContext::Expected("encoding <encoding>".into())), + repeat( + 0.., + alt(( + parse::any_header_field_multi_line.map(|(k, o)| (k.as_bstr(), Cow::Owned(o))), + |i: &mut _| { + parse::any_header_field(i, take_till1(NL)).map(|(k, o)| (k.as_bstr(), Cow::Borrowed(o.as_bstr()))) + }, + )), + ) + .context(StrContext::Expected("<field> <single-line|multi-line>".into())), + terminated(message, eof), + ) + .map( + |(tree, parents, author, committer, encoding, extra_headers, message)| CommitRef { + tree, + parents: SmallVec::from(parents), + author, + committer, + encoding: encoding.map(ByteSlice::as_bstr), + message, + extra_headers, }, - ))), - )(i)?; - let (i, message) = all_consuming(message)(i)?; - - Ok(( - i, - CommitRef { - tree, - parents: SmallVec::from(parents), - author, - committer, - encoding: encoding.map(ByteSlice::as_bstr), - message, - extra_headers, - }, - )) + ) + .parse_next(i) } diff --git a/vendor/gix-object/src/commit/message/body.rs b/vendor/gix-object/src/commit/message/body.rs index 855f031be..6301bf30e 100644 --- a/vendor/gix-object/src/commit/message/body.rs +++ b/vendor/gix-object/src/commit/message/body.rs @@ -1,11 +1,10 @@ use std::ops::Deref; -use nom::{ - bytes::complete::{tag, take_until1}, - combinator::all_consuming, - error::{ErrorKind, ParseError}, - sequence::terminated, - IResult, +use winnow::{ + combinator::{eof, rest, separated_pair, terminated}, + error::{ErrorKind, ParserError}, + prelude::*, + token::take_until1, }; use crate::{ @@ -32,12 +31,14 @@ pub struct TrailerRef<'a> { pub value: &'a BStr, } -fn parse_single_line_trailer<'a, E: ParseError<&'a [u8]>>(i: &'a [u8]) -> IResult<&'a [u8], (&'a BStr, &'a BStr), E> { - let (value, token) = terminated(take_until1(b":".as_ref()), tag(b": "))(i.trim_end())?; +fn parse_single_line_trailer<'a, E: ParserError<&'a [u8]>>(i: &mut &'a [u8]) -> PResult<(&'a BStr, &'a BStr), E> { + *i = i.trim_end(); + let (token, value) = separated_pair(take_until1(b":".as_ref()), b": ", rest).parse_next(i)?; + if token.trim_end().len() != token.len() || value.trim_start().len() != value.len() { - Err(nom::Err::Failure(E::from_error_kind(i, ErrorKind::Fail))) + Err(winnow::error::ErrMode::from_error_kind(i, ErrorKind::Fail).cut()) } else { - Ok((&[], (token.as_bstr(), value.as_bstr()))) + Ok((token.as_bstr(), value.as_bstr())) } } @@ -48,15 +49,15 @@ impl<'a> Iterator for Trailers<'a> { if self.cursor.is_empty() { return None; } - for line in self.cursor.lines_with_terminator() { + for mut line in self.cursor.lines_with_terminator() { self.cursor = &self.cursor[line.len()..]; - if let Some(trailer) = - all_consuming(parse_single_line_trailer::<()>)(line) - .ok() - .map(|(_, (token, value))| TrailerRef { - token: token.trim().as_bstr(), - value: value.trim().as_bstr(), - }) + if let Some(trailer) = terminated(parse_single_line_trailer::<()>, eof) + .parse_next(&mut line) + .ok() + .map(|(token, value)| TrailerRef { + token: token.trim().as_bstr(), + value: value.trim().as_bstr(), + }) { return Some(trailer); } @@ -118,7 +119,7 @@ mod test_parse_trailer { use super::*; fn parse(input: &str) -> (&BStr, &BStr) { - parse_single_line_trailer::<()>(input.as_bytes()).unwrap().1 + parse_single_line_trailer::<()>.parse_peek(input.as_bytes()).unwrap().1 } #[test] @@ -141,8 +142,8 @@ mod test_parse_trailer { #[test] fn extra_whitespace_before_token_or_value_is_error() { - assert!(parse_single_line_trailer::<()>(b"foo : bar").is_err()); - assert!(parse_single_line_trailer::<()>(b"foo: bar").is_err()) + assert!(parse_single_line_trailer::<()>.parse_peek(b"foo : bar").is_err()); + assert!(parse_single_line_trailer::<()>.parse_peek(b"foo: bar").is_err()) } #[test] diff --git a/vendor/gix-object/src/commit/message/decode.rs b/vendor/gix-object/src/commit/message/decode.rs index 6224909bd..8038009b4 100644 --- a/vendor/gix-object/src/commit/message/decode.rs +++ b/vendor/gix-object/src/commit/message/decode.rs @@ -1,57 +1,49 @@ -use nom::{ - branch::alt, - bytes::complete::{tag, take_till1}, - combinator::all_consuming, - error::ParseError, - sequence::pair, - IResult, +use winnow::{ + combinator::{alt, eof, preceded, rest, terminated}, + error::ParserError, + prelude::*, + stream::{Offset, Stream}, + token::take_till1, }; use crate::bstr::{BStr, ByteSlice}; -pub(crate) fn newline<'a, E: ParseError<&'a [u8]>>(i: &'a [u8]) -> IResult<&'a [u8], &'a [u8], E> { - alt((tag(b"\r\n"), tag(b"\n")))(i) +pub(crate) fn newline<'a, E: ParserError<&'a [u8]>>(i: &mut &'a [u8]) -> PResult<&'a [u8], E> { + alt((b"\n", b"\r\n")).parse_next(i) } -fn subject_and_body<'a, E: ParseError<&'a [u8]>>(i: &'a [u8]) -> IResult<&'a [u8], (&'a BStr, Option<&'a BStr>), E> { - let mut c = i; - let mut consumed_bytes = 0; - while !c.is_empty() { - c = match take_till1::<_, _, E>(|c| c == b'\n' || c == b'\r')(c) { - Ok((i1, segment)) => { - consumed_bytes += segment.len(); - match pair::<_, _, _, E, _, _>(newline, newline)(i1) { - Ok((body, _)) => { - return Ok(( - &[], - ( - i[0usize..consumed_bytes].as_bstr(), - (!body.is_empty()).then(|| body.as_bstr()), - ), - )); +fn subject_and_body<'a, E: ParserError<&'a [u8]>>(i: &mut &'a [u8]) -> PResult<(&'a BStr, Option<&'a BStr>), E> { + let start_i = *i; + let start = i.checkpoint(); + while !i.is_empty() { + match take_till1::<_, _, E>(|c| c == b'\n' || c == b'\r').parse_next(i) { + Ok(_) => { + let consumed_bytes = i.offset_from(&start); + match preceded((newline::<E>, newline::<E>), rest).parse_next(i) { + Ok(body) => { + let body = (!body.is_empty()).then(|| body.as_bstr()); + return Ok((start_i[0usize..consumed_bytes].as_bstr(), body)); } - Err(_) => match i1.get(1..) { - Some(next) => { - consumed_bytes += 1; - next - } + Err(_) => match i.next_token() { + Some(_) => {} None => break, }, } } - Err(_) => match c.get(1..) { - Some(next) => { - consumed_bytes += 1; - next - } + Err(_) => match i.next_token() { + Some(_) => {} None => break, }, - }; + } } - Ok((&[], (i.as_bstr(), None))) + + i.reset(start); + rest.map(|r: &[u8]| (r.as_bstr(), None)).parse_next(i) } /// Returns title and body, without separator -pub fn message(input: &[u8]) -> (&BStr, Option<&BStr>) { - all_consuming(subject_and_body::<()>)(input).expect("cannot fail").1 +pub fn message(mut input: &[u8]) -> (&BStr, Option<&BStr>) { + terminated(subject_and_body::<()>, eof) + .parse_next(&mut input) + .expect("cannot fail") } diff --git a/vendor/gix-object/src/commit/mod.rs b/vendor/gix-object/src/commit/mod.rs index 9e135df28..b3de9b0a4 100644 --- a/vendor/gix-object/src/commit/mod.rs +++ b/vendor/gix-object/src/commit/mod.rs @@ -1,4 +1,6 @@ -use bstr::{BStr, ByteSlice}; +use std::ops::Range; + +use bstr::{BStr, BString, ByteSlice}; use crate::{Commit, CommitRef, TagRef}; @@ -21,16 +23,49 @@ pub struct MessageRef<'a> { pub body: Option<&'a BStr>, } +/// The raw commit data, parseable by [`CommitRef`] or [`Commit`], which was fed into a program to produce a signature. +/// +/// See [`extract_signature()`](crate::CommitRefIter::signature()) for how to obtain it. +// TODO: implement `std::io::Read` to avoid allocations +#[derive(PartialEq, Eq, Debug, Hash, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct SignedData<'a> { + /// The raw commit data that includes the signature. + data: &'a [u8], + /// The byte range at which we find the signature. All but the signature is the data that was signed. + signature_range: Range<usize>, +} + +impl SignedData<'_> { + /// Convenience method to obtain a copy of the signed data. + pub fn to_bstring(&self) -> BString { + let mut buf = BString::from(&self.data[..self.signature_range.start]); + buf.extend_from_slice(&self.data[self.signature_range.end..]); + buf + } +} + +impl From<SignedData<'_>> for BString { + fn from(value: SignedData<'_>) -> Self { + value.to_bstring() + } +} + /// pub mod ref_iter; mod write; +/// Lifecycle impl<'a> CommitRef<'a> { /// Deserialize a commit from the given `data` bytes while avoiding most allocations. - pub fn from_bytes(data: &'a [u8]) -> Result<CommitRef<'a>, crate::decode::Error> { - decode::commit(data).map(|(_, t)| t).map_err(crate::decode::Error::from) + pub fn from_bytes(mut data: &'a [u8]) -> Result<CommitRef<'a>, crate::decode::Error> { + decode::commit(&mut data).map_err(crate::decode::Error::with_err) } +} + +/// Access +impl<'a> CommitRef<'a> { /// Return the `tree` fields hash digest. pub fn tree(&self) -> gix_hash::ObjectId { gix_hash::ObjectId::from_hex(self.tree).expect("prior validation of tree hash during parsing") @@ -45,7 +80,7 @@ impl<'a> CommitRef<'a> { /// Returns a convenient iterator over all extra headers. pub fn extra_headers(&self) -> crate::commit::ExtraHeaders<impl Iterator<Item = (&BStr, &BStr)>> { - crate::commit::ExtraHeaders::new(self.extra_headers.iter().map(|(k, v)| (*k, v.as_ref()))) + ExtraHeaders::new(self.extra_headers.iter().map(|(k, v)| (*k, v.as_ref()))) } /// Return the author, with whitespace trimmed. @@ -68,7 +103,7 @@ impl<'a> CommitRef<'a> { } /// Returns the time at which this commit was created. - pub fn time(&self) -> gix_actor::Time { + pub fn time(&self) -> gix_date::Time { self.committer.time } } diff --git a/vendor/gix-object/src/commit/ref_iter.rs b/vendor/gix-object/src/commit/ref_iter.rs index 454f69ac0..4401384ca 100644 --- a/vendor/gix-object/src/commit/ref_iter.rs +++ b/vendor/gix-object/src/commit/ref_iter.rs @@ -1,15 +1,21 @@ -use std::borrow::Cow; +use std::{borrow::Cow, ops::Range}; use bstr::BStr; use gix_hash::{oid, ObjectId}; -use nom::{ - branch::alt, - bytes::complete::is_not, - combinator::{all_consuming, opt}, - error::context, +use winnow::{ + combinator::{alt, eof, opt, terminated}, + error::StrContext, + prelude::*, + token::take_till1, }; -use crate::{bstr::ByteSlice, commit::decode, parse, parse::NL, CommitRefIter}; +use crate::{ + bstr::ByteSlice, + commit::{decode, SignedData}, + parse, + parse::NL, + CommitRefIter, +}; #[derive(Copy, Clone)] pub(crate) enum SignatureKind { @@ -30,6 +36,7 @@ pub(crate) enum State { Message, } +/// Lifecycle impl<'a> CommitRefIter<'a> { /// Create a commit iterator from data. pub fn from_bytes(data: &'a [u8]) -> CommitRefIter<'a> { @@ -38,6 +45,37 @@ impl<'a> CommitRefIter<'a> { state: State::default(), } } +} + +/// Access +impl<'a> CommitRefIter<'a> { + /// Parse `data` as commit and return its PGP signature, along with *all non-signature* data as [`SignedData`], or `None` + /// if the commit isn't signed. + /// + /// This allows the caller to validate the signature by passing the signed data along with the signature back to the program + /// that created it. + pub fn signature(data: &'a [u8]) -> Result<Option<(Cow<'a, BStr>, SignedData<'a>)>, crate::decode::Error> { + let mut signature_and_range = None; + + let raw_tokens = CommitRefIterRaw { + data, + state: State::default(), + offset: 0, + }; + for token in raw_tokens { + let token = token?; + if let Token::ExtraHeader((name, value)) = &token.token { + if *name == "gpgsig" { + // keep track of the signature range alongside the signature data, + // because all but the signature is the signed data. + signature_and_range = Some((value.clone(), token.token_range)); + break; + } + } + } + + Ok(signature_and_range.map(|(sig, signature_range)| (sig, SignedData { data, signature_range }))) + } /// Returns the object id of this commits tree if it is the first function called and if there is no error in decoding /// the data. @@ -106,7 +144,7 @@ impl<'a> CommitRefIter<'a> { _ => None, }) .transpose() - .map(|msg| msg.unwrap_or_default()) + .map(Option::unwrap_or_default) } } @@ -115,13 +153,21 @@ fn missing_field() -> crate::decode::Error { } impl<'a> CommitRefIter<'a> { + #[inline] fn next_inner(i: &'a [u8], state: &mut State) -> Result<(&'a [u8], Token<'a>), crate::decode::Error> { + Self::next_inner_(i, state).map_err(crate::decode::Error::with_err) + } + + fn next_inner_( + mut i: &'a [u8], + state: &mut State, + ) -> Result<(&'a [u8], Token<'a>), winnow::error::ErrMode<crate::decode::ParseError>> { use State::*; Ok(match state { Tree => { - let (i, tree) = context("tree <40 lowercase hex char>", |i| { - parse::header_field(i, b"tree", parse::hex_hash) - })(i)?; + let tree = (|i: &mut _| parse::header_field(i, b"tree", parse::hex_hash)) + .context(StrContext::Expected("tree <40 lowercase hex char>".into())) + .parse_next(&mut i)?; *state = State::Parents; ( i, @@ -131,10 +177,9 @@ impl<'a> CommitRefIter<'a> { ) } Parents => { - let (i, parent) = context( - "commit <40 lowercase hex char>", - opt(|i| parse::header_field(i, b"parent", parse::hex_hash)), - )(i)?; + let parent = opt(|i: &mut _| parse::header_field(i, b"parent", parse::hex_hash)) + .context(StrContext::Expected("commit <40 lowercase hex char>".into())) + .parse_next(&mut i)?; match parent { Some(parent) => ( i, @@ -146,7 +191,7 @@ impl<'a> CommitRefIter<'a> { *state = State::Signature { of: SignatureKind::Author, }; - return Self::next_inner(i, state); + return Self::next_inner_(i, state); } } } @@ -162,7 +207,9 @@ impl<'a> CommitRefIter<'a> { (&b"committer"[..], "committer <signature>") } }; - let (i, signature) = context(err_msg, |i| parse::header_field(i, field_name, parse::signature))(i)?; + let signature = (|i: &mut _| parse::header_field(i, field_name, parse::signature)) + .context(StrContext::Expected(err_msg.into())) + .parse_next(&mut i)?; ( i, match who { @@ -172,37 +219,35 @@ impl<'a> CommitRefIter<'a> { ) } Encoding => { - let (i, encoding) = context( - "encoding <encoding>", - opt(|i| parse::header_field(i, b"encoding", is_not(NL))), - )(i)?; + let encoding = opt(|i: &mut _| parse::header_field(i, b"encoding", take_till1(NL))) + .context(StrContext::Expected("encoding <encoding>".into())) + .parse_next(&mut i)?; *state = State::ExtraHeaders; match encoding { Some(encoding) => (i, Token::Encoding(encoding.as_bstr())), - None => return Self::next_inner(i, state), + None => return Self::next_inner_(i, state), } } ExtraHeaders => { - let (i, extra_header) = context( - "<field> <single-line|multi-line>", - opt(alt(( - |i| parse::any_header_field_multi_line(i).map(|(i, (k, o))| (i, (k.as_bstr(), Cow::Owned(o)))), - |i| { - parse::any_header_field(i, is_not(NL)) - .map(|(i, (k, o))| (i, (k.as_bstr(), Cow::Borrowed(o.as_bstr())))) - }, - ))), - )(i)?; + let extra_header = opt(alt(( + |i: &mut _| parse::any_header_field_multi_line(i).map(|(k, o)| (k.as_bstr(), Cow::Owned(o))), + |i: &mut _| { + parse::any_header_field(i, take_till1(NL)) + .map(|(k, o)| (k.as_bstr(), Cow::Borrowed(o.as_bstr()))) + }, + ))) + .context(StrContext::Expected("<field> <single-line|multi-line>".into())) + .parse_next(&mut i)?; match extra_header { Some(extra_header) => (i, Token::ExtraHeader(extra_header)), None => { *state = State::Message; - return Self::next_inner(i, state); + return Self::next_inner_(i, state); } } } Message => { - let (i, message) = all_consuming(decode::message)(i)?; + let message = terminated(decode::message, eof).parse_next(&mut i)?; debug_assert!( i.is_empty(), "we should have consumed all data - otherwise iter may go forever" @@ -233,6 +278,48 @@ impl<'a> Iterator for CommitRefIter<'a> { } } +/// A variation of [`CommitRefIter`] that return's [`RawToken`]s instead. +struct CommitRefIterRaw<'a> { + data: &'a [u8], + state: State, + offset: usize, +} + +impl<'a> Iterator for CommitRefIterRaw<'a> { + type Item = Result<RawToken<'a>, crate::decode::Error>; + + fn next(&mut self) -> Option<Self::Item> { + if self.data.is_empty() { + return None; + } + match CommitRefIter::next_inner(self.data, &mut self.state) { + Ok((remaining, token)) => { + let consumed = self.data.len() - remaining.len(); + let start = self.offset; + let end = start + consumed; + self.offset = end; + + self.data = remaining; + Some(Ok(RawToken { + token, + token_range: start..end, + })) + } + Err(err) => { + self.data = &[]; + Some(Err(err)) + } + } + } +} + +/// A combination of a parsed [`Token`] as well as the range of bytes that were consumed to parse it. +struct RawToken<'a> { + /// The parsed token. + token: Token<'a>, + token_range: Range<usize>, +} + /// A token returned by the [commit iterator][CommitRefIter]. #[allow(missing_docs)] #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)] diff --git a/vendor/gix-object/src/commit/write.rs b/vendor/gix-object/src/commit/write.rs index e498789a2..667d25763 100644 --- a/vendor/gix-object/src/commit/write.rs +++ b/vendor/gix-object/src/commit/write.rs @@ -6,7 +6,7 @@ use crate::{encode, encode::NL, Commit, CommitRef, Kind}; impl crate::WriteTo for Commit { /// Serializes this instance to `out` in the git serialization format. - fn write_to(&self, mut out: impl io::Write) -> io::Result<()> { + fn write_to(&self, mut out: &mut dyn io::Write) -> io::Result<()> { encode::trusted_header_id(b"tree", &self.tree, &mut out)?; for parent in &self.parents { encode::trusted_header_id(b"parent", parent, &mut out)?; @@ -52,7 +52,7 @@ impl crate::WriteTo for Commit { impl<'a> crate::WriteTo for CommitRef<'a> { /// Serializes this instance to `out` in the git serialization format. - fn write_to(&self, mut out: impl io::Write) -> io::Result<()> { + fn write_to(&self, mut out: &mut dyn io::Write) -> io::Result<()> { encode::trusted_header_id(b"tree", &self.tree(), &mut out)?; for parent in self.parents() { encode::trusted_header_id(b"parent", &parent, &mut out)?; diff --git a/vendor/gix-object/src/data.rs b/vendor/gix-object/src/data.rs index e66360357..cea807684 100644 --- a/vendor/gix-object/src/data.rs +++ b/vendor/gix-object/src/data.rs @@ -67,8 +67,7 @@ pub mod verify { /// Compute the checksum of `self` and compare it with the `desired` hash. /// If the hashes do not match, an [`Error`] is returned, containing the actual /// hash of `self`. - pub fn verify_checksum(&self, desired: impl AsRef<gix_hash::oid>) -> Result<(), Error> { - let desired = desired.as_ref(); + pub fn verify_checksum(&self, desired: &gix_hash::oid) -> Result<(), Error> { let actual_id = crate::compute_hash(desired.kind(), self.kind, self.data); if desired != actual_id { return Err(Error::ChecksumMismatch { @@ -87,6 +86,9 @@ mod tests { #[test] fn size_of_object() { + #[cfg(target_pointer_width = "64")] assert_eq!(std::mem::size_of::<Data<'_>>(), 24, "this shouldn't change unnoticed"); + #[cfg(target_pointer_width = "32")] + assert_eq!(std::mem::size_of::<Data<'_>>(), 12, "this shouldn't change unnoticed"); } } diff --git a/vendor/gix-object/src/encode.rs b/vendor/gix-object/src/encode.rs index 6d291c92a..3ba7db0b5 100644 --- a/vendor/gix-object/src/encode.rs +++ b/vendor/gix-object/src/encode.rs @@ -34,9 +34,9 @@ impl From<Error> for io::Error { } } -pub(crate) fn header_field_multi_line(name: &[u8], value: &[u8], mut out: impl io::Write) -> io::Result<()> { +pub(crate) fn header_field_multi_line(name: &[u8], value: &[u8], out: &mut dyn io::Write) -> io::Result<()> { let mut lines = value.as_bstr().split_str(b"\n"); - trusted_header_field(name, lines.next().ok_or(Error::EmptyValue)?, &mut out)?; + trusted_header_field(name, lines.next().ok_or(Error::EmptyValue)?, out)?; for line in lines { out.write_all(SPACE)?; out.write_all(line)?; @@ -45,7 +45,7 @@ pub(crate) fn header_field_multi_line(name: &[u8], value: &[u8], mut out: impl i Ok(()) } -pub(crate) fn trusted_header_field(name: &[u8], value: &[u8], mut out: impl io::Write) -> io::Result<()> { +pub(crate) fn trusted_header_field(name: &[u8], value: &[u8], out: &mut dyn io::Write) -> io::Result<()> { out.write_all(name)?; out.write_all(SPACE)?; out.write_all(value)?; @@ -55,22 +55,26 @@ pub(crate) fn trusted_header_field(name: &[u8], value: &[u8], mut out: impl io:: pub(crate) fn trusted_header_signature( name: &[u8], value: &gix_actor::SignatureRef<'_>, - mut out: impl io::Write, + out: &mut dyn io::Write, ) -> io::Result<()> { out.write_all(name)?; out.write_all(SPACE)?; - value.write_to(&mut out)?; + value.write_to(out)?; out.write_all(NL) } -pub(crate) fn trusted_header_id(name: &[u8], value: &gix_hash::ObjectId, mut out: impl io::Write) -> io::Result<()> { +pub(crate) fn trusted_header_id( + name: &[u8], + value: &gix_hash::ObjectId, + mut out: &mut dyn io::Write, +) -> io::Result<()> { out.write_all(name)?; out.write_all(SPACE)?; value.write_hex_to(&mut out)?; out.write_all(NL) } -pub(crate) fn header_field(name: &[u8], value: &[u8], out: impl io::Write) -> io::Result<()> { +pub(crate) fn header_field(name: &[u8], value: &[u8], out: &mut dyn io::Write) -> io::Result<()> { if value.is_empty() { return Err(Error::EmptyValue.into()); } diff --git a/vendor/gix-object/src/kind.rs b/vendor/gix-object/src/kind.rs index 86df251bf..93e457ca4 100644 --- a/vendor/gix-object/src/kind.rs +++ b/vendor/gix-object/src/kind.rs @@ -10,6 +10,7 @@ pub enum Error { InvalidObjectKind { kind: bstr::BString }, } +/// Initialization impl Kind { /// Parse a `Kind` from its serialized loose git objects. pub fn from_bytes(s: &[u8]) -> Result<Kind, Error> { @@ -21,7 +22,10 @@ impl Kind { _ => return Err(Error::InvalidObjectKind { kind: s.into() }), }) } +} +/// Access +impl Kind { /// Return the name of `self` for use in serialized loose git objects. pub fn as_bytes(&self) -> &[u8] { match self { @@ -31,6 +35,26 @@ impl Kind { Kind::Tag => b"tag", } } + + /// Returns `true` if this instance is representing a commit. + pub fn is_commit(&self) -> bool { + matches!(self, Kind::Commit) + } + + /// Returns `true` if this instance is representing a tree. + pub fn is_tree(&self) -> bool { + matches!(self, Kind::Tree) + } + + /// Returns `true` if this instance is representing a tag. + pub fn is_tag(&self) -> bool { + matches!(self, Kind::Tag) + } + + /// Returns `true` if this instance is representing a blob. + pub fn is_blob(&self) -> bool { + matches!(self, Kind::Blob) + } } impl fmt::Display for Kind { diff --git a/vendor/gix-object/src/lib.rs b/vendor/gix-object/src/lib.rs index d6c9fb760..56e0019fd 100644 --- a/vendor/gix-object/src/lib.rs +++ b/vendor/gix-object/src/lib.rs @@ -14,6 +14,8 @@ use std::borrow::Cow; /// For convenience to allow using `bstr` without adding it to own cargo manifest. pub use bstr; use bstr::{BStr, BString, ByteSlice}; +/// For convenience to allow using `gix-date` without adding it to own cargo manifest. +pub use gix_date as date; use smallvec::SmallVec; /// @@ -213,6 +215,8 @@ pub enum Object { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TreeRef<'a> { /// The directories and files contained in this tree. + /// + /// Beware that the sort order isn't *quite* by name, so one may bisect only with a [`tree::EntryRef`] to handle ordering correctly. #[cfg_attr(feature = "serde", serde(borrow))] pub entries: Vec<tree::EntryRef<'a>>, } @@ -229,6 +233,8 @@ pub struct TreeRefIter<'a> { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Tree { /// The directories and files contained in this tree. They must be and remain sorted by [`filename`][tree::Entry::filename]. + /// + /// Beware that the sort order isn't *quite* by name, so one may bisect only with a [`tree::Entry`] to handle ordering correctly. pub entries: Vec<tree::Entry>, } @@ -252,16 +258,12 @@ pub struct Data<'a> { pub mod decode { #[cfg(feature = "verbose-object-parsing-errors")] mod _decode { - use crate::bstr::{BString, ByteSlice}; - /// The type to be used for parse errors. - pub type ParseError<'a> = nom::error::VerboseError<&'a [u8]>; - /// The owned type to be used for parse errors. - pub type ParseErrorOwned = nom::error::VerboseError<BString>; + pub type ParseError = winnow::error::ContextError<winnow::error::StrContext>; pub(crate) fn empty_error() -> Error { Error { - inner: nom::error::VerboseError::<BString> { errors: Vec::new() }, + inner: winnow::error::ContextError::new(), } } @@ -269,22 +271,13 @@ pub mod decode { #[derive(Debug, Clone)] pub struct Error { /// The actual error - pub inner: ParseErrorOwned, + pub inner: ParseError, } - impl<'a> From<nom::Err<ParseError<'a>>> for Error { - fn from(v: nom::Err<ParseError<'a>>) -> Self { - Error { - inner: match v { - nom::Err::Error(err) | nom::Err::Failure(err) => nom::error::VerboseError { - errors: err - .errors - .into_iter() - .map(|(i, v)| (i.as_bstr().to_owned(), v)) - .collect(), - }, - nom::Err::Incomplete(_) => unreachable!("we don't have streaming parsers"), - }, + impl Error { + pub(crate) fn with_err(err: winnow::error::ErrMode<ParseError>) -> Self { + Self { + inner: err.into_inner().expect("we don't have streaming parsers"), } } } @@ -300,9 +293,7 @@ pub mod decode { #[cfg(not(feature = "verbose-object-parsing-errors"))] mod _decode { /// The type to be used for parse errors, discards everything and is zero size - pub type ParseError<'a> = (); - /// The owned type to be used for parse errors, discards everything and is zero size - pub type ParseErrorOwned = (); + pub type ParseError = (); pub(crate) fn empty_error() -> Error { Error { inner: () } @@ -312,16 +303,13 @@ pub mod decode { #[derive(Debug, Clone)] pub struct Error { /// The actual error - pub inner: ParseErrorOwned, + pub inner: ParseError, } - impl<'a> From<nom::Err<ParseError<'a>>> for Error { - fn from(v: nom::Err<ParseError<'a>>) -> Self { - Error { - inner: match v { - nom::Err::Error(err) | nom::Err::Failure(err) => err, - nom::Err::Incomplete(_) => unreachable!("we don't have streaming parsers"), - }, + impl Error { + pub(crate) fn with_err(err: winnow::error::ErrMode<ParseError>) -> Self { + Self { + inner: err.into_inner().expect("we don't have streaming parsers"), } } } @@ -333,7 +321,7 @@ pub mod decode { } } pub(crate) use _decode::empty_error; - pub use _decode::{Error, ParseError, ParseErrorOwned}; + pub use _decode::{Error, ParseError}; impl std::error::Error for Error {} /// Returned by [`loose_header()`] diff --git a/vendor/gix-object/src/object/mod.rs b/vendor/gix-object/src/object/mod.rs index c0f9dcd52..bebc1cc65 100644 --- a/vendor/gix-object/src/object/mod.rs +++ b/vendor/gix-object/src/object/mod.rs @@ -10,7 +10,7 @@ mod write { /// Serialization impl<'a> WriteTo for ObjectRef<'a> { /// Write the contained object to `out` in the git serialization format. - fn write_to(&self, out: impl io::Write) -> io::Result<()> { + fn write_to(&self, out: &mut dyn io::Write) -> io::Result<()> { use crate::ObjectRef::*; match self { Tree(v) => v.write_to(out), @@ -20,6 +20,10 @@ mod write { } } + fn kind(&self) -> Kind { + self.kind() + } + fn size(&self) -> usize { use crate::ObjectRef::*; match self { @@ -29,16 +33,12 @@ mod write { Tag(v) => v.size(), } } - - fn kind(&self) -> Kind { - self.kind() - } } /// Serialization impl WriteTo for Object { /// Write the contained object to `out` in the git serialization format. - fn write_to(&self, out: impl io::Write) -> io::Result<()> { + fn write_to(&self, out: &mut dyn io::Write) -> io::Result<()> { use crate::Object::*; match self { Tree(v) => v.write_to(out), @@ -48,6 +48,10 @@ mod write { } } + fn kind(&self) -> Kind { + self.kind() + } + fn size(&self) -> usize { use crate::Object::*; match self { @@ -57,10 +61,6 @@ mod write { Tag(v) => v.size(), } } - - fn kind(&self) -> Kind { - self.kind() - } } } diff --git a/vendor/gix-object/src/parse.rs b/vendor/gix-object/src/parse.rs index 20dd443c0..0013d9b72 100644 --- a/vendor/gix-object/src/parse.rs +++ b/vendor/gix-object/src/parse.rs @@ -1,11 +1,10 @@ use bstr::{BStr, BString, ByteVec}; -use nom::{ - bytes::complete::{is_not, tag, take_until, take_while_m_n}, - combinator::{peek, recognize}, - error::{context, ContextError, ParseError}, - multi::many1_count, - sequence::{preceded, terminated, tuple}, - IResult, +use winnow::{ + combinator::{preceded, repeat, terminated}, + error::{AddContext, ParserError, StrContext}, + prelude::*, + token::{take_till1, take_until0, take_while}, + Parser, }; use crate::ByteSlice; @@ -14,68 +13,63 @@ pub(crate) const NL: &[u8] = b"\n"; pub(crate) const SPACE: &[u8] = b" "; const SPACE_OR_NL: &[u8] = b" \n"; -pub(crate) fn any_header_field_multi_line<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>( - i: &'a [u8], -) -> IResult<&'a [u8], (&'a [u8], BString), E> { - let (i, (k, o)) = context( - "name <multi-line-value>", - peek(tuple(( - terminated(is_not(SPACE_OR_NL), tag(SPACE)), - recognize(tuple(( - is_not(NL), - tag(NL), - many1_count(terminated(tuple((tag(SPACE), take_until(NL))), tag(NL))), - ))), - ))), - )(i)?; - assert!(!o.is_empty(), "we have parsed more than one value here"); - let end = &o[o.len() - 1] as *const u8 as usize; - let start_input = &i[0] as *const u8 as usize; - - let bytes = o[..o.len() - 1].as_bstr(); - let mut out = BString::from(Vec::with_capacity(bytes.len())); - let mut lines = bytes.lines(); - out.push_str(lines.next().expect("first line")); - for line in lines { - out.push(b'\n'); - out.push_str(&line[1..]); // cut leading space - } - Ok((&i[end - start_input + 1..], (k, out))) +pub(crate) fn any_header_field_multi_line<'a, E: ParserError<&'a [u8]> + AddContext<&'a [u8], StrContext>>( + i: &mut &'a [u8], +) -> PResult<(&'a [u8], BString), E> { + ( + terminated(take_till1(SPACE_OR_NL), SPACE), + ( + take_till1(NL), + NL, + repeat(1.., terminated((SPACE, take_until0(NL)), NL)).map(|()| ()), + ) + .recognize() + .map(|o: &[u8]| { + let bytes = o.as_bstr(); + let mut out = BString::from(Vec::with_capacity(bytes.len())); + let mut lines = bytes.lines(); + out.push_str(lines.next().expect("first line")); + for line in lines { + out.push(b'\n'); + out.push_str(&line[1..]); // cut leading space + } + out + }), + ) + .context(StrContext::Expected("name <multi-line-value>".into())) + .parse_next(i) } -pub(crate) fn header_field<'a, T, E: ParseError<&'a [u8]>>( - i: &'a [u8], +pub(crate) fn header_field<'a, T, E: ParserError<&'a [u8]>>( + i: &mut &'a [u8], name: &'static [u8], - parse_value: impl Fn(&'a [u8]) -> IResult<&'a [u8], T, E>, -) -> IResult<&'a [u8], T, E> { - terminated(preceded(terminated(tag(name), tag(SPACE)), parse_value), tag(NL))(i) + parse_value: impl Parser<&'a [u8], T, E>, +) -> PResult<T, E> { + terminated(preceded(terminated(name, SPACE), parse_value), NL).parse_next(i) } -pub(crate) fn any_header_field<'a, T, E: ParseError<&'a [u8]>>( - i: &'a [u8], - parse_value: impl Fn(&'a [u8]) -> IResult<&'a [u8], T, E>, -) -> IResult<&'a [u8], (&'a [u8], T), E> { - terminated( - tuple((terminated(is_not(SPACE_OR_NL), tag(SPACE)), parse_value)), - tag(NL), - )(i) +pub(crate) fn any_header_field<'a, T, E: ParserError<&'a [u8]>>( + i: &mut &'a [u8], + parse_value: impl Parser<&'a [u8], T, E>, +) -> PResult<(&'a [u8], T), E> { + terminated((terminated(take_till1(SPACE_OR_NL), SPACE), parse_value), NL).parse_next(i) } fn is_hex_digit_lc(b: u8) -> bool { matches!(b, b'0'..=b'9' | b'a'..=b'f') } -pub fn hex_hash<'a, E: ParseError<&'a [u8]>>(i: &'a [u8]) -> IResult<&'a [u8], &'a BStr, E> { - take_while_m_n( - gix_hash::Kind::shortest().len_in_hex(), - gix_hash::Kind::longest().len_in_hex(), +pub fn hex_hash<'a, E: ParserError<&'a [u8]>>(i: &mut &'a [u8]) -> PResult<&'a BStr, E> { + take_while( + gix_hash::Kind::shortest().len_in_hex()..=gix_hash::Kind::longest().len_in_hex(), is_hex_digit_lc, - )(i) - .map(|(i, hex)| (i, hex.as_bstr())) + ) + .map(ByteSlice::as_bstr) + .parse_next(i) } -pub(crate) fn signature<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>( - i: &'a [u8], -) -> IResult<&'a [u8], gix_actor::SignatureRef<'a>, E> { +pub(crate) fn signature<'a, E: ParserError<&'a [u8]> + AddContext<&'a [u8], StrContext>>( + i: &mut &'a [u8], +) -> PResult<gix_actor::SignatureRef<'a>, E> { gix_actor::signature::decode(i) } diff --git a/vendor/gix-object/src/tag/decode.rs b/vendor/gix-object/src/tag/decode.rs index ba9460af9..73a928638 100644 --- a/vendor/gix-object/src/tag/decode.rs +++ b/vendor/gix-object/src/tag/decode.rs @@ -1,90 +1,75 @@ -use nom::{ - branch::alt, - bytes::complete::{tag, take_until, take_while, take_while1}, - character::is_alphabetic, - combinator::{all_consuming, opt, recognize}, - error::{context, ContextError, ParseError}, - sequence::{preceded, tuple}, - IResult, +use winnow::{ + combinator::{alt, delimited, eof, opt, preceded, rest, terminated}, + error::{AddContext, ParserError, StrContext}, + prelude::*, + stream::AsChar, + token::{take_until0, take_while}, }; use crate::{parse, parse::NL, BStr, ByteSlice, TagRef}; -pub fn git_tag<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(i: &'a [u8]) -> IResult<&[u8], TagRef<'a>, E> { - let (i, target) = context("object <40 lowercase hex char>", |i| { - parse::header_field(i, b"object", parse::hex_hash) - })(i)?; - - let (i, kind) = context("type <object kind>", |i| { - parse::header_field(i, b"type", take_while1(is_alphabetic)) - })(i)?; - let kind = crate::Kind::from_bytes(kind) - .map_err(|_| nom::Err::Error(E::from_error_kind(i, nom::error::ErrorKind::MapRes)))?; - - let (i, tag_version) = context("tag <version>", |i| { - parse::header_field(i, b"tag", take_while1(|b| b != NL[0])) - })(i)?; - - let (i, signature) = context( - "tagger <signature>", - opt(|i| parse::header_field(i, b"tagger", parse::signature)), - )(i)?; - let (i, (message, pgp_signature)) = all_consuming(message)(i)?; - Ok(( - i, - TagRef { - target, - name: tag_version.as_bstr(), - target_kind: kind, - message, - tagger: signature, - pgp_signature, - }, - )) +pub fn git_tag<'a, E: ParserError<&'a [u8]> + AddContext<&'a [u8], StrContext>>( + i: &mut &'a [u8], +) -> PResult<TagRef<'a>, E> { + ( + (|i: &mut _| parse::header_field(i, b"object", parse::hex_hash)) + .context(StrContext::Expected("object <40 lowercase hex char>".into())), + (|i: &mut _| parse::header_field(i, b"type", take_while(1.., AsChar::is_alpha))) + .verify_map(|kind| crate::Kind::from_bytes(kind).ok()) + .context(StrContext::Expected("type <object kind>".into())), + (|i: &mut _| parse::header_field(i, b"tag", take_while(1.., |b| b != NL[0]))) + .context(StrContext::Expected("tag <version>".into())), + opt(|i: &mut _| parse::header_field(i, b"tagger", parse::signature)) + .context(StrContext::Expected("tagger <signature>".into())), + terminated(message, eof), + ) + .map( + |(target, kind, tag_version, signature, (message, pgp_signature))| TagRef { + target, + name: tag_version.as_bstr(), + target_kind: kind, + message, + tagger: signature, + pgp_signature, + }, + ) + .parse_next(i) } -pub fn message<'a, E: ParseError<&'a [u8]>>(i: &'a [u8]) -> IResult<&'a [u8], (&'a BStr, Option<&'a BStr>), E> { +pub fn message<'a, E: ParserError<&'a [u8]>>(i: &mut &'a [u8]) -> PResult<(&'a BStr, Option<&'a BStr>), E> { const PGP_SIGNATURE_BEGIN: &[u8] = b"\n-----BEGIN PGP SIGNATURE-----"; const PGP_SIGNATURE_END: &[u8] = b"-----END PGP SIGNATURE-----"; if i.is_empty() { - return Ok((i, (i.as_bstr(), None))); - } - let (i, _) = tag(NL)(i)?; - fn all_to_end<'a, E: ParseError<&'a [u8]>>(i: &'a [u8]) -> IResult<&'a [u8], (&'a [u8], &'a [u8]), E> { - if i.is_empty() { - // Empty message. That's OK. - return Ok((&[], (&[], &[]))); - } - // an empty signature message signals that there is none - the function signature is needed - // to work with 'alt(…)'. PGP signatures are never empty - Ok((&[], (i, &[]))) + return Ok((b"".as_bstr(), None)); } - let (i, (message, signature)) = alt(( - tuple(( - take_until(PGP_SIGNATURE_BEGIN), - preceded( - tag(NL), - recognize(tuple(( - tag(&PGP_SIGNATURE_BEGIN[1..]), - take_until(PGP_SIGNATURE_END), - tag(PGP_SIGNATURE_END), - take_while(|_| true), - ))), + delimited( + NL, + alt(( + ( + take_until0(PGP_SIGNATURE_BEGIN), + preceded( + NL, + ( + &PGP_SIGNATURE_BEGIN[1..], + take_until0(PGP_SIGNATURE_END), + PGP_SIGNATURE_END, + rest, + ) + .recognize() + .map(|signature: &[u8]| { + if signature.is_empty() { + None + } else { + Some(signature.as_bstr()) + } + }), + ), ), + rest.map(|rest: &[u8]| (rest, None)), )), - all_to_end, - ))(i)?; - let (i, _) = opt(tag(NL))(i)?; - Ok(( - i, - ( - message.as_bstr(), - if signature.is_empty() { - None - } else { - Some(signature.as_bstr()) - }, - ), - )) + opt(NL), + ) + .map(|(message, signature)| (message.as_bstr(), signature)) + .parse_next(i) } diff --git a/vendor/gix-object/src/tag/mod.rs b/vendor/gix-object/src/tag/mod.rs index 1cd353ffb..3813bfc14 100644 --- a/vendor/gix-object/src/tag/mod.rs +++ b/vendor/gix-object/src/tag/mod.rs @@ -10,10 +10,8 @@ pub mod ref_iter; impl<'a> TagRef<'a> { /// Deserialize a tag from `data`. - pub fn from_bytes(data: &'a [u8]) -> Result<TagRef<'a>, crate::decode::Error> { - decode::git_tag(data) - .map(|(_, t)| t) - .map_err(crate::decode::Error::from) + pub fn from_bytes(mut data: &'a [u8]) -> Result<TagRef<'a>, crate::decode::Error> { + decode::git_tag(&mut data).map_err(crate::decode::Error::with_err) } /// The object this tag points to as `Id`. pub fn target(&self) -> gix_hash::ObjectId { diff --git a/vendor/gix-object/src/tag/ref_iter.rs b/vendor/gix-object/src/tag/ref_iter.rs index 1138016b5..b3956ff83 100644 --- a/vendor/gix-object/src/tag/ref_iter.rs +++ b/vendor/gix-object/src/tag/ref_iter.rs @@ -1,10 +1,11 @@ use bstr::BStr; use gix_hash::{oid, ObjectId}; -use nom::{ - bytes::complete::take_while1, - character::is_alphabetic, - combinator::{all_consuming, opt}, - error::{context, ParseError}, +use winnow::{ + combinator::{eof, opt, terminated}, + error::{ParserError, StrContext}, + prelude::*, + stream::AsChar, + token::take_while, }; use crate::{bstr::ByteSlice, parse, parse::NL, tag::decode, Kind, TagRefIter}; @@ -57,13 +58,21 @@ fn missing_field() -> crate::decode::Error { } impl<'a> TagRefIter<'a> { + #[inline] fn next_inner(i: &'a [u8], state: &mut State) -> Result<(&'a [u8], Token<'a>), crate::decode::Error> { + Self::next_inner_(i, state).map_err(crate::decode::Error::with_err) + } + + fn next_inner_( + mut i: &'a [u8], + state: &mut State, + ) -> Result<(&'a [u8], Token<'a>), winnow::error::ErrMode<crate::decode::ParseError>> { use State::*; Ok(match state { Target => { - let (i, target) = context("object <40 lowercase hex char>", |i| { - parse::header_field(i, b"object", parse::hex_hash) - })(i)?; + let target = (|i: &mut _| parse::header_field(i, b"object", parse::hex_hash)) + .context(StrContext::Expected("object <40 lowercase hex char>".into())) + .parse_next(&mut i)?; *state = TargetKind; ( i, @@ -73,36 +82,30 @@ impl<'a> TagRefIter<'a> { ) } TargetKind => { - let (i, kind) = context("type <object kind>", |i| { - parse::header_field(i, b"type", take_while1(is_alphabetic)) - })(i)?; - let kind = Kind::from_bytes(kind).map_err(|_| { - #[allow(clippy::let_unit_value)] - { - let err = crate::decode::ParseError::from_error_kind(i, nom::error::ErrorKind::MapRes); - nom::Err::Error(err) - } - })?; + let kind = (|i: &mut _| parse::header_field(i, b"type", take_while(1.., AsChar::is_alpha))) + .context(StrContext::Expected("type <object kind>".into())) + .parse_next(&mut i)?; + let kind = Kind::from_bytes(kind) + .map_err(|_| winnow::error::ErrMode::from_error_kind(&i, winnow::error::ErrorKind::Verify))?; *state = Name; (i, Token::TargetKind(kind)) } Name => { - let (i, tag_version) = context("tag <version>", |i| { - parse::header_field(i, b"tag", take_while1(|b| b != NL[0])) - })(i)?; + let tag_version = (|i: &mut _| parse::header_field(i, b"tag", take_while(1.., |b| b != NL[0]))) + .context(StrContext::Expected("tag <version>".into())) + .parse_next(&mut i)?; *state = Tagger; (i, Token::Name(tag_version.as_bstr())) } Tagger => { - let (i, signature) = context( - "tagger <signature>", - opt(|i| parse::header_field(i, b"tagger", parse::signature)), - )(i)?; + let signature = opt(|i: &mut _| parse::header_field(i, b"tagger", parse::signature)) + .context(StrContext::Expected("tagger <signature>".into())) + .parse_next(&mut i)?; *state = Message; (i, Token::Tagger(signature)) } Message => { - let (i, (message, pgp_signature)) = all_consuming(decode::message)(i)?; + let (message, pgp_signature) = terminated(decode::message, eof).parse_next(&mut i)?; debug_assert!( i.is_empty(), "we should have consumed all data - otherwise iter may go forever" diff --git a/vendor/gix-object/src/tag/write.rs b/vendor/gix-object/src/tag/write.rs index 5f6d58c86..cee9e587c 100644 --- a/vendor/gix-object/src/tag/write.rs +++ b/vendor/gix-object/src/tag/write.rs @@ -21,12 +21,12 @@ impl From<Error> for io::Error { } impl crate::WriteTo for Tag { - fn write_to(&self, mut out: impl io::Write) -> io::Result<()> { - encode::trusted_header_id(b"object", &self.target, &mut out)?; - encode::trusted_header_field(b"type", self.target_kind.as_bytes(), &mut out)?; - encode::header_field(b"tag", validated_name(self.name.as_ref())?, &mut out)?; + fn write_to(&self, out: &mut dyn io::Write) -> io::Result<()> { + encode::trusted_header_id(b"object", &self.target, out)?; + encode::trusted_header_field(b"type", self.target_kind.as_bytes(), out)?; + encode::header_field(b"tag", validated_name(self.name.as_ref())?, out)?; if let Some(tagger) = &self.tagger { - encode::trusted_header_signature(b"tagger", &tagger.to_ref(), &mut out)?; + encode::trusted_header_signature(b"tagger", &tagger.to_ref(), out)?; } out.write_all(NL)?; @@ -58,7 +58,7 @@ impl crate::WriteTo for Tag { } impl<'a> crate::WriteTo for TagRef<'a> { - fn write_to(&self, mut out: impl io::Write) -> io::Result<()> { + fn write_to(&self, mut out: &mut dyn io::Write) -> io::Result<()> { encode::trusted_header_field(b"object", self.target, &mut out)?; encode::trusted_header_field(b"type", self.target_kind.as_bytes(), &mut out)?; encode::header_field(b"tag", validated_name(self.name)?, &mut out)?; diff --git a/vendor/gix-object/src/traits.rs b/vendor/gix-object/src/traits.rs index 193cd78c3..c0c4adee2 100644 --- a/vendor/gix-object/src/traits.rs +++ b/vendor/gix-object/src/traits.rs @@ -5,7 +5,7 @@ use crate::Kind; /// Writing of objects to a `Write` implementation pub trait WriteTo { /// Write a representation of this instance to `out`. - fn write_to(&self, out: impl std::io::Write) -> std::io::Result<()>; + fn write_to(&self, out: &mut dyn std::io::Write) -> std::io::Result<()>; /// Returns the type of this object. fn kind(&self) -> Kind; @@ -29,15 +29,15 @@ impl<T> WriteTo for &T where T: WriteTo, { - fn write_to(&self, out: impl Write) -> std::io::Result<()> { + fn write_to(&self, out: &mut dyn Write) -> std::io::Result<()> { <T as WriteTo>::write_to(self, out) } - fn size(&self) -> usize { - <T as WriteTo>::size(self) - } - fn kind(&self) -> Kind { <T as WriteTo>::kind(self) } + + fn size(&self) -> usize { + <T as WriteTo>::size(self) + } } diff --git a/vendor/gix-object/src/tree/mod.rs b/vendor/gix-object/src/tree/mod.rs index 147213d70..89e4def48 100644 --- a/vendor/gix-object/src/tree/mod.rs +++ b/vendor/gix-object/src/tree/mod.rs @@ -63,7 +63,7 @@ impl EntryMode { } /// An element of a [`TreeRef`][crate::TreeRef::entries]. -#[derive(PartialEq, Eq, Debug, Hash, Clone)] +#[derive(PartialEq, Eq, Debug, Hash, Clone, Copy)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct EntryRef<'a> { /// The kind of object to which `oid` is pointing. diff --git a/vendor/gix-object/src/tree/ref_iter.rs b/vendor/gix-object/src/tree/ref_iter.rs index fb3ba2dfc..be1244910 100644 --- a/vendor/gix-object/src/tree/ref_iter.rs +++ b/vendor/gix-object/src/tree/ref_iter.rs @@ -1,6 +1,7 @@ use std::convert::TryFrom; -use nom::error::ParseError; +use bstr::BStr; +use winnow::error::ParserError; use crate::{tree, tree::EntryRef, TreeRef, TreeRefIter}; @@ -13,8 +14,29 @@ impl<'a> TreeRefIter<'a> { impl<'a> TreeRef<'a> { /// Deserialize a Tree from `data`. - pub fn from_bytes(data: &'a [u8]) -> Result<TreeRef<'a>, crate::decode::Error> { - decode::tree(data).map(|(_, t)| t).map_err(crate::decode::Error::from) + pub fn from_bytes(mut data: &'a [u8]) -> Result<TreeRef<'a>, crate::decode::Error> { + decode::tree(&mut data).map_err(crate::decode::Error::with_err) + } + + /// Find an entry named `name` knowing if the entry is a directory or not, using a binary search. + /// + /// Note that it's impossible to binary search by name alone as the sort order is special. + pub fn bisect_entry(&self, name: &BStr, is_dir: bool) -> Option<EntryRef<'a>> { + static NULL_HASH: gix_hash::ObjectId = gix_hash::Kind::shortest().null(); + + let search = EntryRef { + mode: if is_dir { + tree::EntryMode::Tree + } else { + tree::EntryMode::Blob + }, + filename: name, + oid: &NULL_HASH, + }; + self.entries + .binary_search_by(|e| e.cmp(&search)) + .ok() + .map(|idx| self.entries[idx]) } /// Create an instance of the empty tree. @@ -46,12 +68,11 @@ impl<'a> Iterator for TreeRefIter<'a> { } None => { self.data = &[]; + let empty = &[] as &[u8]; #[allow(clippy::unit_arg)] - Some(Err(nom::Err::Error(crate::decode::ParseError::from_error_kind( - &[] as &[u8], - nom::error::ErrorKind::MapRes, - )) - .into())) + Some(Err(crate::decode::Error::with_err( + winnow::error::ErrMode::from_error_kind(&empty, winnow::error::ErrorKind::Verify), + ))) } } } @@ -95,14 +116,12 @@ mod decode { use std::convert::TryFrom; use bstr::ByteSlice; - use nom::{ - bytes::complete::{tag, take, take_while1, take_while_m_n}, - character::is_digit, - combinator::all_consuming, - error::ParseError, - multi::many0, - sequence::terminated, - IResult, + use winnow::{ + combinator::{eof, repeat, terminated}, + error::ParserError, + prelude::*, + stream::AsChar, + token::{take, take_while}, }; use crate::{parse::SPACE, tree, tree::EntryRef, TreeRef}; @@ -123,7 +142,7 @@ mod decode { let mode = tree::EntryMode::try_from(mode).ok()?; let (filename, i) = i.split_at(i.find_byte(0)?); let i = &i[1..]; - const HASH_LEN_FIXME: usize = 20; // TODO: know actual /desired length or we may overshoot + const HASH_LEN_FIXME: usize = 20; // TODO(SHA256): know actual/desired length or we may overshoot let (oid, i) = match i.len() { len if len < HASH_LEN_FIXME => return None, _ => i.split_at(20), @@ -138,25 +157,24 @@ mod decode { )) } - pub fn entry<'a, E: ParseError<&'a [u8]>>(i: &'a [u8]) -> IResult<&[u8], EntryRef<'_>, E> { - let (i, mode) = terminated(take_while_m_n(5, 6, is_digit), tag(SPACE))(i)?; - let mode = tree::EntryMode::try_from(mode) - .map_err(|invalid| nom::Err::Error(E::from_error_kind(invalid, nom::error::ErrorKind::MapRes)))?; - let (i, filename) = terminated(take_while1(|b| b != NULL[0]), tag(NULL))(i)?; - let (i, oid) = take(20u8)(i)?; // TODO: make this compatible with other hash lengths - - Ok(( - i, - EntryRef { + pub fn entry<'a, E: ParserError<&'a [u8]>>(i: &mut &'a [u8]) -> PResult<EntryRef<'a>, E> { + ( + terminated(take_while(5..=6, AsChar::is_dec_digit), SPACE) + .verify_map(|mode| tree::EntryMode::try_from(mode).ok()), + terminated(take_while(1.., |b| b != NULL[0]), NULL), + take(20u8), // TODO(SHA256): make this compatible with other hash lengths + ) + .map(|(mode, filename, oid): (_, &[u8], _)| EntryRef { mode, filename: filename.as_bstr(), oid: gix_hash::oid::try_from_bytes(oid).expect("we counted exactly 20 bytes"), - }, - )) + }) + .parse_next(i) } - pub fn tree<'a, E: ParseError<&'a [u8]>>(i: &'a [u8]) -> IResult<&'a [u8], TreeRef<'a>, E> { - let (i, entries) = all_consuming(many0(entry))(i)?; - Ok((i, TreeRef { entries })) + pub fn tree<'a, E: ParserError<&'a [u8]>>(i: &mut &'a [u8]) -> PResult<TreeRef<'a>, E> { + terminated(repeat(0.., entry), eof) + .map(|entries| TreeRef { entries }) + .parse_next(i) } } diff --git a/vendor/gix-object/src/tree/write.rs b/vendor/gix-object/src/tree/write.rs index f826e6749..91e1dc2e0 100644 --- a/vendor/gix-object/src/tree/write.rs +++ b/vendor/gix-object/src/tree/write.rs @@ -25,7 +25,7 @@ impl From<Error> for io::Error { /// Serialization impl crate::WriteTo for Tree { /// Serialize this tree to `out` in the git internal format. - fn write_to(&self, mut out: impl io::Write) -> io::Result<()> { + fn write_to(&self, out: &mut dyn io::Write) -> io::Result<()> { debug_assert_eq!( &{ let mut entries_sorted = self.entries.clone(); @@ -68,7 +68,7 @@ impl crate::WriteTo for Tree { /// Serialization impl<'a> crate::WriteTo for TreeRef<'a> { /// Serialize this tree to `out` in the git internal format. - fn write_to(&self, mut out: impl io::Write) -> io::Result<()> { + fn write_to(&self, out: &mut dyn io::Write) -> io::Result<()> { debug_assert_eq!( &{ let mut entries_sorted = self.entries.clone(); |