用于将二进制数据解析为基本类型的惯用方法

时间:2022-02-10 16:35:56

I've written the following method to parse binary data from a gzipped file using GzDecoder from the Flate2 library

我编写了以下方法,使用Flate2库中的GzDecoder从gzip压缩文件解析二进制数据

fn read_primitive<T: Copy>(reader: &mut GzDecoder<File>) -> std::io::Result<T>
{
    let sz = mem::size_of::<T>();
    let mut vec =  Vec::<u8>::with_capacity(sz);
    let ret: T;
    unsafe{
        vec.set_len(sz);
        let mut s = &mut vec[..];
        try!(reader.read(&mut s));

        let ptr :*const u8 = s.as_ptr();
        ret = *(ptr as *const T)
    }
    Ok(ret)
}

It works, but I'm not particularly happy with the code, especially with using the dummy vector and the temporary variable ptr. It all feels very inelegant to me and I'm sure there's a better way to do this. I'd be happy to hear any suggestions of how to clean up this code.

它有效,但我对代码并不是特别满意,特别是使用虚拟向量和临时变量ptr。这对我来说都是非常不优雅的,我相信有更好的方法可以做到这一点。我很高兴听到有关如何清理此代码的任何建议。

1 个解决方案

#1


Your code allows any copyable T, not just primitives. That means you could try to parse in something with a reference, which is probably not what you want:

您的代码允许任何可复制的T,而不仅仅是原语。这意味着您可以尝试使用引用解析某些内容,这可能不是您想要的:

#[derive(Copy)]
struct Foo(&str);

However, the general sketch of your code is what I'd expect. You need a temporary place to store some data, and then you must convert that data to the appropriate primitive (perhaps dealing with endinaness issues).

但是,代码的一般草图是我所期望的。您需要一个临时的位置来存储一些数据,然后您必须将该数据转换为适当的原语(可能涉及终端问题)。

I'd recommend the byteorder library. With it, you call specific methods for the primitive that is required:

我推荐使用byteorder库。有了它,您可以调用所需的基元的特定方法:

reader.read_u16::<LittleEndian>()

Since these methods know the desired size, they can stack-allocate an array to use as the temporary buffer, which is likely a bit more efficient than a heap-allocation. Additionally, I'd suggest changing your code to accept a generic object with the Read trait, instead of the specific GzDecoder.

由于这些方法知道所需的大小,因此它们可以堆栈分配一个数组以用作临时缓冲区,这可能比堆分配更有效。另外,我建议更改代码以接受具有Read特性的通用对象,而不是特定的GzDecoder。

You may also want to look into a serialization library like rustc-serialize or serde to see if they fit any of your use cases.

您可能还想查看一个序列化库,如rustc-serialize或serde,以查看它们是否适合您的任何用例。

#1


Your code allows any copyable T, not just primitives. That means you could try to parse in something with a reference, which is probably not what you want:

您的代码允许任何可复制的T,而不仅仅是原语。这意味着您可以尝试使用引用解析某些内容,这可能不是您想要的:

#[derive(Copy)]
struct Foo(&str);

However, the general sketch of your code is what I'd expect. You need a temporary place to store some data, and then you must convert that data to the appropriate primitive (perhaps dealing with endinaness issues).

但是,代码的一般草图是我所期望的。您需要一个临时的位置来存储一些数据,然后您必须将该数据转换为适当的原语(可能涉及终端问题)。

I'd recommend the byteorder library. With it, you call specific methods for the primitive that is required:

我推荐使用byteorder库。有了它,您可以调用所需的基元的特定方法:

reader.read_u16::<LittleEndian>()

Since these methods know the desired size, they can stack-allocate an array to use as the temporary buffer, which is likely a bit more efficient than a heap-allocation. Additionally, I'd suggest changing your code to accept a generic object with the Read trait, instead of the specific GzDecoder.

由于这些方法知道所需的大小,因此它们可以堆栈分配一个数组以用作临时缓冲区,这可能比堆分配更有效。另外,我建议更改代码以接受具有Read特性的通用对象,而不是特定的GzDecoder。

You may also want to look into a serialization library like rustc-serialize or serde to see if they fit any of your use cases.

您可能还想查看一个序列化库,如rustc-serialize或serde,以查看它们是否适合您的任何用例。