Reading binary files


#1

Hi,

I’m still in the learning phase of Rust. With a pre-1.0 release of Rust I had already working code to read a binary Blender file and analyze the header and all remaining chunks. The old code can be found here. Anyway, back to the question. The new code compiles, but I’m stuck with how I can continue reading the chunks after having read the header information. Probably I’m doing something wrong. So here is the main idea of what I have done so far:

fn main() {
    // deal with command line arguments
    ...
    do_work(&input, output);
}

An interesting detail I learned was that I had to be within a function which returns io::Result<()> to be able to use the try! macro, so from within do_work(…) I call read_blend_file(…) with the following signature:

fn read_blend_file(inp: &str) -> io::Result<()> {                                     
    // open file                                                                      
    let file = try!(File::open(inp));                                                 
    // read 12 bytes from the Blender file                                            
    let mut take = file.take(12u64);                                                  
    let mut header = String::new();                                                   
    try!(take.read_to_string(&mut header));                                           
    println!("header = \"{}\"", header);                                              
    // analyze header 
    ...
    Ok(())
}

I use file.take(…) to read the first 12 bytes and perform several tests before I continue.

So my question is: How do I continue reading the binary file with different chunk sizes?

The old code was like this:

// assumes 64-bit pointers (in file as well as on platform)               
loop {
    // read_file_dna                                                      
    let io_result = file.read_exact(24); // 4 * int + 64-bit pointer      
    ...
}

Thanks in advance for any help on this topic. Maybe there are some examples (code snippets) somewhere?

Jan


#2

To read from a File you could just use the following Syntax:

use std::fs::File;
use std::io::Read;

fn main()
{
    let mut file=File::open("Cargo.toml").unwrap();
    let mut buf=[0u8;12];
    file.read(&mut buf).unwrap();
    println!("{:?}",buf);
    // use file
}

I tested it on nightly, I hope it works for you.


#3

Thanks a lot.


#4

Read::read is not guaranteed to fill the buffer you specified. It returns a usize indicating how many bytes were read. If you want to be robust you’d have to read in a loop until you’ve filled your buffer.


#5

Thanks. You are right. I guess I could handle it like this:

// read 12 bytes from the Blender file                                                
let mut buf = [0u8; 12];
let bytes_read = file.read(&mut buf).unwrap();
if bytes_read != buf.len() {
    println!("{} bytes read, but {} expected ...", bytes_read, buf.len());
    // handle error or bail out
}

#6

I have follow up questions regarding endianness and converting read bytes into f32. In a pre-1.0 Rust release I was able to read a f32 taking the endianness of the binary file into account.

fn read_le_f32(&mut self) -> IoResult<f32> { ... }
fn read_be_f32(&mut self) -> IoResult<f32> { ... }

With that I was able to read a value like this:

let io_result = file.read_le_f32();
let lens: f32 = io_result.unwrap();

Currently I don’t need to byte-swap, but just in case (for the future): How is endianness handled in Rust? Is byteorder the answer?

For converting a buffer holding 4 bytes into a f32 value I currently use:

let mut buf = [0u8; 4];
...
let lens: f32 = unsafe { mem::transmute(buf) };

Is there a safe version for this?


#7

The byteorder crate is indeed the answer.


#8

I think that is one of Rusts great strengths, if you need something you cannot find in tho std lib use crates.io.