Skip to main content

encoding_rs/
euc_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
17enum EucJpPending {
18    None,
19    Jis0208Lead(u8),
20    Jis0212Shift,
21    Jis0212Lead(u8),
22    HalfWidthKatakana,
23}
24
25impl EucJpPending {
26    fn is_none(&self) -> bool {
27        matches!(*self, EucJpPending::None)
28    }
29
30    fn count(&self) -> usize {
31        match *self {
32            EucJpPending::None => 0,
33            EucJpPending::Jis0208Lead(_)
34            | EucJpPending::Jis0212Shift
35            | EucJpPending::HalfWidthKatakana => 1,
36            EucJpPending::Jis0212Lead(_) => 2,
37        }
38    }
39}
40
41pub struct EucJpDecoder {
42    pending: EucJpPending,
43}
44
45impl EucJpDecoder {
46    pub fn new() -> VariantDecoder {
47        VariantDecoder::EucJp(EucJpDecoder {
48            pending: EucJpPending::None,
49        })
50    }
51
52    pub fn in_neutral_state(&self) -> bool {
53        self.pending.is_none()
54    }
55
56    fn plus_one_if_lead(&self, byte_length: usize) -> Option<usize> {
57        byte_length.checked_add(if self.pending.is_none() { 0 } else { 1 })
58    }
59
60    pub fn max_utf16_buffer_length(&self, byte_length: usize) -> Option<usize> {
61        self.plus_one_if_lead(byte_length)
62    }
63
64    pub fn max_utf8_buffer_length_without_replacement(&self, byte_length: usize) -> Option<usize> {
65        // worst case: 2 to 3
66        let len = self.plus_one_if_lead(byte_length);
67        checked_add(2, checked_add_opt(len, checked_div(checked_add(1, len), 2)))
68    }
69
70    pub fn max_utf8_buffer_length(&self, byte_length: usize) -> Option<usize> {
71        checked_mul(3, self.plus_one_if_lead(byte_length))
72    }
73
74    euc_jp_decoder_functions!(
75        {
76            let trail_minus_offset = byte.wrapping_sub(0xA1);
77            // Fast-track Hiragana (60% according to Lunde)
78            // and Katakana (10% acconding to Lunde).
79            if jis0208_lead_minus_offset == 0x03 && trail_minus_offset < 0x53 {
80                // Hiragana
81                handle.write_upper_bmp(0x3041 + u16::from(trail_minus_offset))
82            } else if jis0208_lead_minus_offset == 0x04 && trail_minus_offset < 0x56 {
83                // Katakana
84                handle.write_upper_bmp(0x30A1 + u16::from(trail_minus_offset))
85            } else if trail_minus_offset > (0xFE - 0xA1) {
86                if byte < 0x80 {
87                    return (
88                        DecoderResult::Malformed(1, 0),
89                        unread_handle_trail.unread(),
90                        handle.written(),
91                    );
92                }
93                return (
94                    DecoderResult::Malformed(2, 0),
95                    unread_handle_trail.consumed(),
96                    handle.written(),
97                );
98            } else {
99                let pointer = mul_94(jis0208_lead_minus_offset) + usize::from(trail_minus_offset);
100                let level1_pointer = pointer.wrapping_sub(1410);
101                if level1_pointer < JIS0208_LEVEL1_KANJI.len() {
102                    handle.write_upper_bmp(JIS0208_LEVEL1_KANJI[level1_pointer])
103                } else {
104                    let level2_pointer = pointer.wrapping_sub(4418);
105                    if level2_pointer < JIS0208_LEVEL2_AND_ADDITIONAL_KANJI.len() {
106                        handle.write_upper_bmp(JIS0208_LEVEL2_AND_ADDITIONAL_KANJI[level2_pointer])
107                    } else {
108                        let ibm_pointer = pointer.wrapping_sub(8272);
109                        if ibm_pointer < IBM_KANJI.len() {
110                            handle.write_upper_bmp(IBM_KANJI[ibm_pointer])
111                        } else if let Some(bmp) = jis0208_symbol_decode(pointer) {
112                            handle.write_bmp_excl_ascii(bmp)
113                        } else if let Some(bmp) = jis0208_range_decode(pointer) {
114                            handle.write_bmp_excl_ascii(bmp)
115                        } else {
116                            return (
117                                DecoderResult::Malformed(2, 0),
118                                unread_handle_trail.consumed(),
119                                handle.written(),
120                            );
121                        }
122                    }
123                }
124            }
125        },
126        {
127            // If lead is between 0xA1 and 0xFE, inclusive,
128            // subtract 0xA1.
129            let jis0212_lead_minus_offset = lead.wrapping_sub(0xA1);
130            if jis0212_lead_minus_offset > (0xFE - 0xA1) {
131                if lead < 0x80 {
132                    return (
133                        DecoderResult::Malformed(1, 0),
134                        unread_handle_jis0212.unread(),
135                        handle.written(),
136                    );
137                }
138                return (
139                    DecoderResult::Malformed(2, 0),
140                    unread_handle_jis0212.consumed(),
141                    handle.written(),
142                );
143            }
144            jis0212_lead_minus_offset
145        },
146        {
147            // If trail is between 0xA1 and 0xFE, inclusive,
148            // subtract 0xA1.
149            let trail_minus_offset = byte.wrapping_sub(0xA1);
150            if trail_minus_offset > (0xFE - 0xA1) {
151                if byte < 0x80 {
152                    return (
153                        DecoderResult::Malformed(2, 0),
154                        unread_handle_trail.unread(),
155                        handle.written(),
156                    );
157                }
158                return (
159                    DecoderResult::Malformed(3, 0),
160                    unread_handle_trail.consumed(),
161                    handle.written(),
162                );
163            }
164            let pointer = mul_94(jis0212_lead_minus_offset) + usize::from(trail_minus_offset);
165            let pointer_minus_kanji = pointer.wrapping_sub(1410);
166            if pointer_minus_kanji < JIS0212_KANJI.len() {
167                handle.write_upper_bmp(JIS0212_KANJI[pointer_minus_kanji])
168            } else if let Some(bmp) = jis0212_accented_decode(pointer) {
169                handle.write_bmp_excl_ascii(bmp)
170            } else {
171                let pointer_minus_upper_cyrillic = pointer.wrapping_sub(597);
172                if pointer_minus_upper_cyrillic <= (607 - 597) {
173                    handle.write_mid_bmp(0x0402 + pointer_minus_upper_cyrillic as u16)
174                } else {
175                    let pointer_minus_lower_cyrillic = pointer.wrapping_sub(645);
176                    if pointer_minus_lower_cyrillic <= (655 - 645) {
177                        handle.write_mid_bmp(0x0452 + pointer_minus_lower_cyrillic as u16)
178                    } else {
179                        return (
180                            DecoderResult::Malformed(3, 0),
181                            unread_handle_trail.consumed(),
182                            handle.written(),
183                        );
184                    }
185                }
186            }
187        },
188        {
189            // If trail is between 0xA1 and 0xDF, inclusive,
190            // subtract 0xA1 and map to half-width Katakana.
191            let trail_minus_offset = byte.wrapping_sub(0xA1);
192            if trail_minus_offset > (0xDF - 0xA1) {
193                if byte < 0x80 {
194                    return (
195                        DecoderResult::Malformed(1, 0),
196                        unread_handle_trail.unread(),
197                        handle.written(),
198                    );
199                }
200                return (
201                    DecoderResult::Malformed(2, 0),
202                    unread_handle_trail.consumed(),
203                    handle.written(),
204                );
205            }
206            handle.write_upper_bmp(0xFF61 + u16::from(trail_minus_offset))
207        },
208        self,
209        non_ascii,
210        jis0208_lead_minus_offset,
211        byte,
212        unread_handle_trail,
213        jis0212_lead_minus_offset,
214        lead,
215        unread_handle_jis0212,
216        source,
217        handle
218    );
219}
220
221#[cfg(feature = "fast-kanji-encode")]
222#[inline(always)]
223fn encode_kanji(bmp: u16) -> Option<(u8, u8)> {
224    jis0208_kanji_euc_jp_encode(bmp)
225}
226
227#[cfg(not(feature = "fast-kanji-encode"))]
228#[inline(always)]
229fn encode_kanji(bmp: u16) -> Option<(u8, u8)> {
230    if 0x4EDD == bmp {
231        // Ideograph on the symbol row!
232        Some((0xA1, 0xB8))
233    } else if let Some((lead, trail)) = jis0208_level1_kanji_euc_jp_encode(bmp) {
234        Some((lead, trail))
235    } else if let Some(pos) = jis0208_level2_and_additional_kanji_encode(bmp) {
236        let lead = (pos / 94) + 0xD0;
237        let trail = (pos % 94) + 0xA1;
238        Some((lead as u8, trail as u8))
239    } else if let Some(pos) = position(&IBM_KANJI[..], bmp) {
240        let lead = (pos / 94) + 0xF9;
241        let trail = (pos % 94) + 0xA1;
242        Some((lead as u8, trail as u8))
243    } else {
244        None
245    }
246}
247
248pub struct EucJpEncoder;
249
250impl EucJpEncoder {
251    pub fn new(encoding: &'static Encoding) -> Encoder {
252        Encoder::new(encoding, VariantEncoder::EucJp(EucJpEncoder))
253    }
254
255    pub fn max_buffer_length_from_utf16_without_replacement(
256        &self,
257        u16_length: usize,
258    ) -> Option<usize> {
259        u16_length.checked_mul(2)
260    }
261
262    pub fn max_buffer_length_from_utf8_without_replacement(
263        &self,
264        byte_length: usize,
265    ) -> Option<usize> {
266        byte_length.checked_add(1)
267    }
268
269    ascii_compatible_bmp_encoder_functions!(
270        {
271            // Lunde says 60% Hiragana, 30% Kanji, 10% Katakana
272            let bmp_minus_hiragana = bmp.wrapping_sub(0x3041);
273            if bmp_minus_hiragana < 0x53 {
274                handle.write_two(0xA4, 0xA1 + bmp_minus_hiragana as u8)
275            } else if in_inclusive_range16(bmp, 0x4E00, 0x9FA0) {
276                if let Some((lead, trail)) = encode_kanji(bmp) {
277                    handle.write_two(lead, trail)
278                } else {
279                    return (
280                        EncoderResult::unmappable_from_bmp(bmp),
281                        source.consumed(),
282                        handle.written(),
283                    );
284                }
285            } else {
286                let bmp_minus_katakana = bmp.wrapping_sub(0x30A1);
287                if bmp_minus_katakana < 0x56 {
288                    handle.write_two(0xA5, 0xA1 + bmp_minus_katakana as u8)
289                } else {
290                    let bmp_minus_space = bmp.wrapping_sub(0x3000);
291                    if bmp_minus_space < 3 {
292                        // fast-track common punctuation
293                        handle.write_two(0xA1, 0xA1 + bmp_minus_space as u8)
294                    } else if bmp == 0xA5 {
295                        handle.write_one(0x5Cu8)
296                    } else if bmp == 0x203E {
297                        handle.write_one(0x7Eu8)
298                    } else if in_inclusive_range16(bmp, 0xFF61, 0xFF9F) {
299                        handle.write_two(0x8Eu8, (bmp - (0xFF61 - 0xA1)) as u8)
300                    } else if bmp == 0x2212 {
301                        handle.write_two(0xA1u8, 0xDDu8)
302                    } else if let Some(pointer) = jis0208_range_encode(bmp) {
303                        let lead = (pointer / 94) + 0xA1;
304                        let trail = (pointer % 94) + 0xA1;
305                        handle.write_two(lead as u8, trail as u8)
306                    } else if in_inclusive_range16(bmp, 0xFA0E, 0xFA2D)
307                        || bmp == 0xF929
308                        || bmp == 0xF9DC
309                    {
310                        // Guaranteed to be found in IBM_KANJI
311                        let pos = position(&IBM_KANJI[..], bmp).unwrap();
312                        let lead = (pos / 94) + 0xF9;
313                        let trail = (pos % 94) + 0xA1;
314                        handle.write_two(lead as u8, trail as u8)
315                    } else if let Some(pointer) = ibm_symbol_encode(bmp) {
316                        let lead = (pointer / 94) + 0xA1;
317                        let trail = (pointer % 94) + 0xA1;
318                        handle.write_two(lead as u8, trail as u8)
319                    } else if let Some(pointer) = jis0208_symbol_encode(bmp) {
320                        let lead = (pointer / 94) + 0xA1;
321                        let trail = (pointer % 94) + 0xA1;
322                        handle.write_two(lead as u8, trail as u8)
323                    } else {
324                        return (
325                            EncoderResult::unmappable_from_bmp(bmp),
326                            source.consumed(),
327                            handle.written(),
328                        );
329                    }
330                }
331            }
332        },
333        bmp,
334        self,
335        source,
336        handle,
337        copy_ascii_to_check_space_two,
338        check_space_two,
339        false
340    );
341}
342
343// Any copyright to the test code below this comment is dedicated to the
344// Public Domain. http://creativecommons.org/publicdomain/zero/1.0/
345
346#[cfg(all(test, feature = "alloc"))]
347mod tests {
348    use super::super::testing::*;
349    use super::super::*;
350
351    fn decode_euc_jp(bytes: &[u8], expect: &str) {
352        decode(EUC_JP, bytes, expect);
353    }
354
355    fn encode_euc_jp(string: &str, expect: &[u8]) {
356        encode(EUC_JP, string, expect);
357    }
358
359    #[test]
360    fn test_euc_jp_decode() {
361        // Empty
362        decode_euc_jp(b"", &"");
363
364        // ASCII
365        decode_euc_jp(b"\x61\x62", "\u{0061}\u{0062}");
366
367        // Half-width
368        decode_euc_jp(b"\x8E\xA1", "\u{FF61}");
369        decode_euc_jp(b"\x8E\xDF", "\u{FF9F}");
370        decode_euc_jp(b"\x8E\xA0", "\u{FFFD}");
371        decode_euc_jp(b"\x8E\xE0", "\u{FFFD}");
372        decode_euc_jp(b"\x8E\xFF", "\u{FFFD}");
373        decode_euc_jp(b"\x8E", "\u{FFFD}");
374
375        // JIS 0212
376        decode_euc_jp(b"\x8F\xA1\xA1", "\u{FFFD}");
377        decode_euc_jp(b"\x8F\xA2\xAF", "\u{02D8}");
378        decode_euc_jp(b"\x8F\xA2\xFF", "\u{FFFD}");
379        decode_euc_jp(b"\x8F\xA1", "\u{FFFD}");
380        decode_euc_jp(b"\x8F", "\u{FFFD}");
381
382        // JIS 0208
383        decode_euc_jp(b"\xA1\xA1", "\u{3000}");
384        decode_euc_jp(b"\xA1\xA0", "\u{FFFD}");
385        decode_euc_jp(b"\xFC\xFE", "\u{FF02}");
386        decode_euc_jp(b"\xFE\xFE", "\u{FFFD}");
387        decode_euc_jp(b"\xA1", "\u{FFFD}");
388
389        // Bad leads
390        decode_euc_jp(b"\xFF\xA1\xA1", "\u{FFFD}\u{3000}");
391        decode_euc_jp(b"\xA0\xA1\xA1", "\u{FFFD}\u{3000}");
392        decode_euc_jp(b"\x80\xA1\xA1", "\u{FFFD}\u{3000}");
393        decode_euc_jp(b"\x81\xA1\xA1", "\u{FFFD}\u{3000}");
394        decode_euc_jp(b"\x82\xA1\xA1", "\u{FFFD}\u{3000}");
395        decode_euc_jp(b"\x83\xA1\xA1", "\u{FFFD}\u{3000}");
396        decode_euc_jp(b"\x84\xA1\xA1", "\u{FFFD}\u{3000}");
397        decode_euc_jp(b"\x85\xA1\xA1", "\u{FFFD}\u{3000}");
398        decode_euc_jp(b"\x86\xA1\xA1", "\u{FFFD}\u{3000}");
399        decode_euc_jp(b"\x87\xA1\xA1", "\u{FFFD}\u{3000}");
400        decode_euc_jp(b"\x88\xA1\xA1", "\u{FFFD}\u{3000}");
401        decode_euc_jp(b"\x89\xA1\xA1", "\u{FFFD}\u{3000}");
402        decode_euc_jp(b"\x8A\xA1\xA1", "\u{FFFD}\u{3000}");
403        decode_euc_jp(b"\x8B\xA1\xA1", "\u{FFFD}\u{3000}");
404        decode_euc_jp(b"\x8C\xA1\xA1", "\u{FFFD}\u{3000}");
405        decode_euc_jp(b"\x8D\xA1\xA1", "\u{FFFD}\u{3000}");
406
407        // Bad ASCII trail
408        decode_euc_jp(b"\xA1\x40", "\u{FFFD}\u{0040}");
409    }
410
411    #[test]
412    fn test_euc_jp_encode() {
413        // Empty
414        encode_euc_jp("", b"");
415
416        // ASCII
417        encode_euc_jp("\u{0061}\u{0062}", b"\x61\x62");
418
419        // Exceptional code points
420        encode_euc_jp("\u{00A5}", b"\x5C");
421        encode_euc_jp("\u{203E}", b"\x7E");
422        encode_euc_jp("\u{2212}", b"\xA1\xDD");
423
424        // Half-width
425        encode_euc_jp("\u{FF61}", b"\x8E\xA1");
426        encode_euc_jp("\u{FF9F}", b"\x8E\xDF");
427
428        // JIS 0212
429        encode_euc_jp("\u{02D8}", b"&#728;");
430
431        // JIS 0208
432        encode_euc_jp("\u{3000}", b"\xA1\xA1");
433        encode_euc_jp("\u{FF02}", b"\xFC\xFE");
434    }
435}