如何检查std :: io :: Cursor是否有未使用的数据?

时间:2018-05-30 10:11:03

标签: stream rust

我正在编写一个处理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 :(得分:2)

使用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()方法

Rust样式表示empty方法是动词 - 这会删除数据(如果可能的话)。您想要的方法应该被称为is_empty,如切片所示。