Skip to content

Commit

Permalink
fix: Handle partial parsing in the num parsers
Browse files Browse the repository at this point in the history
  • Loading branch information
Marwes committed Aug 26, 2019
1 parent 7f4a310 commit 47764c7
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 12 deletions.
6 changes: 6 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,12 @@ impl<T, E> ParseResult<T, E> {
ConsumedErr(_) | EmptyErr(_) => false,
}
}

#[inline]
pub fn is_err(&self) -> bool {
!self.is_ok()
}

pub fn as_ref(&self) -> ParseResult<&T, &E> {
match *self {
ConsumedOk(ref t) => ConsumedOk(t),
Expand Down
38 changes: 27 additions & 11 deletions src/parser/byte.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ use self::ascii::AsciiChar;

use crate::combinator::{satisfy, skip_many, token, Expected, Satisfy, SkipMany, Token};
use crate::error::{Info, ParseError, ParseResult, Tracked};
use crate::parser::item::tokens_cmp;
use crate::parser::range::{take_fn, TakeRange};
use crate::parser::sequence::With;
use crate::parser::{
item::tokens_cmp,
range::{take_fn, TakeRange},
sequence::With,
ParseMode,
};
use crate::stream::{FullRangeStream, RangeStream, Stream, StreamOnce};
use crate::Parser;

Expand Down Expand Up @@ -492,6 +495,7 @@ fn memslice(needle: &[u8], haystack: &[u8]) -> Option<usize> {
/// Parsers for decoding numbers in big-endian or little-endian order.
pub mod num {
use super::*;
use crate::error::ResultExt;
use crate::stream::uncons;

use byteorder::{ByteOrder, BE, LE};
Expand All @@ -517,16 +521,28 @@ pub mod num {
type Output = $func_name;
type PartialState = ();

#[inline]
fn parse_lazy(
parse_mode!(Input);
fn parse_mode_impl<M>(
&mut self,
input: &mut Input
) -> ParseResult<Self::Output, <Input as StreamOnce>::Error> {
let buffer = &mut [0u8; 8][..size_of::<Self::Output>()];
for elem in &mut *buffer {
*elem = ctry!(uncons(input)).0;
_mode: M,
input: &mut Input,
_state: &mut Self::PartialState,
) -> ParseResult<Self::Output, <Input as StreamOnce>::Error>
where
M: ParseMode,
{
let checkpoint = input.checkpoint();
let result = (|| {
let buffer = &mut [0u8; 8][..size_of::<Self::Output>()];
for elem in &mut *buffer {
*elem = ctry!(uncons(input)).0;
}
ConsumedOk(B::$read_name(buffer))
})();
if result.is_err() {
ctry!(input.reset(checkpoint).consumed());
}
ConsumedOk(B::$read_name(buffer))
result
}
}

Expand Down
77 changes: 76 additions & 1 deletion tests/async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use combine::{
error::{ParseError, StreamError},
many1, parser,
parser::{
byte::take_until_bytes,
byte::{num, take_until_bytes},
char::{char, digit, letter},
item::item,
range::{
Expand Down Expand Up @@ -122,6 +122,60 @@ macro_rules! impl_decoder {
}
}

macro_rules! impl_byte_decoder {
($typ: ident, $item: ty, $parser: expr, $custom_state: ty) => {
#[derive(Default)]
struct $typ(AnyPartialState, $custom_state);
impl_byte_decoder!{$typ, $item, $parser; ($custom_state)}
};
($typ: ident, $item: ty, $parser: expr) => {
#[derive(Default)]
struct $typ(AnyPartialState);
impl_byte_decoder!{$typ, $item, $parser; ()}
};
($typ: ident, $item: ty, $parser: expr; ( $($custom_state: tt)* )) => {
impl Decoder for $typ {
type Item = $item;
type Error = Error;

fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
(&mut &mut *self).decode(src)
}
}

impl<'a> Decoder for &'a mut $typ {
type Item = $item;
type Error = Error;

fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
let (opt, removed_len) = {
let str_src = &src[..];
println!("Decoding `{:?}`", str_src);
combine::stream::decode(
any_partial_state(mk_parser!($parser, self, ($($custom_state)*))),
easy::Stream(combine::stream::PartialStream(str_src)),
&mut self.0,
).map_err(|err| {
// Since err contains references into `src` we must remove these before
// returning the error and before we call `split_to` to remove the input we
// just consumed
let err = err.map_range(|r| format!("{:?}", r))
.map_position(|p| p.translate_position(&str_src[..]));
format!("{}\nIn input: `{:?}`", err, str_src)
})?
};

src.split_to(removed_len);
match opt {
None => println!("Need more input!"),
Some(_) => (),
}
Ok(opt)
}
}
}
}

use partial_io::{GenWouldBlock, PartialAsyncRead, PartialOp, PartialWithErrors};

fn run_decoder<B, D, S>(input: &B, seq: S, decoder: D) -> Result<Vec<D::Item>, D::Error>
Expand Down Expand Up @@ -504,6 +558,27 @@ quickcheck! {
assert!(result.as_ref().is_ok(), "{}", result.unwrap_err());
assert_eq!(result.unwrap(), sizes);
}

fn num_test(ints: Vec<u16>, seq: PartialWithErrors<GenWouldBlock>) -> () {
impl_byte_decoder!{ TestParser, u16,
num::be_u16()
.skip(take(2))
}

let input: Vec<u8> = ints.iter()
.flat_map(|i| {
let mut v = Vec::new();
v.extend_from_slice(&i.to_be_bytes());
v.extend_from_slice(b"\r\n");
v
})
.collect();

let result = run_decoder(&input, seq, TestParser(Default::default()));

assert!(result.as_ref().is_ok(), "{}", result.unwrap_err());
assert_eq!(result.unwrap(), ints);
}
}

#[test]
Expand Down

0 comments on commit 47764c7

Please sign in to comment.