Skip to main content

encoding_rs/
gb18030.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::gb18030_2022::*;
13use crate::handles::*;
14use crate::variant::*;
15// Rust 1.14.0 requires the following despite the asterisk above.
16use super::in_inclusive_range16;
17use super::in_range16;
18
19enum Gb18030Pending {
20    None,
21    One(u8),
22    Two(u8, u8),
23    Three(u8, u8, u8),
24}
25
26impl Gb18030Pending {
27    fn is_none(&self) -> bool {
28        matches!(*self, Gb18030Pending::None)
29    }
30
31    fn count(&self) -> usize {
32        match *self {
33            Gb18030Pending::None => 0,
34            Gb18030Pending::One(_) => 1,
35            Gb18030Pending::Two(_, _) => 2,
36            Gb18030Pending::Three(_, _, _) => 3,
37        }
38    }
39}
40
41pub struct Gb18030Decoder {
42    first: Option<u8>,
43    second: Option<u8>,
44    third: Option<u8>,
45    pending: Gb18030Pending,
46    pending_ascii: Option<u8>,
47}
48
49impl Gb18030Decoder {
50    pub fn new() -> VariantDecoder {
51        VariantDecoder::Gb18030(Gb18030Decoder {
52            first: None,
53            second: None,
54            third: None,
55            pending: Gb18030Pending::None,
56            pending_ascii: None,
57        })
58    }
59
60    pub fn in_neutral_state(&self) -> bool {
61        self.first.is_none()
62            && self.second.is_none()
63            && self.third.is_none()
64            && self.pending.is_none()
65            && self.pending_ascii.is_none()
66    }
67
68    fn extra_from_state(&self, byte_length: usize) -> Option<usize> {
69        byte_length.checked_add(
70            self.pending.count()
71                + match self.first {
72                    None => 0,
73                    Some(_) => 1,
74                }
75                + match self.second {
76                    None => 0,
77                    Some(_) => 1,
78                }
79                + match self.third {
80                    None => 0,
81                    Some(_) => 1,
82                }
83                + match self.pending_ascii {
84                    None => 0,
85                    Some(_) => 1,
86                },
87        )
88    }
89
90    pub fn max_utf16_buffer_length(&self, byte_length: usize) -> Option<usize> {
91        // ASCII: 1 to 1 (worst case)
92        // gbk: 2 to 1
93        // ranges: 4 to 1 or 4 to 2
94        checked_add(1, self.extra_from_state(byte_length))
95    }
96
97    pub fn max_utf8_buffer_length_without_replacement(&self, byte_length: usize) -> Option<usize> {
98        // ASCII: 1 to 1
99        // gbk: 2 to 2 or 2 to 3
100        // ranges: 4 to 2, 4 to 3 or 4 to 4
101        // 0x80: 1 to 3 (worst case)
102        self.max_utf8_buffer_length(byte_length)
103    }
104
105    pub fn max_utf8_buffer_length(&self, byte_length: usize) -> Option<usize> {
106        checked_add(1, checked_mul(3, self.extra_from_state(byte_length)))
107    }
108
109    gb18030_decoder_functions!(
110        {
111            // If first is between 0x81 and 0xFE, inclusive,
112            // subtract offset 0x81.
113            let non_ascii_minus_offset = non_ascii.wrapping_sub(0x81);
114            if non_ascii_minus_offset > (0xFE - 0x81) {
115                if non_ascii == 0x80 {
116                    handle.write_upper_bmp(0x20ACu16);
117                    continue 'outermost;
118                }
119                return (DecoderResult::Malformed(1, 0),
120                        source.consumed(),
121                        handle.written());
122            }
123            non_ascii_minus_offset
124        },
125        {
126            // Two-byte (or error)
127            if first_minus_offset >= 0x20 {
128                // Not the gbk ideograph range above GB2312
129                let trail_minus_offset = second.wrapping_sub(0xA1);
130                if trail_minus_offset <= (0xFE - 0xA1) {
131                    // GB2312
132                    let hanzi_lead = first_minus_offset.wrapping_sub(0x2F);
133                    if hanzi_lead < (0x77 - 0x2F) {
134                        // Level 1 Hanzi, Level 2 Hanzi
135                        // or one of the 5 PUA code
136                        // points in between.
137                        let hanzi_pointer = mul_94(hanzi_lead) + trail_minus_offset as usize;
138                        let upper_bmp = GB2312_HANZI[hanzi_pointer];
139                        handle.write_upper_bmp(upper_bmp)
140                    } else if first_minus_offset == 0x20 {
141                        // Symbols (starting with ideographic space)
142                        let bmp = GB2312_SYMBOLS[trail_minus_offset as usize];
143                        handle.write_bmp_excl_ascii(bmp)
144                    } else if first_minus_offset == 0x25 && ((trail_minus_offset.wrapping_sub(63) as usize) < GB2312_SYMBOLS_AFTER_GREEK.len()) {
145                        handle.write_bmp_excl_ascii(GB2312_SYMBOLS_AFTER_GREEK[trail_minus_offset.wrapping_sub(63) as usize])
146                    } else if first_minus_offset == 0x27 && (trail_minus_offset as usize) < GB2312_PINYIN.len() {
147                        handle.write_bmp_excl_ascii(GB2312_PINYIN[trail_minus_offset as usize])
148                    } else if first_minus_offset > 0x76 {
149                        // Bottom PUA
150                        let pua = (0xE234 + mul_94(first_minus_offset - 0x77) + trail_minus_offset as usize) as u16;
151                        handle.write_upper_bmp(pua)
152                    } else {
153                        let bmp = gb2312_other_decode((mul_94(first_minus_offset - 0x21) + (trail_minus_offset as usize)) as u16);
154                        handle.write_bmp_excl_ascii(bmp)
155                    }
156                } else {
157                    // gbk range on the left
158                    let mut trail_minus_offset = second.wrapping_sub(0x40);
159                    if trail_minus_offset > (0x7E - 0x40) {
160                        let trail_minus_range_start = second.wrapping_sub(0x80);
161                        if trail_minus_range_start > (0xA0 - 0x80) {
162                            if second < 0x80 {
163                                return (DecoderResult::Malformed(1, 0),
164                                        unread_handle_second.unread(),
165                                        handle.written());
166                            }
167                            return (DecoderResult::Malformed(2, 0),
168                                    unread_handle_second.consumed(),
169                                    handle.written());
170                        }
171                        trail_minus_offset = second - 0x41;
172                    }
173                    // Zero-base lead
174                    let left_lead = first_minus_offset - 0x20;
175                    let left_pointer = left_lead as usize * (190 - 94) +
176                                       trail_minus_offset as usize;
177                    let gbk_left_ideograph_pointer = left_pointer.wrapping_sub((0x29 - 0x20) * (190 - 94));
178                    if gbk_left_ideograph_pointer < (((0x7D - 0x29) * (190 - 94)) - 5) {
179                        let upper_bmp = gbk_left_ideograph_decode(gbk_left_ideograph_pointer as u16);
180                        handle.write_upper_bmp(upper_bmp)
181                    } else if left_pointer < ((0x29 - 0x20) * (190 - 94)) {
182                        let bmp = gbk_other_decode(left_pointer as u16);
183                        handle.write_bmp_excl_ascii(bmp)
184                    } else {
185                        let bottom_pointer = left_pointer - (((0x7D - 0x20) * (190 - 94)) - 5);
186                        let upper_bmp = GBK_BOTTOM[bottom_pointer];
187                        handle.write_upper_bmp(upper_bmp)
188                    }
189                }
190            } else {
191                // gbk ideograph range above GB2312
192                let mut trail_minus_offset = second.wrapping_sub(0x40);
193                if trail_minus_offset > (0x7E - 0x40) {
194                    let trail_minus_range_start = second.wrapping_sub(0x80);
195                    if trail_minus_range_start > (0xFE - 0x80) {
196                        if second < 0x80 {
197                            return (DecoderResult::Malformed(1, 0),
198                                    unread_handle_second.unread(),
199                                    handle.written());
200                        }
201                        return (DecoderResult::Malformed(2, 0),
202                                unread_handle_second.consumed(),
203                                handle.written());
204                    }
205                    trail_minus_offset = second - 0x41;
206                }
207                let pointer = first_minus_offset as usize * 190usize +
208                              trail_minus_offset as usize;
209                let upper_bmp = gbk_top_ideograph_decode(pointer as u16);
210                handle.write_upper_bmp(upper_bmp)
211            }
212        },
213        {
214            // If third is between 0x81 and 0xFE, inclusive,
215            // subtract offset 0x81.
216            let third_minus_offset = third.wrapping_sub(0x81);
217            if third_minus_offset > (0xFE - 0x81) {
218                // We have an error. Let's inline what's going
219                // to happen when `second` is
220                // reprocessed. (`third` gets unread.)
221                // `second` is guaranteed ASCII, so let's
222                // put it in `pending_ascii`. Recompute
223                // `second` from `second_minus_offset`.
224                self.pending_ascii = Some(second_minus_offset + 0x30);
225                // Now unread `third` and designate the previous
226                // `first` as being in error.
227                return (DecoderResult::Malformed(1, 1),
228                        unread_handle_third.unread(),
229                        handle.written());
230            }
231            third_minus_offset
232        },
233        {
234            // If fourth is between 0x30 and 0x39, inclusive,
235            // subtract offset 0x30.
236            //
237            // If we have an error, we'll inline what's going
238            // to happen when `second` and `third` are
239            // reprocessed. (`fourth` gets unread.)
240            // `second` is guaranteed ASCII, so let's
241            // put it in `pending_ascii`. Recompute
242            // `second` from `second_minus_offset` to
243            // make this block reusable when `second`
244            // is not in scope.
245            //
246            // `third` is guaranteed to be in the range
247            // that makes it become the new `self.first`.
248            //
249            // `fourth` gets unread and the previous
250            // `first` gets designates as being in error.
251            let fourth_minus_offset = fourth.wrapping_sub(0x30);
252            if fourth_minus_offset > (0x39 - 0x30) {
253                self.pending_ascii = Some(second_minus_offset + 0x30);
254                self.pending = Gb18030Pending::One(third_minus_offset);
255                return (DecoderResult::Malformed(1, 2),
256                        unread_handle_fourth.unread(),
257                        handle.written());
258            }
259            let pointer = (first_minus_offset as usize * (10 * 126 * 10)) +
260                          (second_minus_offset as usize * (10 * 126)) +
261                          (third_minus_offset as usize * 10) +
262                          fourth_minus_offset as usize;
263            if pointer <= 39419 {
264                // BMP
265                if pointer == 7457 {
266                    handle.write_upper_bmp(0xE7C7)
267                } else {
268                    handle.write_bmp_excl_ascii(gb18030_range_decode(pointer as u16))
269                }
270            } else if (189_000..=1_237_575).contains(&pointer) {
271                // Astral
272                handle.write_astral((pointer - (189_000usize - 0x1_0000usize)) as u32)
273            } else {
274                return (DecoderResult::Malformed(4, 0),
275                        unread_handle_fourth.consumed(),
276                        handle.written());
277            }
278        },
279        self,
280        non_ascii,
281        first_minus_offset,
282        second,
283        second_minus_offset,
284        unread_handle_second,
285        third,
286        third_minus_offset,
287        unread_handle_third,
288        fourth,
289        fourth_minus_offset,
290        unread_handle_fourth,
291        source,
292        handle,
293        'outermost);
294}
295
296// XXX Experiment with inline directives
297fn gbk_encode_non_unified(bmp: u16) -> Option<(usize, usize)> {
298    // Try ideographic punctuation first as it's the most likely case.
299    // Throwing in the check for full-width currencies and tilde is probably
300    // more size-efficient here than elsewhere.
301    if (in_inclusive_range16(bmp, 0x2014, 0x3017) || in_inclusive_range16(bmp, 0xFF04, 0xFFE1))
302        && let Some(pos) = position(&GB2312_SYMBOLS[..], bmp)
303    {
304        return Some((0xA1, pos + 0xA1));
305    }
306    // Ext A
307    if in_range16(bmp, 0x3400, 0x4E00) {
308        return position(&GBK_BOTTOM[21..100], bmp).map(|pos| {
309            (
310                0xFE,
311                pos + if pos < (0x3F - 16) {
312                    0x40 + 16
313                } else {
314                    0x41 + 16
315                },
316            )
317        });
318    }
319    // Compatibility ideographs
320    if in_range16(bmp, 0xF900, 0xFB00) {
321        return position(&GBK_BOTTOM[0..21], bmp).map(|pos| {
322            if pos < 5 {
323                // end of second to last row
324                (0xFD, pos + (190 - 94 - 5 + 0x41))
325            } else {
326                // last row
327                (0xFE, pos + (0x40 - 5))
328            }
329        });
330    }
331    // Handle everything below U+02CA, which is in GBK_OTHER.
332    if bmp < 0x02CA {
333        if in_range16(bmp, 0x00E0, 0x0262) && bmp != 0x00F7 {
334            // Pinyin except U+1E3F
335            if let Some(pos) = position(&GB2312_PINYIN[..], bmp) {
336                return Some((0xA8, pos + 0xA1));
337            }
338        } else if in_inclusive_range16(bmp, 0x00A4, 0x00F7)
339            || in_inclusive_range16(bmp, 0x02C7, 0x02C9)
340        {
341            // Diacritics and Latin 1 symbols
342            if let Some(pos) = position(&GB2312_SYMBOLS[3..(0xAC - 0x60)], bmp) {
343                return Some((0xA1, pos + 0xA1 + 3));
344            }
345        }
346        return None;
347    }
348
349    if in_inclusive_range16(bmp, 0xE78D, 0xE864) {
350        // The array is sorted but short, so let's do linear search.
351        if let Some(pos) = position(&GB18030_2022_OVERRIDE_PUA[..], bmp) {
352            let pair = &GB18030_2022_OVERRIDE_BYTES[pos];
353            return Some((pair[0].into(), pair[1].into()));
354        }
355    } else if bmp >= 0xFE17 {
356        // Various brackets, all in full-width regions
357        if let Some(pos) = position(&GB2312_SYMBOLS_AFTER_GREEK[..], bmp) {
358            return Some((0xA6, pos + (0x9F - 0x60 + 0xA1)));
359        }
360    } else if bmp == 0x1E3F {
361        // The one Pinyin placed elsewhere on the BMP
362        return Some((0xA8, 0x7B - 0x60 + 0xA1));
363    } else if in_range16(bmp, 0xA000, 0xD800) {
364        // Since Korean has usage in China, let's spend a branch to fast-track
365        // Hangul.
366        return None;
367    }
368    // GB2312 other (except bottom PUA and PUA between Hanzi levels).
369    if let Some(other_pointer) = gb2312_other_encode(bmp) {
370        let other_lead = other_pointer as usize / 94;
371        let other_trail = other_pointer as usize % 94;
372        return Some((0xA2 + other_lead, 0xA1 + other_trail));
373    }
374    // At this point, we've handled all mappable characters above U+02D9 but
375    // below U+2010. Let's check for that range in order to let lower BMP
376    // characters used for minority languages in China avoid the subsequent
377    // search that deals mainly with various symbols.
378    if in_range16(bmp, 0x02DA, 0x2010) {
379        return None;
380    }
381    // GBK other (except radicals and PUA in GBK_BOTTOM).
382    if let Some(other_pointer) = gbk_other_encode(bmp) {
383        let other_lead = other_pointer as usize / (190 - 94);
384        let other_trail = other_pointer as usize % (190 - 94);
385        let offset = if other_trail < 0x3F { 0x40 } else { 0x41 };
386        return Some((other_lead + (0x81 + 0x20), other_trail + offset));
387    }
388    // CJK Radicals Supplement, PUA, and U+9FBx ideographs in GBK_BOTTOM
389    if (in_inclusive_range16(bmp, 0x2E81, 0x2ECA)
390        || in_inclusive_range16(bmp, 0x9FB4, 0x9FBB)
391        || in_inclusive_range16(bmp, 0xE816, 0xE855))
392        && let Some(pos) = position(&GBK_BOTTOM[21..], bmp)
393    {
394        let trail = pos + 16;
395        let offset = if trail < 0x3F { 0x40 } else { 0x41 };
396        return Some((0xFE, trail + offset));
397    }
398    // GB2312 bottom PUA
399    let bmp_minus_gb2312_bottom_pua = bmp.wrapping_sub(0xE234);
400    if bmp_minus_gb2312_bottom_pua <= (0xE4C5 - 0xE234) {
401        let pua_lead = bmp_minus_gb2312_bottom_pua as usize / 94;
402        let pua_trail = bmp_minus_gb2312_bottom_pua as usize % 94;
403        return Some((0x81 + 0x77 + pua_lead, 0xA1 + pua_trail));
404    }
405    // PUA between Hanzi Levels
406    let bmp_minus_pua_between_hanzi = bmp.wrapping_sub(0xE810);
407    if bmp_minus_pua_between_hanzi < 5 {
408        return Some((0x81 + 0x56, 0xFF - 5 + bmp_minus_pua_between_hanzi as usize));
409    }
410    None
411}
412
413#[cfg(not(feature = "fast-gb-hanzi-encode"))]
414#[inline(always)]
415fn encode_hanzi(bmp: u16, _: u16) -> (u8, u8) {
416    if let Some((lead, trail)) = gb2312_level1_hanzi_encode(bmp) {
417        (lead, trail)
418    } else if let Some(hanzi_pointer) = gb2312_level2_hanzi_encode(bmp) {
419        let hanzi_lead = (hanzi_pointer / 94) + (0xD8);
420        let hanzi_trail = (hanzi_pointer % 94) + 0xA1;
421        (hanzi_lead as u8, hanzi_trail as u8)
422    } else {
423        let (lead, gbk_trail) = if bmp < 0x72DC {
424            // Above GB2312
425            let pointer = gbk_top_ideograph_encode(bmp) as usize;
426            let lead = (pointer / 190) + 0x81;
427            let gbk_trail = pointer % 190;
428            (lead, gbk_trail)
429        } else {
430            // To the left of GB2312
431            let gbk_left_ideograph_pointer = gbk_left_ideograph_encode(bmp) as usize;
432            let lead = (gbk_left_ideograph_pointer / (190 - 94)) + (0x81 + 0x29);
433            let gbk_trail = gbk_left_ideograph_pointer % (190 - 94);
434            (lead, gbk_trail)
435        };
436        let offset = if gbk_trail < 0x3F { 0x40 } else { 0x41 };
437        (lead as u8, (gbk_trail + offset) as u8)
438    }
439}
440
441#[cfg(feature = "fast-gb-hanzi-encode")]
442#[inline(always)]
443fn encode_hanzi(_: u16, bmp_minus_unified_start: u16) -> (u8, u8) {
444    gbk_hanzi_encode(bmp_minus_unified_start)
445}
446
447pub struct Gb18030Encoder {
448    extended: bool,
449}
450
451impl Gb18030Encoder {
452    pub fn new(encoding: &'static Encoding, extended_range: bool) -> Encoder {
453        Encoder::new(
454            encoding,
455            VariantEncoder::Gb18030(Gb18030Encoder {
456                extended: extended_range,
457            }),
458        )
459    }
460
461    pub fn max_buffer_length_from_utf16_without_replacement(
462        &self,
463        u16_length: usize,
464    ) -> Option<usize> {
465        if self.extended {
466            u16_length.checked_mul(4)
467        } else {
468            // Need to add, because space check is done with the four-byte
469            // assumption.
470            checked_add(2, u16_length.checked_mul(2))
471        }
472    }
473
474    pub fn max_buffer_length_from_utf8_without_replacement(
475        &self,
476        byte_length: usize,
477    ) -> Option<usize> {
478        if self.extended {
479            // 1 to 1
480            // 2 to 2
481            // 3 to 2
482            // 2 to 4 (worst)
483            // 3 to 4
484            // 4 to 4
485            checked_add(2, byte_length.checked_mul(2))
486        } else {
487            // 1 to 1
488            // 2 to 2
489            // 3 to 2
490            // Need to add, because space check is done with the four-byte
491            // assumption.
492            byte_length.checked_add(3)
493        }
494    }
495
496    ascii_compatible_encoder_functions!(
497        {
498            let bmp_minus_unified_start = bmp.wrapping_sub(0x4E00);
499            if bmp_minus_unified_start < (0x9FA6 - 0x4E00) {
500                // CJK Unified Ideographs
501                // Can't fail now, since all are
502                // mapped.
503                let (lead, trail) = encode_hanzi(bmp, bmp_minus_unified_start);
504                handle.write_two(lead, trail)
505            } else if bmp == 0xE5E5 {
506                // It's not optimal to check for the unmappable
507                // and for euro at this stage, but getting
508                // the out of the way makes the rest of the
509                // code less messy.
510                return (
511                    EncoderResult::unmappable_from_bmp(bmp),
512                    source.consumed(),
513                    handle.written(),
514                );
515            } else if bmp == 0x20AC && !self.extended {
516                handle.write_one(0x80u8)
517            } else {
518                match gbk_encode_non_unified(bmp) {
519                    Some((lead, trail)) => handle.write_two(lead as u8, trail as u8),
520                    None => {
521                        if !self.extended {
522                            return (
523                                EncoderResult::unmappable_from_bmp(bmp),
524                                source.consumed(),
525                                handle.written(),
526                            );
527                        }
528                        let range_pointer = gb18030_range_encode(bmp);
529                        let first = range_pointer / (10 * 126 * 10);
530                        let rem_first = range_pointer % (10 * 126 * 10);
531                        let second = rem_first / (10 * 126);
532                        let rem_second = rem_first % (10 * 126);
533                        let third = rem_second / 10;
534                        let fourth = rem_second % 10;
535                        handle.write_four(
536                            (first + 0x81) as u8,
537                            (second + 0x30) as u8,
538                            (third + 0x81) as u8,
539                            (fourth + 0x30) as u8,
540                        )
541                    }
542                }
543            }
544        },
545        {
546            if !self.extended {
547                return (
548                    EncoderResult::Unmappable(astral),
549                    source.consumed(),
550                    handle.written(),
551                );
552            }
553            let range_pointer = astral as usize + (189_000usize - 0x1_0000usize);
554            let first = range_pointer / (10 * 126 * 10);
555            let rem_first = range_pointer % (10 * 126 * 10);
556            let second = rem_first / (10 * 126);
557            let rem_second = rem_first % (10 * 126);
558            let third = rem_second / 10;
559            let fourth = rem_second % 10;
560            handle.write_four(
561                (first + 0x81) as u8,
562                (second + 0x30) as u8,
563                (third + 0x81) as u8,
564                (fourth + 0x30) as u8,
565            )
566        },
567        bmp,
568        astral,
569        self,
570        source,
571        handle,
572        copy_ascii_to_check_space_four,
573        check_space_four,
574        false
575    );
576}
577
578// Any copyright to the test code below this comment is dedicated to the
579// Public Domain. http://creativecommons.org/publicdomain/zero/1.0/
580
581#[cfg(all(test, feature = "alloc"))]
582mod tests {
583    use super::super::testing::*;
584    use super::super::*;
585
586    fn decode_gb18030(bytes: &[u8], expect: &str) {
587        decode(GB18030, bytes, expect);
588    }
589
590    fn encode_gb18030(string: &str, expect: &[u8]) {
591        encode(GB18030, string, expect);
592    }
593
594    fn encode_gbk(string: &str, expect: &[u8]) {
595        encode(GBK, string, expect);
596    }
597
598    #[test]
599    fn test_gb18030_decode() {
600        // Empty
601        decode_gb18030(b"", &"");
602
603        // ASCII
604        decode_gb18030(b"\x61\x62", "\u{0061}\u{0062}");
605
606        // euro
607        decode_gb18030(b"\x80", "\u{20AC}");
608        decode_gb18030(b"\xA2\xE3", "\u{20AC}");
609
610        // two bytes
611        decode_gb18030(b"\x81\x40", "\u{4E02}");
612        decode_gb18030(b"\x81\x7E", "\u{4E8A}");
613        decode_gb18030(b"\x81\x7F", "\u{FFFD}\u{007F}");
614        decode_gb18030(b"\x81\x80", "\u{4E90}");
615        decode_gb18030(b"\x81\xFE", "\u{4FA2}");
616        decode_gb18030(b"\xFE\x40", "\u{FA0C}");
617        decode_gb18030(b"\xFE\x7F", "\u{FFFD}\u{007F}");
618        decode_gb18030(b"\xFE\x80", "\u{4723}");
619        decode_gb18030(b"\xFE\xFE", "\u{E4C5}");
620
621        // Changes between GB18030-2005 and GB18030-2022
622        decode_gb18030(b"\xFE\x7E", "\u{9FB9}");
623        decode_gb18030(b"\xA6\xDD", "\u{FE14}");
624
625        // These mappings remain in place the GB18030-2005 way despite GB18030-2022
626        decode_gb18030(b"\x82\x35\x91\x32", "\u{9FB9}");
627        decode_gb18030(b"\x84\x31\x83\x30", "\u{FE14}");
628
629        // The difference from the original GB18030
630        decode_gb18030(b"\xA3\xA0", "\u{3000}");
631        decode_gb18030(b"\xA1\xA1", "\u{3000}");
632
633        // 0xFF
634        decode_gb18030(b"\xFF\x40", "\u{FFFD}\u{0040}");
635        decode_gb18030(b"\xE3\xFF\x9A\x33", "\u{FFFD}\u{FFFD}"); // not \u{FFFD}\u{FFFD}\u{0033} !
636        decode_gb18030(b"\xFF\x32\x9A\x33", "\u{FFFD}\u{0032}\u{FFFD}"); // not \u{FFFD}\u{0032}\u{FFFD}\u{0033} !
637        decode_gb18030(b"\xFF\x40\x00", "\u{FFFD}\u{0040}\u{0000}");
638        decode_gb18030(b"\xE3\xFF\x9A\x33\x00", "\u{FFFD}\u{FFFD}\u{0033}\u{0000}");
639        decode_gb18030(
640            b"\xFF\x32\x9A\x33\x00",
641            "\u{FFFD}\u{0032}\u{FFFD}\u{0033}\u{0000}",
642        );
643
644        // Four bytes
645        decode_gb18030(b"\x81\x30\x81\x30", "\u{0080}");
646        decode_gb18030(b"\x81\x35\xF4\x37", "\u{E7C7}");
647        decode_gb18030(b"\x81\x37\xA3\x30", "\u{2603}");
648        decode_gb18030(b"\x94\x39\xDA\x33", "\u{1F4A9}");
649        decode_gb18030(b"\xE3\x32\x9A\x35", "\u{10FFFF}");
650        decode_gb18030(b"\xE3\x32\x9A\x36\x81\x30", "\u{FFFD}\u{FFFD}");
651        decode_gb18030(b"\xE3\x32\x9A\x36\x81\x40", "\u{FFFD}\u{4E02}");
652        decode_gb18030(b"\xE3\x32\x9A", "\u{FFFD}"); // not \u{FFFD}\u{0032}\u{FFFD} !
653        decode_gb18030(b"\xE3\x32\x9A\x00", "\u{FFFD}\u{0032}\u{FFFD}\u{0000}");
654    }
655
656    #[test]
657    fn test_gb18030_encode() {
658        // Empty
659        encode_gb18030("", b"");
660
661        // ASCII
662        encode_gb18030("\u{0061}\u{0062}", b"\x61\x62");
663
664        // euro
665        encode_gb18030("\u{20AC}", b"\xA2\xE3");
666
667        // two bytes
668        encode_gb18030("\u{4E02}", b"\x81\x40");
669        encode_gb18030("\u{4E8A}", b"\x81\x7E");
670        if !cfg!(miri) {
671            // Miri is too slow
672            encode_gb18030("\u{4E90}", b"\x81\x80");
673            encode_gb18030("\u{4FA2}", b"\x81\xFE");
674            encode_gb18030("\u{FA0C}", b"\xFE\x40");
675            encode_gb18030("\u{E843}", b"\xFE\x7E");
676            encode_gb18030("\u{4723}", b"\xFE\x80");
677            encode_gb18030("\u{E4C5}", b"\xFE\xFE");
678        }
679
680        // The difference from the original GB18030
681        encode_gb18030("\u{E5E5}", b"&#58853;");
682        encode_gb18030("\u{3000}", b"\xA1\xA1");
683
684        // Four bytes
685        encode_gb18030("\u{0080}", b"\x81\x30\x81\x30");
686        encode_gb18030("\u{E7C7}", b"\x81\x35\xF4\x37");
687        if !cfg!(miri) {
688            // Miri is too slow
689            encode_gb18030("\u{2603}", b"\x81\x37\xA3\x30");
690            encode_gb18030("\u{1F4A9}", b"\x94\x39\xDA\x33");
691            encode_gb18030("\u{10FFFF}", b"\xE3\x32\x9A\x35");
692        }
693
694        // Edge cases
695        encode_gb18030("\u{00F7}", b"\xA1\xC2");
696
697        // GB18030-2022
698        encode_gb18030("\u{9FB9}", b"\xFE\x7E");
699        encode_gb18030("\u{FE14}", b"\xA6\xDD");
700        encode_gb18030("\u{E843}", b"\xFE\x7E");
701        encode_gb18030("\u{E791}", b"\xA6\xDD");
702
703        // Non-change in GB18030-2022
704        encode_gb18030("\u{E817}", b"\xFE\x52");
705    }
706
707    #[test]
708    fn test_gbk_encode() {
709        // Empty
710        encode_gbk("", b"");
711
712        // ASCII
713        encode_gbk("\u{0061}\u{0062}", b"\x61\x62");
714
715        // euro
716        encode_gbk("\u{20AC}", b"\x80");
717
718        // two bytes
719        encode_gbk("\u{4E02}", b"\x81\x40");
720        encode_gbk("\u{4E8A}", b"\x81\x7E");
721        if !cfg!(miri) {
722            // Miri is too slow
723            encode_gbk("\u{4E90}", b"\x81\x80");
724            encode_gbk("\u{4FA2}", b"\x81\xFE");
725            encode_gbk("\u{FA0C}", b"\xFE\x40");
726            encode_gbk("\u{E843}", b"\xFE\x7E");
727            encode_gbk("\u{4723}", b"\xFE\x80");
728            encode_gbk("\u{E4C5}", b"\xFE\xFE");
729        }
730
731        // The difference from the original gb18030
732        encode_gbk("\u{E5E5}", b"&#58853;");
733        encode_gbk("\u{3000}", b"\xA1\xA1");
734
735        // Four bytes
736        encode_gbk("\u{0080}", b"&#128;");
737        encode_gbk("\u{E7C7}", b"&#59335;");
738        if !cfg!(miri) {
739            // Miri is too slow
740            encode_gbk("\u{2603}", b"&#9731;");
741            encode_gbk("\u{1F4A9}", b"&#128169;");
742            encode_gbk("\u{10FFFF}", b"&#1114111;");
743        }
744
745        // Edge cases
746        encode_gbk("\u{00F7}", b"\xA1\xC2");
747
748        // GB18030-2022
749        encode_gb18030("\u{9FB9}", b"\xFE\x7E");
750        encode_gb18030("\u{FE14}", b"\xA6\xDD");
751        encode_gb18030("\u{E843}", b"\xFE\x7E");
752        encode_gb18030("\u{E791}", b"\xA6\xDD");
753
754        // Non-change in GB18030-2022
755        encode_gb18030("\u{E817}", b"\xFE\x52");
756    }
757
758    #[test]
759    fn test_gb18030_encode_from_utf16_max_length() {
760        let mut output = [0u8; 20];
761        let mut encoder = GB18030.new_encoder();
762        {
763            let needed = encoder
764                .max_buffer_length_from_utf16_without_replacement(1)
765                .unwrap();
766            let (result, read, written) = encoder.encode_from_utf16_without_replacement(
767                &[0x3000],
768                &mut output[..needed],
769                true,
770            );
771            assert_eq!(result, EncoderResult::InputEmpty);
772            assert_eq!(read, 1);
773            assert_eq!(written, 2);
774            assert_eq!(output[0], 0xA1);
775            assert_eq!(output[1], 0xA1);
776        }
777    }
778}