use fallible_iterator::FallibleIterator;
use super::{FromHexError, FromHexErrorKind};
use crate::decode::common::{has_0x_prefix, has_0x_prefix_ascii};
use crate::decode::streaming::HexDecodeAsciiIterator;
#[derive(Debug, PartialEq, Eq)]
enum OutputLength {
MayBeShorterThanDst,
MustEqualDst,
}
fn decode_ascii_to_buf_internal<I: FallibleIterator<Item = (usize, u8), Error = FromHexError>>(
mut iter: HexDecodeAsciiIterator<I>,
dst: &mut [u8],
output_length: OutputLength,
) -> Result<usize, FromHexError> {
let mut off_dst = 0;
loop {
match iter.next() {
Ok(Some(byte)) => match dst.get_mut(off_dst) {
Some(x) => {
*x = byte;
off_dst += 1;
}
None => {
return Err(FromHexError {
position: iter.last_position(),
kind: FromHexErrorKind::Eof,
});
}
},
Ok(None) => {
if output_length == OutputLength::MustEqualDst && off_dst < dst.len() {
return Err(FromHexError {
position: iter.last_position(),
kind: FromHexErrorKind::OutputBufferTooShort,
});
} else {
return Ok(off_dst);
}
}
Err(e) => {
return Err(e);
}
}
}
}
fn decode_to_buf_internal(
hex: &str,
dst: &mut [u8],
output_length: OutputLength,
) -> Result<usize, FromHexError> {
let chars_as_bytes_with_position = hex
.char_indices()
.map(|(position, c)| match u8::try_from(c) {
Ok(byte) if byte < 128 => Ok((position, byte)),
_ => Err(FromHexError {
position,
kind: FromHexErrorKind::UnexpectedCharacter(c),
}),
})
.skip(if has_0x_prefix(hex) { 2 } else { 0 });
let result = decode_ascii_to_buf_internal(
HexDecodeAsciiIterator::new(fallible_iterator::convert(chars_as_bytes_with_position)),
dst,
output_length,
);
match result {
Ok(len) => Ok(len),
Err(FromHexError {
position,
kind: FromHexErrorKind::UnexpectedByte(byte),
}) => Err(FromHexError {
position,
kind: FromHexErrorKind::UnexpectedCharacter(byte as char), }),
Err(err) => Err(err),
}
}
pub fn decode_ascii_to_buf(hex: &[u8], dst: &mut [u8]) -> Result<usize, FromHexError> {
decode_ascii_to_buf_internal(
HexDecodeAsciiIterator::new(fallible_iterator::convert(
hex.iter()
.copied()
.enumerate()
.skip(if has_0x_prefix_ascii(hex) { 2 } else { 0 })
.map(Ok),
)),
dst,
OutputLength::MayBeShorterThanDst,
)
}
pub fn decode_ascii_to_buf_exact(hex: &[u8], dst: &mut [u8]) -> Result<(), FromHexError> {
decode_ascii_to_buf_internal(
HexDecodeAsciiIterator::new(fallible_iterator::convert(
hex.iter()
.copied()
.enumerate()
.skip(if has_0x_prefix_ascii(hex) { 2 } else { 0 })
.map(Ok),
)),
dst,
OutputLength::MustEqualDst,
)
.map(|_| ())
}
pub fn decode_to_buf(hex: &str, dst: &mut [u8]) -> Result<usize, FromHexError> {
decode_to_buf_internal(hex, dst, OutputLength::MayBeShorterThanDst)
}
pub fn decode_to_buf_exact(hex: &str, dst: &mut [u8]) -> Result<(), FromHexError> {
decode_to_buf_internal(hex, dst, OutputLength::MustEqualDst).map(|_| ())
}