2015-10-13 4 views
1

Я пытаюсь найти лучший способ использовать буфер и snappy в Rust. Раньше я писал в файл, используя BufWriter. Но теперь я хочу добавить и компрессию. Для функции приведенной выше функции snappy crate требуется параметр &[u8], но BufWriter не даст мне доступ к его буфере, чтобы перейти к мгновенному. Я рассмотрел два способа обойти это.Использование Snappy сжатия с буфером

В первом случае я использую вектор (with_capacity) вместо BufWriter в качестве своего буфера и создаю функцию записи, которая гарантирует, что запись в вектор не приведет к перераспределению. Если бы это было так, я вместо этого сжимал бы то, что в настоящее время находится в буфере, а затем записывает его в файл и освобождает вектор (функция стока). Я написал это на основании того, что делает BufWriter. Недостатком является то, что, поскольку он является вектором, если буфер выходит из области видимости, он автоматически не выгружает буфер в файл. Я должен вручную сделать это в области, которая пишет файлы, что мне не нравится.

С другой стороны, я более или менее скопировал исходный код BufWriter и только что изменил функцию flush, чтобы сжать их буфер (вектор), прежде чем выводить его в файл. Этот способ кажется самым приятным, но мне просто не нравится идея простого копирования кода.

Что было бы лучше всего использовать эти два варианта или другие варианты?

Если это необходимо, объекты, которые я пишу в буфер, имеют одинаковый размер, а размер моего буфера является кратным размеру объекта.

+0

Совместим со сжатием нескольких буферов, а затем упаковывает их вместе? Подпись функции указывает на то, что она ожидает сжимать и распаковывать все за один снимок. Если это так, вам нужно буферизировать все (в 'Vec '), а затем сжать все это в конце. – Shepmaster

+0

Вы правы.Первоначально я думал, что, поскольку я знаю, что размер без сжатия будет постоянным, я бы также смог разделить буферы и разложить их по одному. Но знание несжатого размера не поможет мне, потому что сжатый размер не будет постоянным. Я попробую использовать 'Vec, u8>' и посмотреть, как это влияет на производительность. Благодарю. Кроме того, как мне решить этот вопрос stackoverflow-wise? Должен ли я удалить его или есть способ отметить его без ответа? –

+0

Я думаю, что так, как вы задали вопрос, есть ответ, поэтому я отправлю свой ответ в следующий час или два. Ответ просто не будет делать именно то, что вы хотели :-) – Shepmaster

ответ

1

Так как это выглядит так, как нужно быстро сжимать все за один раз, вам просто нужно буферизировать все до конца. После этого вы можете очистить и сжать в конце:

use std::io::{self, Write, Cursor}; 

fn compress(_data: &[u8]) -> Vec<u8> { 
    // The best compression ever 
    b"compressed".as_ref().into() 
} 

struct SnappyCompressor<W> { 
    inner: W, 
    buffer: Vec<u8>, 
} 

impl<W> SnappyCompressor<W> 
    where W: Write 
{ 
    fn new(inner: W) -> Self { 
     SnappyCompressor { 
      inner: inner, 
      buffer: vec![], 
     } 
    } 
} 

impl<W> Write for SnappyCompressor<W> 
    where W: Write 
{ 
    fn write(&mut self, data: &[u8]) -> io::Result<usize> { 
     self.buffer.extend(data); 
     Ok(data.len()) 
    } 

    fn flush(&mut self) -> io::Result<()> { 
     let compressed = compress(&self.buffer); 
     self.inner.write_all(&compressed) 
    } 
} 

fn main() { 
    let mut output = Cursor::new(vec![]); 
    { 
     let mut compressor = SnappyCompressor::new(output.by_ref()); 
     assert_eq!(5, compressor.write(b"hello").unwrap()); 
     assert_eq!(5, compressor.write(b"world").unwrap()); 
     compressor.flush().unwrap(); 
    } 
    let bytes = output.into_inner(); 
    assert_eq!(&b"compressed"[..], &bytes[..]); 
} 

Это решение имеет один большой сомнительный аспект - мы используем flush, чтобы отметить конец потока, но это на самом деле не смысл этого метода. Вероятно, было бы намного лучше использовать чисто потоковый компрессор, но иногда вы должны делать то, что должны делать.

Там также несколько наземных мин:

  1. Вы должны явно вызвать flush
  2. Вы не можете позвонить flush дважды.

Чтобы разрешить пользователю просто уронить компрессор и он будет завершен, вы можете реализовать Drop:

impl<W> Drop for SnappyCompressor<W> 
    where W: Write 
{ 
    fn drop(&mut self) { 
     self.flush().unwrap(); 
    } 
} 

Чтобы предотвратить попытки промывки дважды, вам необходимо добавить флаг отслеживать, что:

fn write(&mut self, data: &[u8]) -> io::Result<usize> { 
    if self.is_flushed { 
     return Err(Error::new(ErrorKind::Other, "Buffer has already been compressed, cannot add more data")); 
    } 

    self.buffer.extend(data); 
    Ok(data.len()) 
} 

fn flush(&mut self) -> io::Result<()> { 
    if self.is_flushed { 
     return Ok(()) 
    } 

    self.is_flushed = true; 
    let compressed = compress(&self.buffer); 
    self.inner.write_all(&compressed) 
} 

Все вместе, окончательный вариант выглядит следующим образом:

use std::io::{self, Write, Cursor, Error, ErrorKind}; 

fn compress(_data: &[u8]) -> Vec<u8> { 
    // The best compression ever 
    b"compressed".as_ref().into() 
} 

struct SnappyCompressor<W> 
    where W: Write 
{ 
    inner: W, 
    buffer: Vec<u8>, 
    is_flushed: bool, 
} 

impl<W> SnappyCompressor<W> 
    where W: Write 
{ 
    fn new(inner: W) -> Self { 
     SnappyCompressor { 
      inner: inner, 
      buffer: vec![], 
      is_flushed: false, 
     } 
    } 

    // fn into_inner 
} 

impl<W> Write for SnappyCompressor<W> 
    where W: Write 
{ 
    fn write(&mut self, data: &[u8]) -> io::Result<usize> { 
     if self.is_flushed { 
      return Err(Error::new(ErrorKind::Other, "Buffer has already been compressed, cannot add more data")); 
     } 

     self.buffer.extend(data); 
     Ok(data.len()) 
    } 

    fn flush(&mut self) -> io::Result<()> { 
     if self.is_flushed { 
      return Ok(()) 
     } 

     self.is_flushed = true; 
     let compressed = compress(&self.buffer); 
     self.inner.write_all(&compressed) 
    } 
} 

impl<W> Drop for SnappyCompressor<W> 
    where W: Write 
{ 
    fn drop(&mut self) { 
     self.flush().unwrap(); 
    } 
} 

fn main() { 
    let mut output = Cursor::new(vec![]); 
    { 
     let mut compressor = SnappyCompressor::new(output.by_ref()); 
     assert_eq!(5, compressor.write(b"hello").unwrap()); 
     assert_eq!(5, compressor.write(b"world").unwrap()); 
     compressor.flush().unwrap(); 
    } 
    let bytes = output.into_inner(); 
    assert_eq!(&b"compressed"[..], &bytes[..]); 
} 
+0

Это выглядит хорошо, как что-то, что более или менее ответили на мой вопрос. Я попробую это, и еще что-нибудь, и посмотрю, как они работают. Еще раз спасибо! –

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