Переключиться с brotly-sys на Dropbox brotli

Я использовал Brotly-sys, но он кажется необслуживаемым. Поэтому я использую Brotli. Я делаю интерфейс для сжатия и распаковки (я также использую lzma и zstd):

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

pub use brotli::writer::DecompressorWriter;
pub use brotli::enc::writer::CompressorWriter;

use super::Coder;

impl<W: Write> Coder<W> for DecompressorWriter<W> {
    fn get_mut(&mut self) -> &mut W {
        DecompressorWriter::get_mut(self)
    }

    fn finish(self) -> std::io::Result<W> {
        DecompressorWriter::flush(&mut self).map_err(|_| {
            io::Error::new(io::ErrorKind::Other, "brotli decoder failed to finalize stream")
        });
    }

    fn finish_boxed(self: Box<Self>) -> io::Result<W> {
        self.finish()
    }
}

impl<W: Write> Coder<W> for CompressorWriter<W> {
    fn get_mut(&mut self) -> &mut W {
        CompressorWriter::get_mut(self)
    }

    fn finish(self) -> std::io::Result<W> {
        self.flush()?;
        CompressorWriter::flush(&mut self).map_err(|_| {
            io::Error::new(io::ErrorKind::Other, "brotli encoder failed to finalize stream")
        })
    }

    fn finish_boxed(self: Box<Self>) -> io::Result<W> {
        self.finish()
    }
}

Но получить

error[E0308]: mismatched types
  --> lib/src/codecs/brotli.rs:13:24
   |
13 |     fn finish(self) -> std::io::Result<W> {
   |        ------          ^^^^^^^^^^^^^^^^^^ expected enum `std::result::Result`, found `()`
   |        |
   |        implicitly returns `()` as its body has no tail or `return` expression
   |
   = note:   expected enum `std::result::Result<W, std::io::Error>`
           found unit type `()`

error[E0308]: mismatched types
  --> lib/src/codecs/brotli.rs:31:9
   |
24 |   impl<W: Write> Coder<W> for CompressorWriter<W> {
   |        - this type parameter
...
29 |       fn finish(self) -> std::io::Result<W> {
   |                          ------------------ expected `std::result::Result<W, std::io::Error>` because of return type
30 |           self.flush()?;
31 | /         CompressorWriter::flush(&mut self).map_err(|_| {
32 | |             io::Error::new(io::ErrorKind::Other, "brotli encoder failed to finalize stream")
33 | |         })
   | |__________^ expected type parameter `W`, found `()`
   |
   = note: expected enum `std::result::Result<W, _>`
              found enum `std::result::Result<(), _>`

Но функция flush не реализует черту Write.

Coder интерфейс здесь: https://github.com/Ludea/speedupdate-rs/blob/master/lib/src/codecs/mod.rs#L21

Итак, как я могу исправить мою проблему? (Я могу предоставить репозиторий GH с этой проблемой) Я получаю эту проблему на MacOS/Win64/Linux x86_64. Спасибо

Пожалуйста, предоставьте источник для Coder или простой макет для него.

Finomnis 09.12.2022 08:19

Я обновляю свой пост, чтобы предоставить исходный код Coder

Vana 09.12.2022 08:24
CompressorWriter, если он стоит прямо сейчас, не совместим с вашим Coder интерфейсом, потому что он не держит внутренний буфер. Какова ваша цель с интерфейсом Coder? Самая большая проблема интерфейса Coder заключается в том, что возвращаемый тип finish — это W, а тип возвращаемого значения get_mut — тоже W, что несовместимо с CompressorWriter. Если я что-то не так понял.
Finomnis 09.12.2022 08:34

Не могли бы вы опубликовать пример того, как вы будете использовать интерфейс Coder позже? Я не на 100% понимаю, как он предназначен для использования.

Finomnis 09.12.2022 08:34

Пожалуйста, не публикуйте ссылки на код, это противоречит правилам Stackoverflow.

Finomnis 09.12.2022 08:35

Да, я думаю, что ваш Coder интерфейс неисправен. Возможно, вам придется изменить его.

Finomnis 09.12.2022 08:38

У вас есть лишний ; после map_err в DecompressionWriter с импл.

cafce25 09.12.2022 08:42

Мне нужно удалить черту Write в get_mut ?

Vana 09.12.2022 09:04
pub trait Coder<W>: io::Write { /// Acquires a mutable reference to the underlying writer /// /// Note that mutation of the writer may result in surprising results if /// this decoder is continued to be used. fn get_mut(&mut self) ; fn finish(self) -> io::Result<W>; fn finish_boxed(self: Box<Self>) -> io::Result<W>; }
Vana 09.12.2022 09:04

Вы можете отредактировать свой вопрос, чтобы включить такую ​​информацию :) Многострочный код плохо форматируется в комментариях

Finomnis 09.12.2022 09:25

Цитата из вашего репозитория: «Обратите внимание, что мутация писателя может привести к неожиданным результатам, если этот декодер будет продолжать использоваться». - Обратите внимание, что это невозможно, проверка заимствования предотвратит это. В этом весь смысл :)

Finomnis 09.12.2022 10:00
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
11
77
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваша черта Coder несовместима со структурой CompressorWriter. Возможно, вам придется изменить черту Coder.

Я предполагаю, что поток Coder:

  • Создайте алгоритм сжатия, реализующий Coder.
  • Получите изменяемый объект Write, вызвав get_mut().
  • Напишите на этот объект.
  • Вызовите finish() и извлеките сжатые данные из возвращаемого значения.

Ваш признак заставляет возвращаемое значение get_mut() быть идентичным возвращаемому значению finish(), что не относится к CompressorWriter.

Однако для этого вам не нужно вводить новый дженерик; вы можете просто вернуть &mut dyn Write из вашей get_mut() функции. Это может потребовать незначительных изменений в других реализациях Coder, но должно работать.

Небольшое примечание: я переименовал get_mut() в get_input_writer(), потому что он конфликтовал с CompressorWriter::get_mut(), что делало работу с ним намного более раздражающей.

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

pub use brotli::enc::writer::CompressorWriter;
pub use brotli::writer::DecompressorWriter;

trait Coder<W> {
    fn get_input_writer(&mut self) -> &mut dyn Write;
    fn finish(self) -> io::Result<W>;
    fn finish_boxed(self: Box<Self>) -> io::Result<W>;
}

impl<W: Write> Coder<W> for CompressorWriter<W> {
    fn get_input_writer(&mut self) -> &mut dyn Write {
        self
    }

    fn finish(mut self) -> io::Result<W> {
        self.flush().map_err(|_| {
            io::Error::new(
                io::ErrorKind::Other,
                "brotli encoder failed to finalize stream",
            )
        })?;
        Ok(self.into_inner())
    }

    fn finish_boxed(self: Box<Self>) -> io::Result<W> {
        self.finish()
    }
}

impl<W: Write> Coder<W> for DecompressorWriter<W> {
    fn get_input_writer(&mut self) -> &mut dyn Write {
        self
    }

    fn finish(mut self) -> io::Result<W> {
        self.flush().map_err(|_| {
            io::Error::new(
                io::ErrorKind::Other,
                "brotli decoder failed to finalize stream",
            )
        })?;
        self.into_inner().map_err(|_| {
            io::Error::new(
                io::ErrorKind::Other,
                "brotli decoder failed to finalize stream",
            )
        })
    }

    fn finish_boxed(self: Box<Self>) -> io::Result<W> {
        self.finish()
    }
}

fn main() {
    // Encode
    let mut coder: Box<dyn Coder<_>> = Box::new(CompressorWriter::with_params(
        Vec::new(),
        1024,
        &Default::default(),
    ));

    coder.get_input_writer().write_all(b"Hello world!").unwrap();
    let output = coder.finish_boxed().unwrap();

    println!("{:?}", output);

    // Decode
    let mut decoder: Box<dyn Coder<_>> = Box::new(DecompressorWriter::new(Vec::new(), 1024));

    decoder.get_input_writer().write_all(&output).unwrap();
    let output = decoder.finish_boxed().unwrap();

    println!("{:?}", String::from_utf8_lossy(&output));
}
[139, 5, 128, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 3]
"Hello world!"

Пока вы на нем, я бы также изменил некоторые другие вещи для удобства:

  • Сделайте обобщение признака ассоциированным типом. Лучше всего избегать универсальных шаблонов, если они не могут быть настроены пользователем.
  • Добавьте функцию into_boxed_coder для удобства.
use std::io::{self, Write};

pub use brotli::enc::writer::CompressorWriter;
pub use brotli::writer::DecompressorWriter;

trait Coder {
    type Out;

    fn into_boxed_coder(self) -> Box<dyn Coder<Out = Self::Out>>
    where
        Self: Sized + 'static,
    {
        Box::new(self)
    }

    fn get_input_writer(&mut self) -> &mut dyn Write;
    fn finish(self) -> io::Result<Self::Out>;
    fn finish_boxed(self: Box<Self>) -> io::Result<Self::Out>;
}

impl<W: Write> Coder for CompressorWriter<W> {
    type Out = W;

    fn get_input_writer(&mut self) -> &mut dyn Write {
        self
    }

    fn finish(mut self) -> io::Result<W> {
        self.flush().map_err(|_| {
            io::Error::new(
                io::ErrorKind::Other,
                "brotli encoder failed to finalize stream",
            )
        })?;
        Ok(self.into_inner())
    }

    fn finish_boxed(self: Box<Self>) -> io::Result<W> {
        self.finish()
    }
}

impl<W: Write> Coder for DecompressorWriter<W> {
    type Out = W;

    fn get_input_writer(&mut self) -> &mut dyn Write {
        self
    }

    fn finish(mut self) -> io::Result<W> {
        self.flush().map_err(|_| {
            io::Error::new(
                io::ErrorKind::Other,
                "brotli decoder failed to finalize stream",
            )
        })?;
        self.into_inner().map_err(|_| {
            io::Error::new(
                io::ErrorKind::Other,
                "brotli decoder failed to finalize stream",
            )
        })
    }

    fn finish_boxed(self: Box<Self>) -> io::Result<W> {
        self.finish()
    }
}

fn main() {
    // Encode
    let mut coder =
        CompressorWriter::with_params(Vec::new(), 1024, &Default::default()).into_boxed_coder();

    coder.get_input_writer().write_all(b"Hello world!").unwrap();
    let output = coder.finish_boxed().unwrap();

    println!("{:?}", output);

    // Decode
    let mut decoder = DecompressorWriter::new(Vec::new(), 1024).into_boxed_coder();

    decoder.get_input_writer().write_all(&output).unwrap();
    let output = decoder.finish_boxed().unwrap();

    println!("{:?}", String::from_utf8_lossy(&output));
}
[139, 5, 128, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 3]
"Hello world!"

Вот более полный пример с реализациями для brotli, zstd и lzma:

use std::{
    io::{self, Write},
    ops::Deref,
};

trait Coder {
    type Out;

    fn into_boxed_coder(self) -> Box<dyn Coder<Out = Self::Out>>
    where
        Self: Sized + 'static,
    {
        Box::new(self)
    }

    fn get_input_writer(&mut self) -> &mut dyn Write;
    fn finish(self) -> io::Result<Self::Out>;
    fn finish_boxed(self: Box<Self>) -> io::Result<Self::Out>;
}

mod zstd {
    use std::io::{self, Write};
    use zstd::stream::{write::Decoder, write::Encoder};

    impl<W: Write> super::Coder for Encoder<'static, W> {
        type Out = W;

        fn get_input_writer(&mut self) -> &mut dyn Write {
            self
        }

        fn finish(self) -> io::Result<W> {
            self.finish()
        }

        fn finish_boxed(self: Box<Self>) -> io::Result<W> {
            self.finish()
        }
    }

    impl<W: Write> super::Coder for Decoder<'static, W> {
        type Out = W;

        fn get_input_writer(&mut self) -> &mut dyn Write {
            self
        }

        fn finish(mut self) -> io::Result<W> {
            self.flush()?;
            Ok(self.into_inner())
        }

        fn finish_boxed(self: Box<Self>) -> io::Result<W> {
            self.finish()
        }
    }
}

mod brotli {
    use std::io::{self, Write};

    pub use brotli::enc::writer::CompressorWriter;
    pub use brotli::writer::DecompressorWriter;

    impl<W: Write> super::Coder for CompressorWriter<W> {
        type Out = W;

        fn get_input_writer(&mut self) -> &mut dyn Write {
            self
        }

        fn finish(mut self) -> io::Result<W> {
            self.flush().map_err(|_| {
                io::Error::new(
                    io::ErrorKind::Other,
                    "brotli encoder failed to finalize stream",
                )
            })?;
            Ok(self.into_inner())
        }

        fn finish_boxed(self: Box<Self>) -> io::Result<W> {
            self.finish()
        }
    }

    impl<W: Write> super::Coder for DecompressorWriter<W> {
        type Out = W;

        fn get_input_writer(&mut self) -> &mut dyn Write {
            self
        }

        fn finish(mut self) -> io::Result<W> {
            self.flush().map_err(|_| {
                io::Error::new(
                    io::ErrorKind::Other,
                    "brotli decoder failed to finalize stream",
                )
            })?;
            self.into_inner().map_err(|_| {
                io::Error::new(
                    io::ErrorKind::Other,
                    "brotli decoder failed to finalize stream",
                )
            })
        }

        fn finish_boxed(self: Box<Self>) -> io::Result<W> {
            self.finish()
        }
    }
}

mod lzma {
    use std::io::{self, Write};

    pub use xz2::write::{XzDecoder, XzEncoder};

    impl<W: Write> super::Coder for XzEncoder<W> {
        type Out = W;

        fn get_input_writer(&mut self) -> &mut dyn Write {
            self
        }

        fn finish(self) -> io::Result<W> {
            XzEncoder::finish(self)
        }

        fn finish_boxed(self: Box<Self>) -> io::Result<W> {
            self.finish()
        }
    }

    impl<W: Write> super::Coder for XzDecoder<W> {
        type Out = W;

        fn get_input_writer(&mut self) -> &mut dyn Write {
            self
        }

        fn finish(mut self) -> io::Result<W> {
            XzDecoder::finish(&mut self)
        }

        fn finish_boxed(self: Box<Self>) -> io::Result<W> {
            self.finish()
        }
    }
}

fn run_example<OutEncode, OutDecode>(
    mut encoder: Box<dyn Coder<Out = OutEncode>>,
    mut decoder: Box<dyn Coder<Out = OutDecode>>,
) where
    OutEncode: Write + std::fmt::Debug + Deref<Target = [u8]>,
    OutDecode: Write + Deref<Target = [u8]>,
{
    // Encode
    encoder
        .get_input_writer()
        .write_all(b"Hello world!")
        .unwrap();
    let output = encoder.finish_boxed().unwrap();

    println!("{:?}", output);

    // Decode
    decoder.get_input_writer().write_all(&output).unwrap();
    let output = decoder.finish_boxed().unwrap();

    println!("{:?}", String::from_utf8_lossy(&output));
}

fn main() {
    println!("zstd:");
    run_example(
        ::zstd::stream::write::Encoder::new(Vec::new(), 0)
            .unwrap()
            .into_boxed_coder(),
        ::zstd::stream::write::Decoder::new(Vec::new())
            .unwrap()
            .into_boxed_coder(),
    );
    println!();

    println!("brotli:");
    run_example(
        ::brotli::enc::writer::CompressorWriter::with_params(Vec::new(), 1024, &Default::default())
            .into_boxed_coder(),
        ::brotli::writer::DecompressorWriter::new(Vec::new(), 1024).into_boxed_coder(),
    );
    println!();

    println!("lzma:");
    run_example(
        ::xz2::write::XzEncoder::new(Vec::new(), 5).into_boxed_coder(),
        ::xz2::write::XzDecoder::new(Vec::new()).into_boxed_coder(),
    );
    println!();
}
zstd:
[40, 181, 47, 253, 0, 88, 97, 0, 0, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33]
"Hello world!"

brotli:
[139, 5, 128, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 3]
"Hello world!"

lzma:
[253, 55, 122, 88, 90, 0, 0, 4, 230, 214, 180, 70, 2, 0, 33, 1, 22, 0, 0, 0, 116, 47, 229, 163, 1, 0, 11, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 0, 10, 99, 214, 243, 246, 128, 91, 211, 0, 1, 36, 12, 166, 24, 216, 216, 31, 182, 243, 125, 1, 0, 0, 0, 0, 4, 89, 90]
"Hello world!"

вы меняете черту Write на &mut dyn Write Но я делаю то же самое с zstd.

Vana 09.12.2022 15:10
impl<W: Write> super::Coder<W> for Decoder<'static, W> { fn get_mut(&mut self) -> &mut dyn Write { Decoder::get_mut(self) } fn finish(self) -> std::io::Result<W> { Ok(Decoder::into_inner(self)) } fn finish_boxed(self: Box<Self>) -> io::Result<W> { self.finish() } }
Vana 09.12.2022 15:10

получить expected trait object dyn std::io::Write, found parameter type W

Vana 09.12.2022 15:12

То, как вы реализуете свой Coder for Decoder, не будет никакого декодирования. Вы напрямую записываете в его выходной буфер, а затем возвращаете весь выходной буфер. Вы в основном обходите весь энкодер. Вам нужно писать в декодер напрямую. Decoder::get_mut — бесполезная функция для ваших целей. Не просто пробуйте и ошибайтесь, пока API не сделает что-то, на самом деле поймите API, которые вы используете.

Finomnis 10.12.2022 09:14

@Vana Добавлен пример, включающий реализации для lzma, brotli и zstd. Обратите внимание, что реализация zstd в вашем репозитории на самом деле ничего не делает, она выполняет нулевое сжатие и просто возвращает ввод. Я переписал его, чтобы он действительно что-то делал. Кроме того, ваша реализация lzma была намного сложнее, чем нужно; ящик xz2 также предлагает API на основе записи. Я также добавил это.

Finomnis 10.12.2022 09:27

Работает как положено. Теперь мне нужно реорганизовать main fn для сжатия и распаковки.

Vana 10.12.2022 13:52

Я получаю ^^^^^^ method not found in `Box<dyn codecs::Coder<CheckWriter<W, C>>>` | при использовании pub fn output_checks(&mut self) -> &mut C { &mut self.writer.writer.get_mut().check }. Я должен использовать get_input_writer ?

Vana 10.12.2022 13:54

(я обновляю репозиторий GH)

Vana 10.12.2022 13:54

Ну, если вы также изменили get_mut() на get_input_writer(), тогда да, вы также должны изменить то, где вы это называете.

Finomnis 10.12.2022 14:26

Также, пожалуйста, не вставляйте в комментарии многострочный код, он плохо форматируется.

Finomnis 10.12.2022 14:26

Я получаю no field check on type &mut dyn std::io::Write, если использую get_input_writer

Vana 10.12.2022 22:08

@Vana может быть актуально: github.com/Speedy37/speedupdate-rs/issues/8

Finomnis 10.12.2022 22:27

Кажется, оригинальный автор не будет использовать Coder::get_input_writer() :s

Vana 11.12.2022 08:40

Также получите the trait codecs::Coder<_>` не реализован для CompressorWriter<W> на return Ok(BoxCoderDirect::boxed(brotli::CompressorWriter::new(outpu‌​t, 4096, quality, lgwin)));

Vana 11.12.2022 09:34

Stackoverflow не предназначен для чата, он предназначен для одного вопроса и ответа. Если у вас есть новый вопрос, пожалуйста, создайте новый вопрос. По словам автора вашего ящика, мы даже неправильно используем Coder. Так что я не уверен, ведет ли это обсуждение куда-либо.

Finomnis 11.12.2022 09:56

Хорошо, отменит все изменения и перезапустится из исходного ящика.

Vana 11.12.2022 10:25

я пробовал :) приветствую

Finomnis 11.12.2022 13:54

Я успешно переключаюсь на Dropbox Brotli ;)

Vana 11.12.2022 21:10

Другие вопросы по теме