如何在nom中实现"take while, but at most N characters"?
How to implement "take while, but at most N characters" in nom?
我如何给 nom 的 take_while
一个它应该匹配的字符数的上限?我想按一定条件取字符,但是最多N
.
由于这将是解析器中性能非常关键的部分,我想避免使用一堆单独的 take(1usize)
。部分原因是因为不得不一个接一个地处理单个元素切片感觉很尴尬,而且还因为编译器可能无法在编译时知道它们的大小必须为 1,即,它可能必须生成绑定检查或尺寸断言,对吗?
从概念上讲,带有上限的 take_while
感觉更合适。我希望我可以像这样自己使用可变循环变量进行计数:
let mut i = 0;
let (input, _) = take_while(|c| {
i += 1;
c >= 0x80 && i < 9
})(input)?;
事实上,我什至可以在闭包中提取必要的信息,这很可能会导致非常高效的代码生成。 (目标是实现某种 VarInt LSB 编码,即我可以更新局部可变变量 x = x + (if last_byte { c } else { c & 0x7f }) << 7
。)
不幸的是 take_while
似乎只允许 Fn
而不是 FnMut
这可能意味着这是不可能的(为什么要限制?)。我还能做些什么来很好地实现它?
使用 Cell
使您的闭包具有内部可变性。然后它可以有可变状态,但仍然实现 Fn
:
let i = Cell::new(0);
let (input, _) = take_while(|c| {
i.set(i.get() + 1);
c > 0x80 && i.get() < 9
})(input)?;
有个nom-function take_while_m_n
:
const N: usize = ...
fn take_native(input: &[u8]) -> IResult<&[u8], &[u8]> {
take_while_m_n(0, N, |c| c > 0x80)(input)
}
然而,使用快速基准测试,它似乎比 慢得多(或者基准优化错误,因为 Cell
-version 只需要 1ns/iter,而 13ns/iter take_while_m_n).
#![feature(test)]
extern crate test;
use std::cell::Cell;
use nom::{
bytes::complete::{take_while, take_while_m_n},
IResult,
};
fn take_cell(input: &[u8]) -> IResult<&[u8], &[u8]> {
let i = Cell::new(0);
let (input, output) = take_while(|c| {
i.set(i.get() + 1);
c > 0x80 && i.get() < 5
})(input)?;
Ok((input, output))
}
fn take_native(input: &[u8]) -> IResult<&[u8], &[u8]> {
take_while_m_n(0, 4, |c| c > 0x80)(input)
}
#[cfg(test)]
mod tests {
use super::*;
const INPUT: &[u8] = &[
0x81, 0x82, 0x83, 0x84, 0x81, 0x82, 0x83, 0x84, 0x81, 0x82, 0x83, 0x84, 0x81, 0x82, 0x83,
0x84, 0x81, 0x82, 0x83, 0x84, 0x81, 0x82, 0x83, 0x84, 0x81, 0x82, 0x83, 0x84,
];
#[bench]
fn bench_cell(b: &mut test::Bencher) {
assert_eq!(take_cell(INPUT).unwrap().1, &[0x81, 0x82, 0x83, 0x84]);
b.iter(|| take_cell(INPUT).unwrap());
}
#[bench]
fn bench_native(b: &mut test::Bencher) {
assert_eq!(take_native(INPUT).unwrap().1, &[0x81, 0x82, 0x83, 0x84]);
b.iter(|| take_native(INPUT).unwrap());
}
}
我如何给 nom 的 take_while
一个它应该匹配的字符数的上限?我想按一定条件取字符,但是最多N
.
由于这将是解析器中性能非常关键的部分,我想避免使用一堆单独的 take(1usize)
。部分原因是因为不得不一个接一个地处理单个元素切片感觉很尴尬,而且还因为编译器可能无法在编译时知道它们的大小必须为 1,即,它可能必须生成绑定检查或尺寸断言,对吗?
从概念上讲,带有上限的 take_while
感觉更合适。我希望我可以像这样自己使用可变循环变量进行计数:
let mut i = 0;
let (input, _) = take_while(|c| {
i += 1;
c >= 0x80 && i < 9
})(input)?;
事实上,我什至可以在闭包中提取必要的信息,这很可能会导致非常高效的代码生成。 (目标是实现某种 VarInt LSB 编码,即我可以更新局部可变变量 x = x + (if last_byte { c } else { c & 0x7f }) << 7
。)
不幸的是 take_while
似乎只允许 Fn
而不是 FnMut
这可能意味着这是不可能的(为什么要限制?)。我还能做些什么来很好地实现它?
使用 Cell
使您的闭包具有内部可变性。然后它可以有可变状态,但仍然实现 Fn
:
let i = Cell::new(0);
let (input, _) = take_while(|c| {
i.set(i.get() + 1);
c > 0x80 && i.get() < 9
})(input)?;
有个nom-function take_while_m_n
:
const N: usize = ...
fn take_native(input: &[u8]) -> IResult<&[u8], &[u8]> {
take_while_m_n(0, N, |c| c > 0x80)(input)
}
然而,使用快速基准测试,它似乎比 Cell
-version 只需要 1ns/iter,而 13ns/iter take_while_m_n).
#![feature(test)]
extern crate test;
use std::cell::Cell;
use nom::{
bytes::complete::{take_while, take_while_m_n},
IResult,
};
fn take_cell(input: &[u8]) -> IResult<&[u8], &[u8]> {
let i = Cell::new(0);
let (input, output) = take_while(|c| {
i.set(i.get() + 1);
c > 0x80 && i.get() < 5
})(input)?;
Ok((input, output))
}
fn take_native(input: &[u8]) -> IResult<&[u8], &[u8]> {
take_while_m_n(0, 4, |c| c > 0x80)(input)
}
#[cfg(test)]
mod tests {
use super::*;
const INPUT: &[u8] = &[
0x81, 0x82, 0x83, 0x84, 0x81, 0x82, 0x83, 0x84, 0x81, 0x82, 0x83, 0x84, 0x81, 0x82, 0x83,
0x84, 0x81, 0x82, 0x83, 0x84, 0x81, 0x82, 0x83, 0x84, 0x81, 0x82, 0x83, 0x84,
];
#[bench]
fn bench_cell(b: &mut test::Bencher) {
assert_eq!(take_cell(INPUT).unwrap().1, &[0x81, 0x82, 0x83, 0x84]);
b.iter(|| take_cell(INPUT).unwrap());
}
#[bench]
fn bench_native(b: &mut test::Bencher) {
assert_eq!(take_native(INPUT).unwrap().1, &[0x81, 0x82, 0x83, 0x84]);
b.iter(|| take_native(INPUT).unwrap());
}
}