Skip to main content

encoding_rs/
iso_2022_jp.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
10use super::*;
11use crate::data::*;
12use crate::handles::*;
13use crate::variant::*;
14// Rust 1.14.0 requires the following despite the asterisk above.
15use super::in_inclusive_range16;
16
17#[derive(Copy, Clone, PartialEq)]
18enum Iso2022JpDecoderState {
19    Ascii,
20    Roman,
21    Katakana,
22    LeadByte,
23    TrailByte,
24    EscapeStart,
25    Escape,
26}
27
28pub struct Iso2022JpDecoder {
29    decoder_state: Iso2022JpDecoderState,
30    output_state: Iso2022JpDecoderState, // only takes 1 of first 4 values
31    lead: u8,
32    output_flag: bool,
33    pending_prepended: bool,
34}
35
36impl Iso2022JpDecoder {
37    pub fn new() -> VariantDecoder {
38        VariantDecoder::Iso2022Jp(Iso2022JpDecoder {
39            decoder_state: Iso2022JpDecoderState::Ascii,
40            output_state: Iso2022JpDecoderState::Ascii,
41            lead: 0u8,
42            output_flag: false,
43            pending_prepended: false,
44        })
45    }
46
47    pub fn in_neutral_state(&self) -> bool {
48        self.decoder_state == Iso2022JpDecoderState::Ascii
49            && self.output_state == Iso2022JpDecoderState::Ascii
50            && self.lead == 0u8
51            && !self.output_flag
52            && !self.pending_prepended
53    }
54
55    fn extra_to_input_from_state(&self, byte_length: usize) -> Option<usize> {
56        byte_length.checked_add(
57            if self.lead == 0 || self.pending_prepended {
58                0
59            } else {
60                1
61            } + match self.decoder_state {
62                Iso2022JpDecoderState::Escape | Iso2022JpDecoderState::EscapeStart => 1,
63                _ => 0,
64            },
65        )
66    }
67
68    fn extra_to_output_from_state(&self) -> usize {
69        if self.lead != 0 && self.pending_prepended {
70            1 + self.output_flag as usize
71        } else {
72            self.output_flag as usize
73        }
74    }
75
76    pub fn max_utf16_buffer_length(&self, byte_length: usize) -> Option<usize> {
77        checked_add(
78            self.extra_to_output_from_state(),
79            self.extra_to_input_from_state(byte_length),
80        )
81    }
82
83    pub fn max_utf8_buffer_length_without_replacement(&self, byte_length: usize) -> Option<usize> {
84        // worst case: 1 to 3 (half-width katakana)
85        self.max_utf8_buffer_length(byte_length)
86    }
87
88    pub fn max_utf8_buffer_length(&self, byte_length: usize) -> Option<usize> {
89        checked_mul(
90            3,
91            checked_add(
92                self.extra_to_output_from_state(),
93                self.extra_to_input_from_state(byte_length),
94            ),
95        )
96    }
97
98    decoder_functions!(
99        preamble = {
100            if self.pending_prepended {
101                // lead was set in EscapeStart and "prepended"
102                // in Escape.
103                debug_assert!(self.lead == 0x24u8 || self.lead == 0x28u8);
104                match dest.check_space_bmp() {
105                    Space::Full(_) => {
106                        return (DecoderResult::OutputFull, 0, 0);
107                    }
108                    Space::Available(destination_handle) => {
109                        self.pending_prepended = false;
110                        self.output_flag = false;
111                        match self.decoder_state {
112                            Iso2022JpDecoderState::Ascii | Iso2022JpDecoderState::Roman => {
113                                destination_handle.write_ascii(self.lead);
114                                self.lead = 0x0u8;
115                            }
116                            Iso2022JpDecoderState::Katakana => {
117                                destination_handle
118                                    .write_upper_bmp(u16::from(self.lead) - 0x21u16 + 0xFF61u16);
119                                self.lead = 0x0u8;
120                            }
121                            Iso2022JpDecoderState::LeadByte => {
122                                self.decoder_state = Iso2022JpDecoderState::TrailByte;
123                            }
124                            _ => unreachable!(),
125                        }
126                    }
127                }
128            }
129        },
130        loop_preamble = {},
131        eof = {
132            match self.decoder_state {
133                Iso2022JpDecoderState::TrailByte | Iso2022JpDecoderState::EscapeStart => {
134                    self.decoder_state = self.output_state;
135                    return (DecoderResult::Malformed(1, 0), src_consumed, dest.written());
136                }
137                Iso2022JpDecoderState::Escape => {
138                    self.pending_prepended = true;
139                    self.decoder_state = self.output_state;
140                    return (DecoderResult::Malformed(1, 1), src_consumed, dest.written());
141                }
142                _ => {}
143            }
144        },
145        body = {
146            match self.decoder_state {
147                Iso2022JpDecoderState::Ascii => {
148                    if b == 0x1Bu8 {
149                        self.decoder_state = Iso2022JpDecoderState::EscapeStart;
150                        continue;
151                    }
152                    self.output_flag = false;
153                    if b > 0x7Fu8 || b == 0x0Eu8 || b == 0x0Fu8 {
154                        return (
155                            DecoderResult::Malformed(1, 0),
156                            unread_handle.consumed(),
157                            destination_handle.written(),
158                        );
159                    }
160                    destination_handle.write_ascii(b);
161                    continue;
162                }
163                Iso2022JpDecoderState::Roman => {
164                    if b == 0x1Bu8 {
165                        self.decoder_state = Iso2022JpDecoderState::EscapeStart;
166                        continue;
167                    }
168                    self.output_flag = false;
169                    if b == 0x5Cu8 {
170                        destination_handle.write_mid_bmp(0x00A5u16);
171                        continue;
172                    }
173                    if b == 0x7Eu8 {
174                        destination_handle.write_upper_bmp(0x203Eu16);
175                        continue;
176                    }
177                    if b > 0x7Fu8 || b == 0x0Eu8 || b == 0x0Fu8 {
178                        return (
179                            DecoderResult::Malformed(1, 0),
180                            unread_handle.consumed(),
181                            destination_handle.written(),
182                        );
183                    }
184                    destination_handle.write_ascii(b);
185                    continue;
186                }
187                Iso2022JpDecoderState::Katakana => {
188                    if b == 0x1Bu8 {
189                        self.decoder_state = Iso2022JpDecoderState::EscapeStart;
190                        continue;
191                    }
192                    self.output_flag = false;
193                    if (0x21u8..=0x5Fu8).contains(&b) {
194                        destination_handle.write_upper_bmp(u16::from(b) - 0x21u16 + 0xFF61u16);
195                        continue;
196                    }
197                    return (
198                        DecoderResult::Malformed(1, 0),
199                        unread_handle.consumed(),
200                        destination_handle.written(),
201                    );
202                }
203                Iso2022JpDecoderState::LeadByte => {
204                    if b == 0x1Bu8 {
205                        self.decoder_state = Iso2022JpDecoderState::EscapeStart;
206                        continue;
207                    }
208                    self.output_flag = false;
209                    if (0x21u8..=0x7Eu8).contains(&b) {
210                        self.lead = b;
211                        self.decoder_state = Iso2022JpDecoderState::TrailByte;
212                        continue;
213                    }
214                    return (
215                        DecoderResult::Malformed(1, 0),
216                        unread_handle.consumed(),
217                        destination_handle.written(),
218                    );
219                }
220                Iso2022JpDecoderState::TrailByte => {
221                    if b == 0x1Bu8 {
222                        self.decoder_state = Iso2022JpDecoderState::EscapeStart;
223                        // The byte in error is the previous
224                        // lead byte.
225                        return (
226                            DecoderResult::Malformed(1, 1),
227                            unread_handle.consumed(),
228                            destination_handle.written(),
229                        );
230                    }
231                    self.decoder_state = Iso2022JpDecoderState::LeadByte;
232                    let jis0208_lead_minus_offset = self.lead - 0x21;
233                    let byte = b;
234                    let handle = destination_handle;
235                    // The code below uses else after continue in
236                    // order to retain the structure seen in EUC-JP.
237                    let trail_minus_offset = byte.wrapping_sub(0x21);
238                    // Fast-track Hiragana (60% according to Lunde)
239                    // and Katakana (10% acconding to Lunde).
240                    if jis0208_lead_minus_offset == 0x03 && trail_minus_offset < 0x53 {
241                        // Hiragana
242                        handle.write_upper_bmp(0x3041 + u16::from(trail_minus_offset));
243                        continue;
244                    } else if jis0208_lead_minus_offset == 0x04 && trail_minus_offset < 0x56 {
245                        // Katakana
246                        handle.write_upper_bmp(0x30A1 + u16::from(trail_minus_offset));
247                        continue;
248                    } else if trail_minus_offset > (0xFE - 0xA1) {
249                        return (
250                            DecoderResult::Malformed(2, 0),
251                            unread_handle.consumed(),
252                            handle.written(),
253                        );
254                    } else {
255                        let pointer =
256                            mul_94(jis0208_lead_minus_offset) + trail_minus_offset as usize;
257                        let level1_pointer = pointer.wrapping_sub(1410);
258                        if level1_pointer < JIS0208_LEVEL1_KANJI.len() {
259                            handle.write_upper_bmp(JIS0208_LEVEL1_KANJI[level1_pointer]);
260                            continue;
261                        } else {
262                            let level2_pointer = pointer.wrapping_sub(4418);
263                            if level2_pointer < JIS0208_LEVEL2_AND_ADDITIONAL_KANJI.len() {
264                                handle.write_upper_bmp(
265                                    JIS0208_LEVEL2_AND_ADDITIONAL_KANJI[level2_pointer],
266                                );
267                                continue;
268                            } else {
269                                let ibm_pointer = pointer.wrapping_sub(8272);
270                                if ibm_pointer < IBM_KANJI.len() {
271                                    handle.write_upper_bmp(IBM_KANJI[ibm_pointer]);
272                                    continue;
273                                } else if let Some(bmp) = jis0208_symbol_decode(pointer) {
274                                    handle.write_bmp_excl_ascii(bmp);
275                                    continue;
276                                } else if let Some(bmp) = jis0208_range_decode(pointer) {
277                                    handle.write_bmp_excl_ascii(bmp);
278                                    continue;
279                                } else {
280                                    return (
281                                        DecoderResult::Malformed(2, 0),
282                                        unread_handle.consumed(),
283                                        handle.written(),
284                                    );
285                                }
286                            }
287                        }
288                    }
289                }
290                Iso2022JpDecoderState::EscapeStart => {
291                    if b == 0x24u8 || b == 0x28u8 {
292                        self.lead = b;
293                        self.decoder_state = Iso2022JpDecoderState::Escape;
294                        continue;
295                    }
296                    self.output_flag = false;
297                    self.decoder_state = self.output_state;
298                    return (
299                        DecoderResult::Malformed(1, 0),
300                        unread_handle.unread(),
301                        destination_handle.written(),
302                    );
303                }
304                Iso2022JpDecoderState::Escape => {
305                    let mut state: Option<Iso2022JpDecoderState> = None;
306                    if self.lead == 0x28u8 && b == 0x42u8 {
307                        state = Some(Iso2022JpDecoderState::Ascii);
308                    } else if self.lead == 0x28u8 && b == 0x4Au8 {
309                        state = Some(Iso2022JpDecoderState::Roman);
310                    } else if self.lead == 0x28u8 && b == 0x49u8 {
311                        state = Some(Iso2022JpDecoderState::Katakana);
312                    } else if self.lead == 0x24u8 && (b == 0x40u8 || b == 0x42u8) {
313                        state = Some(Iso2022JpDecoderState::LeadByte);
314                    }
315                    match state {
316                        Some(s) => {
317                            self.lead = 0x0u8;
318                            self.decoder_state = s;
319                            self.output_state = s;
320                            let flag = self.output_flag;
321                            self.output_flag = true;
322                            if flag {
323                                // We had an escape sequence
324                                // immediately following another
325                                // escape sequence. Therefore,
326                                // the first one of these was
327                                // useless.
328                                return (
329                                    DecoderResult::Malformed(3, 3),
330                                    unread_handle.consumed(),
331                                    destination_handle.written(),
332                                );
333                            }
334                            continue;
335                        }
336                        None => {
337                            // self.lead is still the previous
338                            // byte. It will be processed in
339                            // the preabmle upon next call.
340                            self.pending_prepended = true;
341                            self.output_flag = false;
342                            self.decoder_state = self.output_state;
343                            // The byte in error is not the
344                            // current or the previous byte but
345                            // the one before those (lone 0x1B).
346                            return (
347                                DecoderResult::Malformed(1, 1),
348                                unread_handle.unread(),
349                                destination_handle.written(),
350                            );
351                        }
352                    }
353                }
354            }
355        },
356        self = self,
357        src_consumed = src_consumed,
358        dest = dest,
359        source = source,
360        byte = b,
361        destination_handle = destination_handle,
362        unread_handle = unread_handle,
363        destination_check = check_space_bmp
364    );
365}
366
367#[cfg(feature = "fast-kanji-encode")]
368#[inline(always)]
369fn is_kanji_mapped(bmp: u16) -> bool {
370    // Use the shift_jis variant, because we don't care about the
371    // byte values here.
372    jis0208_kanji_shift_jis_encode(bmp).is_some()
373}
374
375#[cfg(not(feature = "fast-kanji-encode"))]
376#[allow(clippy::redundant_pattern_matching, clippy::if_same_then_else)]
377#[inline(always)]
378fn is_kanji_mapped(bmp: u16) -> bool {
379    0x4EDD == bmp
380        || jis0208_level1_kanji_shift_jis_encode(bmp).is_some()
381        // Use the shift_jis variant, because we don't care about the
382        // byte values here.
383        || jis0208_level2_and_additional_kanji_encode(bmp).is_some()
384        || position(&IBM_KANJI[..], bmp).is_some()
385}
386
387#[allow(clippy::redundant_pattern_matching, clippy::if_same_then_else)]
388fn is_mapped_for_two_byte_encode(bmp: u16) -> bool {
389    // The code below uses else after return to
390    // keep the same structure as in EUC-JP.
391    // Lunde says 60% Hiragana, 30% Kanji, 10% Katakana
392    let bmp_minus_hiragana = bmp.wrapping_sub(0x3041);
393    if bmp_minus_hiragana < 0x53 {
394        true
395    } else if in_inclusive_range16(bmp, 0x4E00, 0x9FA0) {
396        is_kanji_mapped(bmp)
397    } else {
398        let bmp_minus_katakana = bmp.wrapping_sub(0x30A1);
399        if bmp_minus_katakana < 0x56 {
400            true
401        } else {
402            let bmp_minus_space = bmp.wrapping_sub(0x3000);
403            // fast-track common punctuation
404            bmp_minus_space < 3
405                || in_inclusive_range16(bmp, 0xFF61, 0xFF9F)
406                || bmp == 0x2212
407                || jis0208_range_encode(bmp).is_some()
408                || in_inclusive_range16(bmp, 0xFA0E, 0xFA2D)
409                || bmp == 0xF929
410                || bmp == 0xF9DC
411                || ibm_symbol_encode(bmp).is_some()
412                || jis0208_symbol_encode(bmp).is_some()
413        }
414    }
415}
416
417#[cfg(feature = "fast-kanji-encode")]
418#[inline(always)]
419fn encode_kanji(bmp: u16) -> Option<(u8, u8)> {
420    jis0208_kanji_iso_2022_jp_encode(bmp)
421}
422
423#[cfg(not(feature = "fast-kanji-encode"))]
424#[inline(always)]
425fn encode_kanji(bmp: u16) -> Option<(u8, u8)> {
426    if 0x4EDD == bmp {
427        // Ideograph on the symbol row!
428        Some((0x21, 0xB8 - 0x80))
429    } else if let Some((lead, trail)) = jis0208_level1_kanji_iso_2022_jp_encode(bmp) {
430        Some((lead, trail))
431    } else if let Some(pos) = jis0208_level2_and_additional_kanji_encode(bmp) {
432        let lead = (pos / 94) + (0xD0 - 0x80);
433        let trail = (pos % 94) + 0x21;
434        Some((lead as u8, trail as u8))
435    } else if let Some(pos) = position(&IBM_KANJI[..], bmp) {
436        let lead = (pos / 94) + (0xF9 - 0x80);
437        let trail = (pos % 94) + 0x21;
438        Some((lead as u8, trail as u8))
439    } else {
440        None
441    }
442}
443
444enum Iso2022JpEncoderState {
445    Ascii,
446    Roman,
447    Jis0208,
448}
449
450pub struct Iso2022JpEncoder {
451    state: Iso2022JpEncoderState,
452}
453
454impl Iso2022JpEncoder {
455    pub fn new(encoding: &'static Encoding) -> Encoder {
456        Encoder::new(
457            encoding,
458            VariantEncoder::Iso2022Jp(Iso2022JpEncoder {
459                state: Iso2022JpEncoderState::Ascii,
460            }),
461        )
462    }
463
464    pub fn has_pending_state(&self) -> bool {
465        !matches!(self.state, Iso2022JpEncoderState::Ascii)
466    }
467
468    pub fn max_buffer_length_from_utf16_without_replacement(
469        &self,
470        u16_length: usize,
471    ) -> Option<usize> {
472        // Worst case: every other character is ASCII/Roman and every other
473        // JIS0208.
474        // Two UTF-16 input units:
475        // Transition to Roman: 3
476        // Roman/ASCII: 1
477        // Transition to JIS0208: 3
478        // JIS0208: 2
479        // End transition: 3
480        checked_add_opt(
481            checked_add(3, u16_length.checked_mul(4)),
482            checked_div(u16_length.checked_add(1), 2),
483        )
484    }
485
486    pub fn max_buffer_length_from_utf8_without_replacement(
487        &self,
488        byte_length: usize,
489    ) -> Option<usize> {
490        // Worst case: every other character is ASCII/Roman and every other
491        // JIS0208.
492        // Three UTF-8 input units: 1 ASCII, 2 JIS0208
493        // Transition to ASCII: 3
494        // Roman/ASCII: 1
495        // Transition to JIS0208: 3
496        // JIS0208: 2
497        // End transition: 3
498        checked_add(3, byte_length.checked_mul(3))
499    }
500
501    encoder_functions!(
502        eof = {
503            match self.state {
504                Iso2022JpEncoderState::Ascii => {}
505                _ => match dest.check_space_three() {
506                    Space::Full(dst_written) => {
507                        return (EncoderResult::OutputFull, src_consumed, dst_written);
508                    }
509                    Space::Available(destination_handle) => {
510                        self.state = Iso2022JpEncoderState::Ascii;
511                        destination_handle.write_three(0x1Bu8, 0x28u8, 0x42u8);
512                    }
513                },
514            }
515        },
516        body = {
517            match self.state {
518                Iso2022JpEncoderState::Ascii => {
519                    if c == '\u{0E}' || c == '\u{0F}' || c == '\u{1B}' {
520                        return (
521                            EncoderResult::Unmappable('\u{FFFD}'),
522                            unread_handle.consumed(),
523                            destination_handle.written(),
524                        );
525                    }
526                    if c <= '\u{7F}' {
527                        destination_handle.write_one(c as u8);
528                        continue;
529                    }
530                    if c == '\u{A5}' || c == '\u{203E}' {
531                        self.state = Iso2022JpEncoderState::Roman;
532                        destination_handle.write_three(0x1Bu8, 0x28u8, 0x4Au8);
533                        unread_handle.unread();
534                        continue;
535                    }
536                    if c > '\u{FFFF}' {
537                        return (
538                            EncoderResult::Unmappable(c),
539                            unread_handle.consumed(),
540                            destination_handle.written(),
541                        );
542                    }
543                    // Yes, if c is in index, we'll search
544                    // again in the Jis0208 state, but this
545                    // encoder is not worth optimizing.
546                    if is_mapped_for_two_byte_encode(c as u16) {
547                        self.state = Iso2022JpEncoderState::Jis0208;
548                        destination_handle.write_three(0x1Bu8, 0x24u8, 0x42u8);
549                        unread_handle.unread();
550                        continue;
551                    }
552                    return (
553                        EncoderResult::Unmappable(c),
554                        unread_handle.consumed(),
555                        destination_handle.written(),
556                    );
557                }
558                Iso2022JpEncoderState::Roman => {
559                    if c == '\u{0E}' || c == '\u{0F}' || c == '\u{1B}' {
560                        return (
561                            EncoderResult::Unmappable('\u{FFFD}'),
562                            unread_handle.consumed(),
563                            destination_handle.written(),
564                        );
565                    }
566                    if c == '\u{5C}' || c == '\u{7E}' {
567                        self.state = Iso2022JpEncoderState::Ascii;
568                        destination_handle.write_three(0x1Bu8, 0x28u8, 0x42u8);
569                        unread_handle.unread();
570                        continue;
571                    }
572                    if c <= '\u{7F}' {
573                        destination_handle.write_one(c as u8);
574                        continue;
575                    }
576                    if c == '\u{A5}' {
577                        destination_handle.write_one(0x5Cu8);
578                        continue;
579                    }
580                    if c == '\u{203E}' {
581                        destination_handle.write_one(0x7Eu8);
582                        continue;
583                    }
584                    if c > '\u{FFFF}' {
585                        return (
586                            EncoderResult::Unmappable(c),
587                            unread_handle.consumed(),
588                            destination_handle.written(),
589                        );
590                    }
591                    // Yes, if c is in index, we'll search
592                    // again in the Jis0208 state, but this
593                    // encoder is not worth optimizing.
594                    if is_mapped_for_two_byte_encode(c as u16) {
595                        self.state = Iso2022JpEncoderState::Jis0208;
596                        destination_handle.write_three(0x1Bu8, 0x24u8, 0x42u8);
597                        unread_handle.unread();
598                        continue;
599                    }
600                    return (
601                        EncoderResult::Unmappable(c),
602                        unread_handle.consumed(),
603                        destination_handle.written(),
604                    );
605                }
606                Iso2022JpEncoderState::Jis0208 => {
607                    if c <= '\u{7F}' {
608                        self.state = Iso2022JpEncoderState::Ascii;
609                        destination_handle.write_three(0x1Bu8, 0x28u8, 0x42u8);
610                        unread_handle.unread();
611                        continue;
612                    }
613                    if c == '\u{A5}' || c == '\u{203E}' {
614                        self.state = Iso2022JpEncoderState::Roman;
615                        destination_handle.write_three(0x1Bu8, 0x28u8, 0x4Au8);
616                        unread_handle.unread();
617                        continue;
618                    }
619                    if c > '\u{FFFF}' {
620                        // Transition to ASCII here in order
621                        // not to make it the responsibility
622                        // of the caller.
623                        self.state = Iso2022JpEncoderState::Ascii;
624                        return (
625                            EncoderResult::Unmappable(c),
626                            unread_handle.consumed(),
627                            destination_handle.write_three_return_written(0x1Bu8, 0x28u8, 0x42u8),
628                        );
629                    }
630                    let bmp = c as u16;
631                    let handle = destination_handle;
632                    // The code below uses else after continue to
633                    // keep the same structure as in EUC-JP.
634                    // Lunde says 60% Hiragana, 30% Kanji, 10% Katakana
635                    let bmp_minus_hiragana = bmp.wrapping_sub(0x3041);
636                    if bmp_minus_hiragana < 0x53 {
637                        handle.write_two(0x24, 0x21 + bmp_minus_hiragana as u8);
638                        continue;
639                    } else if in_inclusive_range16(bmp, 0x4E00, 0x9FA0) {
640                        if let Some((lead, trail)) = encode_kanji(bmp) {
641                            handle.write_two(lead, trail);
642                            continue;
643                        } else {
644                            self.state = Iso2022JpEncoderState::Ascii;
645                            return (
646                                EncoderResult::Unmappable(c),
647                                unread_handle.consumed(),
648                                handle.write_three_return_written(0x1Bu8, 0x28u8, 0x42u8),
649                            );
650                        }
651                    } else {
652                        let bmp_minus_katakana = bmp.wrapping_sub(0x30A1);
653                        if bmp_minus_katakana < 0x56 {
654                            handle.write_two(0x25, 0x21 + bmp_minus_katakana as u8);
655                            continue;
656                        } else {
657                            let bmp_minus_space = bmp.wrapping_sub(0x3000);
658                            if bmp_minus_space < 3 {
659                                // fast-track common punctuation
660                                handle.write_two(0x21, 0x21 + bmp_minus_space as u8);
661                                continue;
662                            }
663                            let bmp_minus_half_width = bmp.wrapping_sub(0xFF61);
664                            if bmp_minus_half_width <= (0xFF9F - 0xFF61) {
665                                // We have half-width katakana. The lead is either
666                                // row 1 or 5 of JIS X 0208, so the lookup table
667                                // only stores the trail.
668                                let lead =
669                                    if bmp != 0xFF70 && in_inclusive_range16(bmp, 0xFF66, 0xFF9D) {
670                                        0x25u8
671                                    } else {
672                                        0x21u8
673                                    };
674                                let trail =
675                                    ISO_2022_JP_HALF_WIDTH_TRAIL[bmp_minus_half_width as usize];
676                                handle.write_two(lead, trail);
677                                continue;
678                            } else if bmp == 0x2212 {
679                                handle.write_two(0x21, 0x5D);
680                                continue;
681                            } else if let Some(pointer) = jis0208_range_encode(bmp) {
682                                let lead = (pointer / 94) + 0x21;
683                                let trail = (pointer % 94) + 0x21;
684                                handle.write_two(lead as u8, trail as u8);
685                                continue;
686                            } else if in_inclusive_range16(bmp, 0xFA0E, 0xFA2D)
687                                || bmp == 0xF929
688                                || bmp == 0xF9DC
689                            {
690                                // Guaranteed to be found in IBM_KANJI
691                                let pos = position(&IBM_KANJI[..], bmp).unwrap();
692                                let lead = (pos / 94) + (0xF9 - 0x80);
693                                let trail = (pos % 94) + 0x21;
694                                handle.write_two(lead as u8, trail as u8);
695                                continue;
696                            } else if let Some(pointer) = ibm_symbol_encode(bmp) {
697                                let lead = (pointer / 94) + 0x21;
698                                let trail = (pointer % 94) + 0x21;
699                                handle.write_two(lead as u8, trail as u8);
700                                continue;
701                            } else if let Some(pointer) = jis0208_symbol_encode(bmp) {
702                                let lead = (pointer / 94) + 0x21;
703                                let trail = (pointer % 94) + 0x21;
704                                handle.write_two(lead as u8, trail as u8);
705                                continue;
706                            } else {
707                                self.state = Iso2022JpEncoderState::Ascii;
708                                return (
709                                    EncoderResult::Unmappable(c),
710                                    unread_handle.consumed(),
711                                    handle.write_three_return_written(0x1Bu8, 0x28u8, 0x42u8),
712                                );
713                            }
714                        }
715                    }
716                }
717            }
718        },
719        self = self,
720        src_consumed = src_consumed,
721        source = source,
722        dest = dest,
723        c = c,
724        destination_handle = destination_handle,
725        unread_handle = unread_handle,
726        destination_check = check_space_three
727    );
728}
729
730// Any copyright to the test code below this comment is dedicated to the
731// Public Domain. http://creativecommons.org/publicdomain/zero/1.0/
732
733#[cfg(all(test, feature = "alloc"))]
734mod tests {
735    use super::super::testing::*;
736    use super::super::*;
737
738    fn decode_iso_2022_jp(bytes: &[u8], expect: &str) {
739        decode(ISO_2022_JP, bytes, expect);
740    }
741
742    fn encode_iso_2022_jp(string: &str, expect: &[u8]) {
743        encode(ISO_2022_JP, string, expect);
744    }
745
746    #[test]
747    fn test_iso_2022_jp_decode() {
748        // Empty
749        decode_iso_2022_jp(b"", &"");
750
751        // ASCII
752        decode_iso_2022_jp(b"\x61\x62", "\u{0061}\u{0062}");
753        decode_iso_2022_jp(b"\x7F\x0E\x0F", "\u{007F}\u{FFFD}\u{FFFD}");
754
755        // Partial escapes
756        decode_iso_2022_jp(b"\x1B", "\u{FFFD}");
757        decode_iso_2022_jp(b"\x1B$", "\u{FFFD}$");
758        decode_iso_2022_jp(b"\x1B(", "\u{FFFD}(");
759        decode_iso_2022_jp(b"\x1B.", "\u{FFFD}.");
760
761        // ISO escapes
762        decode_iso_2022_jp(b"\x1B(B", ""); // ASCII
763        decode_iso_2022_jp(b"\x1B(J", ""); // Roman
764        decode_iso_2022_jp(b"\x1B$@", ""); // 0208
765        decode_iso_2022_jp(b"\x1B$B", ""); // 0208
766        decode_iso_2022_jp(b"\x1B$(D", "\u{FFFD}$(D"); // 2012
767        decode_iso_2022_jp(b"\x1B$A", "\u{FFFD}$A"); // GB2312
768        decode_iso_2022_jp(b"\x1B$(C", "\u{FFFD}$(C"); // KR
769        decode_iso_2022_jp(b"\x1B.A", "\u{FFFD}.A"); // Latin-1
770        decode_iso_2022_jp(b"\x1B.F", "\u{FFFD}.F"); // Greek
771        decode_iso_2022_jp(b"\x1B(I", ""); // Half-width Katakana
772        decode_iso_2022_jp(b"\x1B$(O", "\u{FFFD}$(O"); // 2013
773        decode_iso_2022_jp(b"\x1B$(P", "\u{FFFD}$(P"); // 2013
774        decode_iso_2022_jp(b"\x1B$(Q", "\u{FFFD}$(Q"); // 2013
775        decode_iso_2022_jp(b"\x1B$)C", "\u{FFFD}$)C"); // KR
776        decode_iso_2022_jp(b"\x1B$)A", "\u{FFFD}$)A"); // GB2312
777        decode_iso_2022_jp(b"\x1B$)G", "\u{FFFD}$)G"); // CNS
778        decode_iso_2022_jp(b"\x1B$*H", "\u{FFFD}$*H"); // CNS
779        decode_iso_2022_jp(b"\x1B$)E", "\u{FFFD}$)E"); // IR
780        decode_iso_2022_jp(b"\x1B$+I", "\u{FFFD}$+I"); // CNS
781        decode_iso_2022_jp(b"\x1B$+J", "\u{FFFD}$+J"); // CNS
782        decode_iso_2022_jp(b"\x1B$+K", "\u{FFFD}$+K"); // CNS
783        decode_iso_2022_jp(b"\x1B$+L", "\u{FFFD}$+L"); // CNS
784        decode_iso_2022_jp(b"\x1B$+M", "\u{FFFD}$+M"); // CNS
785        decode_iso_2022_jp(b"\x1B$(@", "\u{FFFD}$(@"); // 0208
786        decode_iso_2022_jp(b"\x1B$(A", "\u{FFFD}$(A"); // GB2312
787        decode_iso_2022_jp(b"\x1B$(B", "\u{FFFD}$(B"); // 0208
788        decode_iso_2022_jp(b"\x1B%G", "\u{FFFD}%G"); // UTF-8
789
790        // ASCII
791        decode_iso_2022_jp(b"\x5B", "\u{005B}");
792        decode_iso_2022_jp(b"\x5C", "\u{005C}");
793        decode_iso_2022_jp(b"\x7E", "\u{007E}");
794        decode_iso_2022_jp(b"\x0E", "\u{FFFD}");
795        decode_iso_2022_jp(b"\x0F", "\u{FFFD}");
796        decode_iso_2022_jp(b"\x80", "\u{FFFD}");
797        decode_iso_2022_jp(b"\xFF", "\u{FFFD}");
798        decode_iso_2022_jp(b"\x1B(B\x5B", "\u{005B}");
799        decode_iso_2022_jp(b"\x1B(B\x5C", "\u{005C}");
800        decode_iso_2022_jp(b"\x1B(B\x7E", "\u{007E}");
801        decode_iso_2022_jp(b"\x1B(B\x0E", "\u{FFFD}");
802        decode_iso_2022_jp(b"\x1B(B\x0F", "\u{FFFD}");
803        decode_iso_2022_jp(b"\x1B(B\x80", "\u{FFFD}");
804        decode_iso_2022_jp(b"\x1B(B\xFF", "\u{FFFD}");
805
806        // Roman
807        decode_iso_2022_jp(b"\x1B(J\x5B", "\u{005B}");
808        decode_iso_2022_jp(b"\x1B(J\x5C", "\u{00A5}");
809        decode_iso_2022_jp(b"\x1B(J\x7E", "\u{203E}");
810        decode_iso_2022_jp(b"\x1B(J\x0E", "\u{FFFD}");
811        decode_iso_2022_jp(b"\x1B(J\x0F", "\u{FFFD}");
812        decode_iso_2022_jp(b"\x1B(J\x80", "\u{FFFD}");
813        decode_iso_2022_jp(b"\x1B(J\xFF", "\u{FFFD}");
814
815        // Katakana
816        decode_iso_2022_jp(b"\x1B(I\x20", "\u{FFFD}");
817        decode_iso_2022_jp(b"\x1B(I\x21", "\u{FF61}");
818        decode_iso_2022_jp(b"\x1B(I\x5F", "\u{FF9F}");
819        decode_iso_2022_jp(b"\x1B(I\x60", "\u{FFFD}");
820        decode_iso_2022_jp(b"\x1B(I\x0E", "\u{FFFD}");
821        decode_iso_2022_jp(b"\x1B(I\x0F", "\u{FFFD}");
822        decode_iso_2022_jp(b"\x1B(I\x80", "\u{FFFD}");
823        decode_iso_2022_jp(b"\x1B(I\xFF", "\u{FFFD}");
824
825        // 0208 differences from 1978 to 1983
826        decode_iso_2022_jp(b"\x1B$@\x54\x64", "\u{58FA}");
827        decode_iso_2022_jp(b"\x1B$@\x44\x5B", "\u{58F7}");
828        decode_iso_2022_jp(b"\x1B$@\x74\x21", "\u{582F}");
829        decode_iso_2022_jp(b"\x1B$@\x36\x46", "\u{5C2D}");
830        decode_iso_2022_jp(b"\x1B$@\x28\x2E", "\u{250F}");
831        decode_iso_2022_jp(b"\x1B$B\x54\x64", "\u{58FA}");
832        decode_iso_2022_jp(b"\x1B$B\x44\x5B", "\u{58F7}");
833        decode_iso_2022_jp(b"\x1B$B\x74\x21", "\u{582F}");
834        decode_iso_2022_jp(b"\x1B$B\x36\x46", "\u{5C2D}");
835        decode_iso_2022_jp(b"\x1B$B\x28\x2E", "\u{250F}");
836
837        // Broken 0208
838        decode_iso_2022_jp(b"\x1B$B\x28\x41", "\u{FFFD}");
839        decode_iso_2022_jp(b"\x1B$@\x80\x54\x64", "\u{FFFD}\u{58FA}");
840        decode_iso_2022_jp(b"\x1B$B\x28\x80", "\u{FFFD}");
841
842        if cfg!(miri) {
843            // Miri is too slow
844            return;
845        }
846
847        // Transitions
848        decode_iso_2022_jp(b"\x1B(B\x5C\x1B(J\x5C", "\u{005C}\u{00A5}");
849        decode_iso_2022_jp(b"\x1B(B\x5C\x1B(I\x21", "\u{005C}\u{FF61}");
850        decode_iso_2022_jp(b"\x1B(B\x5C\x1B$@\x54\x64", "\u{005C}\u{58FA}");
851        decode_iso_2022_jp(b"\x1B(B\x5C\x1B$B\x54\x64", "\u{005C}\u{58FA}");
852
853        decode_iso_2022_jp(b"\x1B(J\x5C\x1B(B\x5C", "\u{00A5}\u{005C}");
854        decode_iso_2022_jp(b"\x1B(J\x5C\x1B(I\x21", "\u{00A5}\u{FF61}");
855        decode_iso_2022_jp(b"\x1B(J\x5C\x1B$@\x54\x64", "\u{00A5}\u{58FA}");
856        decode_iso_2022_jp(b"\x1B(J\x5C\x1B$B\x54\x64", "\u{00A5}\u{58FA}");
857
858        decode_iso_2022_jp(b"\x1B(I\x21\x1B(J\x5C", "\u{FF61}\u{00A5}");
859        decode_iso_2022_jp(b"\x1B(I\x21\x1B(B\x5C", "\u{FF61}\u{005C}");
860        decode_iso_2022_jp(b"\x1B(I\x21\x1B$@\x54\x64", "\u{FF61}\u{58FA}");
861        decode_iso_2022_jp(b"\x1B(I\x21\x1B$B\x54\x64", "\u{FF61}\u{58FA}");
862
863        decode_iso_2022_jp(b"\x1B$@\x54\x64\x1B(J\x5C", "\u{58FA}\u{00A5}");
864        decode_iso_2022_jp(b"\x1B$@\x54\x64\x1B(I\x21", "\u{58FA}\u{FF61}");
865        decode_iso_2022_jp(b"\x1B$@\x54\x64\x1B(B\x5C", "\u{58FA}\u{005C}");
866        decode_iso_2022_jp(b"\x1B$@\x54\x64\x1B$B\x54\x64", "\u{58FA}\u{58FA}");
867
868        decode_iso_2022_jp(b"\x1B$B\x54\x64\x1B(J\x5C", "\u{58FA}\u{00A5}");
869        decode_iso_2022_jp(b"\x1B$B\x54\x64\x1B(I\x21", "\u{58FA}\u{FF61}");
870        decode_iso_2022_jp(b"\x1B$B\x54\x64\x1B$@\x54\x64", "\u{58FA}\u{58FA}");
871        decode_iso_2022_jp(b"\x1B$B\x54\x64\x1B(B\x5C", "\u{58FA}\u{005C}");
872
873        // Empty transitions
874        decode_iso_2022_jp(b"\x1B(B\x1B(J", "\u{FFFD}");
875        decode_iso_2022_jp(b"\x1B(B\x1B(I", "\u{FFFD}");
876        decode_iso_2022_jp(b"\x1B(B\x1B$@", "\u{FFFD}");
877        decode_iso_2022_jp(b"\x1B(B\x1B$B", "\u{FFFD}");
878
879        decode_iso_2022_jp(b"\x1B(J\x1B(B", "\u{FFFD}");
880        decode_iso_2022_jp(b"\x1B(J\x1B(I", "\u{FFFD}");
881        decode_iso_2022_jp(b"\x1B(J\x1B$@", "\u{FFFD}");
882        decode_iso_2022_jp(b"\x1B(J\x1B$B", "\u{FFFD}");
883
884        decode_iso_2022_jp(b"\x1B(I\x1B(J", "\u{FFFD}");
885        decode_iso_2022_jp(b"\x1B(I\x1B(B", "\u{FFFD}");
886        decode_iso_2022_jp(b"\x1B(I\x1B$@", "\u{FFFD}");
887        decode_iso_2022_jp(b"\x1B(I\x1B$B", "\u{FFFD}");
888
889        decode_iso_2022_jp(b"\x1B$@\x1B(J", "\u{FFFD}");
890        decode_iso_2022_jp(b"\x1B$@\x1B(I", "\u{FFFD}");
891        decode_iso_2022_jp(b"\x1B$@\x1B(B", "\u{FFFD}");
892        decode_iso_2022_jp(b"\x1B$@\x1B$B", "\u{FFFD}");
893
894        decode_iso_2022_jp(b"\x1B$B\x1B(J", "\u{FFFD}");
895        decode_iso_2022_jp(b"\x1B$B\x1B(I", "\u{FFFD}");
896        decode_iso_2022_jp(b"\x1B$B\x1B$@", "\u{FFFD}");
897        decode_iso_2022_jp(b"\x1B$B\x1B(B", "\u{FFFD}");
898
899        // Transitions to self
900        decode_iso_2022_jp(b"\x1B(B\x5C\x1B(B\x5C", "\u{005C}\u{005C}");
901        decode_iso_2022_jp(b"\x1B(J\x5C\x1B(J\x5C", "\u{00A5}\u{00A5}");
902        decode_iso_2022_jp(b"\x1B(I\x21\x1B(I\x21", "\u{FF61}\u{FF61}");
903        decode_iso_2022_jp(b"\x1B$@\x54\x64\x1B$@\x54\x64", "\u{58FA}\u{58FA}");
904        decode_iso_2022_jp(b"\x1B$B\x54\x64\x1B$B\x54\x64", "\u{58FA}\u{58FA}");
905    }
906
907    #[test]
908    fn test_iso_2022_jp_encode() {
909        // Empty
910        encode_iso_2022_jp("", b"");
911
912        // ASCII
913        encode_iso_2022_jp("ab", b"ab");
914        encode_iso_2022_jp("\u{1F4A9}", b"&#128169;");
915        encode_iso_2022_jp("\x1B", b"&#65533;");
916        encode_iso_2022_jp("\x0E", b"&#65533;");
917        encode_iso_2022_jp("\x0F", b"&#65533;");
918
919        // Roman
920        encode_iso_2022_jp("a\u{00A5}b", b"a\x1B(J\x5Cb\x1B(B");
921        encode_iso_2022_jp("a\u{203E}b", b"a\x1B(J\x7Eb\x1B(B");
922        if !cfg!(miri) {
923            // Miri is too slow
924            encode_iso_2022_jp("a\u{00A5}b\x5C", b"a\x1B(J\x5Cb\x1B(B\x5C");
925            encode_iso_2022_jp("a\u{203E}b\x7E", b"a\x1B(J\x7Eb\x1B(B\x7E");
926            encode_iso_2022_jp("\u{00A5}\u{1F4A9}", b"\x1B(J\x5C&#128169;\x1B(B");
927            encode_iso_2022_jp("\u{00A5}\x1B", b"\x1B(J\x5C&#65533;\x1B(B");
928            encode_iso_2022_jp("\u{00A5}\x0E", b"\x1B(J\x5C&#65533;\x1B(B");
929            encode_iso_2022_jp("\u{00A5}\x0F", b"\x1B(J\x5C&#65533;\x1B(B");
930            encode_iso_2022_jp("\u{00A5}\u{58FA}", b"\x1B(J\x5C\x1B$B\x54\x64\x1B(B");
931        }
932
933        // Half-width Katakana
934        encode_iso_2022_jp("\u{FF61}", b"\x1B$B\x21\x23\x1B(B");
935        encode_iso_2022_jp("\u{FF65}", b"\x1B$B\x21\x26\x1B(B");
936        if !cfg!(miri) {
937            // Miri is too slow
938            encode_iso_2022_jp("\u{FF66}", b"\x1B$B\x25\x72\x1B(B");
939            encode_iso_2022_jp("\u{FF70}", b"\x1B$B\x21\x3C\x1B(B");
940            encode_iso_2022_jp("\u{FF9D}", b"\x1B$B\x25\x73\x1B(B");
941            encode_iso_2022_jp("\u{FF9E}", b"\x1B$B\x21\x2B\x1B(B");
942            encode_iso_2022_jp("\u{FF9F}", b"\x1B$B\x21\x2C\x1B(B");
943        }
944
945        // 0208
946        encode_iso_2022_jp("\u{58FA}", b"\x1B$B\x54\x64\x1B(B");
947        encode_iso_2022_jp("\u{58FA}\u{250F}", b"\x1B$B\x54\x64\x28\x2E\x1B(B");
948        if !cfg!(miri) {
949            // Miri is too slow
950            encode_iso_2022_jp("\u{58FA}\u{1F4A9}", b"\x1B$B\x54\x64\x1B(B&#128169;");
951            encode_iso_2022_jp("\u{58FA}\x1B", b"\x1B$B\x54\x64\x1B(B&#65533;");
952            encode_iso_2022_jp("\u{58FA}\x0E", b"\x1B$B\x54\x64\x1B(B&#65533;");
953            encode_iso_2022_jp("\u{58FA}\x0F", b"\x1B$B\x54\x64\x1B(B&#65533;");
954            encode_iso_2022_jp("\u{58FA}\u{00A5}", b"\x1B$B\x54\x64\x1B(J\x5C\x1B(B");
955            encode_iso_2022_jp("\u{58FA}a", b"\x1B$B\x54\x64\x1B(Ba");
956        }
957    }
958
959    #[test]
960    fn test_iso_2022_jp_half_width_katakana_length() {
961        let mut output = [0u8; 20];
962        let mut decoder = ISO_2022_JP.new_decoder();
963        {
964            let (result, read, written) =
965                decoder.decode_to_utf8_without_replacement(b"\x1B\x28\x49", &mut output, false);
966            assert_eq!(result, DecoderResult::InputEmpty);
967            assert_eq!(read, 3);
968            assert_eq!(written, 0);
969        }
970        {
971            let needed = decoder
972                .max_utf8_buffer_length_without_replacement(1)
973                .unwrap();
974            let (result, read, written) =
975                decoder.decode_to_utf8_without_replacement(b"\x21", &mut output[..needed], true);
976            assert_eq!(result, DecoderResult::InputEmpty);
977            assert_eq!(read, 1);
978            assert_eq!(written, 3);
979            assert_eq!(output[0], 0xEF);
980            assert_eq!(output[1], 0xBD);
981            assert_eq!(output[2], 0xA1);
982        }
983    }
984
985    #[test]
986    fn test_iso_2022_jp_length_after_escape() {
987        let mut output = [0u16; 20];
988        let mut decoder = ISO_2022_JP.new_decoder();
989        {
990            let (result, read, written, had_errors) =
991                decoder.decode_to_utf16(b"\x1B", &mut output, false);
992            assert_eq!(result, CoderResult::InputEmpty);
993            assert_eq!(read, 1);
994            assert_eq!(written, 0);
995            assert!(!had_errors);
996        }
997        {
998            let needed = decoder.max_utf16_buffer_length(1).unwrap();
999            let (result, read, written, had_errors) =
1000                decoder.decode_to_utf16(b"A", &mut output[..needed], true);
1001            assert_eq!(result, CoderResult::InputEmpty);
1002            assert_eq!(read, 1);
1003            assert_eq!(written, 2);
1004            assert!(had_errors);
1005            assert_eq!(output[0], 0xFFFD);
1006            assert_eq!(output[1], 0x0041);
1007        }
1008    }
1009
1010    #[test]
1011    fn test_iso_2022_jp_encode_from_two_low_surrogates() {
1012        let expectation = b"&#65533;&#65533;";
1013        let mut output = [0u8; 40];
1014        let mut encoder = ISO_2022_JP.new_encoder();
1015        let (result, read, written, had_errors) =
1016            encoder.encode_from_utf16(&[0xDC00u16, 0xDEDEu16], &mut output[..], true);
1017        assert_eq!(result, CoderResult::InputEmpty);
1018        assert_eq!(read, 2);
1019        assert_eq!(written, expectation.len());
1020        assert!(had_errors);
1021        assert_eq!(&output[..written], expectation);
1022    }
1023}