use futures::StreamExt;
use std::{
cmp::Ordering,
io::{Read, Seek},
};
use structopt::StructOpt;
use tempfile::tempfile;
use tokio::io::AsyncWriteExt;
use zstd_seekable_s3::{SeekableDecompress, StreamCompress};
#[derive(StructOpt)]
#[structopt(
name = "roundtrip_file",
about = "Compress and uncompress some file via seekable interfaces.",
rename_all = "kebab"
)]
struct Opt {
#[structopt(default_value = "256")]
buffer_size: usize,
#[structopt(default_value = "5000")]
num_lines: u64,
#[structopt(default_value = "1024")]
frame_size: usize,
#[structopt(default_value = "1")]
compression_level: usize,
}
#[tokio::main]
async fn main() {
let opt = Opt::from_args();
let mut tempfile = tokio::fs::File::from(tempfile().unwrap());
let uncompressed_bytes_until_middle =
std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
{
struct Lines {
current: usize,
uncompressed_bytes_until_middle: usize,
}
let middle = (opt.num_lines / 2) as usize;
let max_lines = opt.num_lines as usize;
fn infallible_err<T>(t: T) -> Result<T, std::convert::Infallible> {
Ok(t)
}
let compressed_lines = futures::stream::unfold(
Lines {
current: 0,
uncompressed_bytes_until_middle: 0,
},
|mut lines: Lines| async {
if lines.current >= max_lines {
None
} else {
let line = format!("This is line {}.\n", lines.current);
match lines.current.cmp(&middle) {
Ordering::Less => lines.uncompressed_bytes_until_middle += line.len(),
Ordering::Equal => {
uncompressed_bytes_until_middle.store(
lines.uncompressed_bytes_until_middle,
std::sync::atomic::Ordering::SeqCst,
);
}
Ordering::Greater => {}
}
lines.current += 1;
Some((bytes::Bytes::copy_from_slice(line.as_bytes()), lines))
}
},
)
.map(infallible_err)
.compress(opt.compression_level, opt.frame_size)
.unwrap();
let mut compressed_lines = Box::pin(compressed_lines);
while let Some(ebytes) = compressed_lines.next().await {
match ebytes {
Ok(bytes) => tempfile.write_all(&bytes).await.unwrap(),
Err(e) => panic!("{}", zstd_seekable::Error::from(e)),
}
}
}
let uncompressed_bytes_until_middle =
uncompressed_bytes_until_middle.load(std::sync::atomic::Ordering::SeqCst);
let mut tempfile = SeekableDecompress::new(tempfile.into_std().await).unwrap();
tempfile
.seek(std::io::SeekFrom::Start(
uncompressed_bytes_until_middle as u64,
))
.unwrap();
{
let mut decompressed_content = vec![0; opt.buffer_size];
let decompressed_bytes = tempfile.read(decompressed_content.as_mut_slice()).unwrap();
let string_data = std::str::from_utf8(&decompressed_content[..decompressed_bytes]).unwrap();
println!(
"Content from decompressed bytes {} to {}",
uncompressed_bytes_until_middle,
uncompressed_bytes_until_middle + decompressed_bytes
);
println!("{}", string_data);
}
}