1use std::collections::HashMap;
17use std::sync::OnceLock;
18
19const VN_CHAR_COUNT: usize = 213;
48
49static UNICODE_TABLE: [char; VN_CHAR_COUNT] = [
54 'A', 'a', '\u{00C1}', '\u{00E1}', '\u{00C0}', '\u{00E0}',
56 '\u{1EA2}', '\u{1EA3}', '\u{00C3}', '\u{00E3}', '\u{1EA0}', '\u{1EA1}',
57 '\u{00C2}', '\u{00E2}', '\u{1EA4}', '\u{1EA5}', '\u{1EA6}', '\u{1EA7}',
59 '\u{1EA8}', '\u{1EA9}', '\u{1EAA}', '\u{1EAB}', '\u{1EAC}', '\u{1EAD}',
60 '\u{0102}', '\u{0103}', '\u{1EAE}', '\u{1EAF}', '\u{1EB0}', '\u{1EB1}',
62 '\u{1EB2}', '\u{1EB3}', '\u{1EB4}', '\u{1EB5}', '\u{1EB6}', '\u{1EB7}',
63 'B', 'b', 'C', 'c', 'D', 'd',
65 '\u{0110}', '\u{0111}',
67 'E', 'e', '\u{00C9}', '\u{00E9}', '\u{00C8}', '\u{00E8}',
69 '\u{1EBA}', '\u{1EBB}', '\u{1EBC}', '\u{1EBD}', '\u{1EB8}', '\u{1EB9}',
70 '\u{00CA}', '\u{00EA}', '\u{1EBE}', '\u{1EBF}', '\u{1EC0}', '\u{1EC1}',
72 '\u{1EC2}', '\u{1EC3}', '\u{1EC4}', '\u{1EC5}', '\u{1EC6}', '\u{1EC7}',
73 'F', 'f', 'G', 'g', 'H', 'h',
75 'I', 'i', '\u{00CD}', '\u{00ED}', '\u{00CC}', '\u{00EC}',
77 '\u{1EC8}', '\u{1EC9}', '\u{0128}', '\u{0129}', '\u{1ECA}', '\u{1ECB}',
78 'J', 'j', 'K', 'k', 'L', 'l', 'M', 'm', 'N', 'n',
80 'O', 'o', '\u{00D3}', '\u{00F3}', '\u{00D2}', '\u{00F2}',
82 '\u{1ECE}', '\u{1ECF}', '\u{00D5}', '\u{00F5}', '\u{1ECC}', '\u{1ECD}',
83 '\u{00D4}', '\u{00F4}', '\u{1ED0}', '\u{1ED1}', '\u{1ED2}', '\u{1ED3}',
85 '\u{1ED4}', '\u{1ED5}', '\u{1ED6}', '\u{1ED7}', '\u{1ED8}', '\u{1ED9}',
86 '\u{01A0}', '\u{01A1}', '\u{1EDA}', '\u{1EDB}', '\u{1EDC}', '\u{1EDD}',
88 '\u{1EDE}', '\u{1EDF}', '\u{1EE0}', '\u{1EE1}', '\u{1EE2}', '\u{1EE3}',
89 'P', 'p', 'Q', 'q', 'R', 'r', 'S', 's', 'T', 't',
91 'U', 'u', '\u{00DA}', '\u{00FA}', '\u{00D9}', '\u{00F9}',
93 '\u{1EE6}', '\u{1EE7}', '\u{0168}', '\u{0169}', '\u{1EE4}', '\u{1EE5}',
94 '\u{01AF}', '\u{01B0}', '\u{1EE8}', '\u{1EE9}', '\u{1EEA}', '\u{1EEB}',
96 '\u{1EEC}', '\u{1EED}', '\u{1EEE}', '\u{1EEF}', '\u{1EF0}', '\u{1EF1}',
97 'V', 'v', 'W', 'w', 'X', 'x',
99 'Y', 'y', '\u{00DD}', '\u{00FD}', '\u{1EF2}', '\u{1EF3}',
101 '\u{1EF6}', '\u{1EF7}', '\u{1EF8}', '\u{1EF9}', '\u{1EF4}', '\u{1EF5}',
102 'Z', 'z',
104 '\u{20AC}', '\u{20A1}', '\u{0192}', '\u{201E}', '\u{2026}', '\u{2020}',
106 '\u{2021}', '\u{02C6}', '\u{2030}', '\u{0160}', '\u{2039}', '\u{0152}',
107 '\u{017D}', '\u{2018}', '\u{2019}', '\u{201C}', '\u{201D}', '\u{2022}',
108 '\u{2013}', '\u{2014}', '\u{02DC}', '\u{2122}', '\u{0161}', '\u{203A}',
109 '\u{0153}', '\u{017E}', '\u{0178}',
110];
111
112static TCVN3_TABLE: [u8; VN_CHAR_COUNT] = [
118 b'A', b'a', 0xB8, 0xB8, 0xB5, 0xB5, 0xB6, 0xB6, 0xB7, 0xB7, 0xB9, 0xB9,
120 0xA2, 0xA9, 0xCA, 0xCA, 0xC7, 0xC7, 0xC8, 0xC8, 0xC9, 0xC9, 0xCB, 0xCB,
122 0xA1, 0xA8, 0xBE, 0xBE, 0xBB, 0xBB, 0xBC, 0xBC, 0xBD, 0xBD, 0xC6, 0xC6,
124 b'B', b'b', b'C', b'c', b'D', b'd',
126 0xA5, 0xB0,
128 b'E', b'e', 0xC4, 0xC4, 0xBF, 0xBF, 0xC0, 0xC0, 0xC1, 0xC1, 0xC2, 0xC2,
130 0xA4, 0xAB, 0xCC, 0xCC, 0xC3, 0xC3, 0xCE, 0xCE, 0xCF, 0xCF, 0xD0, 0xD0,
132 b'F', b'f', b'G', b'g', b'H', b'h',
134 b'I', b'i', 0xAE, 0xAE, 0xA6, 0xA6, 0xB1, 0xB1, 0xB2, 0xB2, 0xB3, 0xB3,
136 b'J', b'j', b'K', b'k', b'L', b'l', b'M', b'm', b'N', b'n',
138 b'O', b'o', 0xD3, 0xD3, 0xD4, 0xD4, 0xD5, 0xD5, 0xD6, 0xD6, 0xD8, 0xD8,
140 0xA3, 0xAA, 0xD9, 0xD9, 0xD1, 0xD1, 0xDA, 0xDA, 0xDB, 0xDB, 0xDC, 0xDC,
142 0xA7, 0xAD, 0xE1, 0xE1, 0xDD, 0xDD, 0xDE, 0xDE, 0xDF, 0xDF, 0xE0, 0xE0,
144 b'P', b'p', b'Q', b'q', b'R', b'r', b'S', b's', b'T', b't',
146 b'U', b'u', 0xE7, 0xE7, 0xE4, 0xE4, 0xE5, 0xE5, 0xE6, 0xE6, 0xE8, 0xE8,
148 0xB4, 0xAC, 0xE3, 0xE3, 0xD7, 0xD7, 0xE9, 0xE9, 0xEA, 0xEA, 0xEB, 0xEB,
150 b'V', b'v', b'W', b'w', b'X', b'x',
152 b'Y', b'y', 0xED, 0xED, 0xEE, 0xEE, 0xF0, 0xF0, 0xF1, 0xF1, 0xEF, 0xEF,
154 b'Z', b'z',
156 0x80, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
158 0x89, 0x8A, 0x8B, 0x8C, 0x8E, 0x91, 0x92, 0x93,
159 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B,
160 0x9C, 0x9E, 0x9F,
161];
162
163static VNIWIN_TABLE: [u16; VN_CHAR_COUNT] = [
169 0x0041, 0x0061, 0xd941, 0xf961, 0xd841, 0xf861, 0xdb41, 0xfb61,
171 0xd541, 0xf561, 0xcf41, 0xef61,
172 0xc241, 0xe261, 0xc141, 0xe161, 0xc041, 0xe061, 0xc541, 0xe561,
174 0xc341, 0xe361, 0xc441, 0xe461,
175 0xca41, 0xea61, 0xc941, 0xe961, 0xc841, 0xe861, 0xda41, 0xfa61,
177 0xdc41, 0xfc61, 0xcb41, 0xeb61,
178 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064,
180 0x00d1, 0x00f1,
182 0x0045, 0x0065, 0xd945, 0xf965, 0xd845, 0xf865, 0xdb45, 0xfb65,
184 0xd545, 0xf565, 0xcf45, 0xef65,
185 0xc245, 0xe265, 0xc145, 0xe165, 0xc045, 0xe065, 0xc545, 0xe565,
187 0xc345, 0xe365, 0xc445, 0xe465,
188 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068,
190 0x0049, 0x0069, 0x00cd, 0x00ed, 0x00cc, 0x00ec, 0x00c6, 0x00e6,
192 0x00d3, 0x00f3, 0x00d2, 0x00f2,
193 0x004a, 0x006a, 0x004b, 0x006b, 0x004c, 0x006c, 0x004d, 0x006d,
195 0x004e, 0x006e,
196 0x004f, 0x006f, 0xd94f, 0xf96f, 0xd84f, 0xf86f, 0xdb4f, 0xfb6f,
198 0xd54f, 0xf56f, 0xcf4f, 0xef6f,
199 0xc24f, 0xe26f, 0xc14f, 0xe16f, 0xc04f, 0xe06f, 0xc54f, 0xe56f,
201 0xc34f, 0xe36f, 0xc44f, 0xe46f,
202 0x00d4, 0x00f4, 0xd9d4, 0xf9f4, 0xd8d4, 0xf8f4, 0xdbd4, 0xfbf4,
204 0xd5d4, 0xf5f4, 0xcfd4, 0xeff4,
205 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072, 0x0053, 0x0073,
207 0x0054, 0x0074,
208 0x0055, 0x0075, 0xd955, 0xf975, 0xd855, 0xf875, 0xdb55, 0xfb75,
210 0xd555, 0xf575, 0xcf55, 0xef75,
211 0x00d6, 0x00f6, 0xd9d6, 0xf9f6, 0xd8d6, 0xf8f6, 0xdbd6, 0xfbf6,
213 0xd5d6, 0xf5f6, 0xcfd6, 0xeff6,
214 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078,
216 0x0059, 0x0079, 0xd959, 0xf979, 0xd859, 0xf879, 0xdb59, 0xfb79,
218 0xd559, 0xf579, 0x00ce, 0x00ee,
219 0x005a, 0x007a,
221 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088,
223 0x0089, 0x008A, 0x008B, 0x008C, 0x008E, 0x0091, 0x0092, 0x0093,
224 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B,
225 0x009C, 0x009E, 0x009F,
226];
227
228static VIQR_TABLE: [u32; VN_CHAR_COUNT] = [
233 0x00000041, 0x00000061, 0x00002741, 0x00002761, 0x00006041, 0x00006061,
235 0x00003F41, 0x00003F61, 0x00007E41, 0x00007E61, 0x00002E41, 0x00002E61,
236 0x005E0041, 0x005E0061, 0x00275E41, 0x00275E61, 0x00605E41, 0x00605E61,
238 0x003F5E41, 0x003F5E61, 0x007E5E41, 0x007E5E61, 0x002E5E41, 0x002E5E61,
239 0x00280041, 0x00280061, 0x00272841, 0x00272861, 0x00602841, 0x00602861,
241 0x003F2841, 0x003F2861, 0x007E2841, 0x007E2861, 0x002E2841, 0x002E2861,
242 0x42, 0x62, 0x43, 0x63, 0x44, 0x64, 0x00004444, 0x00006464,
244 0x45, 0x65, 0x00002745, 0x00002765, 0x00006045, 0x00006065,
246 0x00003F45, 0x00003F65, 0x00007E45, 0x00007E65, 0x00002E45, 0x00002E65,
247 0x005E0045, 0x005E0065, 0x00275E45, 0x00275E65, 0x00605E45, 0x00605E65,
249 0x003F5E45, 0x003F5E65, 0x007E5E45, 0x007E5E65, 0x002E5E45, 0x002E5E65,
250 0x46, 0x66, 0x47, 0x67, 0x48, 0x68,
252 0x49, 0x69, 0x00002749, 0x00002769, 0x00006049, 0x00006069,
253 0x00003F49, 0x00003F69, 0x00007E49, 0x00007E69, 0x00002E49, 0x00002E69,
254 0x4A, 0x6A, 0x4B, 0x6B, 0x4C, 0x6C, 0x4D, 0x6D, 0x4E, 0x6E,
256 0x4F, 0x6F, 0x0000274F, 0x0000276F, 0x0000604F, 0x0000606F,
258 0x00003F4F, 0x00003F6F, 0x00007E4F, 0x00007E6F, 0x00002E4F, 0x00002E6F,
259 0x005E004F, 0x005E006F, 0x00275E4F, 0x00275E6F, 0x00605E4F, 0x00605E6F,
261 0x003F5E4F, 0x003F5E6F, 0x007E5E4F, 0x007E5E6F, 0x002E5E4F, 0x002E5E6F,
262 0x002B004F, 0x002B006F, 0x00272B4F, 0x00272B6F, 0x00602B4F, 0x00602B6F,
264 0x003F2B4F, 0x003F2B6F, 0x007E2B4F, 0x007E2B6F, 0x002E2B4F, 0x002E2B6F,
265 0x50, 0x70, 0x51, 0x71, 0x52, 0x72, 0x53, 0x73, 0x54, 0x74,
267 0x55, 0x75, 0x00002755, 0x00002775, 0x00006055, 0x00006075,
269 0x00003F55, 0x00003F75, 0x00007E55, 0x00007E75, 0x00002E55, 0x00002E75,
270 0x002B0055, 0x002B0075, 0x00272B55, 0x00272B75, 0x00602B55, 0x00602B75,
272 0x003F2B55, 0x003F2B75, 0x007E2B55, 0x007E2B75, 0x002E2B55, 0x002E2B75,
273 0x56, 0x76, 0x57, 0x77, 0x58, 0x78,
275 0x59, 0x79, 0x00002759, 0x00002779, 0x00006059, 0x00006079,
277 0x00003F59, 0x00003F79, 0x00007E59, 0x00007E79, 0x00002E59, 0x00002E79,
278 0x5A, 0x7A,
280 0x80, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
282 0x89, 0x8A, 0x8B, 0x8C, 0x8E, 0x91, 0x92, 0x93,
283 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B,
284 0x9C, 0x9E, 0x9F,
285];
286
287fn push_viqr_bytes(out: &mut Vec<u8>, w: u32) {
289 let b0 = (w & 0xFF) as u8;
290 let b1 = ((w >> 8) & 0xFF) as u8;
291 let b2 = ((w >> 16) & 0xFF) as u8;
292 if b0 != 0 { out.push(b0); }
293 if b1 != 0 { out.push(b1); }
294 if b2 != 0 { out.push(b2); }
295}
296
297static WINCP1258_TABLE: [u16; VN_CHAR_COUNT] = [
304 0x0041, 0x0061, 0x00C1, 0x00E1, 0x00C0, 0x00E0,
306 0xD241, 0xD261, 0xDE41, 0xDE61, 0xF241, 0xF261,
307 0x00C2, 0x00E2, 0xECC2, 0xECE2, 0xCCC2, 0xCCE2,
309 0xD2C2, 0xD2E2, 0xDEC2, 0xDEE2, 0xF2C2, 0xF2E2,
310 0x00C3, 0x00E3, 0xECC3, 0xECE3, 0xCCC3, 0xCCE3,
312 0xD2C3, 0xD2E3, 0xDEC3, 0xDEE3, 0xF2C3, 0xF2E3,
313 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064, 0x00D0, 0x00F0,
315 0x0045, 0x0065, 0x00C9, 0x00E9, 0x00C8, 0x00E8,
317 0xD245, 0xD265, 0xDE45, 0xDE65, 0xF245, 0xF265,
318 0x00CA, 0x00EA, 0xECCA, 0xECEA, 0xCCCA, 0xCCEA,
320 0xD2CA, 0xD2EA, 0xDECA, 0xDEEA, 0xF2CA, 0xF2EA,
321 0x0046, 0x0066, 0x0047, 0x0067, 0x0048, 0x0068,
323 0x0049, 0x0069, 0x00CD, 0x00ED, 0xCC49, 0xCC69,
325 0xD249, 0xD269, 0xDE49, 0xDE69, 0xF249, 0xF269,
326 0x004A, 0x006A, 0x004B, 0x006B, 0x004C, 0x006C,
328 0x004D, 0x006D, 0x004E, 0x006E,
329 0x004F, 0x006F, 0x00D3, 0x00F3, 0xCC4F, 0xCC6F,
331 0xD24F, 0xD26F, 0xDE4F, 0xDE6F, 0xF24F, 0xF26F,
332 0x00D4, 0x00F4, 0xECD4, 0xECF4, 0xCCD4, 0xCCF4,
334 0xD2D4, 0xD2F4, 0xDED4, 0xDEF4, 0xF2D4, 0xF2F4,
335 0x00D5, 0x00F5, 0xECD5, 0xECF5, 0xCCD5, 0xCCF5,
337 0xD2D5, 0xD2F5, 0xDED5, 0xDEF5, 0xF2D5, 0xF2F5,
338 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072,
340 0x0053, 0x0073, 0x0054, 0x0074,
341 0x0055, 0x0075, 0x00DA, 0x00FA, 0x00D9, 0x00F9,
343 0xD255, 0xD275, 0xDE55, 0xDE75, 0xF255, 0xF275,
344 0x00DD, 0x00FD, 0xECDD, 0xECFD, 0xCCDD, 0xCCFD,
346 0xD2DD, 0xD2FD, 0xDEDD, 0xDEFD, 0xF2DD, 0xF2FD,
347 0x0056, 0x0076, 0x0057, 0x0077, 0x0058, 0x0078,
349 0x0059, 0x0079, 0xEC59, 0xEC79, 0xCC59, 0xCC79,
351 0xD259, 0xD279, 0xDE59, 0xDE79, 0xF259, 0xF279,
352 0x005A, 0x007A,
354 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086,
356 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C,
357 0x008E, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095,
358 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B,
359 0x009C, 0x009E, 0x009F,
360];
361
362static VPS_TABLE: [u8; VN_CHAR_COUNT] = [
365 0x41, 0x61, 0xC1, 0xE1, 0x80, 0xE0, 0x81, 0xE4, 0x82, 0xE3, 0xE5, 0xE5,
366 0xC2, 0xE2, 0x83, 0xC3, 0x84, 0xC0, 0x85, 0xC4, 0xC5, 0xC5, 0xC6, 0xC6,
367 0x88, 0xE6, 0x8D, 0xA1, 0x8E, 0xA2, 0x8F, 0xA3, 0xF0, 0xA4, 0xA5, 0xA5,
368 0x42, 0x62, 0x43, 0x63, 0x44, 0x64, 0xF1, 0xC7, 0x45, 0x65, 0xC9, 0xE9,
369 0xD7, 0xE8, 0xDE, 0xC8, 0xFE, 0xEB, 0xCB, 0xCB, 0xCA, 0xEA, 0x90, 0x89,
370 0x93, 0x8A, 0x94, 0x8B, 0x95, 0xCD, 0x8C, 0x8C, 0x46, 0x66, 0x47, 0x67,
371 0x48, 0x68, 0x49, 0x69, 0xB4, 0xED, 0xB5, 0xEC, 0xB7, 0xCC, 0xB8, 0xEF,
372 0xCE, 0xCE, 0x4A, 0x6A, 0x4B, 0x6B, 0x4C, 0x6C, 0x4D, 0x6D, 0x4E, 0x6E,
373 0x4F, 0x6F, 0xB9, 0xF3, 0xBC, 0xF2, 0xBD, 0xD5, 0xBE, 0xF5, 0x86, 0x86,
374 0xD4, 0xF4, 0x96, 0xD3, 0x97, 0xD2, 0x98, 0xB0, 0x99, 0x87, 0xB6, 0xB6,
375 0xF7, 0xD6, 0x9D, 0xA7, 0x9E, 0xA9, 0x9F, 0xAA, 0xA6, 0xAB, 0xAE, 0xAE,
376 0x50, 0x70, 0x51, 0x71, 0x52, 0x72, 0x53, 0x73, 0x54, 0x74, 0x55, 0x75,
377 0xDA, 0xFA, 0xA8, 0xF9, 0xD1, 0xFB, 0xAC, 0xDB, 0xF8, 0xF8, 0xD0, 0xDC,
378 0xAD, 0xD9, 0xAF, 0xD8, 0xB1, 0xBA, 0xBB, 0xBB, 0xBF, 0xBF, 0x56, 0x76,
379 0x57, 0x77, 0x58, 0x78, 0x59, 0x79, 0xDD, 0x9A, 0xB2, 0xFF, 0xFD, 0x9B,
380 0xB3, 0xCF, 0x9C, 0x9C, 0x5A, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
381 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x91, 0x92, 0x00, 0x00, 0x00,
382 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E, 0x00,];
383static VISCII_TABLE: [u8; VN_CHAR_COUNT] = [
384 0x41, 0x61, 0xC1, 0xE1, 0xC0, 0xE0, 0xC4, 0xE4, 0xC3, 0xE3, 0x80, 0xD5,
385 0xC2, 0xE2, 0x84, 0xA4, 0x85, 0xA5, 0x86, 0xA6, 0xE7, 0xE7, 0x87, 0xA7,
386 0xC5, 0xE5, 0x81, 0xA1, 0x82, 0xA2, 0xC6, 0xC6, 0xC7, 0xC7, 0x83, 0xA3,
387 0x42, 0x62, 0x43, 0x63, 0x44, 0x64, 0xD0, 0xF0, 0x45, 0x65, 0xC9, 0xE9,
388 0xC8, 0xE8, 0xCB, 0xEB, 0x88, 0xA8, 0x89, 0xA9, 0xCA, 0xEA, 0x8A, 0xAA,
389 0x8B, 0xAB, 0x8C, 0xAC, 0x8D, 0xAD, 0x8E, 0xAE, 0x46, 0x66, 0x47, 0x67,
390 0x48, 0x68, 0x49, 0x69, 0xCD, 0xED, 0xCC, 0xEC, 0x9B, 0xEF, 0xCE, 0xEE,
391 0x98, 0xB8, 0x4A, 0x6A, 0x4B, 0x6B, 0x4C, 0x6C, 0x4D, 0x6D, 0x4E, 0x6E,
392 0x4F, 0x6F, 0xD3, 0xF3, 0xD2, 0xF2, 0x99, 0xF6, 0xF5, 0xF5, 0x9A, 0xF7,
393 0xD4, 0xF4, 0x8F, 0xAF, 0x90, 0xB0, 0x91, 0xB1, 0x92, 0xB2, 0x93, 0xB5,
394 0xB4, 0xBD, 0x95, 0xBE, 0x96, 0xB6, 0x97, 0xB7, 0xB3, 0xDE, 0x94, 0xFE,
395 0x50, 0x70, 0x51, 0x71, 0x52, 0x72, 0x53, 0x73, 0x54, 0x74, 0x55, 0x75,
396 0xDA, 0xFA, 0xD9, 0xF9, 0x9C, 0xFC, 0x9D, 0xFB, 0x9E, 0xF8, 0xBF, 0xDF,
397 0xBA, 0xD1, 0xBB, 0xD7, 0xBC, 0xD8, 0xFF, 0xE6, 0xB9, 0xF1, 0x56, 0x76,
398 0x57, 0x77, 0x58, 0x78, 0x59, 0x79, 0xDD, 0xFD, 0x9F, 0xCF, 0xD6, 0xD6,
399 0xDB, 0xDB, 0xDC, 0xDC, 0x5A, 0x7A, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
400 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x00, 0x00,
401 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E, 0x00,];
402static BKHCM1_TABLE: [u8; VN_CHAR_COUNT] = [
403 0x41, 0x61, 0x80, 0xBE, 0x81, 0xBF, 0x82, 0xC0, 0x83, 0xC1, 0x84, 0xC2,
404 0x9F, 0xDD, 0x7E, 0xDE, 0xA1, 0xDF, 0xA2, 0xE0, 0xA3, 0xE1, 0xA4, 0xE2,
405 0x99, 0xD7, 0x9A, 0xD8, 0x9B, 0xD9, 0x9C, 0xDA, 0x9D, 0xDB, 0x98, 0xDC,
406 0x42, 0x62, 0x43, 0x63, 0x44, 0x64, 0x7D, 0xBD, 0x45, 0x65, 0x85, 0xC3,
407 0x86, 0xC4, 0x87, 0xC5, 0x88, 0xC6, 0x89, 0xC7, 0xA5, 0xE3, 0xA6, 0xE4,
408 0xA7, 0xE5, 0xA8, 0xE6, 0xA9, 0xE7, 0xAA, 0xE8, 0x46, 0x66, 0x47, 0x67,
409 0x48, 0x68, 0x49, 0x69, 0x8A, 0xC8, 0x8B, 0xC9, 0x8C, 0xCA, 0x8D, 0xCB,
410 0x8E, 0xCC, 0x4A, 0x6A, 0x4B, 0x6B, 0x4C, 0x6C, 0x4D, 0x6D, 0x4E, 0x6E,
411 0x4F, 0x6F, 0x8F, 0xCD, 0x90, 0xCE, 0x91, 0xCF, 0x92, 0xD0, 0x93, 0xD1,
412 0xAB, 0xE9, 0xAC, 0xEA, 0xAD, 0xEB, 0xAE, 0xEC, 0xAF, 0xED, 0xB0, 0xEE,
413 0xB1, 0xEF, 0xB2, 0xF0, 0xB3, 0xF1, 0xB4, 0xF2, 0xB5, 0xF3, 0xB6, 0xF4,
414 0x50, 0x70, 0x51, 0x71, 0x52, 0x72, 0x53, 0x73, 0x54, 0x74, 0x55, 0x75,
415 0x94, 0xD2, 0x95, 0xD3, 0x96, 0xD4, 0x97, 0xD5, 0x98, 0xD6, 0xB7, 0xF5,
416 0xB8, 0xF6, 0xB9, 0xF7, 0xBA, 0xF8, 0xBB, 0xF9, 0xBC, 0xFA, 0x56, 0x76,
417 0x57, 0x77, 0x58, 0x78, 0x59, 0x79, 0x7B, 0xFB, 0x5E, 0xFC, 0x60, 0xFD,
418 0x7C, 0xFE, 0x8E, 0xFF, 0x5A, 0x7A, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
419 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x00, 0x00,
420 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E, 0x00,];
421static VIETWAREF_TABLE: [u8; VN_CHAR_COUNT] = [
422 0x41, 0x61, 0xC0, 0xC0, 0xAA, 0xAA, 0xB6, 0xB6, 0xBA, 0xBA, 0xC1, 0xC1,
423 0x97, 0xA1, 0xCA, 0xCA, 0xC7, 0xC7, 0xC8, 0xC8, 0xC9, 0xC9, 0xCB, 0xCB,
424 0x96, 0x9F, 0xC5, 0xC5, 0xC2, 0xC2, 0xC3, 0xC3, 0xC4, 0xC4, 0xC6, 0xC6,
425 0x42, 0x62, 0x43, 0x63, 0x44, 0x64, 0x98, 0xA2, 0x45, 0x65, 0xCF, 0xCF,
426 0xCC, 0xCC, 0xCD, 0xCD, 0xCE, 0xCE, 0xD1, 0xD1, 0x99, 0xA3, 0xD5, 0xD5,
427 0xD2, 0xD2, 0xD3, 0xD3, 0xD4, 0xD4, 0xD6, 0xD6, 0x46, 0x66, 0x47, 0x67,
428 0x48, 0x68, 0x49, 0x69, 0xDB, 0xDB, 0xD8, 0xD8, 0xD9, 0xD9, 0xDA, 0xDA,
429 0xDC, 0xDC, 0x4A, 0x6A, 0x4B, 0x6B, 0x4C, 0x6C, 0x4D, 0x6D, 0x4E, 0x6E,
430 0x4F, 0x6F, 0xE2, 0xE2, 0xDF, 0xDF, 0xE0, 0xE0, 0xE1, 0xE1, 0xE3, 0xE3,
431 0x9A, 0xA4, 0xE7, 0xE7, 0xE4, 0xE4, 0xE5, 0xE5, 0xE6, 0xE6, 0xE8, 0xE8,
432 0x9B, 0xA5, 0xEC, 0xEC, 0xE9, 0xE9, 0xEA, 0xEA, 0xEB, 0xEB, 0xED, 0xED,
433 0x50, 0x70, 0x51, 0x71, 0x52, 0x72, 0x53, 0x73, 0x54, 0x74, 0x55, 0x75,
434 0xF2, 0xF2, 0xEE, 0xEE, 0xEF, 0xEF, 0xF1, 0xF1, 0xF3, 0xF3, 0x9C, 0xA7,
435 0xF7, 0xF7, 0xF4, 0xF4, 0xF5, 0xF5, 0xF6, 0xF6, 0xF8, 0xF8, 0x56, 0x76,
436 0x57, 0x77, 0x58, 0x78, 0x59, 0x79, 0xFC, 0xFC, 0xF9, 0xF9, 0xFA, 0xFA,
437 0xFB, 0xFB, 0xFF, 0xFF, 0x5A, 0x7A, 0x80, 0x82, 0x83, 0x84, 0x85, 0x86,
438 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8E, 0x91, 0x92, 0x93, 0x94, 0x95,
439 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E, 0x00,];
440static ISC_TABLE: [u8; VN_CHAR_COUNT] = [
441 0x41, 0x61, 0x83, 0xB8, 0x80, 0xB5, 0x81, 0xB6, 0x82, 0xB7, 0x84, 0xB9,
442 0xA2, 0xA9, 0xC4, 0xA0, 0xC1, 0xC7, 0xC2, 0xC8, 0xC3, 0xC9, 0x86, 0xCB,
443 0xA1, 0xA8, 0xC0, 0xBE, 0xAF, 0xBB, 0xBA, 0xBC, 0xBF, 0xBD, 0x85, 0xC6,
444 0x42, 0x62, 0x43, 0x63, 0x44, 0x64, 0xA7, 0xAE, 0x45, 0x65, 0xD0, 0x8A,
445 0x87, 0xCC, 0x88, 0xCE, 0x89, 0xCF, 0xD1, 0x8B, 0xA3, 0xAA, 0xDA, 0xD5,
446 0xC5, 0xD2, 0xCD, 0xD3, 0xD9, 0xD4, 0x8C, 0xD6, 0x46, 0x66, 0x47, 0x67,
447 0x48, 0x68, 0x49, 0x69, 0x90, 0xDD, 0x8D, 0xD7, 0x8E, 0xD8, 0x8F, 0xDC,
448 0x91, 0xDE, 0x4A, 0x6A, 0x4B, 0x6B, 0x4C, 0x6C, 0x4D, 0x6D, 0x4E, 0x6E,
449 0x4F, 0x6F, 0x95, 0xE3, 0x92, 0xDF, 0x93, 0xE1, 0x94, 0xE2, 0x96, 0xE4,
450 0xA4, 0xAB, 0xFF, 0xE8, 0xDB, 0xE5, 0xE0, 0xE6, 0xF0, 0xE7, 0x97, 0xE9,
451 0xA5, 0xAC, 0x9B, 0xED, 0x98, 0xEA, 0x99, 0xEB, 0x9A, 0xEC, 0x9C, 0xEE,
452 0x50, 0x70, 0x51, 0x71, 0x52, 0x72, 0x53, 0x73, 0x54, 0x74, 0x55, 0x75,
453 0x40, 0xF3, 0x9D, 0xEF, 0x9E, 0xF1, 0x9F, 0xF2, 0x7C, 0xF4, 0xA6, 0xAD,
454 0x60, 0xF8, 0x5C, 0xF5, 0x5E, 0xF6, 0x7E, 0xF7, 0x23, 0xF9, 0x56, 0x76,
455 0x57, 0x77, 0x58, 0x78, 0x59, 0x79, 0xB3, 0xFD, 0xB0, 0xFA, 0xB1, 0xFB,
456 0xB2, 0xFC, 0xB4, 0xFE, 0x5A, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
457 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x12, 0x13, 0x14, 0x00,
458 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,];
459
460static BKHCM2_TABLE: [u16; VN_CHAR_COUNT] = [
463 0x0041, 0x0061, 0xC141, 0xE161, 0xC241, 0xE261,
464 0xC341, 0xE361, 0xC441, 0xE461, 0xC541, 0xE561,
465 0x00CA, 0x00EA, 0xCBCA, 0xEBEA, 0xCCCA, 0xECEA,
466 0xCDCA, 0xEDEA, 0xCECA, 0xEEEA, 0xC5CA, 0xE5EA,
467 0x00D9, 0x00F9, 0xC6D9, 0xE6F9, 0xC7D9, 0xE7F9,
468 0xC8D9, 0xE8F9, 0xC9D9, 0xE9F9, 0xC5D9, 0xE5F9,
469 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064,
470 0x00C0, 0x00E0, 0x0045, 0x0065, 0xC145, 0xE165,
471 0xC245, 0xE265, 0xC345, 0xE365, 0xC445, 0xE465,
472 0xC545, 0xE565, 0x00CF, 0x00EF, 0xCBCF, 0xEBEF,
473 0xCCCF, 0xECEF, 0xCDCF, 0xEDEF, 0xCECF, 0xEEEF,
474 0xE5CF, 0xE5EF, 0x0046, 0x0066, 0x0047, 0x0067,
475 0x0048, 0x0068, 0x0049, 0x0069, 0x00D1, 0x00F1,
476 0x00D2, 0x00F2, 0x00D3, 0x00F3, 0x00D4, 0x00F4,
477 0x00D5, 0x00F5, 0x004A, 0x006A, 0x004B, 0x006B,
478 0x004C, 0x006C, 0x004D, 0x006D, 0x004E, 0x006E,
479 0x004F, 0x006F, 0xC14F, 0xE16F, 0xC24F, 0xE26F,
480 0xC34F, 0xE36F, 0xC44F, 0xE46F, 0xC54F, 0xE56F,
481 0x00D6, 0x00F6, 0xCBD6, 0xEBF6, 0xCCD6, 0xECF6,
482 0xCDD6, 0xEDF6, 0xCED6, 0xEEF6, 0xC5D6, 0xE5F6,
483 0x00DA, 0x00FA, 0xC1DA, 0xE1FA, 0xC2DA, 0xE2FA,
484 0xC3DA, 0xE3FA, 0xC4DA, 0xE4FA, 0xC5DA, 0xE5FA,
485 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072,
486 0x0053, 0x0073, 0x0054, 0x0074, 0x0055, 0x0075,
487 0xC155, 0xE175, 0xC255, 0xE275, 0xC355, 0xE375,
488 0xC455, 0xE475, 0xC555, 0xE575, 0x00DB, 0x00FB,
489 0xC1DB, 0xE1FB, 0xC2DB, 0xE2FB, 0xC3DB, 0xE3FB,
490 0xC4DB, 0xE4FB, 0xC5DB, 0xE5FB, 0x0056, 0x0076,
491 0x0057, 0x0077, 0x0058, 0x0078, 0x0059, 0x0079,
492 0xC159, 0xE179, 0xC259, 0xE279, 0xC359, 0xE379,
493 0xC459, 0xE479, 0xC559, 0xE579, 0x005A, 0x007A,
494 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086,
495 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C,
496 0x008E, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095,
497 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B,
498 0x009C, 0x009E, 0x009F,];
499static VIETWAREX_TABLE: [u16; VN_CHAR_COUNT] = [
500 0x0041, 0x0061, 0xCF41, 0xEF61, 0xCC41, 0xEC61,
501 0xCD41, 0xED61, 0xCE41, 0xEE61, 0xDB41, 0xFB61,
502 0x00C1, 0x00E1, 0xDAC1, 0xFAE1, 0xD6C1, 0xF6E1,
503 0xD8C1, 0xF8E1, 0xD9C1, 0xF9E1, 0xDBC1, 0xFBE1,
504 0x00C0, 0x00E0, 0xD5C0, 0xF5E0, 0xD2C0, 0xF2E0,
505 0xD3C0, 0xF3E0, 0xD4C0, 0xF4E0, 0xDBC0, 0xFBE0,
506 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064,
507 0x00C2, 0x00E2, 0x0045, 0x0065, 0xCF45, 0xEF65,
508 0xCC45, 0xEC65, 0xCD45, 0xED65, 0xCE45, 0xEE65,
509 0xDB45, 0xFB65, 0x00C3, 0x00E3, 0xDAC3, 0xFAE3,
510 0xD6C3, 0xF6E3, 0xD8C3, 0xF8E3, 0xD9C3, 0xF9E3,
511 0xDBC3, 0xFBE3, 0x0046, 0x0066, 0x0047, 0x0067,
512 0x0048, 0x0068, 0x0049, 0x0069, 0x00CA, 0x00EA,
513 0x00C7, 0x00E7, 0x00C8, 0x00E8, 0x00C9, 0x00E9,
514 0x00CB, 0x00EB, 0x004A, 0x006A, 0x004B, 0x006B,
515 0x004C, 0x006C, 0x004D, 0x006D, 0x004E, 0x006E,
516 0x004F, 0x006F, 0xCF4F, 0xEF6F, 0xCC4F, 0xEC6F,
517 0xCD4F, 0xED6F, 0xCE4F, 0xEE6F, 0xDC4F, 0xFC6F,
518 0x00C4, 0x00E4, 0xDAC4, 0xFAE4, 0xD6C4, 0xF6E4,
519 0xD8C4, 0xF8E4, 0xD9C4, 0xF9E4, 0xDCC4, 0xFCE4,
520 0x00C5, 0x00E5, 0xCFC5, 0xEFE5, 0xCCC5, 0xECE5,
521 0xCDC5, 0xEDE5, 0xCEC5, 0xEEE5, 0xDCC5, 0xFCE5,
522 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072,
523 0x0053, 0x0073, 0x0054, 0x0074, 0x0055, 0x0075,
524 0xCF55, 0xEF75, 0xCC55, 0xEC75, 0xCD55, 0xED75,
525 0xCE55, 0xEE75, 0xDB55, 0xFB75, 0x00C6, 0x00E6,
526 0xCFC6, 0xEFE6, 0xCCC6, 0xECE6, 0xCDC6, 0xEDE6,
527 0xCEC6, 0xEEE6, 0xDBC6, 0xFBE6, 0x0056, 0x0076,
528 0x0057, 0x0077, 0x0058, 0x0078, 0x0059, 0x0079,
529 0xCF59, 0xEF79, 0xCC59, 0xEC79, 0xCD59, 0xED79,
530 0xCE59, 0xEE79, 0xD159, 0xF179, 0x005A, 0x007A,
531 0x0080, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086,
532 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C,
533 0x008E, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095,
534 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B,
535 0x009C, 0x009E, 0x009F,];
536static VNI_MAC_TABLE: [u16; VN_CHAR_COUNT] = [
537 0x0041, 0x0061, 0xF441, 0x9D61, 0xAF41, 0xBF61,
538 0xF341, 0x9E61, 0xCD41, 0x9B61, 0xEC41, 0x9561,
539 0xE541, 0x8961, 0xE741, 0x8761, 0xCB41, 0x8861,
540 0x8141, 0x8C61, 0xCC41, 0x8B61, 0x8041, 0x8A61,
541 0xE641, 0x9061, 0x8341, 0x8E61, 0xE941, 0x8F61,
542 0xF241, 0x9C61, 0x8641, 0x9F61, 0xE841, 0x9161,
543 0x0042, 0x0062, 0x0043, 0x0063, 0x0044, 0x0064,
544 0x0084, 0x0096, 0x0045, 0x0065, 0xF445, 0x9D65,
545 0xAF45, 0xBF65, 0xF345, 0x9E65, 0xCD45, 0x9B65,
546 0xEC45, 0x9565, 0xE545, 0x8965, 0xE745, 0x8765,
547 0xCB45, 0x8865, 0x8145, 0x8C65, 0xCC45, 0x8B65,
548 0x8045, 0x8A65, 0x0046, 0x0066, 0x0047, 0x0067,
549 0x0048, 0x0068, 0x0049, 0x0069, 0x00EA, 0x0092,
550 0x00ED, 0x0093, 0x00AE, 0x00BE, 0x00EE, 0x0097,
551 0x00F1, 0x0098, 0x004A, 0x006A, 0x004B, 0x006B,
552 0x004C, 0x006C, 0x004D, 0x006D, 0x004E, 0x006E,
553 0x004F, 0x006F, 0xF44F, 0x9D6F, 0xAF4F, 0xBF6F,
554 0xF34F, 0x9E6F, 0xCD4F, 0x9B6F, 0xEC4F, 0x956F,
555 0xE54F, 0x896F, 0xE74F, 0x876F, 0xCB4F, 0x886F,
556 0x814F, 0x8C6F, 0xCC4F, 0x8B6F, 0x804F, 0x8A6F,
557 0x00EF, 0x0099, 0xF4EF, 0x9D99, 0xAFEF, 0xBF99,
558 0xF3EF, 0x9E99, 0xCDEF, 0x9B99, 0xECEF, 0x9599,
559 0x0050, 0x0070, 0x0051, 0x0071, 0x0052, 0x0072,
560 0x0053, 0x0073, 0x0054, 0x0074, 0x0055, 0x0075,
561 0xF455, 0x9D75, 0xAF55, 0xBF75, 0xF355, 0x9E75,
562 0xCD55, 0x9B75, 0xEC55, 0x9575, 0x0085, 0x009A,
563 0xF485, 0x9D9A, 0xAF85, 0xBF9A, 0xF385, 0x9E9A,
564 0xCD85, 0x9B9A, 0xEC85, 0x959A, 0x0056, 0x0076,
565 0x0057, 0x0077, 0x0058, 0x0078, 0x0059, 0x0079,
566 0xF459, 0x9D79, 0xAF59, 0xBF79, 0xF359, 0x9E79,
567 0xCD59, 0x9B79, 0x00EB, 0x0094, 0x005A, 0x007A,
568 0x00DB, 0x00E2, 0x00C4, 0x00E3, 0x00C9, 0x00A0,
569 0x00E0, 0x00F6, 0x00E4, 0x003F, 0x00DC, 0x00CE,
570 0x003F, 0x00D4, 0x00D5, 0x00D2, 0x00D3, 0x00A5,
571 0x00D0, 0x00D1, 0x00F7, 0x00AA, 0x003F, 0x00DD,
572 0x00CF, 0x003F, 0x00D9,];
573
574static UNICOMP_TABLE: [u32; VN_CHAR_COUNT] = [ 0x00000041, 0x00000061, 0x03010041, 0x03010061,
577 0x03000041, 0x03000061, 0x03090041, 0x03090061,
578 0x03030041, 0x03030061, 0x03230041, 0x03230061,
579 0x000000C2, 0x000000E2, 0x030100C2, 0x030100E2,
580 0x030000C2, 0x030000E2, 0x030900C2, 0x030900E2,
581 0x030300C2, 0x030300E2, 0x032300C2, 0x032300E2,
582 0x00000102, 0x00000103, 0x03010102, 0x03010103,
583 0x03000102, 0x03000103, 0x03090102, 0x03090103,
584 0x03030102, 0x03030103, 0x03230102, 0x03230103,
585 0x00000042, 0x00000062, 0x00000043, 0x00000063,
586 0x00000044, 0x00000064, 0x00000110, 0x00000111,
587 0x000000D1, 0x000000F1, 0x00000045, 0x00000065,
588 0x03010045, 0x03010065, 0x03000045, 0x03000065,
589 0x03090045, 0x03090065, 0x03030045, 0x03030065,
590 0x03230045, 0x03230065, 0x000000CA, 0x000000EA,
591 0x030100CA, 0x030100EA, 0x030000CA, 0x030000EA,
592 0x030900CA, 0x030900EA, 0x030300CA, 0x030300EA,
593 0x032300CA, 0x032300EA, 0x00000046, 0x00000066,
594 0x00000047, 0x00000067, 0x00000048, 0x00000068,
595 0x00000049, 0x00000069, 0x03010049, 0x03010069,
596 0x03000049, 0x03000069, 0x03090049, 0x03090069,
597 0x03030049, 0x03030069, 0x03230049, 0x03230069,
598 0x0000004A, 0x0000006A, 0x0000004B, 0x0000006B,
599 0x0000004C, 0x0000006C, 0x0000004D, 0x0000006D,
600 0x0000004E, 0x0000006E, 0x0000004F, 0x0000006F,
601 0x0301004F, 0x0301006F, 0x0300004F, 0x0300006F,
602 0x0309004F, 0x0309006F, 0x0303004F, 0x0303006F,
603 0x0323004F, 0x0323006F, 0x000000D4, 0x000000F4,
604 0x030100D4, 0x030100F4, 0x030000D4, 0x030000F4,
605 0x030900D4, 0x030900F4, 0x030300D4, 0x030300F4,
606 0x032300D4, 0x032300F4, 0x000001A0, 0x000001A1,
607 0x030101A0, 0x030101A1, 0x030001A0, 0x030001A1,
608 0x030901A0, 0x030901A1, 0x030301A0, 0x030301A1,
609 0x032301A0, 0x032301A1, 0x00000050, 0x00000070,
610 0x00000051, 0x00000071, 0x00000052, 0x00000072,
611 0x00000053, 0x00000073, 0x00000054, 0x00000074,
612 0x00000055, 0x00000075, 0x03010055, 0x03010075,
613 0x03000055, 0x03000075, 0x03090055, 0x03090075,
614 0x03030055, 0x03030075, 0x03230055, 0x03230075,
615 0x000001AF, 0x000001B0, 0x030101AF, 0x030101B0,
616 0x030001AF, 0x030001B0, 0x030901AF, 0x030901B0,
617 0x030301AF, 0x030301B0, 0x032301AF, 0x032301B0,
618 0x00000056, 0x00000076, 0x00000057, 0x00000077,
619 0x00000058, 0x00000078, 0x00000059, 0x00000079,
620 0x03010059, 0x03010079, 0x03000059, 0x03000079,
621 0x03090059, 0x03090079, 0x03030059, 0x03030079,
622 0x03230059, 0x03230079, 0x0000005A, 0x0000007A,
623 0x000020AC, 0x000020A1, 0x00000192, 0x0000201E,
624 0x00002026, 0x00002020, 0x00002021, 0x000002C6,
625 0x00002030, 0x00000160, 0x00002039, 0x00000152,
626 0x0000017D, 0x00002018, 0x00002019, 0x0000201C,
627 0x0000201D, 0x00002022, 0x00002013, 0x00002014,
628 0x000002DC, 0x00002122, 0x00000161, 0x0000203A,
629 0x00000153,];
630
631static NO_TONE: &[usize; VN_CHAR_COUNT] = &[
636 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
638 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13,
640 24, 25, 24, 25, 24, 25, 24, 25, 24, 25, 24, 25,
642 36, 37, 38, 39, 40, 41,
644 42, 43,
646 44, 45, 44, 45, 44, 45, 44, 45, 44, 45, 44, 45,
648 56, 57, 56, 57, 56, 57, 56, 57, 56, 57, 56, 57,
650 68, 69, 70, 71, 72, 73,
652 74, 75, 74, 75, 74, 75, 74, 75, 74, 75, 74, 75,
654 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
656 96, 97, 96, 97, 96, 97, 96, 97, 96, 97, 96, 97,
658 108, 109, 108, 109, 108, 109, 108, 109, 108, 109, 108, 109,
660 120, 121, 120, 121, 120, 121, 120, 121, 120, 121, 120, 121,
662 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
664 142, 143, 142, 143, 142, 143, 142, 143, 142, 143, 142, 143,
666 154, 155, 154, 155, 154, 155, 154, 155, 154, 155, 154, 155,
668 166, 167, 168, 169, 170, 171,
670 172, 173, 172, 173, 172, 173, 172, 173, 172, 173, 172, 173,
672 184, 185,
674 186, 187, 188, 189, 190, 191, 192, 193,
676 194, 195, 196, 197, 198, 199, 200, 201,
677 202, 203, 204, 205, 206, 207, 208, 209,
678 210, 211, 212,
679];
680
681#[derive(Clone, Copy, PartialEq, Eq, Debug)]
685pub enum VietCharset {
686 Unicode = 0,
687 TCVN3 = 1,
688 VNIWin = 2,
689 WinCP1258 = 3,
690 VIQR = 4,
691 VPS = 5,
693 VISCII = 6,
694 BKHCM1 = 7,
695 VietwareF = 8,
696 ISC = 9,
697 BKHCM2 = 10,
699 VietwareX = 11,
700 VNIMac = 12,
701 UnicodeDecomposed = 13,
703}
704
705fn unicode_to_index_map() -> &'static HashMap<char, usize> {
708 static MAP: OnceLock<HashMap<char, usize>> = OnceLock::new();
709 MAP.get_or_init(|| {
710 let mut m = HashMap::with_capacity(VN_CHAR_COUNT);
711 for (i, &ch) in UNICODE_TABLE.iter().enumerate() {
712 m.insert(ch, i);
713 }
714 m
715 })
716}
717
718fn tcvn3_to_index_map() -> &'static HashMap<u8, usize> {
719 static MAP: OnceLock<HashMap<u8, usize>> = OnceLock::new();
720 MAP.get_or_init(|| {
721 let mut m = HashMap::with_capacity(VN_CHAR_COUNT);
722 for (i, &b) in TCVN3_TABLE.iter().enumerate() {
723 m.insert(b, i);
724 }
725 m
726 })
727}
728
729fn vniwin_to_index_map() -> &'static HashMap<u16, usize> {
730 static MAP: OnceLock<HashMap<u16, usize>> = OnceLock::new();
731 MAP.get_or_init(|| {
732 let mut m = HashMap::with_capacity(VN_CHAR_COUNT);
733 for (i, &w) in VNIWIN_TABLE.iter().enumerate() {
734 m.insert(w, i);
735 }
736 m
737 })
738}
739
740fn sb_table(cs: VietCharset) -> &'static [u8] {
744 match cs {
745 VietCharset::TCVN3 => &TCVN3_TABLE,
746 VietCharset::VPS => &VPS_TABLE,
747 VietCharset::VISCII => &VISCII_TABLE,
748 VietCharset::BKHCM1 => &BKHCM1_TABLE,
749 VietCharset::VietwareF => &VIETWAREF_TABLE,
750 VietCharset::ISC => &ISC_TABLE,
751 _ => &TCVN3_TABLE,
752 }
753}
754
755fn db_table(cs: VietCharset) -> &'static [u16] {
757 match cs {
758 VietCharset::VNIWin => &VNIWIN_TABLE,
759 VietCharset::BKHCM2 => &BKHCM2_TABLE,
760 VietCharset::VietwareX => &VIETWAREX_TABLE,
761 VietCharset::VNIMac => &VNI_MAC_TABLE,
762 _ => &VNIWIN_TABLE,
763 }
764}
765
766fn is_single_byte(cs: VietCharset) -> bool {
767 matches!(cs, VietCharset::TCVN3 | VietCharset::VPS | VietCharset::VISCII
768 | VietCharset::BKHCM1 | VietCharset::VietwareF | VietCharset::ISC)
769}
770
771fn is_double_byte(cs: VietCharset) -> bool {
772 matches!(cs, VietCharset::VNIWin | VietCharset::BKHCM2
773 | VietCharset::VietwareX | VietCharset::VNIMac)
774}
775
776pub fn encode(input: &str, charset: VietCharset) -> Vec<u8> {
778 match charset {
779 VietCharset::Unicode => input.as_bytes().to_vec(),
780 cs if is_single_byte(cs) => {
781 encode_sb(input, sb_table(cs))
782 }
783 cs if is_double_byte(cs) => {
784 encode_db(input, db_table(cs))
785 }
786 VietCharset::WinCP1258 => encode_wincp(input),
787 VietCharset::VIQR => encode_viqr(input),
788 VietCharset::UnicodeDecomposed => encode_nfd(input),
789 _ => input.as_bytes().to_vec(),
790 }
791}
792
793fn encode_sb(input: &str, table: &[u8]) -> Vec<u8> {
794 let map = unicode_to_index_map();
795 let mut out = Vec::with_capacity(input.len());
796 for ch in input.chars() {
797 if let Some(&idx) = map.get(&ch) {
798 out.push(table[idx]);
799 } else {
800 let mut buf = [0u8; 4];
801 let s = ch.encode_utf8(&mut buf);
802 out.extend_from_slice(s.as_bytes());
803 }
804 }
805 out
806}
807
808fn encode_db(input: &str, table: &[u16]) -> Vec<u8> {
809 let map = unicode_to_index_map();
810 let mut out = Vec::with_capacity(input.len() * 2);
811 for ch in input.chars() {
812 if let Some(&idx) = map.get(&ch) {
813 let w = table[idx];
814 out.push((w & 0xFF) as u8);
815 out.push((w >> 8) as u8);
816 } else if ch.is_ascii() {
817 out.push(ch as u8);
818 } else {
819 let mut buf = [0u8; 4];
820 let s = ch.encode_utf8(&mut buf);
821 out.extend_from_slice(s.as_bytes());
822 }
823 }
824 out
825}
826
827fn encode_wincp(input: &str) -> Vec<u8> {
828 let map = unicode_to_index_map();
829 let mut out = Vec::with_capacity(input.len() * 2);
830 for ch in input.chars() {
831 if let Some(&idx) = map.get(&ch) {
832 let w = WINCP1258_TABLE[idx];
833 out.push((w & 0xFF) as u8);
834 let hi = (w >> 8) as u8;
835 if hi != 0 { out.push(hi); }
836 } else if ch.is_ascii() {
837 out.push(ch as u8);
838 } else {
839 let mut buf = [0u8; 4];
840 let s = ch.encode_utf8(&mut buf);
841 out.extend_from_slice(s.as_bytes());
842 }
843 }
844 out
845}
846
847fn encode_viqr(input: &str) -> Vec<u8> {
848 let map = unicode_to_index_map();
849 let mut out = Vec::new();
850 for ch in input.chars() {
851 if let Some(&idx) = map.get(&ch) {
852 push_viqr_bytes(&mut out, VIQR_TABLE[idx]);
853 } else if ch.is_ascii() {
854 out.push(ch as u8);
855 } else {
856 let mut buf = [0u8; 4];
857 let s = ch.encode_utf8(&mut buf);
858 out.extend_from_slice(s.as_bytes());
859 }
860 }
861 out
862}
863
864fn encode_nfd(input: &str) -> Vec<u8> {
865 let map = unicode_to_index_map();
866 let mut out = Vec::with_capacity(input.len() * 6);
867 for ch in input.chars() {
868 if let Some(&idx) = map.get(&ch) {
869 let w = UNICOMP_TABLE[idx];
870 let base = w & 0xFFFF;
871 let comb = (w >> 16) & 0xFFFF;
872 if let Some(c) = char::from_u32(base) {
874 let mut buf = [0u8; 4];
875 let s = c.encode_utf8(&mut buf);
876 out.extend_from_slice(s.as_bytes());
877 }
878 if comb != 0 {
880 if let Some(c) = char::from_u32(comb) {
881 let mut buf = [0u8; 4];
882 let s = c.encode_utf8(&mut buf);
883 out.extend_from_slice(s.as_bytes());
884 }
885 }
886 } else {
887 let mut buf = [0u8; 4];
888 let s = ch.encode_utf8(&mut buf);
889 out.extend_from_slice(s.as_bytes());
890 }
891 }
892 out
893}
894
895pub fn decode(input: &[u8], charset: VietCharset) -> String {
897 match charset {
898 VietCharset::Unicode => String::from_utf8_lossy(input).to_string(),
899 VietCharset::WinCP1258 => {
900 let _map = unicode_to_index_map();
901 let mut rev: HashMap<(u8, u8), usize> = HashMap::with_capacity(VN_CHAR_COUNT);
903 for (i, &w) in WINCP1258_TABLE.iter().enumerate() {
904 let lo = (w & 0xFF) as u8;
905 let hi = (w >> 8) as u8;
906 rev.insert((lo, hi), i);
907 }
908 let mut out = String::with_capacity(input.len());
909 let mut i = 0;
910 while i < input.len() {
911 let lo = input[i];
912 if i + 1 < input.len() {
914 let hi = input[i + 1];
915 if let Some(&idx) = rev.get(&(lo, hi)) {
916 out.push(UNICODE_TABLE[idx]);
917 i += 2;
918 continue;
919 }
920 }
921 if let Some(&idx) = rev.get(&(lo, 0)) {
923 out.push(UNICODE_TABLE[idx]);
924 } else {
925 out.push(lo as char);
926 }
927 i += 1;
928 }
929 out
930 }
931 VietCharset::VIQR => {
932 let s = String::from_utf8_lossy(input);
934 crate::engine::convert_viqr(&s)
935 }
936 VietCharset::TCVN3 => {
937 let map = tcvn3_to_index_map();
938 let mut out = String::with_capacity(input.len());
939 for &b in input {
940 if let Some(&idx) = map.get(&b) {
941 out.push(UNICODE_TABLE[idx]);
942 } else {
943 out.push(b as char);
944 }
945 }
946 out
947 }
948 VietCharset::VNIWin => {
949 let map = vniwin_to_index_map();
950 let mut out = String::with_capacity(input.len());
951 let mut i = 0;
952 while i < input.len() {
953 if i + 1 < input.len() {
955 let lo = input[i];
956 let hi = input[i + 1];
957 let w = lo as u16 | ((hi as u16) << 8);
958 if let Some(&idx) = map.get(&w) {
959 out.push(UNICODE_TABLE[idx]);
960 i += 2;
961 continue;
962 }
963 }
964 out.push(input[i] as char);
966 i += 1;
967 }
968 out
969 }
970 cs if is_single_byte(cs) => {
972 let table = sb_table(cs);
973 let mut rev: HashMap<u8, usize> = HashMap::with_capacity(VN_CHAR_COUNT);
974 for (i, &b) in table.iter().enumerate() {
975 rev.insert(b, i);
976 }
977 let mut out = String::with_capacity(input.len());
978 for &b in input {
979 if let Some(&idx) = rev.get(&b) {
980 out.push(UNICODE_TABLE[idx]);
981 } else {
982 out.push(b as char);
983 }
984 }
985 out
986 }
987 cs if is_double_byte(cs) => {
989 let table = db_table(cs);
990 let mut rev: HashMap<u16, usize> = HashMap::with_capacity(VN_CHAR_COUNT);
991 for (i, &w) in table.iter().enumerate() {
992 rev.insert(w, i);
993 }
994 let mut out = String::with_capacity(input.len());
995 let mut i = 0;
996 while i < input.len() {
997 if i + 1 < input.len() {
998 let lo = input[i];
999 let hi = input[i + 1];
1000 let w = lo as u16 | ((hi as u16) << 8);
1001 if let Some(&idx) = rev.get(&w) {
1002 out.push(UNICODE_TABLE[idx]);
1003 i += 2;
1004 continue;
1005 }
1006 }
1007 out.push(input[i] as char);
1008 i += 1;
1009 }
1010 out
1011 }
1012 VietCharset::UnicodeDecomposed => String::from_utf8_lossy(input).to_string(),
1014 _ => String::from_utf8_lossy(input).to_string(),
1016 }
1017}
1018
1019pub fn remove_tone(input: &str) -> String {
1022 let map = unicode_to_index_map();
1023 let mut out = String::with_capacity(input.len());
1024 for ch in input.chars() {
1025 if let Some(&idx) = map.get(&ch) {
1026 let toneless_idx = NO_TONE[idx];
1027 out.push(UNICODE_TABLE[toneless_idx]);
1028 } else {
1029 out.push(ch);
1030 }
1031 }
1032 out
1033}
1034
1035#[cfg(test)]
1036mod tests {
1037 use super::*;
1038
1039 #[test]
1040 fn unicode_table_size() {
1041 assert_eq!(UNICODE_TABLE.len(), 213);
1042 assert_eq!(TCVN3_TABLE.len(), 213);
1043 assert_eq!(VNIWIN_TABLE.len(), 213);
1044 assert_eq!(NO_TONE.len(), 213);
1045 }
1046
1047 #[test]
1048 fn unicode_roundtrip() {
1049 let input = "tiếng Việt";
1050 let encoded = encode(input, VietCharset::Unicode);
1051 let decoded = decode(&encoded, VietCharset::Unicode);
1052 assert_eq!(decoded, input);
1053 }
1054
1055 #[test]
1056 fn tcvn3_roundtrip() {
1057 let input = "tiếng Việt";
1058 let encoded = encode(input, VietCharset::TCVN3);
1059 let decoded = decode(&encoded, VietCharset::TCVN3);
1060 assert_eq!(decoded, input);
1061 }
1062
1063 #[test]
1064 fn vniwin_roundtrip() {
1065 let input = "tiếng Việt";
1066 let encoded = encode(input, VietCharset::VNIWin);
1067 let decoded = decode(&encoded, VietCharset::VNIWin);
1068 assert_eq!(decoded, input);
1069 }
1070
1071 #[test]
1072 fn all_vietnamese_vowels() {
1073 let all_vowels = "aàáảãạ âầấẩẫậ ăằắẳẵặ eèéẻẽẹ êềếểễệ iìíỉĩị oòóỏõọ ôồốổỗộ ơờớởỡợ uùúủũụ ưừứửữự yỳýỷỹỵ";
1075 for charset in [VietCharset::TCVN3, VietCharset::VNIWin] {
1076 let encoded = encode(all_vowels, charset);
1077 let decoded = decode(&encoded, charset);
1078 assert_eq!(decoded, all_vowels, "roundtrip failed for {:?}", charset);
1079 }
1080 }
1081
1082 #[test]
1083 fn remove_tone_works() {
1084 assert_eq!(remove_tone("tiếng"), "tiêng");
1085 assert_eq!(remove_tone("Việt"), "Viêt");
1086 assert_eq!(remove_tone("được"), "đươc");
1087 assert_eq!(remove_tone("không"), "không"); assert_eq!(remove_tone("xin chào"), "xin chao");
1089 }
1090
1091 #[test]
1092 fn wincp1258_roundtrip() {
1093 let input = "tiếng Việt";
1094 let encoded = encode(input, VietCharset::WinCP1258);
1095 let decoded = decode(&encoded, VietCharset::WinCP1258);
1096 assert_eq!(decoded, input);
1097 }
1098
1099 #[test]
1100 fn viqr_output() {
1101 let input = "tiếng Việt";
1102 let encoded = encode(input, VietCharset::VIQR);
1103 assert!(encoded.iter().all(|&b| b <= 0x7F));
1105 let s = String::from_utf8(encoded).unwrap();
1107 assert!(s.contains("tie^'ng") || s.contains("tie^'ng") || s.len() > 0);
1108 }
1109
1110 #[test]
1111 fn english_pass_through() {
1112 let input = "Hello world 123!";
1113 for charset in [VietCharset::TCVN3, VietCharset::VNIWin, VietCharset::WinCP1258, VietCharset::Unicode] {
1114 let encoded = encode(input, charset);
1115 let decoded = decode(&encoded, charset);
1116 assert_eq!(decoded, input, "English pass-through failed for {:?}", charset);
1117 }
1118 }
1119}