Coverage Report

Created: 2026-10-10 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}