Skip to main content

encoding_rs/
shift_jis.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_range;
16use super::in_inclusive_range16;
17
18pub struct ShiftJisDecoder {
19    lead: Option<u8>,
20}
21
22impl ShiftJisDecoder {
23    pub fn new() -> VariantDecoder {
24        VariantDecoder::ShiftJis(ShiftJisDecoder { lead: None })
25    }
26
27    pub fn in_neutral_state(&self) -> bool {
28        self.lead.is_none()
29    }
30
31    fn plus_one_if_lead(&self, byte_length: usize) -> Option<usize> {
32        byte_length.checked_add(match self.lead {
33            None => 0,
34            Some(_) => 1,
35        })
36    }
37
38    pub fn max_utf16_buffer_length(&self, byte_length: usize) -> Option<usize> {
39        self.plus_one_if_lead(byte_length)
40    }
41
42    pub fn max_utf8_buffer_length_without_replacement(&self, byte_length: usize) -> Option<usize> {
43        // worst case: 1 to 3 (half-width katakana)
44        self.max_utf8_buffer_length(byte_length)
45    }
46
47    pub fn max_utf8_buffer_length(&self, byte_length: usize) -> Option<usize> {
48        checked_mul(3, self.plus_one_if_lead(byte_length))
49    }
50
51    ascii_compatible_two_byte_decoder_functions!(
52        lead = {
53           // If lead is between 0x81 and 0x9F, inclusive,
54           // subtract offset 0x81. Else if lead is
55           // between 0xE0 and 0xFC, inclusive, subtract
56           // offset 0xC1. Else if lead is between
57           // 0xA1 and 0xDF, inclusive, map to half-width
58           // Katakana. Else if lead is 0x80, pass through.
59            let mut non_ascii_minus_offset =
60                non_ascii.wrapping_sub(0x81);
61            if non_ascii_minus_offset > (0x9F - 0x81) {
62                let non_ascii_minus_range_start = non_ascii.wrapping_sub(0xE0);
63                if non_ascii_minus_range_start > (0xFC - 0xE0) {
64                    let non_ascii_minus_half_with_katakana_start = non_ascii.wrapping_sub(0xA1);
65                    if non_ascii_minus_half_with_katakana_start > (0xDF - 0xA1) {
66                        if non_ascii == 0x80 {
67                            handle.write_mid_bmp(0x80);
68                            // Not caring about optimizing subsequent non-ASCII
69                            continue 'outermost;
70                        }
71                        return (DecoderResult::Malformed(1, 0),
72                                source.consumed(),
73                                handle.written());
74                    }
75                    handle.write_upper_bmp(0xFF61 + u16::from(non_ascii_minus_half_with_katakana_start));
76                    // Not caring about optimizing subsequent non-ASCII
77                    continue 'outermost;
78                }
79                non_ascii_minus_offset = non_ascii - 0xC1;
80            }
81            non_ascii_minus_offset
82        },
83        trail = {
84            // If trail is between 0x40 and 0x7E, inclusive,
85            // subtract offset 0x40. Else if trail is
86            // between 0x80 and 0xFC, inclusive, subtract
87            // offset 0x41.
88            // Fast-track Hiragana (60% according to Lunde)
89            // and Katakana (10% acconding to Lunde).
90            // Hiragana doesn't cross 0x7F, but Katakana does.
91            // We can check for Hiragana before normalizing
92            // trail.
93            let trail_minus_hiragana = byte.wrapping_sub(0x9F);
94            if lead_minus_offset == 0x01 && trail_minus_hiragana < 0x53 {
95            // Hiragana
96                handle.write_upper_bmp(0x3041 + u16::from(trail_minus_hiragana))
97            } else {
98                let mut trail_minus_offset =
99                    byte.wrapping_sub(0x40);
100                if trail_minus_offset > (0x7E - 0x40) {
101                    let trail_minus_range_start =
102                        byte.wrapping_sub(0x80);
103                    if trail_minus_range_start > (0xFC - 0x80) {
104                        if byte < 0x80 {
105                            return (DecoderResult::Malformed(1, 0),
106                                    unread_handle_trail.unread(),
107                                    handle.written());
108                        }
109                        return (DecoderResult::Malformed(2, 0),
110                                unread_handle_trail.consumed(),
111                                handle.written());
112                    }
113                    trail_minus_offset = byte - 0x41;
114                }
115                if lead_minus_offset == 0x02 &&
116                   trail_minus_offset < 0x56 {
117                    // Katakana
118                    handle.write_upper_bmp(0x30A1 + u16::from(trail_minus_offset))
119                } else {
120                    let pointer = lead_minus_offset as usize *
121                                  188usize +
122                                  trail_minus_offset as usize;
123                    let level1_pointer = pointer.wrapping_sub(1410);
124                    if level1_pointer < JIS0208_LEVEL1_KANJI.len() {
125                        handle.write_upper_bmp(JIS0208_LEVEL1_KANJI[level1_pointer])
126                    } else {
127                        let level2_pointer = pointer.wrapping_sub(4418);
128                        if level2_pointer <
129                           JIS0208_LEVEL2_AND_ADDITIONAL_KANJI.len() {
130                            handle.write_upper_bmp(JIS0208_LEVEL2_AND_ADDITIONAL_KANJI[level2_pointer])
131                        } else {
132                            let upper_ibm_pointer = pointer.wrapping_sub(10744);
133                            if upper_ibm_pointer < IBM_KANJI.len() {
134                                handle.write_upper_bmp(IBM_KANJI[upper_ibm_pointer])
135                            } else {
136                                let lower_ibm_pointer = pointer.wrapping_sub(8272);
137                                if lower_ibm_pointer < IBM_KANJI.len() {
138                                    handle.write_upper_bmp(IBM_KANJI[lower_ibm_pointer])
139                                } else if in_inclusive_range(pointer, 8836, 10715) {
140                                    handle.write_upper_bmp((0xE000 - 8836 + pointer) as u16)
141                                } else if let Some(bmp) = jis0208_symbol_decode(pointer) {
142                                    handle.write_bmp_excl_ascii(bmp)
143                                } else if let Some(bmp) = jis0208_range_decode(pointer) {
144                                    handle.write_bmp_excl_ascii(bmp)
145                                } else {
146                                    if byte < 0x80 {
147                                        return (DecoderResult::Malformed(1, 0),
148                                                unread_handle_trail.unread(),
149                                                handle.written());
150                                    }
151                                    return (DecoderResult::Malformed(2, 0),
152                                            unread_handle_trail.consumed(),
153                                            handle.written());
154                                }
155                            }
156                        }
157                    }
158                }
159            }
160        },
161        self = self,
162        non_ascii = non_ascii,
163        byte = byte,
164        lead_minus_offset = lead_minus_offset,
165        unread_handle_trail = unread_handle_trail,
166        source = source,
167        handle = handle,
168        outermost = 'outermost,
169        copy_ascii = copy_ascii_from_check_space_bmp,
170        destination_check = check_space_bmp,
171        ascii_punctuation = false);
172}
173
174#[cfg(feature = "fast-kanji-encode")]
175#[inline(always)]
176fn encode_kanji(bmp: u16) -> Option<(u8, u8)> {
177    jis0208_kanji_shift_jis_encode(bmp)
178}
179
180#[cfg(not(feature = "fast-kanji-encode"))]
181#[inline(always)]
182fn encode_kanji(bmp: u16) -> Option<(u8, u8)> {
183    if let Some((lead, trail)) = jis0208_level1_kanji_shift_jis_encode(bmp) {
184        return Some((lead, trail));
185    }
186    let pointer = if 0x4EDD == bmp {
187        // Ideograph on the symbol row!
188        23
189    } else if let Some(pos) = jis0208_level2_and_additional_kanji_encode(bmp) {
190        4418 + pos
191    } else {
192        let pos = position(&IBM_KANJI[..], bmp)?;
193        10744 + pos
194    };
195    let lead = pointer / 188;
196    let lead_offset = if lead < 0x1F { 0x81usize } else { 0xC1usize };
197    let trail = pointer % 188;
198    let trail_offset = if trail < 0x3F { 0x40usize } else { 0x41usize };
199    Some(((lead + lead_offset) as u8, (trail + trail_offset) as u8))
200}
201
202pub struct ShiftJisEncoder;
203
204impl ShiftJisEncoder {
205    pub fn new(encoding: &'static Encoding) -> Encoder {
206        Encoder::new(encoding, VariantEncoder::ShiftJis(ShiftJisEncoder))
207    }
208
209    pub fn max_buffer_length_from_utf16_without_replacement(
210        &self,
211        u16_length: usize,
212    ) -> Option<usize> {
213        u16_length.checked_mul(2)
214    }
215
216    pub fn max_buffer_length_from_utf8_without_replacement(
217        &self,
218        byte_length: usize,
219    ) -> Option<usize> {
220        byte_length.checked_add(1)
221    }
222
223    ascii_compatible_bmp_encoder_functions!(
224        {
225            // Lunde says 60% Hiragana, 30% Kanji, 10% Katakana
226            let bmp_minus_hiragana = bmp.wrapping_sub(0x3041);
227            if bmp_minus_hiragana < 0x53 {
228                handle.write_two(0x82, 0x9F + bmp_minus_hiragana as u8)
229            } else if in_inclusive_range16(bmp, 0x4E00, 0x9FA0) {
230                if let Some((lead, trail)) = encode_kanji(bmp) {
231                    handle.write_two(lead, trail)
232                } else {
233                    return (
234                        EncoderResult::unmappable_from_bmp(bmp),
235                        source.consumed(),
236                        handle.written(),
237                    );
238                }
239            } else {
240                let bmp_minus_katakana = bmp.wrapping_sub(0x30A1);
241                if bmp_minus_katakana < 0x56 {
242                    let trail_offset = if bmp_minus_katakana < 0x3F {
243                        0x40
244                    } else {
245                        0x41
246                    };
247                    handle.write_two(0x83, (trail_offset + bmp_minus_katakana) as u8)
248                } else {
249                    let bmp_minus_space = bmp.wrapping_sub(0x3000);
250                    if bmp_minus_space < 3 {
251                        // fast-track common punctuation
252                        handle.write_two(0x81, 0x40 + bmp_minus_space as u8)
253                    } else if bmp == 0xA5 {
254                        handle.write_one(0x5Cu8)
255                    } else if bmp == 0x80 {
256                        handle.write_one(0x80u8)
257                    } else if bmp == 0x203E {
258                        handle.write_one(0x7Eu8)
259                    } else if in_inclusive_range16(bmp, 0xFF61, 0xFF9F) {
260                        handle.write_one((bmp - (0xFF61 - 0xA1)) as u8)
261                    } else if bmp == 0x2212 {
262                        handle.write_two(0x81u8, 0x7Cu8)
263                    } else {
264                        let bmp_minus_roman = bmp.wrapping_sub(0x2170);
265                        let pointer = if bmp_minus_roman <= (0x2179 - 0x2170) {
266                            10716 + bmp_minus_roman as usize
267                        } else if let Some(pointer) = jis0208_range_encode(bmp) {
268                            pointer
269                        } else if in_inclusive_range16(bmp, 0xFA0E, 0xFA2D)
270                            || bmp == 0xF929
271                            || bmp == 0xF9DC
272                        {
273                            // Guaranteed to be found in IBM_KANJI
274                            let pos = position(&IBM_KANJI[..], bmp).unwrap();
275                            10744 + pos
276                        } else if let Some(pointer) = jis0208_symbol_encode(bmp) {
277                            pointer
278                        } else {
279                            return (
280                                EncoderResult::unmappable_from_bmp(bmp),
281                                source.consumed(),
282                                handle.written(),
283                            );
284                        };
285                        let lead = pointer / 188;
286                        let lead_offset = if lead < 0x1F { 0x81usize } else { 0xC1usize };
287                        let trail = pointer % 188;
288                        let trail_offset = if trail < 0x3F { 0x40usize } else { 0x41usize };
289                        handle.write_two((lead + lead_offset) as u8, (trail + trail_offset) as u8)
290                    }
291                }
292            }
293        },
294        bmp,
295        self,
296        source,
297        handle,
298        copy_ascii_to_check_space_two,
299        check_space_two,
300        false
301    );
302}
303
304// Any copyright to the test code below this comment is dedicated to the
305// Public Domain. http://creativecommons.org/publicdomain/zero/1.0/
306
307#[cfg(all(test, feature = "alloc"))]
308mod tests {
309    use super::super::testing::*;
310    use super::super::*;
311
312    fn decode_shift_jis(bytes: &[u8], expect: &str) {
313        decode(SHIFT_JIS, bytes, expect);
314    }
315
316    fn encode_shift_jis(string: &str, expect: &[u8]) {
317        encode(SHIFT_JIS, string, expect);
318    }
319
320    #[test]
321    fn test_shift_jis_decode() {
322        // Empty
323        decode_shift_jis(b"", &"");
324
325        // ASCII
326        decode_shift_jis(b"\x61\x62", "\u{0061}\u{0062}");
327
328        // Half-width
329        decode_shift_jis(b"\xA1", "\u{FF61}");
330        decode_shift_jis(b"\xDF", "\u{FF9F}");
331        decode_shift_jis(b"\xA0", "\u{FFFD}");
332        decode_shift_jis(b"\xE0", "\u{FFFD}");
333        decode_shift_jis(b"\xA0+", "\u{FFFD}+");
334        decode_shift_jis(b"\xE0+", "\u{FFFD}+");
335
336        // EUDC
337        decode_shift_jis(b"\xF0\x40", "\u{E000}");
338        decode_shift_jis(b"\xF9\xFC", "\u{E757}");
339        decode_shift_jis(b"\xEF\xFC", "\u{FFFD}");
340        decode_shift_jis(b"\xFA\x40", "\u{2170}");
341
342        // JIS 0208
343        decode_shift_jis(b"\x81\x40", "\u{3000}");
344        decode_shift_jis(b"\x81\x3F", "\u{FFFD}?");
345        decode_shift_jis(b"\xEE\xFC", "\u{FF02}");
346        decode_shift_jis(b"\xEE\xFD", "\u{FFFD}");
347        decode_shift_jis(b"\xFA\x40", "\u{2170}");
348        decode_shift_jis(b"\xFA\x3F", "\u{FFFD}?");
349        decode_shift_jis(b"\xFC\x4B", "\u{9ED1}");
350        decode_shift_jis(b"\xFC\x4C", "\u{FFFD}L");
351        //
352    }
353
354    #[test]
355    fn test_shift_jis_encode() {
356        // Empty
357        encode_shift_jis("", b"");
358
359        // ASCII
360        encode_shift_jis("\u{0061}\u{0062}", b"\x61\x62");
361
362        // Exceptional code points
363        encode_shift_jis("\u{0080}", b"\x80");
364        encode_shift_jis("\u{00A5}", b"\x5C");
365        encode_shift_jis("\u{203E}", b"\x7E");
366        encode_shift_jis("\u{2212}", b"\x81\x7C");
367
368        // Half-width
369        encode_shift_jis("\u{FF61}", b"\xA1");
370        encode_shift_jis("\u{FF9F}", b"\xDF");
371
372        // EUDC
373        encode_shift_jis("\u{E000}", b"&#57344;");
374        encode_shift_jis("\u{E757}", b"&#59223;");
375
376        // JIS 0212
377        encode_shift_jis("\u{02D8}", b"&#728;");
378
379        // JIS 0208
380        encode_shift_jis("\u{3000}", b"\x81\x40");
381        encode_shift_jis("\u{FF02}", b"\xFA\x57");
382        encode_shift_jis("\u{2170}", b"\xFA\x40");
383        encode_shift_jis("\u{9ED1}", b"\xFC\x4B");
384    }
385
386    #[test]
387    fn test_shift_jis_half_width_katakana_length() {
388        let mut output = [0u8; 20];
389        let mut decoder = SHIFT_JIS.new_decoder();
390        {
391            let needed = decoder
392                .max_utf8_buffer_length_without_replacement(1)
393                .unwrap();
394            let (result, read, written) =
395                decoder.decode_to_utf8_without_replacement(b"\xA1", &mut output[..needed], true);
396            assert_eq!(result, DecoderResult::InputEmpty);
397            assert_eq!(read, 1);
398            assert_eq!(written, 3);
399            assert_eq!(output[0], 0xEF);
400            assert_eq!(output[1], 0xBD);
401            assert_eq!(output[2], 0xA1);
402        }
403    }
404}