Так как это выглядит так, как нужно быстро сжимать все за один раз, вам просто нужно буферизировать все до конца. После этого вы можете очистить и сжать в конце:
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[..]);
}
Совместим со сжатием нескольких буферов, а затем упаковывает их вместе? Подпись функции указывает на то, что она ожидает сжимать и распаковывать все за один снимок. Если это так, вам нужно буферизировать все (в 'Vec'), а затем сжать все это в конце. –
Shepmaster
Вы правы.Первоначально я думал, что, поскольку я знаю, что размер без сжатия будет постоянным, я бы также смог разделить буферы и разложить их по одному. Но знание несжатого размера не поможет мне, потому что сжатый размер не будет постоянным. Я попробую использовать 'Vec, u8>' и посмотреть, как это влияет на производительность. Благодарю. Кроме того, как мне решить этот вопрос stackoverflow-wise? Должен ли я удалить его или есть способ отметить его без ответа? –
Я думаю, что так, как вы задали вопрос, есть ответ, поэтому я отправлю свой ответ в следующий час или два. Ответ просто не будет делать именно то, что вы хотели :-) – Shepmaster