Skip to main content

encoding_rs/
macros.rs

1// Copyright Mozilla Foundation. See the COPYRIGHT
2// file at the top-level directory of this distribution.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10macro_rules! decoder_function {
11    ($preamble:block,
12     $loop_preamble:block,
13     $eof:block,
14     $body:block,
15     $slf:ident,
16     $src_consumed:ident,
17     $dest:ident,
18     $source:ident,
19     $b:ident,
20     $destination_handle:ident,
21     $unread_handle:ident,
22     $destination_check:ident,
23     $name:ident,
24     $code_unit:ty,
25     $dest_struct:ident) => (
26    #[inline(always)]
27    pub fn $name(&mut $slf,
28                 src: &[u8],
29                 dst: &mut [$code_unit],
30                 last: bool)
31                 -> (DecoderResult, usize, usize) {
32        let mut $source = ByteSource::new(src);
33        let mut $dest = $dest_struct::new(dst);
34        loop { // TODO: remove this loop
35            {
36                // Start non-boilerplate
37                $preamble
38                // End non-boilerplate
39            }
40            loop {
41                {
42                    $loop_preamble
43                }
44                match $source.check_available() {
45                    Space::Full($src_consumed) => {
46                        if last {
47                            // Start non-boilerplate
48                            $eof
49                            // End non-boilerplate
50                        }
51                        return (DecoderResult::InputEmpty, $src_consumed, $dest.written());
52                    }
53                    Space::Available(source_handle) => {
54                        match $dest.$destination_check() {
55                            Space::Full(dst_written) => {
56                                return (DecoderResult::OutputFull,
57                                        source_handle.consumed(),
58                                        dst_written);
59                            }
60                            Space::Available($destination_handle) => {
61                                let ($b, $unread_handle) = source_handle.read();
62                                // Start non-boilerplate
63                                $body
64                                // End non-boilerplate
65                            }
66                        }
67                    }
68                }
69            }
70        }
71    });
72}
73
74macro_rules! decoder_functions {
75    (
76        preamble = $preamble:block,
77        loop_preamble = $loop_preamble:block,
78        eof = $eof:block,
79        body = $body:block,
80        self = $slf:ident,
81        src_consumed = $src_consumed:ident,
82        dest = $dest:ident,
83        source = $source:ident,
84        byte = $b:ident,
85        destination_handle = $destination_handle:ident,
86        unread_handle = $unread_handle:ident,
87        destination_check = $destination_check:ident
88    ) => {
89        decoder_function!(
90            $preamble,
91            $loop_preamble,
92            $eof,
93            $body,
94            $slf,
95            $src_consumed,
96            $dest,
97            $source,
98            $b,
99            $destination_handle,
100            $unread_handle,
101            $destination_check,
102            decode_to_utf8_raw,
103            u8,
104            Utf8Destination
105        );
106        decoder_function!(
107            $preamble,
108            $loop_preamble,
109            $eof,
110            $body,
111            $slf,
112            $src_consumed,
113            $dest,
114            $source,
115            $b,
116            $destination_handle,
117            $unread_handle,
118            $destination_check,
119            decode_to_utf16_raw,
120            u16,
121            Utf16Destination
122        );
123    };
124}
125
126macro_rules! ascii_compatible_two_byte_decoder_function {
127    ($lead:block,
128     $trail:block,
129     $slf:ident,
130     $non_ascii:ident,
131     $byte:ident,
132     $lead_minus_offset:ident,
133     $unread_handle_trail:ident,
134     $source:ident,
135     $handle:ident,
136     $outermost:tt,
137     $copy_ascii:ident,
138     $destination_check:ident,
139     $name:ident,
140     $code_unit:ty,
141     $dest_struct:ident,
142     $ascii_punctuation:expr) => (
143    #[inline(always)]
144    pub fn $name(&mut $slf,
145                 src: &[u8],
146                 dst: &mut [$code_unit],
147                 last: bool)
148                 -> (DecoderResult, usize, usize) {
149        let mut $source = ByteSource::new(src);
150        let mut dest_prolog = $dest_struct::new(dst);
151        let dest = match $slf.lead {
152            Some(lead) => {
153                let $lead_minus_offset = lead;
154                // Since we don't have `goto` we could use to jump into the trail
155                // handling part of the main loop, we need to repeat trail handling
156                // here.
157                match $source.check_available() {
158                    Space::Full(src_consumed_prolog) => {
159                        if last {
160                            $slf.lead = None;
161                            return (DecoderResult::Malformed(1, 0),
162                                    src_consumed_prolog,
163                                    dest_prolog.written());
164                        }
165                        return (DecoderResult::InputEmpty, src_consumed_prolog, dest_prolog.written());
166                    }
167                    Space::Available(source_handle_prolog) => {
168                        match dest_prolog.$destination_check() {
169                            Space::Full(dst_written_prolog) => {
170                                return (DecoderResult::OutputFull,
171                                    source_handle_prolog.consumed(),
172                                    dst_written_prolog);
173                            }
174                            Space::Available($handle) => {
175                                $slf.lead = None;
176                                let ($byte, $unread_handle_trail) = source_handle_prolog.read();
177                                // Start non-boilerplate
178                                $trail
179                                // End non-boilerplate
180                            }
181                        }
182                    }
183                }
184            },
185            None => {
186                &mut dest_prolog
187            }
188        };
189        $outermost: loop {
190            match dest.$copy_ascii(&mut $source) {
191                CopyAsciiResult::Stop(ret) => return ret,
192                CopyAsciiResult::GoOn((mut $non_ascii, mut $handle)) => {
193                    'middle: loop {
194                        let dest_again = {
195                            let $lead_minus_offset = {
196                                // Start non-boilerplate
197                                $lead
198                                // End non-boilerplate
199                            };
200                            match $source.check_available() {
201                                Space::Full(src_consumed_trail) => {
202                                    if last {
203                                        return (DecoderResult::Malformed(1, 0),
204                                                src_consumed_trail,
205                                                $handle.written());
206                                    }
207                                    $slf.lead = Some($lead_minus_offset);
208                                    return (DecoderResult::InputEmpty,
209                                            src_consumed_trail,
210                                            $handle.written());
211                                }
212                                Space::Available(source_handle_trail) => {
213                                    let ($byte, $unread_handle_trail) = source_handle_trail.read();
214                                    // Start non-boilerplate
215                                    $trail
216                                    // End non-boilerplate
217                                }
218                            }
219                        };
220                        match $source.check_available() {
221                            Space::Full(src_consumed) => {
222                                return (DecoderResult::InputEmpty,
223                                        src_consumed,
224                                        dest_again.written());
225                            }
226                            Space::Available(source_handle) => {
227                                match dest_again.$destination_check() {
228                                    Space::Full(dst_written) => {
229                                        return (DecoderResult::OutputFull,
230                                                source_handle.consumed(),
231                                                dst_written);
232                                    }
233                                    Space::Available(mut destination_handle) => {
234                                        let (mut b, unread_handle) = source_handle.read();
235                                        let source_again = unread_handle.commit();
236                                        'innermost: loop {
237                                            if b > 127 {
238                                                $non_ascii = b;
239                                                $handle = destination_handle;
240                                                continue 'middle;
241                                            }
242                                            // Testing on Haswell says that we should write the
243                                            // byte unconditionally instead of trying to unread it
244                                            // to make it part of the next SIMD stride.
245                                            let dest_again_again =
246                                                destination_handle.write_ascii(b);
247                                            if $ascii_punctuation && b < 60 {
248                                                // We've got punctuation
249                                                match source_again.check_available() {
250                                                    Space::Full(src_consumed_again) => {
251                                                        return (DecoderResult::InputEmpty,
252                                                                src_consumed_again,
253                                                                dest_again_again.written());
254                                                    }
255                                                    Space::Available(source_handle_again) => {
256                                                        match dest_again_again.$destination_check() {
257                                                            Space::Full(dst_written_again) => {
258                                                                return (DecoderResult::OutputFull,
259                                                                        source_handle_again.consumed(),
260                                                                        dst_written_again);
261                                                            }
262                                                            Space::Available(destination_handle_again) => {
263                                                                {
264                                                                    let (b_again, unread_handle_again) =
265                                                                        source_handle_again.read();
266                                                                    unread_handle_again.commit();
267                                                                    b = b_again;
268                                                                    destination_handle = destination_handle_again;
269                                                                    continue 'innermost;
270                                                                }
271                                                            }
272                                                        }
273                                                    }
274                                                }
275                                            }
276                                            // We've got markup or ASCII text
277                                            continue $outermost;
278                                        }
279                                    }
280                                }
281                            }
282                        }
283                    }
284                }
285            }
286        }
287    });
288}
289
290macro_rules! ascii_compatible_two_byte_decoder_functions {
291    (
292        lead = $lead:block,
293        trail = $trail:block,
294        self = $slf:ident,
295        non_ascii = $non_ascii:ident,
296        byte = $byte:ident,
297        lead_minus_offset = $lead_minus_offset:ident,
298        unread_handle_trail = $unread_handle_trail:ident,
299        source = $source:ident,
300        handle = $handle:ident,
301        outermost = $outermost:tt,
302        copy_ascii = $copy_ascii:ident,
303        destination_check = $destination_check:ident,
304        ascii_punctuation = $ascii_punctuation:expr
305    ) => {
306        ascii_compatible_two_byte_decoder_function!(
307            $lead,
308            $trail,
309            $slf,
310            $non_ascii,
311            $byte,
312            $lead_minus_offset,
313            $unread_handle_trail,
314            $source,
315            $handle,
316            $outermost,
317            $copy_ascii,
318            $destination_check,
319            decode_to_utf8_raw,
320            u8,
321            Utf8Destination,
322            $ascii_punctuation
323        );
324        ascii_compatible_two_byte_decoder_function!(
325            $lead,
326            $trail,
327            $slf,
328            $non_ascii,
329            $byte,
330            $lead_minus_offset,
331            $unread_handle_trail,
332            $source,
333            $handle,
334            $outermost,
335            $copy_ascii,
336            $destination_check,
337            decode_to_utf16_raw,
338            u16,
339            Utf16Destination,
340            $ascii_punctuation
341        );
342    };
343}
344
345macro_rules! gb18030_decoder_function {
346    ($first_body:block,
347     $second_body:block,
348     $third_body:block,
349     $fourth_body:block,
350     $slf:ident,
351     $non_ascii:ident,
352     $first_minus_offset:ident,
353     $second:ident,
354     $second_minus_offset:ident,
355     $unread_handle_second:ident,
356     $third:ident,
357     $third_minus_offset:ident,
358     $unread_handle_third:ident,
359     $fourth:ident,
360     $fourth_minus_offset:ident,
361     $unread_handle_fourth:ident,
362     $source:ident,
363     $handle:ident,
364     $outermost:tt,
365     $name:ident,
366     $code_unit:ty,
367     $dest_struct:ident) => (
368    #[inline(always)]
369    #[allow(clippy::never_loop)]
370    pub fn $name(&mut $slf,
371                 src: &[u8],
372                 dst: &mut [$code_unit],
373                 last: bool)
374                 -> (DecoderResult, usize, usize) {
375        let mut $source = ByteSource::new(src);
376        let mut dest = $dest_struct::new(dst);
377        {
378            if let Some(ascii) = $slf.pending_ascii {
379                match dest.check_space_bmp() {
380                    Space::Full(_) => {
381                        return (DecoderResult::OutputFull, 0, 0);
382                    }
383                    Space::Available(pending_ascii_handle) => {
384                        $slf.pending_ascii = None;
385                        pending_ascii_handle.write_ascii(ascii);
386                    }
387                }
388            }
389        }
390        while !$slf.pending.is_none() {
391            match $source.check_available() {
392                Space::Full(src_consumed) => {
393                    if last {
394                        // Start non-boilerplate
395                        let count = $slf.pending.count();
396                        $slf.pending = Gb18030Pending::None;
397                        return (DecoderResult::Malformed(count as u8, 0),
398                                src_consumed,
399                                dest.written());
400                        // End non-boilerplate
401                    }
402                    return (DecoderResult::InputEmpty, src_consumed, dest.written());
403                }
404                Space::Available(source_handle) => {
405                    match dest.check_space_astral() {
406                        Space::Full(dst_written) => {
407                            return (DecoderResult::OutputFull,
408                                    source_handle.consumed(),
409                                    dst_written);
410                        }
411                        Space::Available($handle) => {
412                            let (byte, unread_handle) = source_handle.read();
413                            match $slf.pending {
414                                Gb18030Pending::One($first_minus_offset) => {
415                                    $slf.pending = Gb18030Pending::None;
416                                    let $second = byte;
417                                    let $unread_handle_second = unread_handle;
418                                    // If second is between 0x40 and 0x7E,
419                                    // inclusive, subtract offset 0x40. Else if
420                                    // second is between 0x80 and 0xFE, inclusive,
421                                    // subtract offset 0x41. In both cases,
422                                    // handle as a two-byte sequence.
423                                    // Else if second is between 0x30 and 0x39,
424                                    // inclusive, subtract offset 0x30 and
425                                    // handle as a four-byte sequence.
426                                    let $second_minus_offset = $second.wrapping_sub(0x30);
427                                    // It's not optimal to do this check first,
428                                    // but this results in more readable code.
429                                    if $second_minus_offset > (0x39 - 0x30) {
430                                        // Start non-boilerplate
431                                        $second_body
432                                        // End non-boilerplate
433                                    } else {
434                                        // Four-byte!
435                                        $slf.pending = Gb18030Pending::Two($first_minus_offset,
436                                                                           $second_minus_offset);
437                                        $handle.commit()
438                                    }
439                                }
440                                Gb18030Pending::Two($first_minus_offset, $second_minus_offset) => {
441                                    $slf.pending = Gb18030Pending::None;
442                                    let $third = byte;
443                                    let $unread_handle_third = unread_handle;
444                                    let $third_minus_offset = {
445                                        // Start non-boilerplate
446                                        $third_body
447                                        // End non-boilerplate
448                                    };
449                                    $slf.pending = Gb18030Pending::Three($first_minus_offset,
450                                                                         $second_minus_offset,
451                                                                         $third_minus_offset);
452                                    $handle.commit()
453                                }
454                                Gb18030Pending::Three($first_minus_offset,
455                                                      $second_minus_offset,
456                                                      $third_minus_offset) => {
457                                    $slf.pending = Gb18030Pending::None;
458                                    let $fourth = byte;
459                                    let $unread_handle_fourth = unread_handle;
460                                    // Start non-boilerplate
461                                    $fourth_body
462                                    // End non-boilerplate
463                                }
464                                Gb18030Pending::None => unreachable!("Checked in loop condition"),
465                            };
466                        }
467                    }
468                }
469            }
470        }
471        $outermost: loop {
472            match dest.copy_ascii_from_check_space_astral(&mut $source) {
473                CopyAsciiResult::Stop(ret) => return ret,
474                CopyAsciiResult::GoOn((mut $non_ascii, mut $handle)) => {
475                    'middle: loop {
476                        let dest_again = {
477                            let $first_minus_offset = {
478                                // Start non-boilerplate
479                                $first_body
480                                // End non-boilerplate
481                            };
482                            match $source.check_available() {
483                                Space::Full(src_consumed_trail) => {
484                                    if last {
485                                        return (DecoderResult::Malformed(1, 0),
486                                                src_consumed_trail,
487                                                $handle.written());
488                                    }
489                                    $slf.pending = Gb18030Pending::One($first_minus_offset);
490                                    return (DecoderResult::InputEmpty,
491                                            src_consumed_trail,
492                                            $handle.written());
493                                }
494                                Space::Available(source_handle_trail) => {
495                                    let ($second, $unread_handle_second) = source_handle_trail.read();
496                                    // Start non-boilerplate
497                                    // If second is between 0x40 and 0x7E,
498                                    // inclusive, subtract offset 0x40. Else if
499                                    // second is between 0x80 and 0xFE, inclusive,
500                                    // subtract offset 0x41. In both cases,
501                                    // handle as a two-byte sequence.
502                                    // Else if second is between 0x30 and 0x39,
503                                    // inclusive, subtract offset 0x30 and
504                                    // handle as a four-byte sequence.
505                                    let $second_minus_offset = $second.wrapping_sub(0x30);
506                                    // It's not optimal to do this check first,
507                                    // but this results in more readable code.
508                                    if $second_minus_offset > (0x39 - 0x30) {
509                                        // Start non-boilerplate
510                                        $second_body
511                                        // End non-boilerplate
512                                    } else {
513                                        // Four-byte!
514                                        match $unread_handle_second.commit().check_available() {
515                                            Space::Full(src_consumed_third) => {
516                                                if last {
517                                                    return (DecoderResult::Malformed(2, 0),
518                                                            src_consumed_third,
519                                                            $handle.written());
520                                                }
521                                                $slf.pending =
522                                                    Gb18030Pending::Two($first_minus_offset,
523                                                                        $second_minus_offset);
524                                                return (DecoderResult::InputEmpty,
525                                                        src_consumed_third,
526                                                        $handle.written());
527                                            }
528                                            Space::Available(source_handle_third) => {
529                                                let ($third, $unread_handle_third) =
530                                                    source_handle_third.read();
531                                                let $third_minus_offset = {
532                                                    // Start non-boilerplate
533                                                    $third_body
534                                                    // End non-boilerplate
535                                                };
536                                                match $unread_handle_third.commit()
537                                                                         .check_available() {
538                                                    Space::Full(src_consumed_fourth) => {
539                                                        if last {
540                                                            return (DecoderResult::Malformed(3, 0),
541                                                                    src_consumed_fourth,
542                                                                    $handle.written());
543                                                        }
544                                                        $slf.pending = Gb18030Pending::Three($first_minus_offset, $second_minus_offset, $third_minus_offset);
545                                                        return (DecoderResult::InputEmpty,
546                                                                src_consumed_fourth,
547                                                                $handle.written());
548                                                    }
549                                                    Space::Available(source_handle_fourth) => {
550                                                        let ($fourth, $unread_handle_fourth) =
551                                                            source_handle_fourth.read();
552                                                        // Start non-boilerplate
553                                                        $fourth_body
554                                                        // End non-boilerplate
555                                                    }
556                                                }
557                                            }
558                                        }
559                                    }
560                                    // End non-boilerplate
561                                }
562                            }
563                        };
564                        match $source.check_available() {
565                            Space::Full(src_consumed) => {
566                                return (DecoderResult::InputEmpty,
567                                        src_consumed,
568                                        dest_again.written());
569                            }
570                            Space::Available(source_handle) => {
571                                match dest_again.check_space_astral() {
572                                    Space::Full(dst_written) => {
573                                        return (DecoderResult::OutputFull,
574                                                source_handle.consumed(),
575                                                dst_written);
576                                    }
577                                    Space::Available(destination_handle) => {
578                                        let (b, unread_handle) = source_handle.read();
579                                        unread_handle.commit();
580                                        loop {
581                                            if b > 127 {
582                                                $non_ascii = b;
583                                                $handle = destination_handle;
584                                                continue 'middle;
585                                            }
586                                            // Testing on Haswell says that we should write the
587                                            // byte unconditionally instead of trying to unread it
588                                            // to make it part of the next SIMD stride.
589                                            destination_handle.write_ascii(b);
590                                            // We've got markup or ASCII text
591                                            continue $outermost;
592                                        }
593                                    }
594                                }
595                            }
596                        }
597                    }
598                }
599            }
600        }
601    });
602}
603
604macro_rules! gb18030_decoder_functions {
605    (
606        $first_body:block,
607        $second_body:block,
608        $third_body:block,
609        $fourth_body:block,
610        $slf:ident,
611        $non_ascii:ident,
612        $first_minus_offset:ident,
613        $second:ident,
614        $second_minus_offset:ident,
615        $unread_handle_second:ident,
616        $third:ident,
617        $third_minus_offset:ident,
618        $unread_handle_third:ident,
619        $fourth:ident,
620        $fourth_minus_offset:ident,
621        $unread_handle_fourth:ident,
622        $source:ident,
623        $handle:ident,
624        $outermost:tt
625    ) => {
626        gb18030_decoder_function!(
627            $first_body,
628            $second_body,
629            $third_body,
630            $fourth_body,
631            $slf,
632            $non_ascii,
633            $first_minus_offset,
634            $second,
635            $second_minus_offset,
636            $unread_handle_second,
637            $third,
638            $third_minus_offset,
639            $unread_handle_third,
640            $fourth,
641            $fourth_minus_offset,
642            $unread_handle_fourth,
643            $source,
644            $handle,
645            $outermost,
646            decode_to_utf8_raw,
647            u8,
648            Utf8Destination
649        );
650        gb18030_decoder_function!(
651            $first_body,
652            $second_body,
653            $third_body,
654            $fourth_body,
655            $slf,
656            $non_ascii,
657            $first_minus_offset,
658            $second,
659            $second_minus_offset,
660            $unread_handle_second,
661            $third,
662            $third_minus_offset,
663            $unread_handle_third,
664            $fourth,
665            $fourth_minus_offset,
666            $unread_handle_fourth,
667            $source,
668            $handle,
669            $outermost,
670            decode_to_utf16_raw,
671            u16,
672            Utf16Destination
673        );
674    };
675}
676
677macro_rules! euc_jp_decoder_function {
678    ($jis0802_trail_body:block,
679     $jis0812_lead_body:block,
680     $jis0812_trail_body:block,
681     $half_width_katakana_body:block,
682     $slf:ident,
683     $non_ascii:ident,
684     $jis0208_lead_minus_offset:ident,
685     $byte:ident,
686     $unread_handle_trail:ident,
687     $jis0212_lead_minus_offset:ident,
688     $lead:ident,
689     $unread_handle_jis0212:ident,
690     $source:ident,
691     $handle:ident,
692     $name:ident,
693     $code_unit:ty,
694     $dest_struct:ident) => (
695    #[inline(always)]
696    #[allow(clippy::never_loop)]
697    pub fn $name(&mut $slf,
698                 src: &[u8],
699                 dst: &mut [$code_unit],
700                 last: bool)
701                 -> (DecoderResult, usize, usize) {
702        let mut $source = ByteSource::new(src);
703        let mut dest = $dest_struct::new(dst);
704        while !$slf.pending.is_none() {
705            match $source.check_available() {
706                Space::Full(src_consumed) => {
707                    if last {
708                        // Start non-boilerplate
709                        let count = $slf.pending.count();
710                        $slf.pending = EucJpPending::None;
711                        return (DecoderResult::Malformed(count as u8, 0),
712                                src_consumed,
713                                dest.written());
714                        // End non-boilerplate
715                    }
716                    return (DecoderResult::InputEmpty, src_consumed, dest.written());
717                }
718                Space::Available(source_handle) => {
719                    match dest.check_space_bmp() {
720                        Space::Full(dst_written) => {
721                            return (DecoderResult::OutputFull,
722                                    source_handle.consumed(),
723                                    dst_written);
724                        }
725                        Space::Available($handle) => {
726                            let ($byte, $unread_handle_trail) = source_handle.read();
727                            match $slf.pending {
728                                EucJpPending::Jis0208Lead($jis0208_lead_minus_offset) => {
729                                    $slf.pending = EucJpPending::None;
730                                    // Start non-boilerplate
731                                    $jis0802_trail_body
732                                    // End non-boilerplate
733                                }
734                                EucJpPending::Jis0212Shift => {
735                                    $slf.pending = EucJpPending::None;
736                                    let $lead = $byte;
737                                    let $unread_handle_jis0212 = $unread_handle_trail;
738                                    let $jis0212_lead_minus_offset = {
739                                        // Start non-boilerplate
740                                        $jis0812_lead_body
741                                        // End non-boilerplate
742                                    };
743                                    $slf.pending =
744                                        EucJpPending::Jis0212Lead($jis0212_lead_minus_offset);
745                                    $handle.commit()
746                                }
747                                EucJpPending::Jis0212Lead($jis0212_lead_minus_offset) => {
748                                    $slf.pending = EucJpPending::None;
749                                    // Start non-boilerplate
750                                    $jis0812_trail_body
751                                    // End non-boilerplate
752                                }
753                                EucJpPending::HalfWidthKatakana => {
754                                    $slf.pending = EucJpPending::None;
755                                    // Start non-boilerplate
756                                    $half_width_katakana_body
757                                    // End non-boilerplate
758                                }
759                                EucJpPending::None => unreachable!("Checked in loop condition"),
760                            };
761                        }
762                    }
763                }
764            }
765        }
766        'outermost: loop {
767            match dest.copy_ascii_from_check_space_bmp(&mut $source) {
768                CopyAsciiResult::Stop(ret) => return ret,
769                CopyAsciiResult::GoOn((mut $non_ascii, mut $handle)) => {
770                    'middle: loop {
771                        let dest_again = {
772                            // If lead is between 0xA1 and 0xFE, inclusive,
773                            // subtract 0xA1. Else if lead is 0x8E, handle the
774                            // next byte as half-width Katakana. Else if lead is
775                            // 0x8F, expect JIS 0212.
776                            let $jis0208_lead_minus_offset = $non_ascii.wrapping_sub(0xA1);
777                            if $jis0208_lead_minus_offset <= (0xFE - 0xA1) {
778                                // JIS 0208
779                                match $source.check_available() {
780                                    Space::Full(src_consumed_trail) => {
781                                        if last {
782                                            return (DecoderResult::Malformed(1, 0),
783                                                    src_consumed_trail,
784                                                    $handle.written());
785                                        }
786                                        $slf.pending =
787                                            EucJpPending::Jis0208Lead($jis0208_lead_minus_offset);
788                                        return (DecoderResult::InputEmpty,
789                                                src_consumed_trail,
790                                                $handle.written());
791                                    }
792                                    Space::Available(source_handle_trail) => {
793                                        let ($byte, $unread_handle_trail) =
794                                            source_handle_trail.read();
795                                        // Start non-boilerplate
796                                        $jis0802_trail_body
797                                        // End non-boilerplate
798                                    }
799                                }
800                            } else if $non_ascii == 0x8F {
801                                match $source.check_available() {
802                                    Space::Full(src_consumed_jis0212) => {
803                                        if last {
804                                            return (DecoderResult::Malformed(1, 0),
805                                                    src_consumed_jis0212,
806                                                    $handle.written());
807                                        }
808                                        $slf.pending = EucJpPending::Jis0212Shift;
809                                        return (DecoderResult::InputEmpty,
810                                                src_consumed_jis0212,
811                                                $handle.written());
812                                    }
813                                    Space::Available(source_handle_jis0212) => {
814                                        let ($lead, $unread_handle_jis0212) =
815                                            source_handle_jis0212.read();
816                                        let $jis0212_lead_minus_offset = {
817                                            // Start non-boilerplate
818                                            $jis0812_lead_body
819                                            // End non-boilerplate
820                                        };
821                                        match $unread_handle_jis0212.commit().check_available() {
822                                            Space::Full(src_consumed_trail) => {
823                                                if last {
824                                                    return (DecoderResult::Malformed(2, 0),
825                                                            src_consumed_trail,
826                                                            $handle.written());
827                                                }
828                                                $slf.pending = EucJpPending::Jis0212Lead($jis0212_lead_minus_offset);
829                                                return (DecoderResult::InputEmpty,
830                                                        src_consumed_trail,
831                                                        $handle.written());
832                                            }
833                                            Space::Available(source_handle_trail) => {
834                                                let ($byte, $unread_handle_trail) =
835                                                    source_handle_trail.read();
836                                                // Start non-boilerplate
837                                                $jis0812_trail_body
838                                                // End non-boilerplate
839                                            }
840                                        }
841                                    }
842                                }
843                            } else if $non_ascii == 0x8E {
844                                match $source.check_available() {
845                                    Space::Full(src_consumed_trail) => {
846                                        if last {
847                                            return (DecoderResult::Malformed(1, 0),
848                                                    src_consumed_trail,
849                                                    $handle.written());
850                                        }
851                                        $slf.pending = EucJpPending::HalfWidthKatakana;
852                                        return (DecoderResult::InputEmpty,
853                                                src_consumed_trail,
854                                                $handle.written());
855                                    }
856                                    Space::Available(source_handle_trail) => {
857                                        let ($byte, $unread_handle_trail) =
858                                            source_handle_trail.read();
859                                        // Start non-boilerplate
860                                        $half_width_katakana_body
861                                        // End non-boilerplate
862                                    }
863                                }
864                            } else {
865                                return (DecoderResult::Malformed(1, 0),
866                                        $source.consumed(),
867                                        $handle.written());
868                            }
869                        };
870                        match $source.check_available() {
871                            Space::Full(src_consumed) => {
872                                return (DecoderResult::InputEmpty,
873                                        src_consumed,
874                                        dest_again.written());
875                            }
876                            Space::Available(source_handle) => {
877                                match dest_again.check_space_bmp() {
878                                    Space::Full(dst_written) => {
879                                        return (DecoderResult::OutputFull,
880                                                source_handle.consumed(),
881                                                dst_written);
882                                    }
883                                    Space::Available(destination_handle) => {
884                                        let (b, unread_handle) = source_handle.read();
885                                        unread_handle.commit();
886                                        loop {
887                                            if b > 127 {
888                                                $non_ascii = b;
889                                                $handle = destination_handle;
890                                                continue 'middle;
891                                            }
892                                            // Testing on Haswell says that we should write the
893                                            // byte unconditionally instead of trying to unread it
894                                            // to make it part of the next SIMD stride.
895                                            destination_handle.write_ascii(b);
896                                            // We've got markup or ASCII text
897                                            continue 'outermost;
898                                        }
899                                    }
900                                }
901                            }
902                        }
903                    }
904                }
905            }
906        }
907    });
908}
909
910macro_rules! euc_jp_decoder_functions {
911    (
912        $jis0802_trail_body:block,
913        $jis0812_lead_body:block,
914        $jis0812_trail_body:block,
915        $half_width_katakana_body:block,
916        $slf:ident,
917        $non_ascii:ident,
918        $jis0208_lead_minus_offset:ident,
919        $byte:ident,
920        $unread_handle_trail:ident,
921        $jis0212_lead_minus_offset:ident,
922        $lead:ident,
923        $unread_handle_jis0212:ident,
924        $source:ident,
925        $handle:ident
926    ) => {
927        euc_jp_decoder_function!(
928            $jis0802_trail_body,
929            $jis0812_lead_body,
930            $jis0812_trail_body,
931            $half_width_katakana_body,
932            $slf,
933            $non_ascii,
934            $jis0208_lead_minus_offset,
935            $byte,
936            $unread_handle_trail,
937            $jis0212_lead_minus_offset,
938            $lead,
939            $unread_handle_jis0212,
940            $source,
941            $handle,
942            decode_to_utf8_raw,
943            u8,
944            Utf8Destination
945        );
946        euc_jp_decoder_function!(
947            $jis0802_trail_body,
948            $jis0812_lead_body,
949            $jis0812_trail_body,
950            $half_width_katakana_body,
951            $slf,
952            $non_ascii,
953            $jis0208_lead_minus_offset,
954            $byte,
955            $unread_handle_trail,
956            $jis0212_lead_minus_offset,
957            $lead,
958            $unread_handle_jis0212,
959            $source,
960            $handle,
961            decode_to_utf16_raw,
962            u16,
963            Utf16Destination
964        );
965    };
966}
967
968macro_rules! encoder_function {
969    ($eof:block,
970     $body:block,
971     $slf:ident,
972     $src_consumed:ident,
973     $source:ident,
974     $dest:ident,
975     $c:ident,
976     $destination_handle:ident,
977     $unread_handle:ident,
978     $destination_check:ident,
979     $name:ident,
980     $input:ty,
981     $source_struct:ident) => (
982    pub fn $name(&mut $slf,
983                 src: &$input,
984                 dst: &mut [u8],
985                 last: bool)
986                 -> (EncoderResult, usize, usize) {
987        let mut $source = $source_struct::new(src);
988        let mut $dest = ByteDestination::new(dst);
989        loop {
990            match $source.check_available() {
991                Space::Full($src_consumed) => {
992                    if last {
993                        // Start non-boilerplate
994                        $eof
995                        // End non-boilerplate
996                    }
997                    return (EncoderResult::InputEmpty, $src_consumed, $dest.written());
998                }
999                Space::Available(source_handle) => {
1000                    match $dest.$destination_check() {
1001                        Space::Full(dst_written) => {
1002                            return (EncoderResult::OutputFull,
1003                                    source_handle.consumed(),
1004                                    dst_written);
1005                        }
1006                        Space::Available($destination_handle) => {
1007                            let ($c, $unread_handle) = source_handle.read();
1008                            // Start non-boilerplate
1009                            $body
1010                            // End non-boilerplate
1011                        }
1012                    }
1013                }
1014            }
1015        }
1016    });
1017}
1018
1019macro_rules! encoder_functions {
1020    (
1021        eof = $eof:block,
1022        body = $body:block,
1023        self = $slf:ident,
1024        src_consumed = $src_consumed:ident,
1025        source = $source:ident,
1026        dest = $dest:ident,
1027        c = $c:ident,
1028        destination_handle = $destination_handle:ident,
1029        unread_handle = $unread_handle:ident,
1030        destination_check = $destination_check:ident
1031    ) => {
1032        encoder_function!(
1033            $eof,
1034            $body,
1035            $slf,
1036            $src_consumed,
1037            $source,
1038            $dest,
1039            $c,
1040            $destination_handle,
1041            $unread_handle,
1042            $destination_check,
1043            encode_from_utf8_raw,
1044            str,
1045            Utf8Source
1046        );
1047        encoder_function!(
1048            $eof,
1049            $body,
1050            $slf,
1051            $src_consumed,
1052            $source,
1053            $dest,
1054            $c,
1055            $destination_handle,
1056            $unread_handle,
1057            $destination_check,
1058            encode_from_utf16_raw,
1059            [u16],
1060            Utf16Source
1061        );
1062    };
1063}
1064
1065macro_rules! ascii_compatible_encoder_function {
1066    ($bmp_body:block,
1067     $astral_body:block,
1068     $bmp:ident,
1069     $astral:ident,
1070     $slf:ident,
1071     $source:ident,
1072     $handle:ident,
1073     $copy_ascii:ident,
1074     $destination_check:ident,
1075     $name:ident,
1076     $input:ty,
1077     $source_struct:ident,
1078     $ascii_punctuation:expr) => (
1079    pub fn $name(&mut $slf,
1080                 src: &$input,
1081                 dst: &mut [u8],
1082                 _last: bool)
1083                 -> (EncoderResult, usize, usize) {
1084        let mut $source = $source_struct::new(src);
1085        let mut dest = ByteDestination::new(dst);
1086        'outermost: loop {
1087            match $source.$copy_ascii(&mut dest) {
1088                CopyAsciiResult::Stop(ret) => return ret,
1089                CopyAsciiResult::GoOn((mut non_ascii, mut $handle)) => {
1090                    'middle: loop {
1091                        let dest_again = match non_ascii {
1092                            NonAscii::BmpExclAscii($bmp) => {
1093                                // Start non-boilerplate
1094                                $bmp_body
1095                                // End non-boilerplate
1096                            }
1097                            NonAscii::Astral($astral) => {
1098                                // Start non-boilerplate
1099                                $astral_body
1100                                // End non-boilerplate
1101                            }
1102                        };
1103                        match $source.check_available() {
1104                            Space::Full(src_consumed) => {
1105                                return (EncoderResult::InputEmpty,
1106                                        src_consumed,
1107                                        dest_again.written());
1108                            }
1109                            Space::Available(source_handle) => {
1110                                match dest_again.$destination_check() {
1111                                    Space::Full(dst_written) => {
1112                                        return (EncoderResult::OutputFull,
1113                                                source_handle.consumed(),
1114                                                dst_written);
1115                                    }
1116                                    Space::Available(mut destination_handle) => {
1117                                        let (mut c, unread_handle) = source_handle.read_enum();
1118                                        let source_again = unread_handle.commit();
1119                                        'innermost: loop {
1120                                            let ascii = match c {
1121                                                Unicode::NonAscii(non_ascii_again) => {
1122                                                    non_ascii = non_ascii_again;
1123                                                    $handle = destination_handle;
1124                                                    continue 'middle;
1125                                                }
1126                                                Unicode::Ascii(a) => a,
1127                                            };
1128                                            // Testing on Haswell says that we should write the
1129                                            // byte unconditionally instead of trying to unread it
1130                                            // to make it part of the next SIMD stride.
1131                                            let dest_again_again =
1132                                                destination_handle.write_one(ascii);
1133                                            if $ascii_punctuation && ascii < 60 {
1134                                                // We've got punctuation
1135                                                match source_again.check_available() {
1136                                                    Space::Full(src_consumed_again) => {
1137                                                        return (EncoderResult::InputEmpty,
1138                                                                src_consumed_again,
1139                                                                dest_again_again.written());
1140                                                    }
1141                                                    Space::Available(source_handle_again) => {
1142                                                        match dest_again_again.$destination_check() {
1143                                                            Space::Full(dst_written_again) => {
1144                                                                return (EncoderResult::OutputFull,
1145                                                                        source_handle_again.consumed(),
1146                                                                        dst_written_again);
1147                                                            }
1148                                                            Space::Available(destination_handle_again) => {
1149                                                                {
1150                                                                    let (c_again, unread_handle_again) =
1151                                                                        source_handle_again.read_enum();
1152                                                                    unread_handle_again.commit();
1153                                                                    c = c_again;
1154                                                                    destination_handle = destination_handle_again;
1155                                                                    continue 'innermost;
1156                                                                }
1157                                                            }
1158                                                        }
1159                                                    }
1160                                                }
1161                                            }
1162                                            // We've got markup or ASCII text
1163                                            continue 'outermost;
1164                                        }
1165                                    }
1166                                }
1167                            }
1168                        }
1169                    }
1170                }
1171            }
1172        }
1173    });
1174}
1175
1176macro_rules! ascii_compatible_encoder_functions {
1177    (
1178        $bmp_body:block,
1179        $astral_body:block,
1180        $bmp:ident,
1181        $astral:ident,
1182        $slf:ident,
1183        $source:ident,
1184        $handle:ident,
1185        $copy_ascii:ident,
1186        $destination_check:ident,
1187        $ascii_punctuation:expr
1188    ) => {
1189        ascii_compatible_encoder_function!(
1190            $bmp_body,
1191            $astral_body,
1192            $bmp,
1193            $astral,
1194            $slf,
1195            $source,
1196            $handle,
1197            $copy_ascii,
1198            $destination_check,
1199            encode_from_utf8_raw,
1200            str,
1201            Utf8Source,
1202            $ascii_punctuation
1203        );
1204        ascii_compatible_encoder_function!(
1205            $bmp_body,
1206            $astral_body,
1207            $bmp,
1208            $astral,
1209            $slf,
1210            $source,
1211            $handle,
1212            $copy_ascii,
1213            $destination_check,
1214            encode_from_utf16_raw,
1215            [u16],
1216            Utf16Source,
1217            $ascii_punctuation
1218        );
1219    };
1220}
1221
1222macro_rules! ascii_compatible_bmp_encoder_function {
1223    (
1224        $bmp_body:block,
1225        $bmp:ident,
1226        $slf:ident,
1227        $source:ident,
1228        $handle:ident,
1229        $copy_ascii:ident,
1230        $destination_check:ident,
1231        $name:ident,
1232        $input:ty,
1233        $source_struct:ident,
1234        $ascii_punctuation:expr
1235    ) => {
1236        ascii_compatible_encoder_function!(
1237            $bmp_body,
1238            {
1239                return (
1240                    EncoderResult::Unmappable(astral),
1241                    $source.consumed(),
1242                    $handle.written(),
1243                );
1244            },
1245            $bmp,
1246            astral,
1247            $slf,
1248            $source,
1249            $handle,
1250            $copy_ascii,
1251            $destination_check,
1252            $name,
1253            $input,
1254            $source_struct,
1255            $ascii_punctuation
1256        );
1257    };
1258}
1259
1260macro_rules! ascii_compatible_bmp_encoder_functions {
1261    (
1262        $bmp_body:block,
1263        $bmp:ident,
1264        $slf:ident,
1265        $source:ident,
1266        $handle:ident,
1267        $copy_ascii:ident,
1268        $destination_check:ident,
1269        $ascii_punctuation:expr
1270    ) => {
1271        ascii_compatible_encoder_functions!(
1272            $bmp_body,
1273            {
1274                return (
1275                    EncoderResult::Unmappable(astral),
1276                    $source.consumed(),
1277                    $handle.written(),
1278                );
1279            },
1280            $bmp,
1281            astral,
1282            $slf,
1283            $source,
1284            $handle,
1285            $copy_ascii,
1286            $destination_check,
1287            $ascii_punctuation
1288        );
1289    };
1290}
1291
1292macro_rules! public_decode_function{
1293    ($(#[$meta:meta])*,
1294     $decode_to_utf:ident,
1295     $decode_to_utf_raw:ident,
1296     $decode_to_utf_checking_end:ident,
1297     $decode_to_utf_after_one_potential_bom_byte:ident,
1298     $decode_to_utf_after_two_potential_bom_bytes:ident,
1299     $decode_to_utf_checking_end_with_offset:ident,
1300     $code_unit:ty) => (
1301    $(#[$meta])*
1302    pub fn $decode_to_utf(&mut self,
1303                           src: &[u8],
1304                           dst: &mut [$code_unit],
1305                           last: bool)
1306                           -> (DecoderResult, usize, usize) {
1307        let mut offset = 0usize;
1308        loop {
1309            match self.life_cycle {
1310                // The common case. (Post-sniffing.)
1311                DecoderLifeCycle::Converting => {
1312                    return self.$decode_to_utf_checking_end(src, dst, last);
1313                }
1314                // The rest is all BOM sniffing!
1315                DecoderLifeCycle::AtStart => {
1316                    debug_assert_eq!(offset, 0usize);
1317                    if src.is_empty() {
1318                        return (DecoderResult::InputEmpty, 0, 0);
1319                    }
1320                    match src[0] {
1321                        0xEFu8 => {
1322                            self.life_cycle = DecoderLifeCycle::SeenUtf8First;
1323                            offset += 1;
1324                            continue;
1325                        }
1326                        0xFEu8 => {
1327                            self.life_cycle = DecoderLifeCycle::SeenUtf16BeFirst;
1328                            offset += 1;
1329                            continue;
1330                        }
1331                        0xFFu8 => {
1332                            self.life_cycle = DecoderLifeCycle::SeenUtf16LeFirst;
1333                            offset += 1;
1334                            continue;
1335                        }
1336                        _ => {
1337                            self.life_cycle = DecoderLifeCycle::Converting;
1338                            continue;
1339                        }
1340                    }
1341                }
1342                DecoderLifeCycle::AtUtf8Start => {
1343                    debug_assert_eq!(offset, 0usize);
1344                    if src.is_empty() {
1345                        return (DecoderResult::InputEmpty, 0, 0);
1346                    }
1347                    match src[0] {
1348                        0xEFu8 => {
1349                            self.life_cycle = DecoderLifeCycle::SeenUtf8First;
1350                            offset += 1;
1351                            continue;
1352                        }
1353                        _ => {
1354                            self.life_cycle = DecoderLifeCycle::Converting;
1355                            continue;
1356                        }
1357                    }
1358                }
1359                DecoderLifeCycle::AtUtf16BeStart => {
1360                    debug_assert_eq!(offset, 0usize);
1361                    if src.is_empty() {
1362                        return (DecoderResult::InputEmpty, 0, 0);
1363                    }
1364                    match src[0] {
1365                        0xFEu8 => {
1366                            self.life_cycle = DecoderLifeCycle::SeenUtf16BeFirst;
1367                            offset += 1;
1368                            continue;
1369                        }
1370                        _ => {
1371                            self.life_cycle = DecoderLifeCycle::Converting;
1372                            continue;
1373                        }
1374                    }
1375                }
1376                DecoderLifeCycle::AtUtf16LeStart => {
1377                    debug_assert_eq!(offset, 0usize);
1378                    if src.is_empty() {
1379                        return (DecoderResult::InputEmpty, 0, 0);
1380                    }
1381                    match src[0] {
1382                        0xFFu8 => {
1383                            self.life_cycle = DecoderLifeCycle::SeenUtf16LeFirst;
1384                            offset += 1;
1385                            continue;
1386                        }
1387                        _ => {
1388                            self.life_cycle = DecoderLifeCycle::Converting;
1389                            continue;
1390                        }
1391                    }
1392                }
1393                DecoderLifeCycle::SeenUtf8First => {
1394                    if offset >= src.len() {
1395                        if last {
1396                            return self.$decode_to_utf_after_one_potential_bom_byte(src,
1397                                                                                    dst,
1398                                                                                    last,
1399                                                                                    offset,
1400                                                                                    0xEFu8);
1401                        }
1402                        return (DecoderResult::InputEmpty, offset, 0);
1403                    }
1404                    if src[offset] == 0xBBu8 {
1405                        self.life_cycle = DecoderLifeCycle::SeenUtf8Second;
1406                        offset += 1;
1407                        continue;
1408                    }
1409                    return self.$decode_to_utf_after_one_potential_bom_byte(src,
1410                                                                            dst,
1411                                                                            last,
1412                                                                            offset,
1413                                                                            0xEFu8);
1414                }
1415                DecoderLifeCycle::SeenUtf8Second => {
1416                    if offset >= src.len() {
1417                        if last {
1418                            return self.$decode_to_utf_after_two_potential_bom_bytes(src,
1419                                                                                     dst,
1420                                                                                     last,
1421                                                                                     offset);
1422                        }
1423                        return (DecoderResult::InputEmpty, offset, 0);
1424                    }
1425                    if src[offset] == 0xBFu8 {
1426                        self.life_cycle = DecoderLifeCycle::Converting;
1427                        offset += 1;
1428                        if self.encoding != UTF_8 {
1429                            self.encoding = UTF_8;
1430                            self.variant = UTF_8.new_variant_decoder();
1431                        }
1432                        return self.$decode_to_utf_checking_end_with_offset(src,
1433                                                                            dst,
1434                                                                            last,
1435                                                                            offset);
1436                    }
1437                    return self.$decode_to_utf_after_two_potential_bom_bytes(src,
1438                                                                             dst,
1439                                                                             last,
1440                                                                             offset);
1441                }
1442                DecoderLifeCycle::SeenUtf16BeFirst => {
1443                    if offset >= src.len() {
1444                        if last {
1445                            return self.$decode_to_utf_after_one_potential_bom_byte(src,
1446                                                                                    dst,
1447                                                                                    last,
1448                                                                                    offset,
1449                                                                                    0xFEu8);
1450                        }
1451                        return (DecoderResult::InputEmpty, offset, 0);
1452                    }
1453                    if src[offset] == 0xFFu8 {
1454                        self.life_cycle = DecoderLifeCycle::Converting;
1455                        offset += 1;
1456                        if self.encoding != UTF_16BE {
1457                            self.encoding = UTF_16BE;
1458                            self.variant = UTF_16BE.new_variant_decoder();
1459                        }
1460                        return self.$decode_to_utf_checking_end_with_offset(src,
1461                                                                            dst,
1462                                                                            last,
1463                                                                            offset);
1464                    }
1465                    return self.$decode_to_utf_after_one_potential_bom_byte(src,
1466                                                                            dst,
1467                                                                            last,
1468                                                                            offset,
1469                                                                            0xFEu8);
1470                }
1471                DecoderLifeCycle::SeenUtf16LeFirst => {
1472                    if offset >= src.len() {
1473                        if last {
1474                            return self.$decode_to_utf_after_one_potential_bom_byte(src,
1475                                                                                    dst,
1476                                                                                    last,
1477                                                                                    offset,
1478                                                                                    0xFFu8);
1479                        }
1480                        return (DecoderResult::InputEmpty, offset, 0);
1481                    }
1482                    if src[offset] == 0xFEu8 {
1483                        self.life_cycle = DecoderLifeCycle::Converting;
1484                        offset += 1;
1485                        if self.encoding != UTF_16LE {
1486                            self.encoding = UTF_16LE;
1487                            self.variant = UTF_16LE.new_variant_decoder();
1488                        }
1489                        return self.$decode_to_utf_checking_end_with_offset(src,
1490                                                                            dst,
1491                                                                            last,
1492                                                                            offset);
1493                    }
1494                    return self.$decode_to_utf_after_one_potential_bom_byte(src,
1495                                                                            dst,
1496                                                                            last,
1497                                                                            offset,
1498                                                                            0xFFu8);
1499                }
1500                DecoderLifeCycle::ConvertingWithPendingBB => {
1501                    debug_assert_eq!(offset, 0usize);
1502                    return self.$decode_to_utf_after_one_potential_bom_byte(src,
1503                                                                            dst,
1504                                                                            last,
1505                                                                            0usize,
1506                                                                            0xBBu8);
1507                }
1508                DecoderLifeCycle::Finished => panic!("Must not use a decoder that has finished."),
1509            }
1510        }
1511    }
1512
1513    fn $decode_to_utf_after_one_potential_bom_byte(&mut self,
1514                                                   src: &[u8],
1515                                                   dst: &mut [$code_unit],
1516                                                   last: bool,
1517                                                   offset: usize,
1518                                                   first_byte: u8)
1519                                                   -> (DecoderResult, usize, usize) {
1520        self.life_cycle = DecoderLifeCycle::Converting;
1521        if offset == 0usize {
1522            // First byte was seen previously.
1523            let first = [first_byte];
1524            let mut out_read = 0usize;
1525            let (mut first_result, _, mut first_written) =
1526                self.variant
1527                    .$decode_to_utf_raw(&first[..], dst, false);
1528            match first_result {
1529                DecoderResult::InputEmpty => {
1530                    let (result, read, written) =
1531                        self.$decode_to_utf_checking_end(src, &mut dst[first_written..], last);
1532                    first_result = result;
1533                    out_read = read; // Overwrite, don't add!
1534                    first_written += written;
1535                }
1536                DecoderResult::Malformed(_, _) => {
1537                    // Wasn't read from `src`!, leave out_read to 0
1538                }
1539                DecoderResult::OutputFull => {
1540                    panic!("Output buffer must have been too small.");
1541                }
1542            }
1543            return (first_result, out_read, first_written);
1544        }
1545        debug_assert_eq!(offset, 1usize);
1546        // The first byte is in `src`, so no need to push it separately.
1547        self.$decode_to_utf_checking_end(src, dst, last)
1548    }
1549
1550    fn $decode_to_utf_after_two_potential_bom_bytes(&mut self,
1551                                                    src: &[u8],
1552                                                    dst: &mut [$code_unit],
1553                                                    last: bool,
1554                                                    offset: usize)
1555                                                    -> (DecoderResult, usize, usize) {
1556        self.life_cycle = DecoderLifeCycle::Converting;
1557        if offset == 0usize {
1558            // The first two bytes are not in the current buffer..
1559            let ef_bb = [0xEFu8, 0xBBu8];
1560            let (mut first_result, mut first_read, mut first_written) =
1561                self.variant
1562                    .$decode_to_utf_raw(&ef_bb[..], dst, false);
1563            match first_result {
1564                DecoderResult::InputEmpty => {
1565                    let (result, read, written) =
1566                        self.$decode_to_utf_checking_end(src, &mut dst[first_written..], last);
1567                    first_result = result;
1568                    first_read = read; // Overwrite, don't add!
1569                    first_written += written;
1570                }
1571                DecoderResult::Malformed(_, _) => {
1572                    if first_read == 1usize {
1573                        // The first byte was malformed. We need to handle
1574                        // the second one, which isn't in `src`, later.
1575                        self.life_cycle = DecoderLifeCycle::ConvertingWithPendingBB;
1576                    }
1577                    first_read = 0usize; // Wasn't read from `src`!
1578                }
1579                DecoderResult::OutputFull => {
1580                    panic!("Output buffer must have been too small.");
1581                }
1582            }
1583            return (first_result, first_read, first_written);
1584        }
1585        if offset == 1usize {
1586            // The first byte isn't in the current buffer but the second one
1587            // is.
1588            return self.$decode_to_utf_after_one_potential_bom_byte(src,
1589                                                                    dst,
1590                                                                    last,
1591                                                                    0usize,
1592                                                                    0xEFu8);
1593
1594        }
1595        debug_assert_eq!(offset, 2usize);
1596        // The first two bytes are in `src`, so no need to push them separately.
1597        self.$decode_to_utf_checking_end(src, dst, last)
1598    }
1599
1600    /// Calls `$decode_to_utf_checking_end` with `offset` bytes omitted from
1601    /// the start of `src` but adjusting the return values to show those bytes
1602    /// as having been consumed.
1603    fn $decode_to_utf_checking_end_with_offset(&mut self,
1604                                               src: &[u8],
1605                                               dst: &mut [$code_unit],
1606                                               last: bool,
1607                                               offset: usize)
1608                                               -> (DecoderResult, usize, usize) {
1609        debug_assert_eq!(self.life_cycle, DecoderLifeCycle::Converting);
1610        let (result, read, written) = self.$decode_to_utf_checking_end(&src[offset..], dst, last);
1611        (result, read + offset, written)
1612    }
1613
1614    /// Calls through to the delegate and adjusts life cycle iff `last` is
1615    /// `true` and result is `DecoderResult::InputEmpty`.
1616    fn $decode_to_utf_checking_end(&mut self,
1617                                   src: &[u8],
1618                                   dst: &mut [$code_unit],
1619                                   last: bool)
1620                                   -> (DecoderResult, usize, usize) {
1621        debug_assert_eq!(self.life_cycle, DecoderLifeCycle::Converting);
1622        let (result, read, written) = self.variant
1623                                          .$decode_to_utf_raw(src, dst, last);
1624        if last {
1625            if let DecoderResult::InputEmpty = result {
1626                self.life_cycle = DecoderLifeCycle::Finished;
1627            }
1628        }
1629        (result, read, written)
1630    });
1631}