/src/html5ever/tendril/src/stream.rs
Line | Count | Source |
1 | | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
2 | | // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
3 | | // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your |
4 | | // option. This file may not be copied, modified, or distributed |
5 | | // except according to those terms. |
6 | | |
7 | | //! Streams of tendrils. |
8 | | |
9 | | use crate::utf8_decode::{decode_utf8, DecodeError, REPLACEMENT_CHARACTER}; |
10 | | use crate::{fmt, IncompleteUtf8}; |
11 | | use crate::{Atomicity, NonAtomic, Tendril}; |
12 | | |
13 | | use std::borrow::Cow; |
14 | | use std::fs::File; |
15 | | use std::io; |
16 | | use std::marker::PhantomData; |
17 | | use std::path::Path; |
18 | | |
19 | | #[cfg(feature = "encoding_rs")] |
20 | | use encoding_rs::{self, DecoderResult}; |
21 | | |
22 | | /// Trait for types that can process a tendril. |
23 | | /// |
24 | | /// This is a "push" interface, unlike the "pull" interface of |
25 | | /// `Iterator<Item=Tendril<F>>`. The push interface matches |
26 | | /// [html5ever][] and other incremental parsers with a similar |
27 | | /// architecture. |
28 | | /// |
29 | | /// [html5ever]: https://github.com/servo/html5ever |
30 | | pub trait TendrilSink<F, A = NonAtomic> |
31 | | where |
32 | | F: fmt::Format, |
33 | | A: Atomicity, |
34 | | { |
35 | | /// Process this tendril. |
36 | | fn process(&mut self, t: Tendril<F, A>); |
37 | | |
38 | | /// Indicates that an error has occurred. |
39 | | fn error(&mut self, desc: Cow<'static, str>); |
40 | | |
41 | | /// What the overall result of processing is. |
42 | | type Output; |
43 | | |
44 | | /// Indicates the end of the stream. |
45 | | fn finish(self) -> Self::Output; |
46 | | |
47 | | /// Process one tendril and finish. |
48 | 0 | fn one<T>(mut self, t: T) -> Self::Output |
49 | 0 | where |
50 | 0 | Self: Sized, |
51 | 0 | T: Into<Tendril<F, A>>, |
52 | | { |
53 | 0 | self.process(t.into()); |
54 | 0 | self.finish() |
55 | 0 | } |
56 | | |
57 | | /// Consume an iterator of tendrils, processing each item, then finish. |
58 | 0 | fn from_iter<I>(mut self, i: I) -> Self::Output |
59 | 0 | where |
60 | 0 | Self: Sized, |
61 | 0 | I: IntoIterator, |
62 | 0 | I::Item: Into<Tendril<F, A>>, |
63 | | { |
64 | 0 | for t in i { |
65 | 0 | self.process(t.into()) |
66 | | } |
67 | 0 | self.finish() |
68 | 0 | } |
69 | | |
70 | | /// Read from the given stream of bytes until exhaustion and process incrementally, |
71 | | /// then finish. Return `Err` at the first I/O error. |
72 | 12.6k | fn read_from<R>(mut self, r: &mut R) -> io::Result<Self::Output> |
73 | 12.6k | where |
74 | 12.6k | Self: Sized, |
75 | 12.6k | R: io::Read, |
76 | 12.6k | F: fmt::SliceFormat<Slice = [u8]>, |
77 | | { |
78 | | const BUFFER_SIZE: u32 = 4 * 1024; |
79 | | loop { |
80 | 51.4k | let mut tendril = Tendril::<fmt::Bytes, A>::new(); |
81 | 51.4k | tendril.extend_with_byte(BUFFER_SIZE, 0); |
82 | | loop { |
83 | 51.4k | match r.read(&mut tendril) { |
84 | 12.6k | Ok(0) => return Ok(self.finish()), |
85 | 38.7k | Ok(n) => { |
86 | 38.7k | tendril.pop_back(BUFFER_SIZE - n as u32); |
87 | 38.7k | self.process(unsafe { tendril.reinterpret_without_validating() }); |
88 | 38.7k | break; |
89 | | }, |
90 | 0 | Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}, |
91 | 0 | Err(e) => return Err(e), |
92 | | } |
93 | | } |
94 | | } |
95 | 12.6k | } <tendril::stream::Utf8LossyDecoder<xml5ever::driver::XmlParser<markup5ever_rcdom::RcDom>> as tendril::stream::TendrilSink<tendril::fmt::Bytes>>::read_from::<std::io::buffered::bufreader::BufReader<&[u8]>> Line | Count | Source | 72 | 12.6k | fn read_from<R>(mut self, r: &mut R) -> io::Result<Self::Output> | 73 | 12.6k | where | 74 | 12.6k | Self: Sized, | 75 | 12.6k | R: io::Read, | 76 | 12.6k | F: fmt::SliceFormat<Slice = [u8]>, | 77 | | { | 78 | | const BUFFER_SIZE: u32 = 4 * 1024; | 79 | | loop { | 80 | 51.4k | let mut tendril = Tendril::<fmt::Bytes, A>::new(); | 81 | 51.4k | tendril.extend_with_byte(BUFFER_SIZE, 0); | 82 | | loop { | 83 | 51.4k | match r.read(&mut tendril) { | 84 | 12.6k | Ok(0) => return Ok(self.finish()), | 85 | 38.7k | Ok(n) => { | 86 | 38.7k | tendril.pop_back(BUFFER_SIZE - n as u32); | 87 | 38.7k | self.process(unsafe { tendril.reinterpret_without_validating() }); | 88 | 38.7k | break; | 89 | | }, | 90 | 0 | Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}, | 91 | 0 | Err(e) => return Err(e), | 92 | | } | 93 | | } | 94 | | } | 95 | 12.6k | } |
Unexecuted instantiation: <_ as tendril::stream::TendrilSink<_, _>>::read_from::<_> |
96 | | |
97 | | /// Read from the file at the given path and process incrementally, |
98 | | /// then finish. Return `Err` at the first I/O error. |
99 | 0 | fn from_file<P>(self, path: P) -> io::Result<Self::Output> |
100 | 0 | where |
101 | 0 | Self: Sized, |
102 | 0 | P: AsRef<Path>, |
103 | 0 | F: fmt::SliceFormat<Slice = [u8]>, |
104 | | { |
105 | 0 | self.read_from(&mut File::open(path)?) |
106 | 0 | } |
107 | | } |
108 | | |
109 | | /// A `TendrilSink` adaptor that takes bytes, decodes them as UTF-8, |
110 | | /// lossily replace ill-formed byte sequences with U+FFFD replacement characters, |
111 | | /// and emits Unicode (`StrTendril`). |
112 | | /// |
113 | | /// This does not allocate memory: the output is either subtendrils on the input, |
114 | | /// on inline tendrils for a single code point. |
115 | | pub struct Utf8LossyDecoder<Sink, A = NonAtomic> |
116 | | where |
117 | | Sink: TendrilSink<fmt::UTF8, A>, |
118 | | A: Atomicity, |
119 | | { |
120 | | pub inner_sink: Sink, |
121 | | incomplete: Option<IncompleteUtf8>, |
122 | | marker: PhantomData<A>, |
123 | | } |
124 | | |
125 | | impl<Sink, A> Utf8LossyDecoder<Sink, A> |
126 | | where |
127 | | Sink: TendrilSink<fmt::UTF8, A>, |
128 | | A: Atomicity, |
129 | | { |
130 | | /// Create a new incremental UTF-8 decoder. |
131 | | #[inline] |
132 | 12.6k | pub fn new(inner_sink: Sink) -> Self { |
133 | 12.6k | Utf8LossyDecoder { |
134 | 12.6k | inner_sink, |
135 | 12.6k | incomplete: None, |
136 | 12.6k | marker: PhantomData, |
137 | 12.6k | } |
138 | 12.6k | } <tendril::stream::Utf8LossyDecoder<xml5ever::driver::XmlParser<markup5ever_rcdom::RcDom>>>::new Line | Count | Source | 132 | 12.6k | pub fn new(inner_sink: Sink) -> Self { | 133 | 12.6k | Utf8LossyDecoder { | 134 | 12.6k | inner_sink, | 135 | 12.6k | incomplete: None, | 136 | 12.6k | marker: PhantomData, | 137 | 12.6k | } | 138 | 12.6k | } |
Unexecuted instantiation: <tendril::stream::Utf8LossyDecoder<_, _>>::new |
139 | | } |
140 | | |
141 | | impl<Sink, A> TendrilSink<fmt::Bytes, A> for Utf8LossyDecoder<Sink, A> |
142 | | where |
143 | | Sink: TendrilSink<fmt::UTF8, A>, |
144 | | A: Atomicity, |
145 | | { |
146 | | #[inline] |
147 | 38.7k | fn process(&mut self, mut bytes: Tendril<fmt::Bytes, A>) { |
148 | | // FIXME: remove take() and map() when non-lexical borrows are stable. |
149 | 38.7k | if let Some(mut incomplete) = self.incomplete.take() { |
150 | 3.54k | let resume_at = incomplete |
151 | 3.54k | .try_to_complete_codepoint(&bytes) |
152 | 3.54k | .map(|(result, rest)| { |
153 | 3.54k | match result { |
154 | 721 | Ok(decoded_string) => { |
155 | 721 | self.inner_sink.process(Tendril::from_slice(decoded_string)) |
156 | | }, |
157 | 2.82k | Err(_) => { |
158 | 2.82k | self.inner_sink.error("invalid byte sequence".into()); |
159 | 2.82k | self.inner_sink |
160 | 2.82k | .process(Tendril::from_slice(REPLACEMENT_CHARACTER)); |
161 | 2.82k | }, |
162 | | } |
163 | 3.54k | bytes.len() - rest.len() |
164 | 3.54k | }); <tendril::stream::Utf8LossyDecoder<xml5ever::driver::XmlParser<markup5ever_rcdom::RcDom>> as tendril::stream::TendrilSink<tendril::fmt::Bytes>>::process::{closure#0}Line | Count | Source | 152 | 3.54k | .map(|(result, rest)| { | 153 | 3.54k | match result { | 154 | 721 | Ok(decoded_string) => { | 155 | 721 | self.inner_sink.process(Tendril::from_slice(decoded_string)) | 156 | | }, | 157 | 2.82k | Err(_) => { | 158 | 2.82k | self.inner_sink.error("invalid byte sequence".into()); | 159 | 2.82k | self.inner_sink | 160 | 2.82k | .process(Tendril::from_slice(REPLACEMENT_CHARACTER)); | 161 | 2.82k | }, | 162 | | } | 163 | 3.54k | bytes.len() - rest.len() | 164 | 3.54k | }); |
Unexecuted instantiation: <tendril::stream::Utf8LossyDecoder<_, _> as tendril::stream::TendrilSink<tendril::fmt::Bytes, _>>::process::{closure#0} |
165 | 3.54k | match resume_at { |
166 | | None => { |
167 | 3 | self.incomplete = Some(incomplete); |
168 | 3 | return; |
169 | | }, |
170 | 3.54k | Some(resume_at) => bytes.pop_front(resume_at as u32), |
171 | | } |
172 | 35.1k | } |
173 | 39.1M | while !bytes.is_empty() { |
174 | 39.1M | let unborrowed_result = match decode_utf8(&bytes) { |
175 | 25.9k | Ok(s) => { |
176 | 25.9k | debug_assert!(s.as_ptr() == bytes.as_ptr()); |
177 | 25.9k | debug_assert!(s.len() == bytes.len()); |
178 | 25.9k | Ok(()) |
179 | | }, |
180 | | Err(DecodeError::Invalid { |
181 | 39.1M | valid_prefix, |
182 | 39.1M | invalid_sequence, |
183 | | .. |
184 | | }) => { |
185 | 39.1M | debug_assert!(valid_prefix.as_ptr() == bytes.as_ptr()); |
186 | 39.1M | debug_assert!(valid_prefix.len() <= bytes.len()); |
187 | 39.1M | Err(( |
188 | 39.1M | valid_prefix.len(), |
189 | 39.1M | Err(valid_prefix.len() + invalid_sequence.len()), |
190 | 39.1M | )) |
191 | | }, |
192 | | Err(DecodeError::Incomplete { |
193 | 4.38k | valid_prefix, |
194 | 4.38k | incomplete_suffix, |
195 | | }) => { |
196 | 4.38k | debug_assert!(valid_prefix.as_ptr() == bytes.as_ptr()); |
197 | 4.38k | debug_assert!(valid_prefix.len() <= bytes.len()); |
198 | 4.38k | Err((valid_prefix.len(), Ok(incomplete_suffix))) |
199 | | }, |
200 | | }; |
201 | 39.1M | match unborrowed_result { |
202 | | Ok(()) => { |
203 | | unsafe { |
204 | 25.9k | self.inner_sink |
205 | 25.9k | .process(bytes.reinterpret_without_validating()) |
206 | | } |
207 | 25.9k | return; |
208 | | }, |
209 | 39.1M | Err((valid_len, and_then)) => { |
210 | 39.1M | if valid_len > 0 { |
211 | 3.76M | let subtendril = bytes.subtendril(0, valid_len as u32); |
212 | | unsafe { |
213 | 3.76M | self.inner_sink |
214 | 3.76M | .process(subtendril.reinterpret_without_validating()) |
215 | | } |
216 | 35.3M | } |
217 | 39.1M | match and_then { |
218 | 4.38k | Ok(incomplete) => { |
219 | 4.38k | self.incomplete = Some(incomplete); |
220 | 4.38k | return; |
221 | | }, |
222 | 39.1M | Err(offset) => { |
223 | 39.1M | self.inner_sink.error("invalid byte sequence".into()); |
224 | 39.1M | self.inner_sink |
225 | 39.1M | .process(Tendril::from_slice(REPLACEMENT_CHARACTER)); |
226 | 39.1M | bytes.pop_front(offset as u32); |
227 | 39.1M | }, |
228 | | } |
229 | | }, |
230 | | } |
231 | | } |
232 | 38.7k | } <tendril::stream::Utf8LossyDecoder<xml5ever::driver::XmlParser<markup5ever_rcdom::RcDom>> as tendril::stream::TendrilSink<tendril::fmt::Bytes>>::process Line | Count | Source | 147 | 38.7k | fn process(&mut self, mut bytes: Tendril<fmt::Bytes, A>) { | 148 | | // FIXME: remove take() and map() when non-lexical borrows are stable. | 149 | 38.7k | if let Some(mut incomplete) = self.incomplete.take() { | 150 | 3.54k | let resume_at = incomplete | 151 | 3.54k | .try_to_complete_codepoint(&bytes) | 152 | 3.54k | .map(|(result, rest)| { | 153 | | match result { | 154 | | Ok(decoded_string) => { | 155 | | self.inner_sink.process(Tendril::from_slice(decoded_string)) | 156 | | }, | 157 | | Err(_) => { | 158 | | self.inner_sink.error("invalid byte sequence".into()); | 159 | | self.inner_sink | 160 | | .process(Tendril::from_slice(REPLACEMENT_CHARACTER)); | 161 | | }, | 162 | | } | 163 | | bytes.len() - rest.len() | 164 | | }); | 165 | 3.54k | match resume_at { | 166 | | None => { | 167 | 3 | self.incomplete = Some(incomplete); | 168 | 3 | return; | 169 | | }, | 170 | 3.54k | Some(resume_at) => bytes.pop_front(resume_at as u32), | 171 | | } | 172 | 35.1k | } | 173 | 39.1M | while !bytes.is_empty() { | 174 | 39.1M | let unborrowed_result = match decode_utf8(&bytes) { | 175 | 25.9k | Ok(s) => { | 176 | 25.9k | debug_assert!(s.as_ptr() == bytes.as_ptr()); | 177 | 25.9k | debug_assert!(s.len() == bytes.len()); | 178 | 25.9k | Ok(()) | 179 | | }, | 180 | | Err(DecodeError::Invalid { | 181 | 39.1M | valid_prefix, | 182 | 39.1M | invalid_sequence, | 183 | | .. | 184 | | }) => { | 185 | 39.1M | debug_assert!(valid_prefix.as_ptr() == bytes.as_ptr()); | 186 | 39.1M | debug_assert!(valid_prefix.len() <= bytes.len()); | 187 | 39.1M | Err(( | 188 | 39.1M | valid_prefix.len(), | 189 | 39.1M | Err(valid_prefix.len() + invalid_sequence.len()), | 190 | 39.1M | )) | 191 | | }, | 192 | | Err(DecodeError::Incomplete { | 193 | 4.38k | valid_prefix, | 194 | 4.38k | incomplete_suffix, | 195 | | }) => { | 196 | 4.38k | debug_assert!(valid_prefix.as_ptr() == bytes.as_ptr()); | 197 | 4.38k | debug_assert!(valid_prefix.len() <= bytes.len()); | 198 | 4.38k | Err((valid_prefix.len(), Ok(incomplete_suffix))) | 199 | | }, | 200 | | }; | 201 | 39.1M | match unborrowed_result { | 202 | | Ok(()) => { | 203 | | unsafe { | 204 | 25.9k | self.inner_sink | 205 | 25.9k | .process(bytes.reinterpret_without_validating()) | 206 | | } | 207 | 25.9k | return; | 208 | | }, | 209 | 39.1M | Err((valid_len, and_then)) => { | 210 | 39.1M | if valid_len > 0 { | 211 | 3.76M | let subtendril = bytes.subtendril(0, valid_len as u32); | 212 | | unsafe { | 213 | 3.76M | self.inner_sink | 214 | 3.76M | .process(subtendril.reinterpret_without_validating()) | 215 | | } | 216 | 35.3M | } | 217 | 39.1M | match and_then { | 218 | 4.38k | Ok(incomplete) => { | 219 | 4.38k | self.incomplete = Some(incomplete); | 220 | 4.38k | return; | 221 | | }, | 222 | 39.1M | Err(offset) => { | 223 | 39.1M | self.inner_sink.error("invalid byte sequence".into()); | 224 | 39.1M | self.inner_sink | 225 | 39.1M | .process(Tendril::from_slice(REPLACEMENT_CHARACTER)); | 226 | 39.1M | bytes.pop_front(offset as u32); | 227 | 39.1M | }, | 228 | | } | 229 | | }, | 230 | | } | 231 | | } | 232 | 38.7k | } |
Unexecuted instantiation: <tendril::stream::Utf8LossyDecoder<_, _> as tendril::stream::TendrilSink<tendril::fmt::Bytes, _>>::process |
233 | | |
234 | | #[inline] |
235 | 0 | fn error(&mut self, desc: Cow<'static, str>) { |
236 | 0 | self.inner_sink.error(desc); |
237 | 0 | } |
238 | | |
239 | | type Output = Sink::Output; |
240 | | |
241 | | #[inline] |
242 | 12.6k | fn finish(mut self) -> Sink::Output { |
243 | 12.6k | if self.incomplete.is_some() { |
244 | 841 | self.inner_sink |
245 | 841 | .error("incomplete byte sequence at end of stream".into()); |
246 | 841 | self.inner_sink |
247 | 841 | .process(Tendril::from_slice(REPLACEMENT_CHARACTER)); |
248 | 11.8k | } |
249 | 12.6k | self.inner_sink.finish() |
250 | 12.6k | } <tendril::stream::Utf8LossyDecoder<xml5ever::driver::XmlParser<markup5ever_rcdom::RcDom>> as tendril::stream::TendrilSink<tendril::fmt::Bytes>>::finish Line | Count | Source | 242 | 12.6k | fn finish(mut self) -> Sink::Output { | 243 | 12.6k | if self.incomplete.is_some() { | 244 | 841 | self.inner_sink | 245 | 841 | .error("incomplete byte sequence at end of stream".into()); | 246 | 841 | self.inner_sink | 247 | 841 | .process(Tendril::from_slice(REPLACEMENT_CHARACTER)); | 248 | 11.8k | } | 249 | 12.6k | self.inner_sink.finish() | 250 | 12.6k | } |
Unexecuted instantiation: <tendril::stream::Utf8LossyDecoder<_, _> as tendril::stream::TendrilSink<tendril::fmt::Bytes, _>>::finish |
251 | | } |
252 | | |
253 | | /// A `TendrilSink` adaptor that takes bytes, decodes them as the given character encoding, |
254 | | /// lossily replace ill-formed byte sequences with U+FFFD replacement characters, |
255 | | /// and emits Unicode (`StrTendril`). |
256 | | /// |
257 | | /// This allocates new tendrils for encodings other than UTF-8. |
258 | | #[cfg(feature = "encoding_rs")] |
259 | | pub struct LossyDecoder<Sink, A = NonAtomic> |
260 | | where |
261 | | Sink: TendrilSink<fmt::UTF8, A>, |
262 | | A: Atomicity, |
263 | | { |
264 | | inner: LossyDecoderInner<Sink, A>, |
265 | | } |
266 | | |
267 | | #[cfg(feature = "encoding_rs")] |
268 | | enum LossyDecoderInner<Sink, A> |
269 | | where |
270 | | Sink: TendrilSink<fmt::UTF8, A>, |
271 | | A: Atomicity, |
272 | | { |
273 | | Utf8(Utf8LossyDecoder<Sink, A>), |
274 | | #[cfg(feature = "encoding_rs")] |
275 | | EncodingRs(encoding_rs::Decoder, Sink), |
276 | | } |
277 | | |
278 | | #[cfg(feature = "encoding_rs")] |
279 | | impl<Sink, A> LossyDecoder<Sink, A> |
280 | | where |
281 | | Sink: TendrilSink<fmt::UTF8, A>, |
282 | | A: Atomicity, |
283 | | { |
284 | | /// Create a new incremental decoder using the encoding_rs crate. |
285 | | #[cfg(feature = "encoding_rs")] |
286 | | #[inline] |
287 | | pub fn new_encoding_rs(encoding: &'static encoding_rs::Encoding, sink: Sink) -> Self { |
288 | | if encoding == encoding_rs::UTF_8 { |
289 | | return Self::utf8(sink); |
290 | | } |
291 | | Self { |
292 | | inner: LossyDecoderInner::EncodingRs(encoding.new_decoder(), sink), |
293 | | } |
294 | | } |
295 | | |
296 | | /// Create a new incremental decoder using the encoding_rs crate. |
297 | | /// |
298 | | /// This is a more flexible version of [Self::new_encoding_rs], allowing the caller |
299 | | /// to configure the decoder themselves. |
300 | | #[cfg(feature = "encoding_rs")] |
301 | | #[inline] |
302 | | pub fn new_from_encoding_rs_decoder(decoder: encoding_rs::Decoder, sink: Sink) -> Self { |
303 | | Self { |
304 | | inner: LossyDecoderInner::EncodingRs(decoder, sink), |
305 | | } |
306 | | } |
307 | | |
308 | | /// Create a new incremental decoder for the UTF-8 encoding. |
309 | | /// |
310 | | /// This is useful for content that is known at run-time to be UTF-8 |
311 | | /// (whereas `Utf8LossyDecoder` requires knowning at compile-time.) |
312 | | #[inline] |
313 | | pub fn utf8(sink: Sink) -> LossyDecoder<Sink, A> { |
314 | | LossyDecoder { |
315 | | inner: LossyDecoderInner::Utf8(Utf8LossyDecoder::new(sink)), |
316 | | } |
317 | | } |
318 | | |
319 | | /// Give a reference to the inner sink. |
320 | | pub fn inner_sink(&self) -> &Sink { |
321 | | match self.inner { |
322 | | LossyDecoderInner::Utf8(ref utf8) => &utf8.inner_sink, |
323 | | #[cfg(feature = "encoding_rs")] |
324 | | LossyDecoderInner::EncodingRs(_, ref inner_sink) => inner_sink, |
325 | | } |
326 | | } |
327 | | |
328 | | /// Give a mutable reference to the inner sink. |
329 | | pub fn inner_sink_mut(&mut self) -> &mut Sink { |
330 | | match self.inner { |
331 | | LossyDecoderInner::Utf8(ref mut utf8) => &mut utf8.inner_sink, |
332 | | #[cfg(feature = "encoding_rs")] |
333 | | LossyDecoderInner::EncodingRs(_, ref mut inner_sink) => inner_sink, |
334 | | } |
335 | | } |
336 | | } |
337 | | |
338 | | #[cfg(feature = "encoding_rs")] |
339 | | impl<Sink, A> TendrilSink<fmt::Bytes, A> for LossyDecoder<Sink, A> |
340 | | where |
341 | | Sink: TendrilSink<fmt::UTF8, A>, |
342 | | A: Atomicity, |
343 | | { |
344 | | #[inline] |
345 | | fn process(&mut self, t: Tendril<fmt::Bytes, A>) { |
346 | | match self.inner { |
347 | | LossyDecoderInner::Utf8(ref mut utf8) => utf8.process(t), |
348 | | #[cfg(feature = "encoding_rs")] |
349 | | LossyDecoderInner::EncodingRs(ref mut decoder, ref mut sink) => { |
350 | | if t.is_empty() { |
351 | | return; |
352 | | } |
353 | | decode_to_sink(t, decoder, sink, false); |
354 | | }, |
355 | | } |
356 | | } |
357 | | |
358 | | #[inline] |
359 | | fn error(&mut self, desc: Cow<'static, str>) { |
360 | | match self.inner { |
361 | | LossyDecoderInner::Utf8(ref mut utf8) => utf8.error(desc), |
362 | | #[cfg(feature = "encoding_rs")] |
363 | | LossyDecoderInner::EncodingRs(_, ref mut sink) => sink.error(desc), |
364 | | } |
365 | | } |
366 | | |
367 | | type Output = Sink::Output; |
368 | | |
369 | | #[inline] |
370 | | fn finish(self) -> Sink::Output { |
371 | | match self.inner { |
372 | | LossyDecoderInner::Utf8(utf8) => utf8.finish(), |
373 | | #[cfg(feature = "encoding_rs")] |
374 | | LossyDecoderInner::EncodingRs(mut decoder, mut sink) => { |
375 | | decode_to_sink(Tendril::new(), &mut decoder, &mut sink, true); |
376 | | sink.finish() |
377 | | }, |
378 | | } |
379 | | } |
380 | | } |
381 | | |
382 | | #[cfg(feature = "encoding_rs")] |
383 | | fn decode_to_sink<Sink, A>( |
384 | | mut input: Tendril<fmt::Bytes, A>, |
385 | | decoder: &mut encoding_rs::Decoder, |
386 | | sink: &mut Sink, |
387 | | last: bool, |
388 | | ) where |
389 | | Sink: TendrilSink<fmt::UTF8, A>, |
390 | | A: Atomicity, |
391 | | { |
392 | | loop { |
393 | | let mut out = <Tendril<fmt::Bytes, A>>::new(); |
394 | | let max_len = decoder |
395 | | .max_utf8_buffer_length_without_replacement(input.len()) |
396 | | .unwrap_or(8192); |
397 | | unsafe { |
398 | | out.push_uninitialized(max_len.min(8192) as u32); |
399 | | } |
400 | | let (result, bytes_read, bytes_written) = |
401 | | decoder.decode_to_utf8_without_replacement(&input, &mut out, last); |
402 | | if bytes_written > 0 { |
403 | | sink.process(unsafe { |
404 | | out.subtendril(0, bytes_written as u32) |
405 | | .reinterpret_without_validating() |
406 | | }); |
407 | | } |
408 | | match result { |
409 | | DecoderResult::InputEmpty => return, |
410 | | DecoderResult::OutputFull => {}, |
411 | | DecoderResult::Malformed(_, _) => { |
412 | | sink.error(Cow::Borrowed("invalid sequence")); |
413 | | sink.process(Tendril::from_slice(REPLACEMENT_CHARACTER)); |
414 | | }, |
415 | | } |
416 | | input.pop_front(bytes_read as u32); |
417 | | if input.is_empty() { |
418 | | return; |
419 | | } |
420 | | } |
421 | | } |
422 | | |
423 | | #[cfg(test)] |
424 | | mod test { |
425 | | use super::{TendrilSink, Utf8LossyDecoder}; |
426 | | use crate::fmt; |
427 | | use crate::{Atomicity, NonAtomic, Tendril}; |
428 | | use std::borrow::Cow; |
429 | | |
430 | | #[cfg(feature = "encoding_rs")] |
431 | | use super::LossyDecoder; |
432 | | #[cfg(feature = "encoding_rs")] |
433 | | use crate::SliceExt; |
434 | | |
435 | | #[cfg(feature = "encoding_rs")] |
436 | | use encoding_rs as enc_rs; |
437 | | |
438 | | struct Accumulate<A> |
439 | | where |
440 | | A: Atomicity, |
441 | | { |
442 | | tendrils: Vec<Tendril<fmt::UTF8, A>>, |
443 | | errors: Vec<String>, |
444 | | } |
445 | | |
446 | | impl<A> Accumulate<A> |
447 | | where |
448 | | A: Atomicity, |
449 | | { |
450 | | fn new() -> Accumulate<A> { |
451 | | Accumulate { |
452 | | tendrils: vec![], |
453 | | errors: vec![], |
454 | | } |
455 | | } |
456 | | } |
457 | | |
458 | | impl<A> TendrilSink<fmt::UTF8, A> for Accumulate<A> |
459 | | where |
460 | | A: Atomicity, |
461 | | { |
462 | | fn process(&mut self, t: Tendril<fmt::UTF8, A>) { |
463 | | self.tendrils.push(t); |
464 | | } |
465 | | |
466 | | fn error(&mut self, desc: Cow<'static, str>) { |
467 | | self.errors.push(desc.into_owned()); |
468 | | } |
469 | | |
470 | | type Output = (Vec<Tendril<fmt::UTF8, A>>, Vec<String>); |
471 | | |
472 | | fn finish(self) -> Self::Output { |
473 | | (self.tendrils, self.errors) |
474 | | } |
475 | | } |
476 | | |
477 | | fn check_utf8(input: &[&[u8]], expected: &[&str], errs: usize) { |
478 | | let decoder = Utf8LossyDecoder::new(Accumulate::<NonAtomic>::new()); |
479 | | let (tendrils, errors) = decoder.from_iter(input.iter().cloned()); |
480 | | assert_eq!( |
481 | | expected, |
482 | | &*tendrils.iter().map(|t| &**t).collect::<Vec<_>>() |
483 | | ); |
484 | | assert_eq!(errs, errors.len()); |
485 | | } |
486 | | |
487 | | #[test] |
488 | | fn utf8() { |
489 | | check_utf8(&[], &[], 0); |
490 | | check_utf8(&[b""], &[], 0); |
491 | | check_utf8(&[b"xyz"], &["xyz"], 0); |
492 | | check_utf8(&[b"x", b"y", b"z"], &["x", "y", "z"], 0); |
493 | | |
494 | | check_utf8(&[b"xy\xEA\x99\xAEzw"], &["xy\u{a66e}zw"], 0); |
495 | | check_utf8(&[b"xy\xEA", b"\x99\xAEzw"], &["xy", "\u{a66e}z", "w"], 0); |
496 | | check_utf8(&[b"xy\xEA\x99", b"\xAEzw"], &["xy", "\u{a66e}z", "w"], 0); |
497 | | check_utf8( |
498 | | &[b"xy\xEA", b"\x99", b"\xAEzw"], |
499 | | &["xy", "\u{a66e}z", "w"], |
500 | | 0, |
501 | | ); |
502 | | check_utf8(&[b"\xEA", b"", b"\x99", b"", b"\xAE"], &["\u{a66e}"], 0); |
503 | | check_utf8( |
504 | | &[b"", b"\xEA", b"", b"\x99", b"", b"\xAE", b""], |
505 | | &["\u{a66e}"], |
506 | | 0, |
507 | | ); |
508 | | |
509 | | check_utf8( |
510 | | &[b"xy\xEA", b"\xFF", b"\x99\xAEz"], |
511 | | &["xy", "\u{fffd}", "\u{fffd}", "\u{fffd}", "\u{fffd}", "z"], |
512 | | 4, |
513 | | ); |
514 | | check_utf8( |
515 | | &[b"xy\xEA\x99", b"\xFFz"], |
516 | | &["xy", "\u{fffd}", "\u{fffd}", "z"], |
517 | | 2, |
518 | | ); |
519 | | |
520 | | check_utf8(&[b"\xC5\x91\xC5\x91\xC5\x91"], &["őőő"], 0); |
521 | | check_utf8( |
522 | | &[b"\xC5\x91", b"\xC5\x91", b"\xC5\x91"], |
523 | | &["ő", "ő", "ő"], |
524 | | 0, |
525 | | ); |
526 | | check_utf8( |
527 | | &[b"\xC5", b"\x91\xC5", b"\x91\xC5", b"\x91"], |
528 | | &["ő", "ő", "ő"], |
529 | | 0, |
530 | | ); |
531 | | check_utf8( |
532 | | &[b"\xC5", b"\x91\xff", b"\x91\xC5", b"\x91"], |
533 | | &["ő", "\u{fffd}", "\u{fffd}", "ő"], |
534 | | 2, |
535 | | ); |
536 | | |
537 | | // incomplete char at end of input |
538 | | check_utf8(&[b"\xC0"], &["\u{fffd}"], 1); |
539 | | check_utf8(&[b"\xEA\x99"], &["\u{fffd}"], 1); |
540 | | } |
541 | | |
542 | | #[cfg(feature = "encoding_rs")] |
543 | | fn check_decode( |
544 | | mut decoder: LossyDecoder<Accumulate<NonAtomic>>, |
545 | | input: &[&[u8]], |
546 | | expected: &str, |
547 | | errs: usize, |
548 | | ) { |
549 | | for x in input { |
550 | | decoder.process(x.to_tendril()); |
551 | | } |
552 | | let (tendrils, errors) = decoder.finish(); |
553 | | let mut tendril: Tendril<fmt::UTF8> = Tendril::new(); |
554 | | for t in tendrils { |
555 | | tendril.push_tendril(&t); |
556 | | } |
557 | | assert_eq!(expected, &*tendril); |
558 | | assert_eq!(errs, errors.len()); |
559 | | } |
560 | | |
561 | | #[cfg(feature = "encoding_rs")] |
562 | | pub type Tests = &'static [(&'static [&'static [u8]], &'static str, usize)]; |
563 | | |
564 | | #[cfg(feature = "encoding_rs")] |
565 | | const UTF_8: Tests = &[ |
566 | | (&[], "", 0), |
567 | | (&[b""], "", 0), |
568 | | (&[b"xyz"], "xyz", 0), |
569 | | (&[b"x", b"y", b"z"], "xyz", 0), |
570 | | (&[b"\xEA\x99\xAE"], "\u{a66e}", 0), |
571 | | (&[b"\xEA", b"\x99\xAE"], "\u{a66e}", 0), |
572 | | (&[b"\xEA\x99", b"\xAE"], "\u{a66e}", 0), |
573 | | (&[b"\xEA", b"\x99", b"\xAE"], "\u{a66e}", 0), |
574 | | (&[b"\xEA", b"", b"\x99", b"", b"\xAE"], "\u{a66e}", 0), |
575 | | ( |
576 | | &[b"", b"\xEA", b"", b"\x99", b"", b"\xAE", b""], |
577 | | "\u{a66e}", |
578 | | 0, |
579 | | ), |
580 | | (&[b"xy\xEA", b"\x99\xAEz"], "xy\u{a66e}z", 0), |
581 | | ( |
582 | | &[b"xy\xEA", b"\xFF", b"\x99\xAEz"], |
583 | | "xy\u{fffd}\u{fffd}\u{fffd}\u{fffd}z", |
584 | | 4, |
585 | | ), |
586 | | (&[b"xy\xEA\x99", b"\xFFz"], "xy\u{fffd}\u{fffd}z", 2), |
587 | | // incomplete char at end of input |
588 | | (&[b"\xC0"], "\u{fffd}", 1), |
589 | | (&[b"\xEA\x99"], "\u{fffd}", 1), |
590 | | ]; |
591 | | |
592 | | #[cfg(feature = "encoding_rs")] |
593 | | #[test] |
594 | | fn decode_utf8_encoding_rs() { |
595 | | for &(input, expected, errs) in UTF_8 { |
596 | | let decoder = LossyDecoder::new_encoding_rs(enc_rs::UTF_8, Accumulate::new()); |
597 | | check_decode(decoder, input, expected, errs); |
598 | | } |
599 | | } |
600 | | |
601 | | #[cfg(feature = "encoding_rs")] |
602 | | const KOI8_U: Tests = &[ |
603 | | (&[b"\xfc\xce\xc5\xd2\xc7\xc9\xd1"], "Энергия", 0), |
604 | | (&[b"\xfc\xce", b"\xc5\xd2\xc7\xc9\xd1"], "Энергия", 0), |
605 | | (&[b"\xfc\xce", b"\xc5\xd2\xc7", b"\xc9\xd1"], "Энергия", 0), |
606 | | ( |
607 | | &[b"\xfc\xce", b"", b"\xc5\xd2\xc7", b"\xc9\xd1", b""], |
608 | | "Энергия", |
609 | | 0, |
610 | | ), |
611 | | ]; |
612 | | |
613 | | #[cfg(feature = "encoding_rs")] |
614 | | #[test] |
615 | | fn decode_koi8_u_encoding_rs() { |
616 | | for &(input, expected, errs) in KOI8_U { |
617 | | let decoder = LossyDecoder::new_encoding_rs(enc_rs::KOI8_U, Accumulate::new()); |
618 | | check_decode(decoder, input, expected, errs); |
619 | | } |
620 | | } |
621 | | |
622 | | #[cfg(feature = "encoding_rs")] |
623 | | const WINDOWS_949: Tests = &[ |
624 | | (&[], "", 0), |
625 | | (&[b""], "", 0), |
626 | | (&[b"\xbe\xc8\xb3\xe7"], "안녕", 0), |
627 | | (&[b"\xbe", b"\xc8\xb3\xe7"], "안녕", 0), |
628 | | (&[b"\xbe", b"", b"\xc8\xb3\xe7"], "안녕", 0), |
629 | | ( |
630 | | &[b"\xbe\xc8\xb3\xe7\xc7\xcf\xbc\xbc\xbf\xe4"], |
631 | | "안녕하세요", |
632 | | 0, |
633 | | ), |
634 | | (&[b"\xbe\xc8\xb3\xe7\xc7"], "안녕\u{fffd}", 1), |
635 | | (&[b"\xbe", b"", b"\xc8\xb3"], "안\u{fffd}", 1), |
636 | | (&[b"\xbe\x28\xb3\xe7"], "\u{fffd}(녕", 1), |
637 | | ]; |
638 | | |
639 | | #[cfg(feature = "encoding_rs")] |
640 | | #[test] |
641 | | fn decode_windows_949_encoding_rs() { |
642 | | for &(input, expected, errs) in WINDOWS_949 { |
643 | | let decoder = LossyDecoder::new_encoding_rs(enc_rs::EUC_KR, Accumulate::new()); |
644 | | check_decode(decoder, input, expected, errs); |
645 | | } |
646 | | } |
647 | | |
648 | | #[test] |
649 | | fn read_from() { |
650 | | let decoder = Utf8LossyDecoder::new(Accumulate::<NonAtomic>::new()); |
651 | | let mut bytes: &[u8] = b"foo\xffbar"; |
652 | | let (tendrils, errors) = decoder.read_from(&mut bytes).unwrap(); |
653 | | assert_eq!( |
654 | | &*tendrils.iter().map(|t| &**t).collect::<Vec<_>>(), |
655 | | &["foo", "\u{FFFD}", "bar"] |
656 | | ); |
657 | | assert_eq!(errors, &["invalid byte sequence"]); |
658 | | } |
659 | | } |