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