2015-02-21 3 views
3

Я строю реализацию git clone в Rust. Я попал в ту часть, где мне нужно разобрать пакетный файл, чтобы создать индекс, и я почти закончил его разбор.Как я могу сфотографировать данные zlib и узнать, сколько там было входных байтов?

Каждый объект в файле packfile состоит из заголовка (который я уже правильно разбираю), за которым следует содержимое, которое сжато zlib.

Примечательно, что размер, хранящийся в заголовке, составляет , размер и, следовательно, больше, чем фактические данные, которые мы должны пропустить, чтобы перейти к следующему заголовку.

Crates.io показывает 2 ящиков, которые делают ZLIB декомпрессию и имеют более чем несколько загрузок:

  • 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() 
    ); 
    

Вот проблема. После этого мне нужно начать читать заголовок следующего объекта, но ZlibDecoder не дает никакого способа определить, насколько большой вход был.

Он берет на себя ответственность читателя как его ввод, а не ссылку.

Из-за этого, хотя у меня есть выходной размер объекта (и действительно все данные объекта), так как я не знаю размер ввода, я не могу начать читать следующий заголовок объекта.

Как получить объем сжатых входных байтов, необходимых для достижения ожидаемого размера вывода? Если возможно, я бы хотел избежать использования FFI для вызова родного zlib.

PS: flate2 документы свидетельствуют о helper trait, но я понятия не имею, как или, если это поможет мне

ответ

1

Обычно, вы можете передать ссылку на чтения/записи (через 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)); 
    } 
} 

Это делает работу для написания - я вижу заголовок Zlib получить повторяется дважды в выходном файле. Однако, это не работа при считывании. Похоже, что reader::ZlibDecoder может потреблять весь путь до конца базового Reader. Возможно, это может быть ошибка или недосмотр в библиотеке flate2. Несколько минут, глядя на the source, не показало ничего очевидного.

Редактировать

Вот ужасный хак, который "работает", хотя:

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. Он всегда пытается заполнить свой собственный внутренний буфер настолько, насколько это возможно, даже если некоторые из этих данных не будут прочитаны. Это ужасный, неприятный взломать, что заставляет его читать только один байт за раз. Таким образом, вы можете перемотать один байт в конце и начать снова.

Долгосрочное решение, вероятно, заключается в добавлении доступа для total_in до Stream, а затем до ZlibDecoder.

+0

Ужасный неприятный хак - это то, что доктор заказал! –

Смежные вопросы