use std::collections::HashMap;
use std::sync::OnceLock;
const VN_CHAR_COUNT: usize = 213;
static UNICODE_TABLE: [char; VN_CHAR_COUNT] = [
'A', 'a', '\u{00C1}', '\u{00E1}', '\u{00C0}', '\u{00E0}',
'\u{1EA2}', '\u{1EA3}', '\u{00C3}', '\u{00E3}', '\u{1EA0}', '\u{1EA1}',
'\u{00C2}', '\u{00E2}', '\u{1EA4}', '\u{1EA5}', '\u{1EA6}', '\u{1EA7}',
'\u{1EA8}', '\u{1EA9}', '\u{1EAA}', '\u{1EAB}', '\u{1EAC}', '\u{1EAD}',
'\u{0102}', '\u{0103}', '\u{1EAE}', '\u{1EAF}', '\u{1EB0}', '\u{1EB1}',
'\u{1EB2}', '\u{1EB3}', '\u{1EB4}', '\u{1EB5}', '\u{1EB6}', '\u{1EB7}',
'B', 'b', 'C', 'c', 'D', 'd',
'\u{0110}', '\u{0111}',
'E', 'e', '\u{00C9}', '\u{00E9}', '\u{00C8}', '\u{00E8}',
'\u{1EBA}', '\u{1EBB}', '\u{1EBC}', '\u{1EBD}', '\u{1EB8}', '\u{1EB9}',
'\u{00CA}', '\u{00EA}', '\u{1EBE}', '\u{1EBF}', '\u{1EC0}', '\u{1EC1}',
'\u{1EC2}', '\u{1EC3}', '\u{1EC4}', '\u{1EC5}', '\u{1EC6}', '\u{1EC7}',
'F', 'f', 'G', 'g', 'H', 'h',
'I', 'i', '\u{00CD}', '\u{00ED}', '\u{00CC}', '\u{00EC}',
'\u{1EC8}', '\u{1EC9}', '\u{0128}', '\u{0129}', '\u{1ECA}', '\u{1ECB}',
'J', 'j', 'K', 'k', 'L', 'l', 'M', 'm', 'N', 'n',
'O', 'o', '\u{00D3}', '\u{00F3}', '\u{00D2}', '\u{00F2}',
'\u{1ECE}', '\u{1ECF}', '\u{00D5}', '\u{00F5}', '\u{1ECC}', '\u{1ECD}',
'\u{00D4}', '\u{00F4}', '\u{1ED0}', '\u{1ED1}', '\u{1ED2}', '\u{1ED3}',
'\u{1ED4}', '\u{1ED5}', '\u{1ED6}', '\u{1ED7}', '\u{1ED8}', '\u{1ED9}',
'\u{01A0}', '\u{01A1}', '\u{1EDA}', '\u{1EDB}', '\u{1EDC}', '\u{1EDD}',
'\u{1EDE}', '\u{1EDF}', '\u{1EE0}', '\u{1EE1}', '\u{1EE2}', '\u{1EE3}',
'P', 'p', 'Q', 'q', 'R', 'r', 'S', 's', 'T', 't',
'U', 'u', '\u{00DA}', '\u{00FA}', '\u{00D9}', '\u{00F9}',
'\u{1EE6}', '\u{1EE7}', '\u{0168}', '\u{0169}', '\u{1EE4}', '\u{1EE5}',
'\u{01AF}', '\u{01B0}', '\u{1EE8}', '\u{1EE9}', '\u{1EEA}', '\u{1EEB}',
'\u{1EEC}', '\u{1EED}', '\u{1EEE}', '\u{1EEF}', '\u{1EF0}', '\u{1EF1}',
'V', 'v', 'W', 'w', 'X', 'x',
'Y', 'y', '\u{00DD}', '\u{00FD}', '\u{1EF2}', '\u{1EF3}',
'\u{1EF6}', '\u{1EF7}', '\u{1EF8}', '\u{1EF9}', '\u{1EF4}', '\u{1EF5}',
'Z', 'z',
'\u{20AC}', '\u{20A1}', '\u{0192}', '\u{201E}', '\u{2026}', '\u{2020}',
'\u{2021}', '\u{02C6}', '\u{2030}', '\u{0160}', '\u{2039}', '\u{0152}',
'\u{017D}', '\u{2018}', '\u{2019}', '\u{201C}', '\u{201D}', '\u{2022}',
'\u{2013}', '\u{2014}', '\u{02DC}', '\u{2122}', '\u{0161}', '\u{203A}',
'\u{0153}', '\u{017E}', '\u{0178}',
];
static TCVN3_TABLE: [u8; VN_CHAR_COUNT] = [
b'A', b'a', 0xB8, 0xB8, 0xB5, 0xB5, 0xB6, 0xB6, 0xB7, 0xB7, 0xB9, 0xB9,
0xA2, 0xA9, 0xCA, 0xCA, 0xC7, 0xC7, 0xC8, 0xC8, 0xC9, 0xC9, 0xCB, 0xCB,
0xA1, 0xA8, 0xBE, 0xBE, 0xBB, 0xBB, 0xBC, 0xBC, 0xBD, 0xBD, 0xC6, 0xC6,
b'B', b'b', b'C', b'c', b'D', b'd',
0xA5, 0xB0,
b'E', b'e', 0xC4, 0xC4, 0xBF, 0xBF, 0xC0, 0xC0, 0xC1, 0xC1, 0xC2, 0xC2,
0xA4, 0xAB, 0xCC, 0xCC, 0xC3, 0xC3, 0xCE, 0xCE, 0xCF, 0xCF, 0xD0, 0xD0,
b'F', b'f', b'G', b'g', b'H', b'h',
b'I', b'i', 0xAE, 0xAE, 0xA6, 0xA6, 0xB1, 0xB1, 0xB2, 0xB2, 0xB3, 0xB3,
b'J', b'j', b'K', b'k', b'L', b'l', b'M', b'm', b'N', b'n',
b'O', b'o', 0xD3, 0xD3, 0xD4, 0xD4, 0xD5, 0xD5, 0xD6, 0xD6, 0xD8, 0xD8,
0xA3, 0xAA, 0xD9, 0xD9, 0xD1, 0xD1, 0xDA, 0xDA, 0xDB, 0xDB, 0xDC, 0xDC,
0xA7, 0xAD, 0xE1, 0xE1, 0xDD, 0xDD, 0xDE, 0xDE, 0xDF, 0xDF, 0xE0, 0xE0,
b'P', b'p', b'Q', b'q', b'R', b'r', b'S', b's', b'T', b't',
b'U', b'u', 0xE7, 0xE7, 0xE4, 0xE4, 0xE5, 0xE5, 0xE6, 0xE6, 0xE8, 0xE8,
0xB4, 0xAC, 0xE3, 0xE3, 0xD7, 0xD7, 0xE9, 0xE9, 0xEA, 0xEA, 0xEB, 0xEB,
b'V', b'v', b'W', b'w', b'X', b'x',
b'Y', b'y', 0xED, 0xED, 0xEE, 0xEE, 0xF0, 0xF0, 0xF1, 0xF1, 0xEF, 0xEF,
b'Z', b'z',
0x80, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
0x89, 0x8A, 0x8B, 0x8C, 0x8E, 0x91, 0x92, 0x93,
0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B,
0x9C, 0x9E, 0x9F,
];
static VNIWIN_TABLE: [u16; VN_CHAR_COUNT] = [
0x0041, 0x0061, 0xd941, 0xf961, 0xd841, 0xf861, 0xdb41, 0xfb61,
0xd541, 0xf561, 0xcf41, 0xef61,
0xc241, 0xe261, 0xc141, 0xe161, 0xc041, 0xe061, 0xc541, 0xe561,
0xc341, 0xe361, 0xc441, 0xe461,
0xca41, 0xea61, 0xc941, 0xe961, 0xc841, 0xe861, 0xda41, 0xfa61,
0xdc41, 0xfc61, 0xcb41, 0xeb61,
0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064,
0x00d1, 0x00f1,
0x0045, 0x0065, 0xd945, 0xf965, 0xd845, 0xf865, 0xdb45, 0xfb65,
0xd545, 0xf565, 0xcf45, 0xef65,
0xc245, 0xe265, 0xc145, 0xe165, 0xc045, 0xe065, 0xc545, 0xe565,
0xc345, 0xe365, 0xc445, 0xe465,
0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068,
0x0049, 0x0069, 0x00cd, 0x00ed, 0x00cc, 0x00ec, 0x00c6, 0x00e6,
0x00d3, 0x00f3, 0x00d2, 0x00f2,
0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d,
0x004e, 0x006e,
0x004f, 0x006f, 0xd94f, 0xf96f, 0xd84f, 0xf86f, 0xdb4f, 0xfb6f,
0xd54f, 0xf56f, 0xcf4f, 0xef6f,
0xc24f, 0xe26f, 0xc14f, 0xe16f, 0xc04f, 0xe06f, 0xc54f, 0xe56f,
0xc34f, 0xe36f, 0xc44f, 0xe46f,
0x00d4, 0x00f4, 0xd9d4, 0xf9f4, 0xd8d4, 0xf8f4, 0xdbd4, 0xfbf4,
0xd5d4, 0xf5f4, 0xcfd4, 0xeff4,
0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073,
0x0054, 0x0074,
0x0055, 0x0075, 0xd955, 0xf975, 0xd855, 0xf875, 0xdb55, 0xfb75,
0xd555, 0xf575, 0xcf55, 0xef75,
0x00d6, 0x00f6, 0xd9d6, 0xf9f6, 0xd8d6, 0xf8f6, 0xdbd6, 0xfbf6,
0xd5d6, 0xf5f6, 0xcfd6, 0xeff6,
0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078,
0x0059, 0x0079, 0xd959, 0xf979, 0xd859, 0xf879, 0xdb59, 0xfb79,
0xd559, 0xf579, 0x00ce, 0x00ee,
0x005a, 0x007a,
0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088,
0x0089, 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, 0x0092, 0x0093,
0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B,
0x009C, 0x009E, 0x009F,
];
static VIQR_TABLE: [u32; VN_CHAR_COUNT] = [
0x00000041, 0x00000061, 0x00002741, 0x00002761, 0x00006041, 0x00006061,
0x00003F41, 0x00003F61, 0x00007E41, 0x00007E61, 0x00002E41, 0x00002E61,
0x005E0041, 0x005E0061, 0x00275E41, 0x00275E61, 0x00605E41, 0x00605E61,
0x003F5E41, 0x003F5E61, 0x007E5E41, 0x007E5E61, 0x002E5E41, 0x002E5E61,
0x00280041, 0x00280061, 0x00272841, 0x00272861, 0x00602841, 0x00602861,
0x003F2841, 0x003F2861, 0x007E2841, 0x007E2861, 0x002E2841, 0x002E2861,
0x42, 0x62, 0x43, 0x63, 0x44, 0x64, 0x00004444, 0x00006464,
0x45, 0x65, 0x00002745, 0x00002765, 0x00006045, 0x00006065,
0x00003F45, 0x00003F65, 0x00007E45, 0x00007E65, 0x00002E45, 0x00002E65,
0x005E0045, 0x005E0065, 0x00275E45, 0x00275E65, 0x00605E45, 0x00605E65,
0x003F5E45, 0x003F5E65, 0x007E5E45, 0x007E5E65, 0x002E5E45, 0x002E5E65,
0x46, 0x66, 0x47, 0x67, 0x48, 0x68,
0x49, 0x69, 0x00002749, 0x00002769, 0x00006049, 0x00006069,
0x00003F49, 0x00003F69, 0x00007E49, 0x00007E69, 0x00002E49, 0x00002E69,
0x4A, 0x6A, 0x4B, 0x6B, 0x4C, 0x6C, 0x4D, 0x6D, 0x4E, 0x6E,
0x4F, 0x6F, 0x0000274F, 0x0000276F, 0x0000604F, 0x0000606F,
0x00003F4F, 0x00003F6F, 0x00007E4F, 0x00007E6F, 0x00002E4F, 0x00002E6F,
0x005E004F, 0x005E006F, 0x00275E4F, 0x00275E6F, 0x00605E4F, 0x00605E6F,
0x003F5E4F, 0x003F5E6F, 0x007E5E4F, 0x007E5E6F, 0x002E5E4F, 0x002E5E6F,
0x002B004F, 0x002B006F, 0x00272B4F, 0x00272B6F, 0x00602B4F, 0x00602B6F,
0x003F2B4F, 0x003F2B6F, 0x007E2B4F, 0x007E2B6F, 0x002E2B4F, 0x002E2B6F,
0x50, 0x70, 0x51, 0x71, 0x52, 0x72, 0x53, 0x73, 0x54, 0x74,
0x55, 0x75, 0x00002755, 0x00002775, 0x00006055, 0x00006075,
0x00003F55, 0x00003F75, 0x00007E55, 0x00007E75, 0x00002E55, 0x00002E75,
0x002B0055, 0x002B0075, 0x00272B55, 0x00272B75, 0x00602B55, 0x00602B75,
0x003F2B55, 0x003F2B75, 0x007E2B55, 0x007E2B75, 0x002E2B55, 0x002E2B75,
0x56, 0x76, 0x57, 0x77, 0x58, 0x78,
0x59, 0x79, 0x00002759, 0x00002779, 0x00006059, 0x00006079,
0x00003F59, 0x00003F79, 0x00007E59, 0x00007E79, 0x00002E59, 0x00002E79,
0x5A, 0x7A,
0x80, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
0x89, 0x8A, 0x8B, 0x8C, 0x8E, 0x91, 0x92, 0x93,
0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B,
0x9C, 0x9E, 0x9F,
];
fn push_viqr_bytes(out: &mut Vec<u8>, w: u32) {
let b0 = (w & 0xFF) as u8;
let b1 = ((w >> 8) & 0xFF) as u8;
let b2 = ((w >> 16) & 0xFF) as u8;
if b0 != 0 { out.push(b0); }
if b1 != 0 { out.push(b1); }
if b2 != 0 { out.push(b2); }
}
static WINCP1258_TABLE: [u16; VN_CHAR_COUNT] = [
0x0041, 0x0061, 0x00C1, 0x00E1, 0x00C0, 0x00E0,
0xD241, 0xD261, 0xDE41, 0xDE61, 0xF241, 0xF261,
0x00C2, 0x00E2, 0xECC2, 0xECE2, 0xCCC2, 0xCCE2,
0xD2C2, 0xD2E2, 0xDEC2, 0xDEE2, 0xF2C2, 0xF2E2,
0x00C3, 0x00E3, 0xECC3, 0xECE3, 0xCCC3, 0xCCE3,
0xD2C3, 0xD2E3, 0xDEC3, 0xDEE3, 0xF2C3, 0xF2E3,
0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, 0x00D0, 0x00F0,
0x0045, 0x0065, 0x00C9, 0x00E9, 0x00C8, 0x00E8,
0xD245, 0xD265, 0xDE45, 0xDE65, 0xF245, 0xF265,
0x00CA, 0x00EA, 0xECCA, 0xECEA, 0xCCCA, 0xCCEA,
0xD2CA, 0xD2EA, 0xDECA, 0xDEEA, 0xF2CA, 0xF2EA,
0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068,
0x0049, 0x0069, 0x00CD, 0x00ED, 0xCC49, 0xCC69,
0xD249, 0xD269, 0xDE49, 0xDE69, 0xF249, 0xF269,
0x004A, 0x006A, 0x004B, 0x006B, 0x004C, 0x006C,
0x004D, 0x006D, 0x004E, 0x006E,
0x004F, 0x006F, 0x00D3, 0x00F3, 0xCC4F, 0xCC6F,
0xD24F, 0xD26F, 0xDE4F, 0xDE6F, 0xF24F, 0xF26F,
0x00D4, 0x00F4, 0xECD4, 0xECF4, 0xCCD4, 0xCCF4,
0xD2D4, 0xD2F4, 0xDED4, 0xDEF4, 0xF2D4, 0xF2F4,
0x00D5, 0x00F5, 0xECD5, 0xECF5, 0xCCD5, 0xCCF5,
0xD2D5, 0xD2F5, 0xDED5, 0xDEF5, 0xF2D5, 0xF2F5,
0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072,
0x0053, 0x0073, 0x0054, 0x0074,
0x0055, 0x0075, 0x00DA, 0x00FA, 0x00D9, 0x00F9,
0xD255, 0xD275, 0xDE55, 0xDE75, 0xF255, 0xF275,
0x00DD, 0x00FD, 0xECDD, 0xECFD, 0xCCDD, 0xCCFD,
0xD2DD, 0xD2FD, 0xDEDD, 0xDEFD, 0xF2DD, 0xF2FD,
0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078,
0x0059, 0x0079, 0xEC59, 0xEC79, 0xCC59, 0xCC79,
0xD259, 0xD279, 0xDE59, 0xDE79, 0xF259, 0xF279,
0x005A, 0x007A,
0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086,
0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C,
0x008E, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095,
0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B,
0x009C, 0x009E, 0x009F,
];
static VPS_TABLE: [u8; VN_CHAR_COUNT] = [
0x41, 0x61, 0xC1, 0xE1, 0x80, 0xE0, 0x81, 0xE4, 0x82, 0xE3, 0xE5, 0xE5,
0xC2, 0xE2, 0x83, 0xC3, 0x84, 0xC0, 0x85, 0xC4, 0xC5, 0xC5, 0xC6, 0xC6,
0x88, 0xE6, 0x8D, 0xA1, 0x8E, 0xA2, 0x8F, 0xA3, 0xF0, 0xA4, 0xA5, 0xA5,
0x42, 0x62, 0x43, 0x63, 0x44, 0x64, 0xF1, 0xC7, 0x45, 0x65, 0xC9, 0xE9,
0xD7, 0xE8, 0xDE, 0xC8, 0xFE, 0xEB, 0xCB, 0xCB, 0xCA, 0xEA, 0x90, 0x89,
0x93, 0x8A, 0x94, 0x8B, 0x95, 0xCD, 0x8C, 0x8C, 0x46, 0x66, 0x47, 0x67,
0x48, 0x68, 0x49, 0x69, 0xB4, 0xED, 0xB5, 0xEC, 0xB7, 0xCC, 0xB8, 0xEF,
0xCE, 0xCE, 0x4A, 0x6A, 0x4B, 0x6B, 0x4C, 0x6C, 0x4D, 0x6D, 0x4E, 0x6E,
0x4F, 0x6F, 0xB9, 0xF3, 0xBC, 0xF2, 0xBD, 0xD5, 0xBE, 0xF5, 0x86, 0x86,
0xD4, 0xF4, 0x96, 0xD3, 0x97, 0xD2, 0x98, 0xB0, 0x99, 0x87, 0xB6, 0xB6,
0xF7, 0xD6, 0x9D, 0xA7, 0x9E, 0xA9, 0x9F, 0xAA, 0xA6, 0xAB, 0xAE, 0xAE,
0x50, 0x70, 0x51, 0x71, 0x52, 0x72, 0x53, 0x73, 0x54, 0x74, 0x55, 0x75,
0xDA, 0xFA, 0xA8, 0xF9, 0xD1, 0xFB, 0xAC, 0xDB, 0xF8, 0xF8, 0xD0, 0xDC,
0xAD, 0xD9, 0xAF, 0xD8, 0xB1, 0xBA, 0xBB, 0xBB, 0xBF, 0xBF, 0x56, 0x76,
0x57, 0x77, 0x58, 0x78, 0x59, 0x79, 0xDD, 0x9A, 0xB2, 0xFF, 0xFD, 0x9B,
0xB3, 0xCF, 0x9C, 0x9C, 0x5A, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x91, 0x92, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E, 0x00,];
static VISCII_TABLE: [u8; VN_CHAR_COUNT] = [
0x41, 0x61, 0xC1, 0xE1, 0xC0, 0xE0, 0xC4, 0xE4, 0xC3, 0xE3, 0x80, 0xD5,
0xC2, 0xE2, 0x84, 0xA4, 0x85, 0xA5, 0x86, 0xA6, 0xE7, 0xE7, 0x87, 0xA7,
0xC5, 0xE5, 0x81, 0xA1, 0x82, 0xA2, 0xC6, 0xC6, 0xC7, 0xC7, 0x83, 0xA3,
0x42, 0x62, 0x43, 0x63, 0x44, 0x64, 0xD0, 0xF0, 0x45, 0x65, 0xC9, 0xE9,
0xC8, 0xE8, 0xCB, 0xEB, 0x88, 0xA8, 0x89, 0xA9, 0xCA, 0xEA, 0x8A, 0xAA,
0x8B, 0xAB, 0x8C, 0xAC, 0x8D, 0xAD, 0x8E, 0xAE, 0x46, 0x66, 0x47, 0x67,
0x48, 0x68, 0x49, 0x69, 0xCD, 0xED, 0xCC, 0xEC, 0x9B, 0xEF, 0xCE, 0xEE,
0x98, 0xB8, 0x4A, 0x6A, 0x4B, 0x6B, 0x4C, 0x6C, 0x4D, 0x6D, 0x4E, 0x6E,
0x4F, 0x6F, 0xD3, 0xF3, 0xD2, 0xF2, 0x99, 0xF6, 0xF5, 0xF5, 0x9A, 0xF7,
0xD4, 0xF4, 0x8F, 0xAF, 0x90, 0xB0, 0x91, 0xB1, 0x92, 0xB2, 0x93, 0xB5,
0xB4, 0xBD, 0x95, 0xBE, 0x96, 0xB6, 0x97, 0xB7, 0xB3, 0xDE, 0x94, 0xFE,
0x50, 0x70, 0x51, 0x71, 0x52, 0x72, 0x53, 0x73, 0x54, 0x74, 0x55, 0x75,
0xDA, 0xFA, 0xD9, 0xF9, 0x9C, 0xFC, 0x9D, 0xFB, 0x9E, 0xF8, 0xBF, 0xDF,
0xBA, 0xD1, 0xBB, 0xD7, 0xBC, 0xD8, 0xFF, 0xE6, 0xB9, 0xF1, 0x56, 0x76,
0x57, 0x77, 0x58, 0x78, 0x59, 0x79, 0xDD, 0xFD, 0x9F, 0xCF, 0xD6, 0xD6,
0xDB, 0xDB, 0xDC, 0xDC, 0x5A, 0x7A, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E, 0x00,];
static BKHCM1_TABLE: [u8; VN_CHAR_COUNT] = [
0x41, 0x61, 0x80, 0xBE, 0x81, 0xBF, 0x82, 0xC0, 0x83, 0xC1, 0x84, 0xC2,
0x9F, 0xDD, 0x7E, 0xDE, 0xA1, 0xDF, 0xA2, 0xE0, 0xA3, 0xE1, 0xA4, 0xE2,
0x99, 0xD7, 0x9A, 0xD8, 0x9B, 0xD9, 0x9C, 0xDA, 0x9D, 0xDB, 0x98, 0xDC,
0x42, 0x62, 0x43, 0x63, 0x44, 0x64, 0x7D, 0xBD, 0x45, 0x65, 0x85, 0xC3,
0x86, 0xC4, 0x87, 0xC5, 0x88, 0xC6, 0x89, 0xC7, 0xA5, 0xE3, 0xA6, 0xE4,
0xA7, 0xE5, 0xA8, 0xE6, 0xA9, 0xE7, 0xAA, 0xE8, 0x46, 0x66, 0x47, 0x67,
0x48, 0x68, 0x49, 0x69, 0x8A, 0xC8, 0x8B, 0xC9, 0x8C, 0xCA, 0x8D, 0xCB,
0x8E, 0xCC, 0x4A, 0x6A, 0x4B, 0x6B, 0x4C, 0x6C, 0x4D, 0x6D, 0x4E, 0x6E,
0x4F, 0x6F, 0x8F, 0xCD, 0x90, 0xCE, 0x91, 0xCF, 0x92, 0xD0, 0x93, 0xD1,
0xAB, 0xE9, 0xAC, 0xEA, 0xAD, 0xEB, 0xAE, 0xEC, 0xAF, 0xED, 0xB0, 0xEE,
0xB1, 0xEF, 0xB2, 0xF0, 0xB3, 0xF1, 0xB4, 0xF2, 0xB5, 0xF3, 0xB6, 0xF4,
0x50, 0x70, 0x51, 0x71, 0x52, 0x72, 0x53, 0x73, 0x54, 0x74, 0x55, 0x75,
0x94, 0xD2, 0x95, 0xD3, 0x96, 0xD4, 0x97, 0xD5, 0x98, 0xD6, 0xB7, 0xF5,
0xB8, 0xF6, 0xB9, 0xF7, 0xBA, 0xF8, 0xBB, 0xF9, 0xBC, 0xFA, 0x56, 0x76,
0x57, 0x77, 0x58, 0x78, 0x59, 0x79, 0x7B, 0xFB, 0x5E, 0xFC, 0x60, 0xFD,
0x7C, 0xFE, 0x8E, 0xFF, 0x5A, 0x7A, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E, 0x00,];
static VIETWAREF_TABLE: [u8; VN_CHAR_COUNT] = [
0x41, 0x61, 0xC0, 0xC0, 0xAA, 0xAA, 0xB6, 0xB6, 0xBA, 0xBA, 0xC1, 0xC1,
0x97, 0xA1, 0xCA, 0xCA, 0xC7, 0xC7, 0xC8, 0xC8, 0xC9, 0xC9, 0xCB, 0xCB,
0x96, 0x9F, 0xC5, 0xC5, 0xC2, 0xC2, 0xC3, 0xC3, 0xC4, 0xC4, 0xC6, 0xC6,
0x42, 0x62, 0x43, 0x63, 0x44, 0x64, 0x98, 0xA2, 0x45, 0x65, 0xCF, 0xCF,
0xCC, 0xCC, 0xCD, 0xCD, 0xCE, 0xCE, 0xD1, 0xD1, 0x99, 0xA3, 0xD5, 0xD5,
0xD2, 0xD2, 0xD3, 0xD3, 0xD4, 0xD4, 0xD6, 0xD6, 0x46, 0x66, 0x47, 0x67,
0x48, 0x68, 0x49, 0x69, 0xDB, 0xDB, 0xD8, 0xD8, 0xD9, 0xD9, 0xDA, 0xDA,
0xDC, 0xDC, 0x4A, 0x6A, 0x4B, 0x6B, 0x4C, 0x6C, 0x4D, 0x6D, 0x4E, 0x6E,
0x4F, 0x6F, 0xE2, 0xE2, 0xDF, 0xDF, 0xE0, 0xE0, 0xE1, 0xE1, 0xE3, 0xE3,
0x9A, 0xA4, 0xE7, 0xE7, 0xE4, 0xE4, 0xE5, 0xE5, 0xE6, 0xE6, 0xE8, 0xE8,
0x9B, 0xA5, 0xEC, 0xEC, 0xE9, 0xE9, 0xEA, 0xEA, 0xEB, 0xEB, 0xED, 0xED,
0x50, 0x70, 0x51, 0x71, 0x52, 0x72, 0x53, 0x73, 0x54, 0x74, 0x55, 0x75,
0xF2, 0xF2, 0xEE, 0xEE, 0xEF, 0xEF, 0xF1, 0xF1, 0xF3, 0xF3, 0x9C, 0xA7,
0xF7, 0xF7, 0xF4, 0xF4, 0xF5, 0xF5, 0xF6, 0xF6, 0xF8, 0xF8, 0x56, 0x76,
0x57, 0x77, 0x58, 0x78, 0x59, 0x79, 0xFC, 0xFC, 0xF9, 0xF9, 0xFA, 0xFA,
0xFB, 0xFB, 0xFF, 0xFF, 0x5A, 0x7A, 0x80, 0x82, 0x83, 0x84, 0x85, 0x86,
0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8E, 0x91, 0x92, 0x93, 0x94, 0x95,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E, 0x00,];
static ISC_TABLE: [u8; VN_CHAR_COUNT] = [
0x41, 0x61, 0x83, 0xB8, 0x80, 0xB5, 0x81, 0xB6, 0x82, 0xB7, 0x84, 0xB9,
0xA2, 0xA9, 0xC4, 0xA0, 0xC1, 0xC7, 0xC2, 0xC8, 0xC3, 0xC9, 0x86, 0xCB,
0xA1, 0xA8, 0xC0, 0xBE, 0xAF, 0xBB, 0xBA, 0xBC, 0xBF, 0xBD, 0x85, 0xC6,
0x42, 0x62, 0x43, 0x63, 0x44, 0x64, 0xA7, 0xAE, 0x45, 0x65, 0xD0, 0x8A,
0x87, 0xCC, 0x88, 0xCE, 0x89, 0xCF, 0xD1, 0x8B, 0xA3, 0xAA, 0xDA, 0xD5,
0xC5, 0xD2, 0xCD, 0xD3, 0xD9, 0xD4, 0x8C, 0xD6, 0x46, 0x66, 0x47, 0x67,
0x48, 0x68, 0x49, 0x69, 0x90, 0xDD, 0x8D, 0xD7, 0x8E, 0xD8, 0x8F, 0xDC,
0x91, 0xDE, 0x4A, 0x6A, 0x4B, 0x6B, 0x4C, 0x6C, 0x4D, 0x6D, 0x4E, 0x6E,
0x4F, 0x6F, 0x95, 0xE3, 0x92, 0xDF, 0x93, 0xE1, 0x94, 0xE2, 0x96, 0xE4,
0xA4, 0xAB, 0xFF, 0xE8, 0xDB, 0xE5, 0xE0, 0xE6, 0xF0, 0xE7, 0x97, 0xE9,
0xA5, 0xAC, 0x9B, 0xED, 0x98, 0xEA, 0x99, 0xEB, 0x9A, 0xEC, 0x9C, 0xEE,
0x50, 0x70, 0x51, 0x71, 0x52, 0x72, 0x53, 0x73, 0x54, 0x74, 0x55, 0x75,
0x40, 0xF3, 0x9D, 0xEF, 0x9E, 0xF1, 0x9F, 0xF2, 0x7C, 0xF4, 0xA6, 0xAD,
0x60, 0xF8, 0x5C, 0xF5, 0x5E, 0xF6, 0x7E, 0xF7, 0x23, 0xF9, 0x56, 0x76,
0x57, 0x77, 0x58, 0x78, 0x59, 0x79, 0xB3, 0xFD, 0xB0, 0xFA, 0xB1, 0xFB,
0xB2, 0xFC, 0xB4, 0xFE, 0x5A, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x12, 0x13, 0x14, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,];
static BKHCM2_TABLE: [u16; VN_CHAR_COUNT] = [
0x0041, 0x0061, 0xC141, 0xE161, 0xC241, 0xE261,
0xC341, 0xE361, 0xC441, 0xE461, 0xC541, 0xE561,
0x00CA, 0x00EA, 0xCBCA, 0xEBEA, 0xCCCA, 0xECEA,
0xCDCA, 0xEDEA, 0xCECA, 0xEEEA, 0xC5CA, 0xE5EA,
0x00D9, 0x00F9, 0xC6D9, 0xE6F9, 0xC7D9, 0xE7F9,
0xC8D9, 0xE8F9, 0xC9D9, 0xE9F9, 0xC5D9, 0xE5F9,
0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064,
0x00C0, 0x00E0, 0x0045, 0x0065, 0xC145, 0xE165,
0xC245, 0xE265, 0xC345, 0xE365, 0xC445, 0xE465,
0xC545, 0xE565, 0x00CF, 0x00EF, 0xCBCF, 0xEBEF,
0xCCCF, 0xECEF, 0xCDCF, 0xEDEF, 0xCECF, 0xEEEF,
0xE5CF, 0xE5EF, 0x0046, 0x0066, 0x0047, 0x0067,
0x0048, 0x0068, 0x0049, 0x0069, 0x00D1, 0x00F1,
0x00D2, 0x00F2, 0x00D3, 0x00F3, 0x00D4, 0x00F4,
0x00D5, 0x00F5, 0x004A, 0x006A, 0x004B, 0x006B,
0x004C, 0x006C, 0x004D, 0x006D, 0x004E, 0x006E,
0x004F, 0x006F, 0xC14F, 0xE16F, 0xC24F, 0xE26F,
0xC34F, 0xE36F, 0xC44F, 0xE46F, 0xC54F, 0xE56F,
0x00D6, 0x00F6, 0xCBD6, 0xEBF6, 0xCCD6, 0xECF6,
0xCDD6, 0xEDF6, 0xCED6, 0xEEF6, 0xC5D6, 0xE5F6,
0x00DA, 0x00FA, 0xC1DA, 0xE1FA, 0xC2DA, 0xE2FA,
0xC3DA, 0xE3FA, 0xC4DA, 0xE4FA, 0xC5DA, 0xE5FA,
0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072,
0x0053, 0x0073, 0x0054, 0x0074, 0x0055, 0x0075,
0xC155, 0xE175, 0xC255, 0xE275, 0xC355, 0xE375,
0xC455, 0xE475, 0xC555, 0xE575, 0x00DB, 0x00FB,
0xC1DB, 0xE1FB, 0xC2DB, 0xE2FB, 0xC3DB, 0xE3FB,
0xC4DB, 0xE4FB, 0xC5DB, 0xE5FB, 0x0056, 0x0076,
0x0057, 0x0077, 0x0058, 0x0078, 0x0059, 0x0079,
0xC159, 0xE179, 0xC259, 0xE279, 0xC359, 0xE379,
0xC459, 0xE479, 0xC559, 0xE579, 0x005A, 0x007A,
0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086,
0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C,
0x008E, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095,
0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B,
0x009C, 0x009E, 0x009F,];
static VIETWAREX_TABLE: [u16; VN_CHAR_COUNT] = [
0x0041, 0x0061, 0xCF41, 0xEF61, 0xCC41, 0xEC61,
0xCD41, 0xED61, 0xCE41, 0xEE61, 0xDB41, 0xFB61,
0x00C1, 0x00E1, 0xDAC1, 0xFAE1, 0xD6C1, 0xF6E1,
0xD8C1, 0xF8E1, 0xD9C1, 0xF9E1, 0xDBC1, 0xFBE1,
0x00C0, 0x00E0, 0xD5C0, 0xF5E0, 0xD2C0, 0xF2E0,
0xD3C0, 0xF3E0, 0xD4C0, 0xF4E0, 0xDBC0, 0xFBE0,
0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064,
0x00C2, 0x00E2, 0x0045, 0x0065, 0xCF45, 0xEF65,
0xCC45, 0xEC65, 0xCD45, 0xED65, 0xCE45, 0xEE65,
0xDB45, 0xFB65, 0x00C3, 0x00E3, 0xDAC3, 0xFAE3,
0xD6C3, 0xF6E3, 0xD8C3, 0xF8E3, 0xD9C3, 0xF9E3,
0xDBC3, 0xFBE3, 0x0046, 0x0066, 0x0047, 0x0067,
0x0048, 0x0068, 0x0049, 0x0069, 0x00CA, 0x00EA,
0x00C7, 0x00E7, 0x00C8, 0x00E8, 0x00C9, 0x00E9,
0x00CB, 0x00EB, 0x004A, 0x006A, 0x004B, 0x006B,
0x004C, 0x006C, 0x004D, 0x006D, 0x004E, 0x006E,
0x004F, 0x006F, 0xCF4F, 0xEF6F, 0xCC4F, 0xEC6F,
0xCD4F, 0xED6F, 0xCE4F, 0xEE6F, 0xDC4F, 0xFC6F,
0x00C4, 0x00E4, 0xDAC4, 0xFAE4, 0xD6C4, 0xF6E4,
0xD8C4, 0xF8E4, 0xD9C4, 0xF9E4, 0xDCC4, 0xFCE4,
0x00C5, 0x00E5, 0xCFC5, 0xEFE5, 0xCCC5, 0xECE5,
0xCDC5, 0xEDE5, 0xCEC5, 0xEEE5, 0xDCC5, 0xFCE5,
0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072,
0x0053, 0x0073, 0x0054, 0x0074, 0x0055, 0x0075,
0xCF55, 0xEF75, 0xCC55, 0xEC75, 0xCD55, 0xED75,
0xCE55, 0xEE75, 0xDB55, 0xFB75, 0x00C6, 0x00E6,
0xCFC6, 0xEFE6, 0xCCC6, 0xECE6, 0xCDC6, 0xEDE6,
0xCEC6, 0xEEE6, 0xDBC6, 0xFBE6, 0x0056, 0x0076,
0x0057, 0x0077, 0x0058, 0x0078, 0x0059, 0x0079,
0xCF59, 0xEF79, 0xCC59, 0xEC79, 0xCD59, 0xED79,
0xCE59, 0xEE79, 0xD159, 0xF179, 0x005A, 0x007A,
0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086,
0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C,
0x008E, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095,
0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B,
0x009C, 0x009E, 0x009F,];
static VNI_MAC_TABLE: [u16; VN_CHAR_COUNT] = [
0x0041, 0x0061, 0xF441, 0x9D61, 0xAF41, 0xBF61,
0xF341, 0x9E61, 0xCD41, 0x9B61, 0xEC41, 0x9561,
0xE541, 0x8961, 0xE741, 0x8761, 0xCB41, 0x8861,
0x8141, 0x8C61, 0xCC41, 0x8B61, 0x8041, 0x8A61,
0xE641, 0x9061, 0x8341, 0x8E61, 0xE941, 0x8F61,
0xF241, 0x9C61, 0x8641, 0x9F61, 0xE841, 0x9161,
0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064,
0x0084, 0x0096, 0x0045, 0x0065, 0xF445, 0x9D65,
0xAF45, 0xBF65, 0xF345, 0x9E65, 0xCD45, 0x9B65,
0xEC45, 0x9565, 0xE545, 0x8965, 0xE745, 0x8765,
0xCB45, 0x8865, 0x8145, 0x8C65, 0xCC45, 0x8B65,
0x8045, 0x8A65, 0x0046, 0x0066, 0x0047, 0x0067,
0x0048, 0x0068, 0x0049, 0x0069, 0x00EA, 0x0092,
0x00ED, 0x0093, 0x00AE, 0x00BE, 0x00EE, 0x0097,
0x00F1, 0x0098, 0x004A, 0x006A, 0x004B, 0x006B,
0x004C, 0x006C, 0x004D, 0x006D, 0x004E, 0x006E,
0x004F, 0x006F, 0xF44F, 0x9D6F, 0xAF4F, 0xBF6F,
0xF34F, 0x9E6F, 0xCD4F, 0x9B6F, 0xEC4F, 0x956F,
0xE54F, 0x896F, 0xE74F, 0x876F, 0xCB4F, 0x886F,
0x814F, 0x8C6F, 0xCC4F, 0x8B6F, 0x804F, 0x8A6F,
0x00EF, 0x0099, 0xF4EF, 0x9D99, 0xAFEF, 0xBF99,
0xF3EF, 0x9E99, 0xCDEF, 0x9B99, 0xECEF, 0x9599,
0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072,
0x0053, 0x0073, 0x0054, 0x0074, 0x0055, 0x0075,
0xF455, 0x9D75, 0xAF55, 0xBF75, 0xF355, 0x9E75,
0xCD55, 0x9B75, 0xEC55, 0x9575, 0x0085, 0x009A,
0xF485, 0x9D9A, 0xAF85, 0xBF9A, 0xF385, 0x9E9A,
0xCD85, 0x9B9A, 0xEC85, 0x959A, 0x0056, 0x0076,
0x0057, 0x0077, 0x0058, 0x0078, 0x0059, 0x0079,
0xF459, 0x9D79, 0xAF59, 0xBF79, 0xF359, 0x9E79,
0xCD59, 0x9B79, 0x00EB, 0x0094, 0x005A, 0x007A,
0x00DB, 0x00E2, 0x00C4, 0x00E3, 0x00C9, 0x00A0,
0x00E0, 0x00F6, 0x00E4, 0x003F, 0x00DC, 0x00CE,
0x003F, 0x00D4, 0x00D5, 0x00D2, 0x00D3, 0x00A5,
0x00D0, 0x00D1, 0x00F7, 0x00AA, 0x003F, 0x00DD,
0x00CF, 0x003F, 0x00D9,];
static UNICOMP_TABLE: [u32; VN_CHAR_COUNT] = [ 0x00000041, 0x00000061, 0x03010041, 0x03010061,
0x03000041, 0x03000061, 0x03090041, 0x03090061,
0x03030041, 0x03030061, 0x03230041, 0x03230061,
0x000000C2, 0x000000E2, 0x030100C2, 0x030100E2,
0x030000C2, 0x030000E2, 0x030900C2, 0x030900E2,
0x030300C2, 0x030300E2, 0x032300C2, 0x032300E2,
0x00000102, 0x00000103, 0x03010102, 0x03010103,
0x03000102, 0x03000103, 0x03090102, 0x03090103,
0x03030102, 0x03030103, 0x03230102, 0x03230103,
0x00000042, 0x00000062, 0x00000043, 0x00000063,
0x00000044, 0x00000064, 0x00000110, 0x00000111,
0x000000D1, 0x000000F1, 0x00000045, 0x00000065,
0x03010045, 0x03010065, 0x03000045, 0x03000065,
0x03090045, 0x03090065, 0x03030045, 0x03030065,
0x03230045, 0x03230065, 0x000000CA, 0x000000EA,
0x030100CA, 0x030100EA, 0x030000CA, 0x030000EA,
0x030900CA, 0x030900EA, 0x030300CA, 0x030300EA,
0x032300CA, 0x032300EA, 0x00000046, 0x00000066,
0x00000047, 0x00000067, 0x00000048, 0x00000068,
0x00000049, 0x00000069, 0x03010049, 0x03010069,
0x03000049, 0x03000069, 0x03090049, 0x03090069,
0x03030049, 0x03030069, 0x03230049, 0x03230069,
0x0000004A, 0x0000006A, 0x0000004B, 0x0000006B,
0x0000004C, 0x0000006C, 0x0000004D, 0x0000006D,
0x0000004E, 0x0000006E, 0x0000004F, 0x0000006F,
0x0301004F, 0x0301006F, 0x0300004F, 0x0300006F,
0x0309004F, 0x0309006F, 0x0303004F, 0x0303006F,
0x0323004F, 0x0323006F, 0x000000D4, 0x000000F4,
0x030100D4, 0x030100F4, 0x030000D4, 0x030000F4,
0x030900D4, 0x030900F4, 0x030300D4, 0x030300F4,
0x032300D4, 0x032300F4, 0x000001A0, 0x000001A1,
0x030101A0, 0x030101A1, 0x030001A0, 0x030001A1,
0x030901A0, 0x030901A1, 0x030301A0, 0x030301A1,
0x032301A0, 0x032301A1, 0x00000050, 0x00000070,
0x00000051, 0x00000071, 0x00000052, 0x00000072,
0x00000053, 0x00000073, 0x00000054, 0x00000074,
0x00000055, 0x00000075, 0x03010055, 0x03010075,
0x03000055, 0x03000075, 0x03090055, 0x03090075,
0x03030055, 0x03030075, 0x03230055, 0x03230075,
0x000001AF, 0x000001B0, 0x030101AF, 0x030101B0,
0x030001AF, 0x030001B0, 0x030901AF, 0x030901B0,
0x030301AF, 0x030301B0, 0x032301AF, 0x032301B0,
0x00000056, 0x00000076, 0x00000057, 0x00000077,
0x00000058, 0x00000078, 0x00000059, 0x00000079,
0x03010059, 0x03010079, 0x03000059, 0x03000079,
0x03090059, 0x03090079, 0x03030059, 0x03030079,
0x03230059, 0x03230079, 0x0000005A, 0x0000007A,
0x000020AC, 0x000020A1, 0x00000192, 0x0000201E,
0x00002026, 0x00002020, 0x00002021, 0x000002C6,
0x00002030, 0x00000160, 0x00002039, 0x00000152,
0x0000017D, 0x00002018, 0x00002019, 0x0000201C,
0x0000201D, 0x00002022, 0x00002013, 0x00002014,
0x000002DC, 0x00002122, 0x00000161, 0x0000203A,
0x00000153,];
static NO_TONE: &[usize; VN_CHAR_COUNT] = &[
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13,
24, 25, 24, 25, 24, 25, 24, 25, 24, 25, 24, 25,
36, 37, 38, 39, 40, 41,
42, 43,
44, 45, 44, 45, 44, 45, 44, 45, 44, 45, 44, 45,
56, 57, 56, 57, 56, 57, 56, 57, 56, 57, 56, 57,
68, 69, 70, 71, 72, 73,
74, 75, 74, 75, 74, 75, 74, 75, 74, 75, 74, 75,
86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97,
108, 109, 108, 109, 108, 109, 108, 109, 108, 109, 108, 109,
120, 121, 120, 121, 120, 121, 120, 121, 120, 121, 120, 121,
132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
142, 143, 142, 143, 142, 143, 142, 143, 142, 143, 142, 143,
154, 155, 154, 155, 154, 155, 154, 155, 154, 155, 154, 155,
166, 167, 168, 169, 170, 171,
172, 173, 172, 173, 172, 173, 172, 173, 172, 173, 172, 173,
184, 185,
186, 187, 188, 189, 190, 191, 192, 193,
194, 195, 196, 197, 198, 199, 200, 201,
202, 203, 204, 205, 206, 207, 208, 209,
210, 211, 212,
];
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum VietCharset {
Unicode = 0,
TCVN3 = 1,
VNIWin = 2,
WinCP1258 = 3,
VIQR = 4,
VPS = 5,
VISCII = 6,
BKHCM1 = 7,
VietwareF = 8,
ISC = 9,
BKHCM2 = 10,
VietwareX = 11,
VNIMac = 12,
UnicodeDecomposed = 13,
}
fn unicode_to_index_map() -> &'static HashMap<char, usize> {
static MAP: OnceLock<HashMap<char, usize>> = OnceLock::new();
MAP.get_or_init(|| {
let mut m = HashMap::with_capacity(VN_CHAR_COUNT);
for (i, &ch) in UNICODE_TABLE.iter().enumerate() {
m.insert(ch, i);
}
m
})
}
fn tcvn3_to_index_map() -> &'static HashMap<u8, usize> {
static MAP: OnceLock<HashMap<u8, usize>> = OnceLock::new();
MAP.get_or_init(|| {
let mut m = HashMap::with_capacity(VN_CHAR_COUNT);
for (i, &b) in TCVN3_TABLE.iter().enumerate() {
m.insert(b, i);
}
m
})
}
fn vniwin_to_index_map() -> &'static HashMap<u16, usize> {
static MAP: OnceLock<HashMap<u16, usize>> = OnceLock::new();
MAP.get_or_init(|| {
let mut m = HashMap::with_capacity(VN_CHAR_COUNT);
for (i, &w) in VNIWIN_TABLE.iter().enumerate() {
m.insert(w, i);
}
m
})
}
fn sb_table(cs: VietCharset) -> &'static [u8] {
match cs {
VietCharset::TCVN3 => &TCVN3_TABLE,
VietCharset::VPS => &VPS_TABLE,
VietCharset::VISCII => &VISCII_TABLE,
VietCharset::BKHCM1 => &BKHCM1_TABLE,
VietCharset::VietwareF => &VIETWAREF_TABLE,
VietCharset::ISC => &ISC_TABLE,
_ => &TCVN3_TABLE,
}
}
fn db_table(cs: VietCharset) -> &'static [u16] {
match cs {
VietCharset::VNIWin => &VNIWIN_TABLE,
VietCharset::BKHCM2 => &BKHCM2_TABLE,
VietCharset::VietwareX => &VIETWAREX_TABLE,
VietCharset::VNIMac => &VNI_MAC_TABLE,
_ => &VNIWIN_TABLE,
}
}
fn is_single_byte(cs: VietCharset) -> bool {
matches!(cs, VietCharset::TCVN3 | VietCharset::VPS | VietCharset::VISCII
| VietCharset::BKHCM1 | VietCharset::VietwareF | VietCharset::ISC)
}
fn is_double_byte(cs: VietCharset) -> bool {
matches!(cs, VietCharset::VNIWin | VietCharset::BKHCM2
| VietCharset::VietwareX | VietCharset::VNIMac)
}
pub fn encode(input: &str, charset: VietCharset) -> Vec<u8> {
match charset {
VietCharset::Unicode => input.as_bytes().to_vec(),
cs if is_single_byte(cs) => {
encode_sb(input, sb_table(cs))
}
cs if is_double_byte(cs) => {
encode_db(input, db_table(cs))
}
VietCharset::WinCP1258 => encode_wincp(input),
VietCharset::VIQR => encode_viqr(input),
VietCharset::UnicodeDecomposed => encode_nfd(input),
_ => input.as_bytes().to_vec(),
}
}
fn encode_sb(input: &str, table: &[u8]) -> Vec<u8> {
let map = unicode_to_index_map();
let mut out = Vec::with_capacity(input.len());
for ch in input.chars() {
if let Some(&idx) = map.get(&ch) {
out.push(table[idx]);
} else {
let mut buf = [0u8; 4];
let s = ch.encode_utf8(&mut buf);
out.extend_from_slice(s.as_bytes());
}
}
out
}
fn encode_db(input: &str, table: &[u16]) -> Vec<u8> {
let map = unicode_to_index_map();
let mut out = Vec::with_capacity(input.len() * 2);
for ch in input.chars() {
if let Some(&idx) = map.get(&ch) {
let w = table[idx];
out.push((w & 0xFF) as u8);
out.push((w >> 8) as u8);
} else if ch.is_ascii() {
out.push(ch as u8);
} else {
let mut buf = [0u8; 4];
let s = ch.encode_utf8(&mut buf);
out.extend_from_slice(s.as_bytes());
}
}
out
}
fn encode_wincp(input: &str) -> Vec<u8> {
let map = unicode_to_index_map();
let mut out = Vec::with_capacity(input.len() * 2);
for ch in input.chars() {
if let Some(&idx) = map.get(&ch) {
let w = WINCP1258_TABLE[idx];
out.push((w & 0xFF) as u8);
let hi = (w >> 8) as u8;
if hi != 0 { out.push(hi); }
} else if ch.is_ascii() {
out.push(ch as u8);
} else {
let mut buf = [0u8; 4];
let s = ch.encode_utf8(&mut buf);
out.extend_from_slice(s.as_bytes());
}
}
out
}
fn encode_viqr(input: &str) -> Vec<u8> {
let map = unicode_to_index_map();
let mut out = Vec::new();
for ch in input.chars() {
if let Some(&idx) = map.get(&ch) {
push_viqr_bytes(&mut out, VIQR_TABLE[idx]);
} else if ch.is_ascii() {
out.push(ch as u8);
} else {
let mut buf = [0u8; 4];
let s = ch.encode_utf8(&mut buf);
out.extend_from_slice(s.as_bytes());
}
}
out
}
fn encode_nfd(input: &str) -> Vec<u8> {
let map = unicode_to_index_map();
let mut out = Vec::with_capacity(input.len() * 6);
for ch in input.chars() {
if let Some(&idx) = map.get(&ch) {
let w = UNICOMP_TABLE[idx];
let base = w & 0xFFFF;
let comb = (w >> 16) & 0xFFFF;
if let Some(c) = char::from_u32(base) {
let mut buf = [0u8; 4];
let s = c.encode_utf8(&mut buf);
out.extend_from_slice(s.as_bytes());
}
if comb != 0 {
if let Some(c) = char::from_u32(comb) {
let mut buf = [0u8; 4];
let s = c.encode_utf8(&mut buf);
out.extend_from_slice(s.as_bytes());
}
}
} else {
let mut buf = [0u8; 4];
let s = ch.encode_utf8(&mut buf);
out.extend_from_slice(s.as_bytes());
}
}
out
}
pub fn decode(input: &[u8], charset: VietCharset) -> String {
match charset {
VietCharset::Unicode => String::from_utf8_lossy(input).to_string(),
VietCharset::WinCP1258 => {
let _map = unicode_to_index_map();
let mut rev: HashMap<(u8, u8), usize> = HashMap::with_capacity(VN_CHAR_COUNT);
for (i, &w) in WINCP1258_TABLE.iter().enumerate() {
let lo = (w & 0xFF) as u8;
let hi = (w >> 8) as u8;
rev.insert((lo, hi), i);
}
let mut out = String::with_capacity(input.len());
let mut i = 0;
while i < input.len() {
let lo = input[i];
if i + 1 < input.len() {
let hi = input[i + 1];
if let Some(&idx) = rev.get(&(lo, hi)) {
out.push(UNICODE_TABLE[idx]);
i += 2;
continue;
}
}
if let Some(&idx) = rev.get(&(lo, 0)) {
out.push(UNICODE_TABLE[idx]);
} else {
out.push(lo as char);
}
i += 1;
}
out
}
VietCharset::VIQR => {
let s = String::from_utf8_lossy(input);
crate::engine::convert_viqr(&s)
}
VietCharset::TCVN3 => {
let map = tcvn3_to_index_map();
let mut out = String::with_capacity(input.len());
for &b in input {
if let Some(&idx) = map.get(&b) {
out.push(UNICODE_TABLE[idx]);
} else {
out.push(b as char);
}
}
out
}
VietCharset::VNIWin => {
let map = vniwin_to_index_map();
let mut out = String::with_capacity(input.len());
let mut i = 0;
while i < input.len() {
if i + 1 < input.len() {
let lo = input[i];
let hi = input[i + 1];
let w = lo as u16 | ((hi as u16) << 8);
if let Some(&idx) = map.get(&w) {
out.push(UNICODE_TABLE[idx]);
i += 2;
continue;
}
}
out.push(input[i] as char);
i += 1;
}
out
}
cs if is_single_byte(cs) => {
let table = sb_table(cs);
let mut rev: HashMap<u8, usize> = HashMap::with_capacity(VN_CHAR_COUNT);
for (i, &b) in table.iter().enumerate() {
rev.insert(b, i);
}
let mut out = String::with_capacity(input.len());
for &b in input {
if let Some(&idx) = rev.get(&b) {
out.push(UNICODE_TABLE[idx]);
} else {
out.push(b as char);
}
}
out
}
cs if is_double_byte(cs) => {
let table = db_table(cs);
let mut rev: HashMap<u16, usize> = HashMap::with_capacity(VN_CHAR_COUNT);
for (i, &w) in table.iter().enumerate() {
rev.insert(w, i);
}
let mut out = String::with_capacity(input.len());
let mut i = 0;
while i < input.len() {
if i + 1 < input.len() {
let lo = input[i];
let hi = input[i + 1];
let w = lo as u16 | ((hi as u16) << 8);
if let Some(&idx) = rev.get(&w) {
out.push(UNICODE_TABLE[idx]);
i += 2;
continue;
}
}
out.push(input[i] as char);
i += 1;
}
out
}
VietCharset::UnicodeDecomposed => String::from_utf8_lossy(input).to_string(),
_ => String::from_utf8_lossy(input).to_string(),
}
}
pub fn remove_tone(input: &str) -> String {
let map = unicode_to_index_map();
let mut out = String::with_capacity(input.len());
for ch in input.chars() {
if let Some(&idx) = map.get(&ch) {
let toneless_idx = NO_TONE[idx];
out.push(UNICODE_TABLE[toneless_idx]);
} else {
out.push(ch);
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unicode_table_size() {
assert_eq!(UNICODE_TABLE.len(), 213);
assert_eq!(TCVN3_TABLE.len(), 213);
assert_eq!(VNIWIN_TABLE.len(), 213);
assert_eq!(NO_TONE.len(), 213);
}
#[test]
fn unicode_roundtrip() {
let input = "tiếng Việt";
let encoded = encode(input, VietCharset::Unicode);
let decoded = decode(&encoded, VietCharset::Unicode);
assert_eq!(decoded, input);
}
#[test]
fn tcvn3_roundtrip() {
let input = "tiếng Việt";
let encoded = encode(input, VietCharset::TCVN3);
let decoded = decode(&encoded, VietCharset::TCVN3);
assert_eq!(decoded, input);
}
#[test]
fn vniwin_roundtrip() {
let input = "tiếng Việt";
let encoded = encode(input, VietCharset::VNIWin);
let decoded = decode(&encoded, VietCharset::VNIWin);
assert_eq!(decoded, input);
}
#[test]
fn all_vietnamese_vowels() {
let all_vowels = "aàáảãạ âầấẩẫậ ăằắẳẵặ eèéẻẽẹ êềếểễệ iìíỉĩị oòóỏõọ ôồốổỗộ ơờớởỡợ uùúủũụ ưừứửữự yỳýỷỹỵ";
for charset in [VietCharset::TCVN3, VietCharset::VNIWin] {
let encoded = encode(all_vowels, charset);
let decoded = decode(&encoded, charset);
assert_eq!(decoded, all_vowels, "roundtrip failed for {:?}", charset);
}
}
#[test]
fn remove_tone_works() {
assert_eq!(remove_tone("tiếng"), "tiêng");
assert_eq!(remove_tone("Việt"), "Viêt");
assert_eq!(remove_tone("được"), "đươc");
assert_eq!(remove_tone("không"), "không"); assert_eq!(remove_tone("xin chào"), "xin chao");
}
#[test]
fn wincp1258_roundtrip() {
let input = "tiếng Việt";
let encoded = encode(input, VietCharset::WinCP1258);
let decoded = decode(&encoded, VietCharset::WinCP1258);
assert_eq!(decoded, input);
}
#[test]
fn viqr_output() {
let input = "tiếng Việt";
let encoded = encode(input, VietCharset::VIQR);
assert!(encoded.iter().all(|&b| b <= 0x7F));
let s = String::from_utf8(encoded).unwrap();
assert!(s.contains("tie^'ng") || s.contains("tie^'ng") || s.len() > 0);
}
#[test]
fn english_pass_through() {
let input = "Hello world 123!";
for charset in [VietCharset::TCVN3, VietCharset::VNIWin, VietCharset::WinCP1258, VietCharset::Unicode] {
let encoded = encode(input, charset);
let decoded = decode(&encoded, charset);
assert_eq!(decoded, input, "English pass-through failed for {:?}", charset);
}
}
}