2 * Unicode utility routines
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 2006 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
13 #include "unicode-utils.h"
15 const int ws_utf8_seqlen
[256] = {
16 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0x00...0x0f */
17 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0x10...0x1f */
18 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0x20...0x2f */
19 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0x30...0x3f */
20 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0x40...0x4f */
21 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0x50...0x5f */
22 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0x60...0x6f */
23 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0x70...0x7f */
24 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x80...0x8f */
25 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x90...0x9f */
26 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xa0...0xaf */
27 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xb0...0xbf */
28 0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2, /* 0xc0...0xcf */
29 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, /* 0xd0...0xdf */
30 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, /* 0xe0...0xef */
31 4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, /* 0xf0...0xff */
34 /* Given a pointer and a length, validates a string of bytes as UTF-8.
35 * Returns the number of valid bytes, and a pointer immediately past
38 * Differs from Glib's g_utf8_validate_len in that null bytes are
39 * considered valid UTF-8, and that maximal subparts are replaced as
40 * a unit. (I.e., given a sequence of 2 or 3 bytes which are a
41 * truncated version of a 3 or 4 byte UTF-8 character, but the next
42 * byte does not continue the character, the set of 2 or 3 bytes
43 * are replaced with one REPLACMENT CHARACTER.)
46 utf_8_validate(const uint8_t *start
, ssize_t length
, const uint8_t **end
)
48 const uint8_t *ptr
= start
;
50 size_t unichar_len
, valid_bytes
= 0;
65 if (ch
< 0xc2 || ch
> 0xf4) {
72 if (ch
< 0xe0) { /* 110xxxxx, 2 byte char */
74 } else if (ch
< 0xf0) { /* 1110xxxx, 3 byte char */
84 if (*ptr
< 0xa0 || *ptr
> 0xbf) {
90 if (*ptr
< 0x80 || *ptr
> 0x9f) {
96 if (*ptr
< 0x80 || *ptr
> 0xbf) {
101 } else { /* 11110xxx, 4 byte char - > 0xf4 excluded above */
111 if (*ptr
< 0x90 || *ptr
> 0xbf) {
117 if (*ptr
< 0x80 || *ptr
> 0x8f) {
123 if (*ptr
< 0x80 || *ptr
> 0xbf) {
134 if (*ptr
< 0x80 || *ptr
> 0xbf) {
146 if (*ptr
< 0x80 || *ptr
> 0xbf) {
152 valid_bytes
+= unichar_len
;
161 * Given a wmem scope, a pointer, and a length, treat the string of bytes
162 * referred to by the pointer and length as a UTF-8 string, and return a
163 * pointer to a UTF-8 string, allocated using the wmem scope, with all
164 * ill-formed sequences replaced with the Unicode REPLACEMENT CHARACTER
165 * according to the recommended "best practices" given in the Unicode
166 * Standard and specified by W3C/WHATWG.
168 * Note that in conformance with the Unicode Standard, this treats three
169 * byte sequences corresponding to UTF-16 surrogate halves (paired or unpaired)
170 * and two byte overlong encodings of 7-bit ASCII characters as invalid and
171 * substitutes REPLACEMENT CHARACTER for them. Explicit support for nonstandard
172 * derivative encoding formats (e.g. CESU-8, Java Modified UTF-8, WTF-8) could
175 * Compared with g_utf8_make_valid(), this function does not consider
176 * internal NUL bytes as invalid and replace them with replacment characters.
177 * It also replaces maximal subparts as a unit; i.e., a sequence of 2 or 3
178 * bytes which are a truncated version of a valid 3 or 4 byte character (but
179 * the next byte does not continue the character) are replaced with a single
180 * REPLACEMENT CHARACTER, whereas the Glib function replaces each byte of the
181 * sequence with its own (3 octet) REPLACEMENT CHARACTER.
183 * XXX: length should probably be a size_t instead of a int in all
184 * these encoding functions
185 * XXX: the buffer returned can be of different length than the input,
186 * and can have internal NULs as well (so that strlen doesn't give its
187 * length). As with the other encoding functions, we should return the
188 * length of the output buffer (or a wmem_strbuf_t directly) and an
189 * indication of whether there was an invalid character (i.e.
190 * REPLACEMENT CHARACTER was used.)
193 ws_utf8_make_valid_strbuf(wmem_allocator_t
*scope
, const uint8_t *ptr
, ssize_t length
)
197 str
= wmem_strbuf_new_sized(scope
, length
+1);
199 /* See the Unicode Standard conformance chapter at
200 * https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf especially
201 * Table 3-7 "Well-Formed UTF-8 Byte Sequences" and
202 * U+FFFD Substitution of Maximal Subparts. */
205 const uint8_t *prev
= ptr
;
206 size_t valid_bytes
= utf_8_validate(prev
, length
, &ptr
);
209 wmem_strbuf_append_len(str
, prev
, valid_bytes
);
211 length
-= ptr
- prev
;
214 wmem_strbuf_append_unichar_repl(str
);
222 ws_utf8_make_valid(wmem_allocator_t
*scope
, const uint8_t *ptr
, ssize_t length
)
224 wmem_strbuf_t
*str
= ws_utf8_make_valid_strbuf(scope
, ptr
, length
);
225 return wmem_strbuf_finalize(str
);
233 * Unicode utilities (internal interface)
235 * We define UNICODE and _UNICODE under Windows. This means that
236 * Windows SDK routines expect UTF-16 strings, in contrast to newer
237 * versions of Glib and GTK+ which expect UTF-8. This module provides
238 * convenience routines for converting between UTF-8 and UTF-16.
241 #define INITIAL_UTFBUF_SIZE 128
244 * XXX - Should we use g_utf8_to_utf16() and g_utf16_to_utf8()
245 * instead? The goal of the functions below was to provide simple
246 * wrappers for UTF-8 <-> UTF-16 conversion without making the
247 * caller worry about freeing up memory afterward.
250 /* Convert from UTF-8 to UTF-16. */
252 utf_8to16(const char *utf8str
)
254 static wchar_t *utf16buf
[3];
255 static int utf16buf_len
[3];
264 * Allocate the buffer if it's not already allocated.
266 if (utf16buf
[idx
] == NULL
) {
267 utf16buf_len
[idx
] = INITIAL_UTFBUF_SIZE
;
268 utf16buf
[idx
] = g_malloc(utf16buf_len
[idx
] * sizeof(wchar_t));
271 while (MultiByteToWideChar(CP_UTF8
, 0, utf8str
, -1, NULL
, 0) >= utf16buf_len
[idx
]) {
273 * Double the buffer's size if it's not big enough.
274 * The size of the buffer starts at 128, so doubling its size
275 * adds at least another 128 bytes, which is more than enough
276 * for one more character plus a terminating '\0'.
278 utf16buf_len
[idx
] *= 2;
279 utf16buf
[idx
] = g_realloc(utf16buf
[idx
], utf16buf_len
[idx
] * sizeof(wchar_t));
282 if (MultiByteToWideChar(CP_UTF8
, 0, utf8str
, -1, utf16buf
[idx
], utf16buf_len
[idx
]) == 0)
285 return utf16buf
[idx
];
289 utf_8to16_snprintf(TCHAR
*utf16buf
, int utf16buf_len
, const char* fmt
, ...)
295 dst
= ws_strdup_vprintf(fmt
, ap
);
298 StringCchPrintf(utf16buf
, utf16buf_len
, _T("%s"), utf_8to16(dst
));
303 /* Convert from UTF-16 to UTF-8. */
305 utf_16to8(const wchar_t *utf16str
)
307 static char *utf8buf
[3];
308 static int utf8buf_len
[3];
311 if (utf16str
== NULL
)
317 * Allocate the buffer if it's not already allocated.
319 if (utf8buf
[idx
] == NULL
) {
320 utf8buf_len
[idx
] = INITIAL_UTFBUF_SIZE
;
321 utf8buf
[idx
] = g_malloc(utf8buf_len
[idx
]);
324 while (WideCharToMultiByte(CP_UTF8
, 0, utf16str
, -1, NULL
, 0, NULL
, NULL
) >= utf8buf_len
[idx
]) {
326 * Double the buffer's size if it's not big enough.
327 * The size of the buffer starts at 128, so doubling its size
328 * adds at least another 128 bytes, which is more than enough
329 * for one more character plus a terminating '\0'.
331 utf8buf_len
[idx
] *= 2;
332 utf8buf
[idx
] = g_realloc(utf8buf
[idx
], utf8buf_len
[idx
]);
335 if (WideCharToMultiByte(CP_UTF8
, 0, utf16str
, -1, utf8buf
[idx
], utf8buf_len
[idx
], NULL
, NULL
) == 0)
341 /* Convert our argument list from UTF-16 to UTF-8. */
343 arg_list_utf_16to8(int argc
, wchar_t *wc_argv
[]) {
347 argv
= (char **)g_malloc((argc
+ 1) * sizeof(char *));
348 for (i
= 0; i
< argc
; i
++) {
349 argv
[i
] = g_utf16_to_utf8(wc_argv
[i
], -1, NULL
, NULL
, NULL
);
358 * Editor modelines - https://www.wireshark.org/tools/modelines.html
363 * indent-tabs-mode: nil
366 * vi: set shiftwidth=4 tabstop=8 expandtab:
367 * :indentSize=4:tabSize=8:noTabs=true: