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}