3

I am trying to write a HTTP2 parser with nom. I'm implementing the HPACK header compression, but having trouble understanding how to work with bit fields in nom.

For example, the Indexed Header Field Representation starts with the first bit as 1.

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

This gives me a compiler warning I don't really understand (To be honest, I'm having some problems with the types in 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]`

Wwhat should I put here?

Another example is:

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

Here, my problem is that the remaining bits of the first byte are discarded, even though I want to further work on them.

I guess I can do it manually with bitwise-ands, but doing it with nom would be nicer.

I've tried with the following approach, but this gives me many compiler warnings:

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)
}

(Inspired from https://docs.rs/nom/5.1.2/nom/bits/fn.bytes.html).

It tells me, that there is no complete::take_bits (I guess only the documentation is a bit off there), but it also tells me:

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

and other errors, but which just result due to the first errors.

Community
  • 1
  • 1
Hellstorm
  • 562
  • 4
  • 23
  • 1
    See also [How can I combine nom parsers to get a more bit-oriented interface to the data?](https://stackoverflow.com/q/48131656/155423) – Shepmaster Jul 06 '20 at 20:25

1 Answers1

1

The bit-oriented interfaces (e.g. take) accept a tuple (I, usize), representing (input, bit_offset), so you need to use a function such as bits to convert the input from i to (i, 0), then convert the output back to bytes by ignoring any remaining bits in the current byte.

For the second question, see the comments on How can I combine nom parsers to get a more bit-oriented interface to the data? : use bits only when you need to switch between bits and bytes, and make bit-oriented functions use bit-oriented input.

Example code

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)
}
Solomon Ucko
  • 5,724
  • 3
  • 24
  • 45
  • Sorry, I still don't really understand how it works. For example, I have ```b"\xea"``` which represents a 10 stored in the last 5 bits (https://tools.ietf.org/html/rfc7541#appendix-C.1.1). Can you help me how to write this? I need to run a parser on the first bits, check if it starts with 1, 01, 001, 0000 or 0001 and then run another parser on the remaining bits (which can be arbitrarily many, because it is a variable sized integer). – Hellstorm Jul 08 '20 at 17:34
  • @Hellstorm First, use `bits` to convert the input to bits. Then, pass the result of `bits` to each of the parsers. – Solomon Ucko Jul 08 '20 at 17:35
  • Thanks for your hepl. I've added an additional example to my question, which I've unsuccessfully tried. Unfortunately I don't really understand how I can combine all the parsers for `bits`. – Hellstorm Jul 08 '20 at 19:07
  • 1
    @Hellstorm I have added some working example code to my answer. – Solomon Ucko Jul 08 '20 at 19:52