Как я могу проверить, есть ли в std :: io :: Cursor неиспользованные данные? - PullRequest
0 голосов
/ 30 мая 2018

Я пишу низкоуровневое сетевое приложение, которое работает с сокетами TCP, где мне часто приходится обрабатывать потоки двоичных данных.Когда некоторые данные доступны, я считываю их в массив u8, затем оборачиваю в std::io::Cursor<&[u8]> и затем передаю их обработчикам.В обработчике мне часто нужно знать, есть ли еще какие-то данные в Cursor или нет.

Представьте, что функция handle получает данные, а затем обрабатывает их порциями, используя функцию handle_chunk,Для простоты предположим, что размер фрагмента фиксирован и равен 10 байтам;если размер данных не делится на 10, это ошибка.Эта простая логика может быть реализована следующим образом:

fn handle(mut data: Cursor<&[u8]>) {
    while !data.empty() {
        if let Err(err) = handle_chunk(&mut data) {
            eprintln!("Error while handling data: {}", err);
        }
    }
}

fn handle_chunk(data: &mut Cursor<&[u8]>) -> Result<(), String> {
    // Returns Err("unexpected EOF".to_string()) if chunk is incomplete
    // ...
}

Однако Cursor не имеет метода empty() или какого-либо другого метода, способного определить, нужно ли обрабатывать больше данных.Рабочее решение, которое я мог бы придумать:

fn handle(data: Cursor<&[u8]>) {
    let data = data.into_inner();
    let len = data.len();
    let mut data = Cursor::new(data);

    while (data.position() as usize) < len - 1 {
        if let Err(err) = handle_chunk(&mut data) {
            eprintln!("Error while handling data: {}", err);
        }
    }
}

Это выглядит хакерским и не элегантным.Есть ли лучшее решение?Может быть, в стандартной библиотеке Rust есть другой инструмент, который подходит здесь лучше, чем Cursor?

1 Ответ

0 голосов
/ 30 мая 2018

Ваш код может быть упрощен с помощью Cursor::get_ref, чтобы не разбить ввод и не собрать его обратно:

fn handle(mut data: Cursor<&[u8]>) {
    let len = data.get_ref().len();

    while (data.position() as usize) < len - 1 {
        if let Err(err) = handle_chunk(&mut data) {
            eprintln!("Error while handling data: {}", err);
        }
    }
}

Теперь вы не показали ни одного кода, которыйтребует Cursor.Много раз люди думают, что необходимо преобразовать &[u8] во что-то, что реализует Read, но это не так.Read реализовано для &'a [u8]:

use std::io::Read;

fn handle(mut data: &[u8]) {
    while !data.is_empty() {
        if let Err(err) = handle_chunk(&mut data) {
            eprintln!("Error while handling data: {}", err);
        }
    }
}

fn handle_chunk<R: Read>(mut data: R) -> Result<(), String> {
    let mut b = [0; 10];
    data.read_exact(&mut b).unwrap();
    println!("Chunk: {:?}", b);
    Ok(())
}

fn main() {
    let d: Vec<u8> = (0..20).collect();
    handle(&d)
}

При наличии mut data: &[u8] и использовании &mut data код обновит переменную среза, чтобы продвинуть ее вперед.Хотя мы не можем легко вернуться назад.

метод empty()

Стиль ржавчины указывает, что метод empty будет глаголом - это удалит данные (если бы это было возможно).Нужный метод должен называться is_empty, как показано на слайсах.

...