Read file in chunks of exact size except last

Given an ordinary file, the task is to read the file in chunks, but all chunks except the last must be of BLOCK_SIZE. I would like to know the best way to do this.

How I can put it together, I have here a first variant, which seems unreliable under the circumstance that the therein mentioned assertion cannot be fulfilled.

use std::{io, io::Read, fs::File};
const BLOCK_SIZE: usize = 0x10000;

fn process_file(file: &mut File, callback: &mut dyn FnMut(&[u8]))
-> io::Result<()>
{
    let mut buffer: [u8; BLOCK_SIZE] = [0; BLOCK_SIZE];
    let mut last = false;
    loop {
        let n = file.read(&mut buffer)?;
        if n == 0 {break;}
        if n < BLOCK_SIZE {
            assert!(last == false);
            last = true;
        }
        callback(&buffer[..n]);
    }
    Ok(())
}

fn main() -> io::Result<()> {
    let argv: Vec<String> = std::env::args().collect();
    let mut file = File::open(&argv[1])?;
    process_file(&mut file, &mut |data| {
        println!("{}",data.len());
    })?;
    Ok(())
}

And here is a second variant, which seems rather reliable to me but is not streamable:

fn process_file(file: &mut File, callback: &mut dyn FnMut(&[u8]))
-> io::Result<()>
{
    let len = file.metadata()?.len();
    let count = len/(BLOCK_SIZE as u64);
    let rem = (len%(BLOCK_SIZE as u64)) as usize;
    let mut buffer: [u8; BLOCK_SIZE] = [0; BLOCK_SIZE];
    for _ in 0..count {
        file.read_exact(&mut buffer)?;
        callback(&buffer);
    }
    if rem != 0 {
        file.read_exact(&mut buffer[..rem])?;
        callback(&buffer[..rem]);
    }
    Ok(())
}

You can simulate read_exact by calling read several times until you have a full block or a read of length zero.

Note that you should prefer generics to dyn Trait for that kind of callback.

Okay, then I'm arriving at this manifestation.

fn read_exact(file: &mut impl Read, mut buffer: &mut [u8])
-> io::Result<usize>
{
    let mut sum = 0;
    while !buffer.is_empty() {
        let n = file.read(&mut buffer)?;
        if n == 0 {break;}
        buffer = &mut buffer[n..];
        sum += n;
    }
    Ok(sum)
}

fn process_file(file: &mut impl Read,
    mut callback: impl FnMut(&[u8])
) -> io::Result<()>
{
    let mut buffer = [0; BLOCK_SIZE];
    loop {
        let n = read_exact(file, &mut buffer)?;
        if n != 0 {callback(&buffer[..n]);}
        if n < BLOCK_SIZE {break;}
    }
    Ok(())
}

That seems reasonable.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.