use core::convert::TryInto;
use crate::{Decodable, ErrorKind, Length, Result, TagLike};
#[derive(Debug)]
pub struct Decoder<'a> {
bytes: Option<&'a [u8]>,
position: Length,
}
impl<'a> Decoder<'a> {
pub fn new(bytes: &'a [u8]) -> Self {
Self {
bytes: Some(bytes),
position: Length::zero(),
}
}
pub fn decode<T: Decodable<'a>>(&mut self) -> Result<T> {
if self.is_failed() {
self.error(ErrorKind::Failed)?;
}
T::decode(self).map_err(|e| {
self.bytes.take();
e.nested(self.position)
})
}
pub fn decode_tagged_value<T: Decodable<'a> + TagLike, V: Decodable<'a>>(&mut self, tag: T) -> Result<V> {
let tagged: crate::TaggedSlice<T> = self.decode()?;
tagged.tag().assert_eq(tag)?;
Self::new(tagged.as_bytes()).decode()
}
pub fn decode_tagged_slice<T: Decodable<'a> + TagLike>(&mut self, tag: T) -> Result<&'a [u8]> {
let tagged: crate::TaggedSlice<T> = self.decode()?;
tagged.tag().assert_eq(tag)?;
Ok(tagged.as_bytes())
}
pub fn error<T>(&mut self, kind: ErrorKind) -> Result<T> {
self.bytes.take();
Err(kind.at(self.position))
}
pub fn is_failed(&self) -> bool {
self.bytes.is_none()
}
pub fn finish<T>(self, value: T) -> Result<T> {
if self.is_failed() {
Err(ErrorKind::Failed.at(self.position))
} else if !self.is_finished() {
Err(ErrorKind::TrailingData {
decoded: self.position,
remaining: self.remaining_len()?,
}
.at(self.position))
} else {
Ok(value)
}
}
pub fn is_finished(&self) -> bool {
self.remaining().map(|rem| rem.is_empty()).unwrap_or(false)
}
pub(crate) fn byte(&mut self) -> Result<u8> {
match self.bytes(1u8)? {
[byte] => Ok(*byte),
_ => self.error(ErrorKind::Truncated),
}
}
pub(crate) fn bytes(&mut self, len: impl TryInto<Length>) -> Result<&'a [u8]> {
if self.is_failed() {
self.error(ErrorKind::Failed)?;
}
let len = len
.try_into()
.or_else(|_| self.error(ErrorKind::Overflow))?;
let result = self
.remaining()?
.get(..len.to_usize())
.ok_or(ErrorKind::Truncated)?;
self.position = (self.position + len)?;
Ok(result)
}
pub(crate) fn peek(&self) -> Option<u8> {
self.remaining()
.ok()
.and_then(|bytes| bytes.get(0).cloned())
}
fn remaining(&self) -> Result<&'a [u8]> {
self.bytes
.and_then(|b| b.get(self.position.into()..))
.ok_or_else(|| ErrorKind::Truncated.at(self.position))
}
fn remaining_len(&self) -> Result<Length> {
self.remaining()?.len().try_into()
}
}
impl<'a> From<&'a [u8]> for Decoder<'a> {
fn from(bytes: &'a [u8]) -> Decoder<'a> {
Decoder::new(bytes)
}
}
#[cfg(test)]
mod tests {
use crate::{Decodable, Tag, TaggedSlice};
#[test]
fn zero_length() {
let buf: &[u8] = &[0x05, 0x00];
let ts = TaggedSlice::from_bytes(buf).unwrap();
assert_eq!(ts, TaggedSlice::from(Tag::universal(0x5), &[]).unwrap());
}
}