@@ -8,6 +8,8 @@ use super::encoder::EncoderWriter;
8
8
///
9
9
/// # Examples
10
10
///
11
+ /// Buffer base64 in a new String:
12
+ ///
11
13
/// ```
12
14
/// use std::io::Write;
13
15
///
@@ -21,27 +23,40 @@ use super::encoder::EncoderWriter;
21
23
/// assert_eq!("YXNkZg==", &b64_string);
22
24
/// ```
23
25
///
26
+ /// Or, append to an existing String:
27
+ ///
28
+ /// ```
29
+ /// use std::io::Write;
30
+ ///
31
+ /// let mut buf = String::from("base64: ");
32
+ ///
33
+ /// let mut enc = base64::write::EncoderStringWriter::from(&mut buf, base64::STANDARD);
34
+ ///
35
+ /// enc.write_all(b"asdf").unwrap();
36
+ ///
37
+ /// // release the &mut reference on buf
38
+ /// let _ = enc.into_inner();
39
+ ///
40
+ /// assert_eq!("base64: YXNkZg==", &buf);
41
+ /// ```
42
+ ///
24
43
/// # Panics
25
44
///
26
45
/// Calling `write()` (or related methods) or `finish()` after `finish()` has completed without
27
46
/// error is invalid and will panic.
28
47
///
29
48
/// # Performance
30
49
///
31
- /// B64-encoded data is buffered in the heap since the point is to collect it in a String.
32
- pub struct EncoderStringWriter {
33
- encoder : EncoderWriter < Vec < u8 > > ,
50
+ /// Because it has to validate that the base64 is UTF-8, it is about 80% as fast as writing plain
51
+ /// bytes to a `io::Write`.
52
+ pub struct EncoderStringWriter < S : StrWrite > {
53
+ encoder : EncoderWriter < Utf8SingleCodeUnitWriter < S > > ,
34
54
}
35
55
36
- impl EncoderStringWriter {
37
- /// Create a new EncoderStringWriter that will encode with the provided config.
38
- pub fn new ( config : Config ) -> EncoderStringWriter {
39
- EncoderStringWriter :: from ( String :: new ( ) , config)
40
- }
41
-
42
- /// Create a new EncoderStringWriter that will append to the provided string.
43
- pub fn from ( s : String , config : Config ) -> EncoderStringWriter {
44
- EncoderStringWriter { encoder : EncoderWriter :: new ( s. into_bytes ( ) , config) }
56
+ impl < S : StrWrite > EncoderStringWriter < S > {
57
+ /// Create a EncoderStringWriter that will append to the provided `StrWrite`.
58
+ pub fn from ( str_writer : S , config : Config ) -> Self {
59
+ EncoderStringWriter { encoder : EncoderWriter :: new ( Utf8SingleCodeUnitWriter { str_writer } , config) }
45
60
}
46
61
47
62
/// Encode all remaining buffered data, including any trailing incomplete input triples and
@@ -50,15 +65,21 @@ impl EncoderStringWriter {
50
65
/// Once this succeeds, no further writes or calls to this method are allowed.
51
66
///
52
67
/// Returns the base64-encoded form of the accumulated written data.
53
- pub fn into_inner ( mut self ) -> String {
54
- let buf = self . encoder . finish ( )
55
- . expect ( "Writing to a Vec<u8> should never fail" ) ;
68
+ pub fn into_inner ( mut self ) -> S {
69
+ self . encoder . finish ( )
70
+ . expect ( "Writing to a Vec<u8> should never fail" )
71
+ . str_writer
72
+ }
73
+ }
56
74
57
- String :: from_utf8 ( buf) . expect ( "Base64 should always be valid UTF-8" )
75
+ impl EncoderStringWriter < String > {
76
+ /// Create a EncoderStringWriter that will encode into a new String with the provided config.
77
+ pub fn new ( config : Config ) -> Self {
78
+ EncoderStringWriter :: from ( String :: new ( ) , config)
58
79
}
59
80
}
60
81
61
- impl < ' a > Write for EncoderStringWriter {
82
+ impl < S : StrWrite > Write for EncoderStringWriter < S > {
62
83
fn write ( & mut self , buf : & [ u8 ] ) -> io:: Result < usize > {
63
84
self . encoder . write ( buf)
64
85
}
@@ -68,6 +89,50 @@ impl<'a> Write for EncoderStringWriter {
68
89
}
69
90
}
70
91
92
+ /// An abstraction around infallible writes of `str`s.
93
+ ///
94
+ /// Typically, this will just be String.
95
+ pub trait StrWrite {
96
+ /// The write must succeed, and must write the entire `buf`.
97
+ fn write ( & mut self , buf : & str ) ;
98
+ }
99
+
100
+ /// As for io::Write, StrWrite is implemented automatically for `&mut S`.
101
+ impl < S : StrWrite + ?Sized > StrWrite for & mut S {
102
+ fn write ( & mut self , buf : & str ) {
103
+ ( * * self ) . write ( buf)
104
+ }
105
+ }
106
+
107
+ impl StrWrite for String {
108
+ fn write ( & mut self , buf : & str ) {
109
+ self . push_str ( buf)
110
+ }
111
+ }
112
+
113
+ /// A `Write` that only can handle bytes that are valid single-byte UTF-8 code units.
114
+ ///
115
+ /// This is safe because we only use it when writing base64, which is always valid UTF-8.
116
+ struct Utf8SingleCodeUnitWriter < S : StrWrite > {
117
+ str_writer : S
118
+ }
119
+
120
+ impl < S : StrWrite > io:: Write for Utf8SingleCodeUnitWriter < S > {
121
+ fn write ( & mut self , buf : & [ u8 ] ) -> io:: Result < usize > {
122
+ let s = std:: str:: from_utf8 ( buf)
123
+ . expect ( "Input must be valid UTF-8" ) ;
124
+
125
+ self . str_writer . write ( s) ;
126
+
127
+ Ok ( buf. len ( ) )
128
+ }
129
+
130
+ fn flush ( & mut self ) -> io:: Result < ( ) > {
131
+ // no op
132
+ Ok ( ( ) )
133
+ }
134
+ }
135
+
71
136
#[ cfg( test) ]
72
137
mod tests {
73
138
use crate :: encode_config_buf;
0 commit comments