对缓冲区使用 Snappy 压缩
Using Snappy compression with a buffer
我正在尝试找出使用缓冲区和 snappy in Rust. Before, I was writing to a file using BufWriter
的最佳方法。但现在我也想添加压缩。上面的 snappy crate 的 compress
函数需要一个 &[u8]
作为参数,但是 BufWriter
不会让我访问它的缓冲区以传递给 snappy。我研究了两种方法来解决这个问题。
在第一种方式中,我使用向量 (with_capacity) 而不是 BufWriter
作为我的缓冲区,并创建了一个写入函数,以确保写入向量不会导致它重新分配。如果可以,我改为压缩缓冲区中当前的内容,然后将其写出到文件并释放向量(漏函数)。我根据 BufWriter
所做的事情写了这篇文章。缺点是因为它是一个向量,如果缓冲区超出范围,它不会自动将缓冲区刷新到文件中。我必须在写入文件的范围内手动执行此操作,我不喜欢这样。
换句话说,我或多或少地复制了 BufWriter
源代码,只是更改了 flush
函数以在将其输出到文件之前压缩其缓冲区(向量)。这种方式似乎是最好的,但我只是不喜欢只复制代码的想法。
继续这两个选项或其他选项的最佳方法是什么?
如果相关,我写入缓冲区的对象始终具有相同的大小,并且我的缓冲区大小是对象大小的倍数。
由于看起来 snappy 需要一次压缩所有内容,所以您只需要将所有内容缓冲到最后即可。然后你可以在最后刷新和压缩:
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
来标记流的结尾,但这并不是该方法的真正含义。使用纯流压缩器可能会好得多,但有时你必须做你必须做的事情。
还有一些地雷:
- 您必须显式调用
flush
- 您不能调用
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[..]);
}
我正在尝试找出使用缓冲区和 snappy in Rust. Before, I was writing to a file using BufWriter
的最佳方法。但现在我也想添加压缩。上面的 snappy crate 的 compress
函数需要一个 &[u8]
作为参数,但是 BufWriter
不会让我访问它的缓冲区以传递给 snappy。我研究了两种方法来解决这个问题。
在第一种方式中,我使用向量 (with_capacity) 而不是 BufWriter
作为我的缓冲区,并创建了一个写入函数,以确保写入向量不会导致它重新分配。如果可以,我改为压缩缓冲区中当前的内容,然后将其写出到文件并释放向量(漏函数)。我根据 BufWriter
所做的事情写了这篇文章。缺点是因为它是一个向量,如果缓冲区超出范围,它不会自动将缓冲区刷新到文件中。我必须在写入文件的范围内手动执行此操作,我不喜欢这样。
换句话说,我或多或少地复制了 BufWriter
源代码,只是更改了 flush
函数以在将其输出到文件之前压缩其缓冲区(向量)。这种方式似乎是最好的,但我只是不喜欢只复制代码的想法。
继续这两个选项或其他选项的最佳方法是什么?
如果相关,我写入缓冲区的对象始终具有相同的大小,并且我的缓冲区大小是对象大小的倍数。
由于看起来 snappy 需要一次压缩所有内容,所以您只需要将所有内容缓冲到最后即可。然后你可以在最后刷新和压缩:
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
来标记流的结尾,但这并不是该方法的真正含义。使用纯流压缩器可能会好得多,但有时你必须做你必须做的事情。
还有一些地雷:
- 您必须显式调用
flush
- 您不能调用
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[..]);
}