【问题标题】:How can I take N bits of byte in nom?如何在 nom 中获取 N 位字节?
【发布时间】:2020-10-27 00:41:43
【问题描述】:

我正在尝试使用 nom 编写 HTTP2 解析器。我正在实现 HPACK 标头压缩,但无法理解如何使用 nom 中的位字段。

例如,Indexed Header Field Representation 以第一位为 1 开头。

fn indexed_header_field_tag(i: &[u8]) -> IResult<&[u8], ()> {
    nom::bits::streaming::tag(1, 1)(i)
}

这给了我一个我不太明白的编译器警告(老实说,我对 nom 中的类型有一些问题):

error[E0308]: mismatched types
   --> src/parser.rs:179:41
    |
179 |         nom::bits::streaming::tag(1, 1)(i)
    |                                         ^ expected tuple, found `&[u8]`
    |
    = note:  expected tuple `(_, usize)`
            found reference `&[u8]`

我应该在这里放什么?

另一个例子是:

fn take_2_bits(input: &[u8]) -> IResult<&[u8], u64> {
    nom::bits::bits(nom::bits::streaming::take::<_, _, _, (_, _)>(2usize))(input)
}

在这里,我的问题是第一个字节的剩余位被丢弃,即使我想进一步处理它们。

我想我可以使用按位与手动完成,但使用 nom 会更好。

我尝试了以下方法,但这给了我很多编译器警告:

fn check_tag(input: &[u8]) -> IResult<&[u8], ()> {
    use nom::bits::{bits, bytes, complete::take_bits, complete::tag};
    let converted_bits = bits(take_bits(2usize))(2)?;
    let something = tag(0x80, 2)(converted_bits);
    nom::bits::bytes(something)
}

(灵感来自https://docs.rs/nom/5.1.2/nom/bits/fn.bytes.html)。

它告诉我,没有complete::take_bits(我猜只有文档有点偏),但它也告诉我:

368 |         let converted_bits = bits(take_bits(2usize))(2)?;
    |                                                      ^ the trait `nom::traits::Slice<std::ops::RangeFrom<usize>>` is not implemented for `{integer}`

和其他错误,但这只是由于第一个错误造成的。

【问题讨论】:

标签: rust nom


【解决方案1】:

面向位的接口(例如take)接受元组(I, usize)representing (input, bit_offset),因此需要使用bits等函数将输入从i转换为(i, 0),然后通过忽略当前字节中的任何剩余位将输出转换回字节。

关于第二个问题,见How can I combine nom parsers to get a more bit-oriented interface to the data?上的cmets:只有在需要位和字节之间切换时才使用bits,并使面向位的函数使用面向位的输入。

示例代码

use nom::{IResult, bits::{bits, complete::{take, tag}}};

fn take_2_bits(i: (&[u8], usize)) -> IResult<(&[u8], usize), u8> {
    take(2usize)(i)
}

fn check_tag(i: (&[u8], usize)) -> IResult<(&[u8], usize), u8> {
    tag(0x01, 1usize)(i)
}

fn do_everything_bits(i: (&[u8], usize)) -> IResult<(&[u8], usize), (u8, u8)> {
    let (i, a) = take_2_bits(i)?;
    let (i, b) = check_tag(i)?;
    Ok((i, (a, b)))
}

fn do_everything_bytes(i: &[u8]) -> IResult<&[u8], (u8, u8)> {
    bits(do_everything_bits)(i)
}

【讨论】:

  • 抱歉,我还是不太明白它是如何工作的。例如,我有b"\xea",它表示存储在最后 5 位 (tools.ietf.org/html/rfc7541#appendix-C.1.1) 中的 10。你能帮我写这个吗?我需要在第一位运行解析器,检查它是否以 1、01、001、0000 或 0001 开头,然后在剩余位上运行另一个解析器(可以任意多,因为它是可变大小的整数)。
  • @Hellstorm 首先,使用bits 将输入转换为位。然后,将bits 的结果传递给每个解析器。
  • 感谢您的帮助。我在我的问题中添加了一个额外的示例,但我没有成功尝试过。不幸的是,我真的不明白如何组合bits 的所有解析器。
  • @Hellstorm 我在我的答案中添加了一些工作示例代码。
猜你喜欢
  • 2013-02-21
  • 1970-01-01
  • 1970-01-01
  • 2015-05-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-16
相关资源
最近更新 更多