我如何压缩 zlib 数据并找出有多少输入字节?
How can I deflate zlib data and find out how many input bytes there were?
我正在用 Rust 构建一个 git 克隆实现。我已经到了需要解析包文件以创建索引的部分,我几乎完成了解析。
packfile 中的每个 object 都包含一个 header(我已经正确解析了)后跟 zlib 压缩的内容。
值得注意的是,存储在 header 中的大小是 解压缩后的 大小,因此比我们必须跳过才能到达下一个 header.
Crates.io 显示了 2 个进行 zlib 解压缩并且有多个下载的 crate:
libz-sys
:实际上是一个 hello world,几个月来一直如此
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()
);
问题来了。在此之后我需要开始阅读下一个 object 的 header,但是 ZlibDecoder
没有提供任何方法来检测 输入 有多大是。
它在输入时拥有 reader 的所有权,而不是引用。
因此,即使我有 object 的输出大小(实际上 all object 的数据),因为我没有不知道输入大小我无法开始阅读下一个 object header.
如何获得达到预期输出大小所需的压缩输入字节数?如果可能,我想避免使用 FFI 调用原生 zlib。
PS:flate2
文档建议 helper trait 但我不知道这将如何或是否对我有帮助
通常,您可以传递对 Reader / Writer 的引用(通过 ByRefReader
or 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));
}
}
这 确实 用于写作 - 我看到 Zlib header 在输出文件中重复了两次。但是,它在阅读时不起作用。看起来 reader::ZlibDecoder
可能会一直消耗到底层 Reader
的末尾。这可能是 flate2
库中的错误或疏忽。盯着 the source 看了几分钟并没有显示出任何明显的东西。
编辑
不过 "works" 这是一个可怕的技巧:
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));
问题的出现是因为flate2是a bit greedy in how it reads from the reader。它总是尝试尽可能多地填充自己的内部缓冲区,即使其中一些数据不会被读取。这种 可怕、令人讨厌的 黑客攻击导致它一次只能读取一个字节。因此,您可以在末尾倒回一个字节并重新开始。
A longer-term 解决方案可能是为 total_in
up to Stream
添加一个访问器,然后直到到达 ZlibDecoder
.
我正在用 Rust 构建一个 git 克隆实现。我已经到了需要解析包文件以创建索引的部分,我几乎完成了解析。
packfile 中的每个 object 都包含一个 header(我已经正确解析了)后跟 zlib 压缩的内容。
值得注意的是,存储在 header 中的大小是 解压缩后的 大小,因此比我们必须跳过才能到达下一个 header.
Crates.io 显示了 2 个进行 zlib 解压缩并且有多个下载的 crate:
libz-sys
:实际上是一个 hello world,几个月来一直如此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() );
问题来了。在此之后我需要开始阅读下一个 object 的 header,但是 ZlibDecoder
没有提供任何方法来检测 输入 有多大是。
它在输入时拥有 reader 的所有权,而不是引用。
因此,即使我有 object 的输出大小(实际上 all object 的数据),因为我没有不知道输入大小我无法开始阅读下一个 object header.
如何获得达到预期输出大小所需的压缩输入字节数?如果可能,我想避免使用 FFI 调用原生 zlib。
PS:flate2
文档建议 helper trait 但我不知道这将如何或是否对我有帮助
通常,您可以传递对 Reader / Writer 的引用(通过 ByRefReader
or 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));
}
}
这 确实 用于写作 - 我看到 Zlib header 在输出文件中重复了两次。但是,它在阅读时不起作用。看起来 reader::ZlibDecoder
可能会一直消耗到底层 Reader
的末尾。这可能是 flate2
库中的错误或疏忽。盯着 the source 看了几分钟并没有显示出任何明显的东西。
编辑
不过 "works" 这是一个可怕的技巧:
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));
问题的出现是因为flate2是a bit greedy in how it reads from the reader。它总是尝试尽可能多地填充自己的内部缓冲区,即使其中一些数据不会被读取。这种 可怕、令人讨厌的 黑客攻击导致它一次只能读取一个字节。因此,您可以在末尾倒回一个字节并重新开始。
A longer-term 解决方案可能是为 total_in
up to Stream
添加一个访问器,然后直到到达 ZlibDecoder
.