我正在 Rust 中构建一个 git 克隆实现。我已经到了需要解析 packfile 以创建索引的部分,并且我几乎完成了解析。
packfile 中的每个对象都包含一个标头(我已经正确解析了该标头),后跟 zlib 压缩的内容。
值得注意的是,标头中存储的大小是解压缩的大小,因此大于我们必须跳过才能到达下一个标头的实际数据。
Crates.io 显示了 2 个进行 zlib 解压缩并且有多个下载的 crate:
libz-sys:实际上是一个你好世界,几个月来一直如此flate2:这可以轻松正确地缩小数据:
print!("Object type {} size {}", obj_type as u8, obj_size);
println!(" data:\n{}",
String::from_utf8(
ZlibDecoder::new(data).read_exact(obj_size as usize).unwrap()
).unwrap()
);
Run Code Online (Sandbox Code Playgroud)问题就在这里。之后,我需要开始读取下一个对象的标头,但ZlibDecoder没有提供任何方法来检测输入有多大。
它以读者的所有权作为输入,而不是参考。
因此,即使我有对象的输出大小(实际上还有对象的所有数据),因为我不知道输入大小,但我无法开始读取下一个对象头。
如何获得达到预期输出大小所需的压缩输入字节量?如果可能的话,我想避免使用 FFI 调用本机 zlib。
PS:flate2文档提出了一个辅助特征,但我不知道这如何或是否会帮助我
通常,您可以传递对 Reader / Writer 的引用(通过ByRefReader或ByRefWriter),以允许将适配器添加到流中而不会失去对其的控制。像这样的东西应该有效:
#![feature(io,path,env)]
extern crate flate2;
use flate2::CompressionLevel;
use flate2::writer::ZlibEncoder;
use flate2::reader::ZlibDecoder;
use std::env;
use std::old_io::File;
use std::old_io::{ByRefReader,ByRefWriter};
use std::old_path::Path;
fn main() {
let path = "./data";
let write = env::var("WRITE").is_ok();
if write {
println!("Writing to {}", path);
let mut f = File::create(&Path::new(path)).unwrap();
fn write_it<W>(w: &mut W, s: &str) where W: Writer {
let mut z = ZlibEncoder::new(ByRefWriter::by_ref(w), CompressionLevel::Default);
z.write_all(s.as_bytes()).unwrap();
}
write_it(&mut f, "hello world");
write_it(&mut f, "goodbye world");
} else {
println!("Reading from {}", path);
let mut f = File::open(&Path::new(path)).unwrap();
fn read_it<R>(r: &mut R) -> String where R: Reader {
let mut z = ZlibDecoder::new(ByRefReader::by_ref(r));
z.read_to_string().unwrap()
}
println!("{}", read_it(&mut f));
println!("{}", read_it(&mut f));
}
}
Run Code Online (Sandbox Code Playgroud)
这确实适用于写入 - 我看到 Zlib 标头在输出文件中重复两次。然而,阅读时却不起作用。看起来可能reader::ZlibDecoder会一直消耗到底层的末尾Reader。这可能是库中的错误或疏忽flate2。不过,盯着源头几分钟并没有显示出任何明显的东西。
编辑
这是一个可怕的黑客,但“有效”:
fn read_it<R>(r: &mut R) -> String where R: Reader {
let mut z = ZlibDecoder::new_with_buf(ByRefReader::by_ref(r), Vec::with_capacity(1));
z.read_to_string().unwrap()
}
println!("{}", read_it(&mut f));
f.seek(-1, std::old_io::SeekStyle::SeekCur);
println!("{}", read_it(&mut f));
Run Code Online (Sandbox Code Playgroud)
问题的出现是因为 flate2在从 reader 读取数据的方式上有点贪婪。它总是尝试尽可能多地填充自己的内部缓冲区,即使其中一些数据不会被读取。这种可怕的、令人讨厌的黑客行为导致它一次只能读取一个字节。因此,您可以在末尾倒回一个字节并重新开始。
一个长期的解决方案可能是添加一个访问器total_inup to Stream,然后 up 直到你到达ZlibDecoder。