123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- use super::encoder::EncoderWriter;
- use crate::Config;
- use std::io;
- use std::io::Write;
- /// A `Write` implementation that base64-encodes data using the provided config and accumulates the
- /// resulting base64 in memory, which is then exposed as a String via `into_inner()`.
- ///
- /// # Examples
- ///
- /// Buffer base64 in a new String:
- ///
- /// ```
- /// use std::io::Write;
- ///
- /// let mut enc = base64::write::EncoderStringWriter::new(base64::STANDARD);
- ///
- /// enc.write_all(b"asdf").unwrap();
- ///
- /// // get the resulting String
- /// let b64_string = enc.into_inner();
- ///
- /// assert_eq!("YXNkZg==", &b64_string);
- /// ```
- ///
- /// Or, append to an existing String:
- ///
- /// ```
- /// use std::io::Write;
- ///
- /// let mut buf = String::from("base64: ");
- ///
- /// let mut enc = base64::write::EncoderStringWriter::from(&mut buf, base64::STANDARD);
- ///
- /// enc.write_all(b"asdf").unwrap();
- ///
- /// // release the &mut reference on buf
- /// let _ = enc.into_inner();
- ///
- /// assert_eq!("base64: YXNkZg==", &buf);
- /// ```
- ///
- /// # Panics
- ///
- /// Calling `write()` (or related methods) or `finish()` after `finish()` has completed without
- /// error is invalid and will panic.
- ///
- /// # Performance
- ///
- /// Because it has to validate that the base64 is UTF-8, it is about 80% as fast as writing plain
- /// bytes to a `io::Write`.
- pub struct EncoderStringWriter<S: StrConsumer> {
- encoder: EncoderWriter<Utf8SingleCodeUnitWriter<S>>,
- }
- impl<S: StrConsumer> EncoderStringWriter<S> {
- /// Create a EncoderStringWriter that will append to the provided `StrConsumer`.
- pub fn from(str_consumer: S, config: Config) -> Self {
- EncoderStringWriter {
- encoder: EncoderWriter::new(Utf8SingleCodeUnitWriter { str_consumer }, config),
- }
- }
- /// Encode all remaining buffered data, including any trailing incomplete input triples and
- /// associated padding.
- ///
- /// Once this succeeds, no further writes or calls to this method are allowed.
- ///
- /// Returns the base64-encoded form of the accumulated written data.
- pub fn into_inner(mut self) -> S {
- self.encoder
- .finish()
- .expect("Writing to a Vec<u8> should never fail")
- .str_consumer
- }
- }
- impl EncoderStringWriter<String> {
- /// Create a EncoderStringWriter that will encode into a new String with the provided config.
- pub fn new(config: Config) -> Self {
- EncoderStringWriter::from(String::new(), config)
- }
- }
- impl<S: StrConsumer> Write for EncoderStringWriter<S> {
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- self.encoder.write(buf)
- }
- fn flush(&mut self) -> io::Result<()> {
- self.encoder.flush()
- }
- }
- /// An abstraction around consuming `str`s produced by base64 encoding.
- pub trait StrConsumer {
- /// Consume the base64 encoded data in `buf`
- fn consume(&mut self, buf: &str);
- }
- /// As for io::Write, `StrConsumer` is implemented automatically for `&mut S`.
- impl<S: StrConsumer + ?Sized> StrConsumer for &mut S {
- fn consume(&mut self, buf: &str) {
- (**self).consume(buf)
- }
- }
- /// Pushes the str onto the end of the String
- impl StrConsumer for String {
- fn consume(&mut self, buf: &str) {
- self.push_str(buf)
- }
- }
- /// A `Write` that only can handle bytes that are valid single-byte UTF-8 code units.
- ///
- /// This is safe because we only use it when writing base64, which is always valid UTF-8.
- struct Utf8SingleCodeUnitWriter<S: StrConsumer> {
- str_consumer: S,
- }
- impl<S: StrConsumer> io::Write for Utf8SingleCodeUnitWriter<S> {
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
- // Because we expect all input to be valid utf-8 individual bytes, we can encode any buffer
- // length
- let s = std::str::from_utf8(buf).expect("Input must be valid UTF-8");
- self.str_consumer.consume(s);
- Ok(buf.len())
- }
- fn flush(&mut self) -> io::Result<()> {
- // no op
- Ok(())
- }
- }
- #[cfg(test)]
- mod tests {
- use crate::encode_config_buf;
- use crate::tests::random_config;
- use crate::write::encoder_string_writer::EncoderStringWriter;
- use rand::Rng;
- use std::io::Write;
- #[test]
- fn every_possible_split_of_input() {
- let mut rng = rand::thread_rng();
- let mut orig_data = Vec::<u8>::new();
- let mut normal_encoded = String::new();
- let size = 5_000;
- for i in 0..size {
- orig_data.clear();
- normal_encoded.clear();
- for _ in 0..size {
- orig_data.push(rng.gen());
- }
- let config = random_config(&mut rng);
- encode_config_buf(&orig_data, config, &mut normal_encoded);
- let mut stream_encoder = EncoderStringWriter::new(config);
- // Write the first i bytes, then the rest
- stream_encoder.write_all(&orig_data[0..i]).unwrap();
- stream_encoder.write_all(&orig_data[i..]).unwrap();
- let stream_encoded = stream_encoder.into_inner();
- assert_eq!(normal_encoded, stream_encoded);
- }
- }
- }
|