History

omg nom parsing works… (#198)

…but really, it's not super nice especially considering the counting of consumed bytes is based on ptr arithmetic.

Sebastian Thiel committed 5 years ago

Commit cd117041 parent d9afc223 files · +46 −55Browse files

Showing the changes to git-object/src/commit (1 of 3 files). Show all

Unified · Split

git-object/src/commit/message.rs+32−53
@@ -8,72 +8,51 @@ use crate::{ · 7 unmodified lines
8mod decode {8mod decode {
9 use crate::bstr::{BStr, ByteSlice};9 use crate::bstr::{BStr, ByteSlice};
10 use nom::branch::alt;10 use nom::branch::alt;
11 use nom::bytes::complete::{tag, take_till1, take_until1};11 use nom::bytes::complete::{tag, take_till1};
12 use nom::combinator::{all_consuming, map, opt, peek, recognize};12 use nom::combinator::all_consuming;
13 use nom::error::ParseError;13 use nom::error::ParseError;
14 use nom::multi::{fold_many1, length_data};14 use nom::sequence::pair;
15 use nom::sequence::{pair, terminated};
16 use nom::IResult;15 use nom::IResult;
16 use std::convert::TryInto;
17 17
18 fn newline<'a, E: ParseError<&'a [u8]>>(i: &'a [u8]) -> IResult<&'a [u8], &'a [u8], E> {18 fn newline<'a, E: ParseError<&'a [u8]>>(i: &'a [u8]) -> IResult<&'a [u8], &'a [u8], E> {
19 alt((tag(b"\r\n"), tag(b"\n")))(i)19 alt((tag(b"\r\n"), tag(b"\n")))(i)
20 }20 }
21 21
22 fn subject<'a, E: ParseError<&'a [u8]>>(i: &'a [u8]) -> IResult<&'a [u8], &'a BStr, E> {
23 map(
24 length_data(peek(fold_many1(
25 recognize(terminated(take_till1(|c| c == b'\n'), tag(b"\n"))),
26 || 0,
27 |acc: usize, item: &'a [u8]| acc + item.len(),
28 ))),
29 |s| s.as_bstr(),
30 )(i)
31 }
32
33 #[cfg(test)]
34 mod tests {
35 use super::*;
36
37 #[test]
38 fn subject_ending_in_newline_with_newline() {
39 assert_eq!(subject::<()>(b"a\nb\n").unwrap(), (b"".as_ref(), b"a\nb\nc".as_bstr()));
40 }
41
42 #[test]
43 fn subject_with_single_newline() {
44 assert_eq!(subject::<()>(b"a\n").unwrap(), (b"".as_ref(), b"a\n".as_bstr()));
45 }
46 }
47
48 /// Parse a signature from the bytes input `i` using `nom`.
49 pub fn nomfoo<'a, E: ParseError<&'a [u8]>>(i: &'a [u8]) -> IResult<&'a [u8], (&'a BStr, Option<&'a BStr>), E> {22 pub fn nomfoo<'a, E: ParseError<&'a [u8]>>(i: &'a [u8]) -> IResult<&'a [u8], (&'a BStr, Option<&'a BStr>), E> {
50 let (rest, subject) = opt(terminated(subject, pair(newline, newline)))(i)?;23 let mut c = i;
51 Ok((24 while !c.is_empty() {
52 &[],25 c = match take_till1::<_, _, E>(|c| c == b'\n' || c == b'\r')(c) {
53 match subject {26 Ok((i1, segment)) => match pair::<_, _, _, E, _, _>(newline, newline)(i1) {
54 Some(subject) => (subject.as_bstr(), (!rest.is_empty()).then(|| rest.as_bstr())),27 Ok((body, _)) => {
55 None => (i.as_bstr(), None),28 // SAFETY: the pointers are pointing to the same slice.
56 },29 #[allow(unsafe_code)]
57 ))30 let consumed_bytes = unsafe { segment.as_ptr_range().end.offset_from(i.as_ptr()) };
31 return Ok((
32 &[],
33 (
34 &i[0usize..consumed_bytes.try_into().expect("positive offset")].as_bstr(),
35 (!body.is_empty()).then(|| body.as_bstr()),
36 ),
37 ));
38 }
39 Err(_) => match i1.get(1..) {
40 Some(next) => next,
41 None => break,
42 },
43 },
44 Err(_) => match c.get(1..) {
45 Some(next) => next,
46 None => break,
47 },
48 };
49 }
50 Ok((&[], (i.as_bstr(), None)))
58 }51 }
59 52
60 /// Returns title and body, without separator53 /// Returns title and body, without separator
61 pub fn bytes(message: &[u8]) -> (&BStr, Option<&BStr>) {54 pub fn bytes(message: &[u8]) -> (&BStr, Option<&BStr>) {
62 all_consuming(nomfoo::<()>)(message).expect("cannot fail").155 all_consuming(nomfoo::<()>)(message).expect("cannot fail").1
63 // match message
64 // .find(b"\n\n")
65 // .map(|pos| (2, pos))
66 // .or_else(|| message.find(b"\r\n\r\n").map(|pos| (4, pos)))
67 // {
68 // Some((sep_len, end_of_title)) => {
69 // let body = &message[end_of_title + sep_len..];
70 // (
71 // message[..end_of_title].as_bstr(),
72 // if body.is_empty() { None } else { Some(body.as_bstr()) },
73 // )
74 // }
75 // None => (message.as_bstr(), None),
76 // }
77 }56 }
78}57}
79 58