Idiomatic way of parsing binary data into primitive types

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

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

This works, but I'm not very happy with the code, especially using a dummy vector and the ptr temporary variable. All of this seems very inappropriate to me, and I'm sure there is a better way to do this. I would love to hear any suggestions on cleaning this code.

+5
source share
1 answer

Your code allows you to copy T , not just primitives. This means that you can try to parse something with a link, which is probably not what you want:

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

However, a general sketch of your code is what I expect. You need a temporary place to store some data, and then you need to convert this data to the corresponding primitive (possibly dealing with problems that result from endinaness).

I would recommend the byteorder library. With it, you invoke specific methods for the primitive that is required:

 reader.read_u16::<LittleEndian>() 

Since these methods know the desired size, they can allocate a stack for the array, which will be used as a temporary buffer, which is likely to be more efficient than heap allocation. In addition, I suggest changing the code to accept a shared object with a Read tag instead of a specific GzDecoder .

You can also look at a serialization library like rustc-serialize or serde to see if they are suitable for your use.

+7
source

All Articles