…but really, it's not super nice especially considering the counting of consumed bytes is based on ptr arithmetic.
Showing the changes to git-object/src/commit (1 of 3 files). Show all
Unified · Split
| @@ -8,72 +8,51 @@ use crate::{ · 7 unmodified lines | |||
| 8 | mod decode { | 8 | mod 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 separator | 53 | /// 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").1 | 55 | 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 | ||