use bstr::{BStr, BString, ByteSlice, ByteVec};
pub(crate) const NL: &[u8] = b"\n";
pub(crate) const SPACE: &[u8] = b" ";
const SPACE_OR_NL: &[u8] = b" \n";
pub(crate) type ParseResult<T> = Result<T, crate::decode::Error>;
pub(crate) fn any_header_field_multi_line<'a>(i: &mut &'a [u8]) -> ParseResult<(&'a [u8], BString)> {
let mut c = *i;
let input = c;
let name_end = c
.find_byteset(SPACE_OR_NL)
.filter(|pos| *pos > 0)
.ok_or(crate::decode::Error)?;
if c.get(name_end) != Some(&b' ') {
return Err(crate::decode::Error);
}
c = &c[name_end + 1..];
let first_line_end = c.find_byte(b'\n').ok_or(crate::decode::Error)?;
c = &c[first_line_end + 1..];
let mut continuation_end = name_end + 1 + first_line_end + 1;
let mut continuation_count = 0usize;
while c.first() == Some(&b' ') {
let line_end = c.find_byte(b'\n').ok_or(crate::decode::Error)?;
continuation_end += line_end + 1;
c = &c[line_end + 1..];
continuation_count += 1;
}
if continuation_count == 0 {
return Err(crate::decode::Error);
}
let bytes = input[name_end + 1..continuation_end].as_bstr();
let mut out = BString::from(Vec::with_capacity(bytes.len()));
let mut lines = bytes.lines_with_terminator();
out.push_str(lines.next().expect("first line"));
for line in lines {
out.push_str(&line[1..]);
}
*i = &input[continuation_end..];
Ok((input[..name_end].as_bstr(), out))
}
pub(crate) fn header_field<'a, T>(
i: &mut &'a [u8],
name: &'static [u8],
parse_value: impl FnOnce(&'a [u8]) -> ParseResult<T>,
) -> ParseResult<T> {
let c = *i;
let Some(rest) = c.strip_prefix(name).and_then(|rest| rest.strip_prefix(SPACE)) else {
return Err(crate::decode::Error);
};
let Some(nl) = rest.find_byte(b'\n') else {
return Err(crate::decode::Error);
};
let value = parse_value(&rest[..nl])?;
*i = &rest[nl + 1..];
Ok(value)
}
pub(crate) fn any_header_field<'a>(i: &mut &'a [u8]) -> ParseResult<(&'a [u8], &'a [u8])> {
let mut c = *i;
let input = c;
let name_end = c
.find_byteset(SPACE_OR_NL)
.filter(|pos| *pos > 0)
.ok_or(crate::decode::Error)?;
if c.get(name_end) != Some(&b' ') {
return Err(crate::decode::Error);
}
c = &c[name_end + 1..];
if let Some(value_end) = c.find_byte(b'\n') {
let value = &c[..value_end];
let rest = &c[value_end + 1..];
*i = rest;
Ok((&input[..name_end], value))
} else {
Err(crate::decode::Error)
}
}
pub fn hex_hash(i: &[u8], hash_kind: gix_hash::Kind) -> ParseResult<&BStr> {
if i.len() != hash_kind.len_in_hex() || !i.iter().all(u8::is_ascii_hexdigit) {
return Err(crate::decode::Error);
}
Ok(i.as_bstr())
}
pub(crate) fn signature(mut i: &[u8]) -> ParseResult<gix_actor::SignatureRef<'_>> {
let signature = gix_actor::SignatureRef::from_bytes_consuming(&mut i).map_err(|_| crate::decode::Error)?;
if i.is_empty() {
Ok(signature)
} else {
Err(crate::decode::Error)
}
}
pub(crate) fn signature_raw(i: &[u8]) -> ParseResult<&BStr> {
signature(i).map(|_| i.as_bstr())
}
pub(crate) fn parse_signature(raw: &BStr) -> Result<gix_actor::SignatureRef<'_>, crate::decode::Error> {
signature(raw.as_ref())
}