2015-10-13 107 views
1

我想弄清楚在Rust中使用緩衝區和snappy的最佳方式。之前,我正在使用BufWriter寫信給一個文件。但現在我想要添加壓縮。上面那個活潑的箱子的compress函數需要一個&[u8]作爲參數,但BufWriter不會讓我訪問它的緩衝區以傳遞到活動。我已經研究了兩種方法來解決這個問題。使用Snappy壓縮緩衝區

在第一種方式中,我使用一個矢量(with_capacity)而不是BufWriter作爲我的緩衝區,並創建了一個寫入函數,以確保寫入矢量不會導致它重新分配。如果是這樣,我會壓縮緩衝區中的當前內容,然後將其寫入文件並釋放向量(排出函數)。我根據BufWriter所做的來寫這篇文章。缺點是由於它是一個向量,如果緩衝區超出範圍,它不會自動將緩衝區移出到文件。我必須在編寫文件的範圍內手動執行此操作,而我不喜歡這些操作。

另一方面,我或多或少地複製了BufWriter源代碼,並且只是改變了flush函數來在輸出到文件之前壓縮它們的緩衝區(矢量)。這種方式似乎是最好的,但我不喜歡只複製代碼的想法。

什麼是繼續這兩個選項或其他選項的最佳方式?

如果有關,我寫到緩衝區中的對象總是相同的大小和我的緩衝區大小爲對象大小的倍數。

+0

快速兼容壓縮多個緩衝區,然後將它們一起打包?函數簽名似乎表明它期望一次壓縮和解壓縮所有內容。如果是這樣的話,你需要將所有內容都緩存起來(寫入'Vec '),最後壓縮一切。 – Shepmaster

+0

你說得對。我最初認爲,因爲我知道未壓縮的大小將是不變的,我也可以分開緩衝區並逐個解壓縮它們。但知道未壓縮的大小不會幫助我,因爲壓縮後的大小不會是恆定的。 我會嘗試使用'Vec,u8>',看看它是如何影響性能的。謝謝。 另外,我應該如何處理這個問題stackoverflow明智?我應該刪除它嗎,還是有一種方法可以在未選擇答案的情況下將其標記爲已解決? –

+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

這看起來不錯,或多或少地回答了我的問題。我會試試這個,還有別的,看看它們的表現如何。再次感謝! –