2
votes

I'm writing a Rust implementation of the Google Brotli decompression algorithm, which uses a fixed number of "implicit zeroes" at the end of the stream to enable certain optimisations.

The Google C implementation does this using a spaghetti code of various counters, but I'd like to use the adaptors in the Rust std::io library instead. Something like:

pub struct StreamBitReader<R> {
    reader: Chain<R,Take<Repeat>>,
    ...
}

where 'R' is the underlying Read that the bit reader struct is wrapping. However, there are a number of cases where the decompression algorithm checks to see if it has "overrun" the stream, and it does this by checking the number of implicit zero bytes read. This appears to be impossible in safe Rust, as there is no way to obtain a reference to the components of Chain, unless I'm missing something.

When Chain is constructed, it takes ownership of (moves) the underlying Read structs, which then are hidden in private members.

Is there any way to construct the Take<Repeat> part such that I can access the limit() fn of Take even after the Chain adaptor takes ownership?

1

1 Answers

1
votes

I know of one way, but I don't think it will suit your goal.

use std::io::{self, Read};

fn main() {
    let file = io::empty();
    let zeroes = io::repeat(0);
    let mut ten_zeroes = zeroes.take(10);
    {
        let mut with_zeroes = file.chain(ten_zeroes.by_ref());
        let mut buf = [0; 5];  
        with_zeroes.read(&mut buf);
    }
    println!("{}", ten_zeroes.limit());
}

The key being Read::by_ref, which returns a mutable reference. Read is implemented for any mutable reference to a type that implements Read, so you can give ownership of the reference to Chain.

The trick is that you have to destroy the Chain before you can use the inner object again. It would also be very painful / impossible to store this in the struct.

Your best bet might be to write your own ZeroPadded adapter that combines Chain, Repeat, and Take and provides the limit method.

If no one else gives better answers, I could also see adding a feature request for methods on the adapters. It's not uncommon to have a into_inner method that consumes an adapter and gives back the wrapped item, but you could also have an as_inner that returns a reference to the wrapped object.