Skip to main content

tendril/
stream.rs

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
9use crate::utf8_decode::{decode_utf8, DecodeError, REPLACEMENT_CHARACTER};
10use crate::{fmt, IncompleteUtf8};
11use crate::{Atomic, Atomicity, Tendril};
12
13use std::borrow::Cow;
14use std::fs::File;
15use std::io;
16use std::marker::PhantomData;
17use std::path::Path;
18
19#[cfg(feature = "encoding_rs")]
20use 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
30pub trait TendrilSink<F, A = Atomic>
31where
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    fn one<T>(mut self, t: T) -> Self::Output
49    where
50        Self: Sized,
51        T: Into<Tendril<F, A>>,
52    {
53        self.process(t.into());
54        self.finish()
55    }
56
57    /// Consume an iterator of tendrils, processing each item, then finish.
58    fn from_iter<I>(mut self, i: I) -> Self::Output
59    where
60        Self: Sized,
61        I: IntoIterator,
62        I::Item: Into<Tendril<F, A>>,
63    {
64        for t in i {
65            self.process(t.into())
66        }
67        self.finish()
68    }
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    fn read_from<R>(mut self, r: &mut R) -> io::Result<Self::Output>
73    where
74        Self: Sized,
75        R: io::Read,
76        F: fmt::SliceFormat<Slice = [u8]>,
77    {
78        const BUFFER_SIZE: u32 = 4 * 1024;
79        loop {
80            let mut tendril = Tendril::<fmt::Bytes, A>::new();
81            tendril.extend_with_byte(BUFFER_SIZE, 0);
82            loop {
83                match r.read(&mut tendril) {
84                    Ok(0) => return Ok(self.finish()),
85                    Ok(n) => {
86                        tendril.pop_back(BUFFER_SIZE - n as u32);
87                        self.process(unsafe { tendril.reinterpret_without_validating() });
88                        break;
89                    },
90                    Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {},
91                    Err(e) => return Err(e),
92                }
93            }
94        }
95    }
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    fn from_file<P>(self, path: P) -> io::Result<Self::Output>
100    where
101        Self: Sized,
102        P: AsRef<Path>,
103        F: fmt::SliceFormat<Slice = [u8]>,
104    {
105        self.read_from(&mut File::open(path)?)
106    }
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.
115pub struct Utf8LossyDecoder<Sink, A = Atomic>
116where
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
125impl<Sink, A> Utf8LossyDecoder<Sink, A>
126where
127    Sink: TendrilSink<fmt::UTF8, A>,
128    A: Atomicity,
129{
130    /// Create a new incremental UTF-8 decoder.
131    #[inline]
132    pub fn new(inner_sink: Sink) -> Self {
133        Utf8LossyDecoder {
134            inner_sink,
135            incomplete: None,
136            marker: PhantomData,
137        }
138    }
139}
140
141impl<Sink, A> TendrilSink<fmt::Bytes, A> for Utf8LossyDecoder<Sink, A>
142where
143    Sink: TendrilSink<fmt::UTF8, A>,
144    A: Atomicity,
145{
146    #[inline]
147    fn process(&mut self, mut bytes: Tendril<fmt::Bytes, A>) {
148        // FIXME: remove take() and map() when non-lexical borrows are stable.
149        if let Some(mut incomplete) = self.incomplete.take() {
150            let resume_at = incomplete
151                .try_to_complete_codepoint(&bytes)
152                .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            match resume_at {
166                None => {
167                    self.incomplete = Some(incomplete);
168                    return;
169                },
170                Some(resume_at) => bytes.pop_front(resume_at as u32),
171            }
172        }
173        while !bytes.is_empty() {
174            let unborrowed_result = match decode_utf8(&bytes) {
175                Ok(s) => {
176                    debug_assert!(s.as_ptr() == bytes.as_ptr());
177                    debug_assert!(s.len() == bytes.len());
178                    Ok(())
179                },
180                Err(DecodeError::Invalid {
181                    valid_prefix,
182                    invalid_sequence,
183                    ..
184                }) => {
185                    debug_assert!(valid_prefix.as_ptr() == bytes.as_ptr());
186                    debug_assert!(valid_prefix.len() <= bytes.len());
187                    Err((
188                        valid_prefix.len(),
189                        Err(valid_prefix.len() + invalid_sequence.len()),
190                    ))
191                },
192                Err(DecodeError::Incomplete {
193                    valid_prefix,
194                    incomplete_suffix,
195                }) => {
196                    debug_assert!(valid_prefix.as_ptr() == bytes.as_ptr());
197                    debug_assert!(valid_prefix.len() <= bytes.len());
198                    Err((valid_prefix.len(), Ok(incomplete_suffix)))
199                },
200            };
201            match unborrowed_result {
202                Ok(()) => {
203                    unsafe {
204                        self.inner_sink
205                            .process(bytes.reinterpret_without_validating())
206                    }
207                    return;
208                },
209                Err((valid_len, and_then)) => {
210                    if valid_len > 0 {
211                        let subtendril = bytes.subtendril(0, valid_len as u32);
212                        unsafe {
213                            self.inner_sink
214                                .process(subtendril.reinterpret_without_validating())
215                        }
216                    }
217                    match and_then {
218                        Ok(incomplete) => {
219                            self.incomplete = Some(incomplete);
220                            return;
221                        },
222                        Err(offset) => {
223                            self.inner_sink.error("invalid byte sequence".into());
224                            self.inner_sink
225                                .process(Tendril::from_slice(REPLACEMENT_CHARACTER));
226                            bytes.pop_front(offset as u32);
227                        },
228                    }
229                },
230            }
231        }
232    }
233
234    #[inline]
235    fn error(&mut self, desc: Cow<'static, str>) {
236        self.inner_sink.error(desc);
237    }
238
239    type Output = Sink::Output;
240
241    #[inline]
242    fn finish(mut self) -> Sink::Output {
243        if self.incomplete.is_some() {
244            self.inner_sink
245                .error("incomplete byte sequence at end of stream".into());
246            self.inner_sink
247                .process(Tendril::from_slice(REPLACEMENT_CHARACTER));
248        }
249        self.inner_sink.finish()
250    }
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")]
259pub struct LossyDecoder<Sink, A = Atomic>
260where
261    Sink: TendrilSink<fmt::UTF8, A>,
262    A: Atomicity,
263{
264    inner: LossyDecoderInner<Sink, A>,
265}
266
267#[cfg(feature = "encoding_rs")]
268enum LossyDecoderInner<Sink, A>
269where
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")]
279impl<Sink, A> LossyDecoder<Sink, A>
280where
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")]
339impl<Sink, A> TendrilSink<fmt::Bytes, A> for LossyDecoder<Sink, A>
340where
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")]
383fn 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)]
424mod test {
425    use super::{TendrilSink, Utf8LossyDecoder};
426    use crate::fmt;
427    use crate::{Atomic, 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<Atomic>>,
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}