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