2 * Unicode add-ons to reactos ntoskrnl/rtl/unicode.c for libcaptive
3 * Copyright (C) 2002 Jan Kratochvil <project-captive@jankratochvil.net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; exactly version 2 of June 1991 is required
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "captive/unicode.h" /* self */
23 #include "captive/unicode_reactos.h" /* for captive_ucs2 */
24 #include <glib/gtypes.h>
25 #include <glib/gmessages.h>
26 #include <glib/gunicode.h>
27 #include <glib/gmem.h>
28 #include "reactos/napi/types.h" /* for PUNICODE_STRING etc. */
29 #include "reactos/unicode.h"
30 #include "captive/macros.h"
31 #include <glib/gstrfuncs.h>
32 #include <wchar.h> /* for wcslen() */
33 #include <glib/ghash.h>
38 /* Use simplified g_malloc() functions as wrappers around g_alloca() ones.
40 #define FUNCMALLOC_FROM_ALLOCA 1
44 static gboolean captive_validate_unicode_types(void)
46 g_return_val_if_fail(4==sizeof(gunichar),FALSE);
47 g_return_val_if_fail(2==sizeof(WCHAR),FALSE);
48 g_return_val_if_fail(1==sizeof(CHAR),FALSE);
55 * captive_validate_ucs4:
56 * @string_ucs4: #const #gunichar * type string to validate.
57 * Invalid string input is forbidden.
59 * Checks the validity of all 32-bit unicharacters of 0-terminated string.
60 * It is required to have characters complying to g_unichar_validate().
62 * Returns: %TRUE if the string is valid.
64 gboolean captive_validate_ucs4(const gunichar *string_ucs4)
66 const gunichar *cs_ucs4;
68 g_return_val_if_fail(captive_validate_unicode_types(),FALSE);
69 g_return_val_if_fail(string_ucs4!=NULL,FALSE);
71 for (cs_ucs4=string_ucs4;*cs_ucs4;cs_ucs4++)
72 g_return_val_if_fail(g_unichar_validate(*cs_ucs4),FALSE);
79 * captive_validate_ucs2_fixlen:
80 * @string_ucs2: #const #captive_ucs2 * type string to validate.
81 * Invalid string input is forbidden.
82 * UTF-16 encoded strings are forbidden.
83 * @string_ucs2_fixlen: Number of characters from @string_ucs2 to check.
84 * captive_ucs2_strlen(@string_ucs2)>=@string_ucs2_fixlen is required.
85 * Negative value is forbidden.
87 * Checks the validity of first @string_ucs2_fixlen 16-bit unicharacters of @string_ucs2.
88 * It is required to have characters complying to g_unichar_validate().
89 * String length must be equal or larger than @string_ucs2_fixlen;
91 * Returns: %TRUE if the string is valid.
93 gboolean captive_validate_ucs2_fixlen(const captive_ucs2 *string_ucs2,glong string_ucs2_fixlen)
95 const captive_ucs2 *cs_ucs2;
97 g_return_val_if_fail(captive_validate_unicode_types(),FALSE);
98 g_return_val_if_fail(string_ucs2!=NULL,FALSE);
99 g_return_val_if_fail(string_ucs2_fixlen>=0,FALSE);
101 /* g_unichar_validate() will reject surrogates (G_UNICODE_SURROGATE) */
102 for (cs_ucs2=string_ucs2;cs_ucs2<string_ucs2+string_ucs2_fixlen;cs_ucs2++) {
103 g_return_val_if_fail(*cs_ucs2!=0,FALSE);
104 g_return_val_if_fail(g_unichar_validate(*cs_ucs2),FALSE);
112 * captive_validate_ucs2:
113 * @string_ucs2: #const #captive_ucs2 * type string to validate.
114 * Invalid string input is forbidden.
115 * UTF-16 encoded strings are forbidden.
117 * Checks the validity of all 16-bit unicharacters of 0-terminated string.
118 * It is required to have characters complying to g_unichar_validate().
120 * Returns: %TRUE if the string is valid.
122 gboolean captive_validate_ucs2(const captive_ucs2 *string_ucs2)
124 g_return_val_if_fail(captive_validate_unicode_types(),FALSE);
125 g_return_val_if_fail(string_ucs2!=NULL,FALSE);
127 return captive_validate_ucs2_fixlen(string_ucs2,captive_ucs2_strlen(string_ucs2));
132 * captive_validate_utf8:
133 * @string_utf8: #const #gchar * utf8 type string to validate.
134 * Invalid string input is forbidden.
136 * Checks the validity of all utf8 of 0-terminated string.
137 * It is required to have characters complying to g_utf8_validate().
139 * Returns: %TRUE if the string is valid.
141 gboolean captive_validate_utf8(const gchar *string_utf8)
143 g_return_val_if_fail(captive_validate_unicode_types(),FALSE);
144 g_return_val_if_fail(string_utf8!=NULL,FALSE);
146 g_return_val_if_fail(g_utf8_validate(
147 string_utf8, /* str */
148 -1, /* max_len; -1 means '\0'-terminated */
157 * captive_ucs2_strlen:
158 * @string_ucs2: String of type #const #gunichar2 * in pure UCS-2
159 * Invalid string input is forbidden. UTF-16 encoded pairs are forbidden.
161 * Counts the number of characters (=2bytes) in @strings_ucs2.
163 * Returns: @string_ucs2 length in UCS-2 characters.
165 glong captive_ucs2_strlen(const captive_ucs2 *string_ucs2)
169 /* Do not call captive_validate_ucs2(string_ucs2) as we would be looping! */
170 g_return_val_if_fail(captive_validate_unicode_types(),FALSE);
171 g_return_val_if_fail(string_ucs2!=NULL,FALSE);
173 for (r=0;*string_ucs2;string_ucs2++)
181 * captive_validate_UnicodeString:
182 * @string_UnicodeString: #PUNICODE_STRING type string to validate.
183 * Invalid string input is forbidden.
185 * Checks the internal consistency of the given @string_UnicodeString.
186 * It is required to have characters complying to g_unichar_validate().
187 * @string_UnicodeString MUST be zero-terminated.
189 * Returns: %TRUE if the string is valid.
191 gboolean captive_validate_UnicodeString(const UNICODE_STRING *string_UnicodeString)
193 g_return_val_if_fail(captive_validate_unicode_types(),FALSE);
194 g_return_val_if_fail(sizeof(WCHAR)==sizeof(*string_UnicodeString->Buffer),FALSE);
195 g_return_val_if_fail(string_UnicodeString!=NULL,FALSE);
196 g_return_val_if_fail(string_UnicodeString->Length%sizeof(*string_UnicodeString->Buffer)==0,FALSE);
197 g_return_val_if_fail(string_UnicodeString->MaximumLength
198 >=string_UnicodeString->Length+sizeof(*string_UnicodeString->Buffer),FALSE);
199 g_return_val_if_fail(string_UnicodeString->Length==sizeof(*string_UnicodeString->Buffer)*
200 captive_ucs2_strlen(string_UnicodeString->Buffer)
203 g_return_val_if_fail(captive_validate_ucs2(string_UnicodeString->Buffer),FALSE);
210 * captive_validate_UnicodeString_noterm:
211 * @string_UnicodeString_noterm: #PUNICODE_STRING type string to validate.
212 * Invalid string input is forbidden.
214 * Checks the internal consistency of the given @string_UnicodeString.
215 * It is required to have characters complying to g_unichar_validate().
216 * @string_UnicodeString_noterm does not neet to be zero-terminated.
218 * Returns: %TRUE if the string is valid.
220 gboolean captive_validate_UnicodeString_noterm(const UNICODE_STRING *string_UnicodeString_noterm)
224 g_return_val_if_fail(captive_validate_unicode_types(),FALSE);
225 g_return_val_if_fail(sizeof(WCHAR)==sizeof(*string_UnicodeString_noterm->Buffer),FALSE);
226 g_return_val_if_fail(string_UnicodeString_noterm!=NULL,FALSE);
227 g_return_val_if_fail(string_UnicodeString_noterm->Length%sizeof(*string_UnicodeString_noterm->Buffer)==0,FALSE);
228 g_return_val_if_fail(string_UnicodeString_noterm->MaximumLength>=string_UnicodeString_noterm->Length,FALSE);
231 cwp=string_UnicodeString_noterm->Buffer;
232 cwp<string_UnicodeString_noterm->Buffer
233 +(string_UnicodeString_noterm->Length/sizeof(*string_UnicodeString_noterm->Buffer));
235 g_return_val_if_fail(*cwp!=0,FALSE);
237 g_return_val_if_fail(captive_validate_ucs2_fixlen(string_UnicodeString_noterm->Buffer,
238 string_UnicodeString_noterm->Length/sizeof(*string_UnicodeString_noterm->Buffer)),
246 * captive_validate_AnsiString:
247 * @string_AnsiString: #PANSI_STRING type string to validate.
248 * Invalid string input is forbidden.
250 * Checks the internal consistency of the given @string_AnsiString.
252 * Returns: %TRUE if the string is valid.
254 gboolean captive_validate_AnsiString(const ANSI_STRING *string_AnsiString)
256 g_return_val_if_fail(captive_validate_unicode_types(),FALSE);
257 g_return_val_if_fail(sizeof(CHAR)==sizeof(*string_AnsiString->Buffer),FALSE);
258 g_return_val_if_fail(string_AnsiString!=NULL,FALSE);
259 g_return_val_if_fail(string_AnsiString->MaximumLength>=string_AnsiString->Length+1,FALSE);
260 g_return_val_if_fail(string_AnsiString->Length==strlen(string_AnsiString->Buffer),FALSE);
266 /* detect required memory size for g_alloca() */
267 size_t _captive_UnicodeString_to_utf8_alloca_internal_sizeof(const UNICODE_STRING *string_UnicodeString)
271 const WCHAR *cwcharp;
273 g_return_val_if_fail(captive_validate_UnicodeString(string_UnicodeString),1);
275 /* measure 'string_UnicodeString->Buffer' length in UTF-8 to 'r' */
276 cwcharp=string_UnicodeString->Buffer;
278 for (length=string_UnicodeString->Length/sizeof(*string_UnicodeString->Buffer);length;length--) {
281 utf8len=g_unichar_to_utf8(
283 NULL); /* outbuf=NULL => just the length will be computed */
284 g_assert(utf8len>=0);
287 g_assert(*cwcharp==0);
288 r++; /* '\0'-termination */
294 /* transfer 'string_UnicodeString' to memory in 'mem' as utf8 w/o any further allocations */
295 void _captive_UnicodeString_to_utf8_alloca_internal_fill(gchar *mem,const UNICODE_STRING *string_UnicodeString)
297 const WCHAR *cwcharp;
298 #ifndef G_DISABLE_ASSERT
300 #endif /* G_DISABLE_ASSERT */
302 g_return_if_fail(mem!=NULL);
303 if (!captive_validate_UnicodeString(string_UnicodeString)) {
305 g_return_if_reached();
308 /* We can't use any glib string conversions as UNICODE_STRING uses ucs2! */
309 /* We can't use any glib string conversions as we need to write the string
310 * to our supplied memory storage but glib always g_malloc()s it
312 /* copy 'string_UnicodeString->Buffer' to 'mem' */
313 for (cwcharp=string_UnicodeString->Buffer;*cwcharp;cwcharp++) {
316 utf8len=g_unichar_to_utf8(
317 (gunichar)*cwcharp, /* c */
319 g_assert(utf8len>=0);
324 g_assert((size_t)((mem+1)-mem_orig) == _captive_UnicodeString_to_utf8_alloca_internal_sizeof(string_UnicodeString));
325 g_assert(captive_validate_utf8(mem_orig));
330 * captive_UnicodeString_to_utf8_malloc:
331 * @string_UnicodeString: #PUNICODE_STRING type of string to convert.
333 * g_malloc()-based conversion from #PUNICODE_STRING to plain #utf8 string.
334 * You must free the result with g_free() function.
336 * Returns: #const #gchar * g_malloc()ed converted string @string_UnicodeString.
338 gchar *captive_UnicodeString_to_utf8_malloc(const UNICODE_STRING *string_UnicodeString)
341 #ifndef FUNCMALLOC_FROM_ALLOCA
342 glong utf16_read,utf8_written;
344 #endif /* !FUNCMALLOC_FROM_ALLOCA */
346 g_return_val_if_fail(captive_validate_UnicodeString(string_UnicodeString),g_strdup(""));
348 #ifdef FUNCMALLOC_FROM_ALLOCA
350 r=g_malloc(_captive_UnicodeString_to_utf8_alloca_internal_sizeof(string_UnicodeString));
351 _captive_UnicodeString_to_utf8_alloca_internal_fill(r,string_UnicodeString);
355 err=NULL; /* not precleared by g_utf8_to_utf16()! */
357 (const gunichar2 *)string_UnicodeString->Buffer, /* str */
358 -1, /* len=>'\0'-terminated */
359 &utf16_read, /* items_read; counted in unichar2 (NOT UTF-16 characters or bytes!) */
360 &utf8_written, /* items_written; counted in bytes (NOT UTF-8 characters!) */
363 g_warning("%s: utf16_read=%ld,utf8_written=%ld: %s",G_STRLOC,
364 (long)utf16_read,(long)utf8_written,err->message);
367 g_return_val_if_reached(g_strdup(""));
371 g_assert(utf16_read==(glong)(string_UnicodeString->length/sizeof(*string_UnicodeString->Buffer)));
372 g_assert(utf6_written==strlen(r));
374 #endif /* !FUNCMALLOC_FROM_ALLOCA */
376 g_assert(captive_validate_utf8(r));
382 /* detect required memory size for g_alloca() */
383 size_t _captive_utf8_to_UnicodeString_alloca_internal_sizeof(const gchar *string_utf8)
385 g_return_val_if_fail(captive_validate_utf8(string_utf8),1);
387 /* find the value for PUNICODE_STRING->MaximumLength */
389 +sizeof(UNICODE_STRING)
390 +sizeof(WCHAR)*(g_utf8_strlen(string_utf8,
391 -1 /* max; -1 means '\0'-terminated */
392 )+1); /* '\0'-termination */
395 static void terminate_static_UnicodeString(UNICODE_STRING *string_UnicodeString,glong length)
397 /* 'string_UnicodeString' is not yet valid in this point! */
398 g_return_if_fail(string_UnicodeString!=NULL);
399 g_return_if_fail(length>=0);
401 string_UnicodeString->Length=length*sizeof(WCHAR);
402 string_UnicodeString->MaximumLength=(length+1)*sizeof(WCHAR);
403 string_UnicodeString->Buffer[length]=0;
405 g_assert(captive_validate_UnicodeString(string_UnicodeString));
408 /* transfer 'string_UnicodeString' to memory in 'mem' w/o any further allocations */
409 void _captive_utf8_to_UnicodeString_alloca_internal_fill(UNICODE_STRING *mem,const gchar *string_utf8)
413 glong utf8_read,utf16_written;
416 g_return_if_fail(mem!=NULL);
417 mem->Buffer=(PWSTR)(((char *)mem)+sizeof(*mem)); /* for terminate_static_UnicodeString() below */
418 if (!captive_validate_utf8(string_utf8)) {
419 terminate_static_UnicodeString(mem,0);
420 g_return_if_reached();
423 err=NULL; /* not precleared by g_utf8_to_utf16()! */
424 utf16=g_utf8_to_utf16(
425 string_utf8, /* str */
426 -1, /* len=>'\0'-terminated */
427 &utf8_read, /* items_read; counted in bytes (NOT chars!) */
428 &utf16_written, /* items_written; counted in UTF-16 characters (NOT unichar2 or bytes!) */
431 g_warning("%s: utf8_read=%ld,utf16_written=%ld: %s",G_STRLOC,
432 (long)utf8_read,(long)utf16_written,err->message);
434 g_assert(utf16==NULL);
435 terminate_static_UnicodeString(mem,0);
436 g_return_if_reached();
438 g_assert(utf16!=NULL);
440 /* Check for UCS-2 compliance (reject if surrogates inside) */
441 g_assert(captive_validate_ucs2((const captive_ucs2 *)utf16));
443 ucs2=(captive_ucs2 *)utf16;
445 g_assert(utf8_read==(glong)strlen(string_utf8));
446 g_assert(utf16_written==captive_ucs2_strlen(ucs2));
448 /* check of validity of _captive_utf8_to_UnicodeString_alloca_internal_sizeof() result */
449 g_assert((gchar *)(mem->Buffer+(utf16_written+1)) /* +1 => '\0'-termination */
450 == ((gchar *)mem)+_captive_utf8_to_UnicodeString_alloca_internal_sizeof(string_utf8));
452 memcpy(mem->Buffer,ucs2,sizeof(WCHAR)*(utf16_written+1));
454 terminate_static_UnicodeString(mem,utf16_written);
456 g_assert(captive_validate_UnicodeString(mem));
461 * captive_utf8_to_UnicodeString_malloc:
462 * @string_utf8: #const #gchar * string in #utf8 to convert.
464 * g_malloc()-based conversion from plain #utf8 string to #PUNICODE_STRING.
465 * You must free the result with g_free() function.
467 * Returns: #PUNICODE_STRING g_malloc()ed converted string @string_utf8.
469 PUNICODE_STRING captive_utf8_to_UnicodeString_malloc(const gchar *string_utf8)
472 #ifndef FUNCMALLOC_FROM_ALLOCA
474 glong utf8_read,ucs4_written;
476 #endif /* !FUNCMALLOC_FROM_ALLOCA */
478 g_return_val_if_fail(captive_validate_utf8(string_utf8),captive_utf8_to_UnicodeString_malloc(""));
480 #ifdef FUNCMALLOC_FROM_ALLOCA
482 r=g_malloc(_captive_utf8_to_UnicodeString_alloca_internal_sizeof(string_utf8));
483 _captive_utf8_to_UnicodeString_alloca_internal_fill(r,string_utf8);
487 #error "FIXME: NOT IMPLEMENTED"
489 #endif /* !FUNCMALLOC_FROM_ALLOCA */
491 g_assert(captive_validate_UnicodeString(r));
497 /* map: (const gunichar *) -> (const gunichar2 *); UCS-4 -> UTF-16 */
498 static GHashTable *captive_ucs4_to_utf16_hash;
500 static void captive_ucs4_to_utf16_hash_init(void)
502 if (captive_ucs4_to_utf16_hash)
504 captive_ucs4_to_utf16_hash=g_hash_table_new_full(
505 g_direct_hash, /* hash_func */
506 g_direct_equal, /* key_equal_func */
507 (GDestroyNotify)NULL, /* key_destroy_func; we require persistent strings as input */
508 (GDestroyNotify)g_free); /* value_destroy_func; result of g_ucs4_to_utf16() */
512 * captive_ucs4_to_utf16_const:
513 * @string_ucs4: #const #gunichar * type of persistent string to convert.
514 * This string MUST remain readable with the same content forever.
516 * Constant string conversion from 32-bit #wchar_t to 16-bit (possible pairs of) UTF-16.
517 * You may not modify the result in any way.
519 * It is guaranteed to get two different string addresses for two different
520 * input addresses even if the input strings content is the same.
521 * Otherwise we would behave as #GCC option %-fmerge-constants which
522 * results in %C non-conforming behaviour.
524 * FIXME: UTF-16 encoding IS NOT IMPLEMENTED.
526 * See also captive_ucs4_to_ucs2_const().
528 * Returns: #const #gunichar2 * converted string @string_ucs4.
530 const gunichar2 *captive_ucs4_to_utf16_const(const gunichar *string_ucs4)
532 glong ucs4_read,utf16_written;
534 const gunichar2 *r_lookup;
537 g_return_val_if_fail(captive_validate_ucs4(string_ucs4),captive_ucs4_to_utf16_const((const gunichar *)L""));
539 captive_ucs4_to_utf16_hash_init();
541 /* found already existing item in the table */
542 if ((r_lookup=g_hash_table_lookup(captive_ucs4_to_utf16_hash,
543 string_ucs4) /* key */
548 /* Prepare 'r' as UTF-16 */
549 err=NULL; /* not precleared by g_ucs4_to_utf16()! */
551 (const gunichar *)string_ucs4, /* str */
552 -1, /* len; -1 means '\0'-termination */
553 &ucs4_read, /* items_read; counted in chars (==unichars; NOT bytes!) */
554 &utf16_written, /* items_written; counted in gunichar2 (NOT chars or bytes!) */
557 g_warning("%s: ucs4_read=%ld,utf16_written=%ld: %s",G_STRLOC,
558 (long)ucs4_read,(long)utf16_written,err->message);
561 g_return_val_if_reached(captive_ucs4_to_utf16_const((const gunichar *)L""));
564 g_assert(ucs4_read==(glong)wcslen((const wchar_t *)string_ucs4));
565 /* FIXME: We don't have captive_utf16_strlen() */
566 g_assert(utf16_written==(glong)captive_ucs2_strlen((const gunichar2 *)r));
567 /* (ucs4_read==utf16_written) check would discard any double-pair UTF-16 encodings
568 * but this function is designed as UTF-16 compliant.
571 /* store new item to the table */
572 g_hash_table_insert(captive_ucs4_to_utf16_hash,
573 (gpointer)string_ucs4, /* key; de-const */
576 #if 0 /* We don't have captive_validate_utf16() */
577 g_assert(captive_validate_utf16(r));