Reputation: 3368
I am trying to write bytes to a file with random access in the context of downloading random chunks of the file and assembling them in-place. The std::io::Write
seem to be limited to sequential writing so I am currently using the following implementation which first needs to download all the chunks and then do big write_all
once all the chunks are in memory.
use rand::prelude::*;
use rand; // 0.7.3
fn download<W: std::io::Write>(writer: &mut W) -> std::io::Result<()>
{
// the size (20) is known
let mut buf = [0u8; 20];
// simulate downloading four 5 byte chunks at random disjoint slices in *buf*
for n in (0..20).step_by(5).collect::<Vec<usize>>().choose_multiple(&mut thread_rng(), 4) {
let n = *n as usize;
let chunk: [u8; 5] = rand::random();
buf[n..n+5].clone_from_slice(&chunk);
}
println!("filled buffer: {:?}", buf);
// finally, do (unbuffered) write
writer.write_all(&buf)?;
Ok(())
}
fn main() -> std::io::Result<()> {
let mut f = std::fs::File::create("foo.txt")?;
download(&mut f)?;
Ok(())
}
With large files I think this solution may consume to much memory and I would rather write each chunk
directly into the file without any intermediate buffer. I see there is write_at
in the documentation, but is this method unique for unix systems? I am looking for a generic solution.
Intuitively, I would want something like this to work:
use rand::prelude::*;
use rand; // 0.7.3
fn download<W: std::io::Write>(writer: &mut W) -> std::io::Result<()>
{
// the size (20) is known
writer.reserve(20)?;
// simulate downloading four 5 byte chunks at random disjoint slices in *buf*
for n in (0..20).step_by(5).collect::<Vec<usize>>().choose_multiple(&mut thread_rng(), 4) {
let n = *n as usize;
let chunk: [u8; 5] = rand::random();
writer.write_at(chunk, n)?;
}
Ok(())
}
fn main() -> std::io::Result<()> {
let mut f = std::fs::File::create("foo.txt")?;
download(&mut f)?;
Ok(())
}
Is there any specialization for writing to random access file output? Can I use a memory mapped file? Doing a quick search I could only find the positioned-io crate, which has not been updated in 3 years. Is there anything similar to this crate in the rust standard library?
Upvotes: 2
Views: 782
Reputation: 169318
You're looking for Seek
. File
implements both Seek
and Write
so you just need to bound on Seek
as well:
use std::io::{Write, Seek, SeekFrom, Result as IoResult};
fn download<W: Write + Seek>(writer: &mut W) -> IoResult<()> { /* ... */ }
Now you can do writer.seek(SeekFrom::Start(*n as u64 * CHUNK_SIZE))?;
before writing out the chunk.
Upvotes: 4