Tue Aug 6 11:20:49 CEST 2002 Paolo Molaro <lupus@ximian.com>
[mono.git] / mono / metadata / unicode.c
1 /*
2  * unicode.h: Unicode support
3  *
4  * Author:
5  *      Dietmar Maurer (dietmar@ximian.com)
6  *
7  * (C) 2001 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12 #include <errno.h>
13
14 #include <mono/metadata/object.h>
15 #include <mono/metadata/unicode.h>
16
17 #if HAVE_ICONV_H
18 #include <iconv.h> 
19 #elif HAVE_GICONV_H
20 #include <giconv.h> 
21 #endif
22
23 static MonoUnicodeCategory catmap[] = {
24         /* G_UNICODE_CONTROL = */              Control,
25         /* G_UNICODE_FORMAT = */               Format,
26         /* G_UNICODE_UNASSIGNED = */           OtherNotAssigned,
27         /* G_UNICODE_PRIVATE_USE = */          PrivateUse,
28         /* G_UNICODE_SURROGATE = */            Surrogate,
29         /* G_UNICODE_LOWERCASE_LETTER = */     LowercaseLetter,
30         /* G_UNICODE_MODIFIER_LETTER = */      ModifierLetter,
31         /* G_UNICODE_OTHER_LETTER = */         OtherLetter,
32         /* G_UNICODE_TITLECASE_LETTER = */     TitlecaseLetter,
33         /* G_UNICODE_UPPERCASE_LETTER = */     UppercaseLetter,
34         /* G_UNICODE_COMBINING_MARK = */       SpaceCombiningMark,
35         /* G_UNICODE_ENCLOSING_MARK = */       EnclosingMark,
36         /* G_UNICODE_NON_SPACING_MARK = */     NonSpacingMark,
37         /* G_UNICODE_DECIMAL_NUMBER = */       DecimalDigitNumber,
38         /* G_UNICODE_LETTER_NUMBER = */        LetterNumber,
39         /* G_UNICODE_OTHER_NUMBER = */         OtherNumber,
40         /* G_UNICODE_CONNECT_PUNCTUATION = */  ConnectorPunctuation,
41         /* G_UNICODE_DASH_PUNCTUATION = */     DashPunctuation,
42         /* G_UNICODE_CLOSE_PUNCTUATION = */    ClosePunctuation,
43         /* G_UNICODE_FINAL_PUNCTUATION = */    FinalQuotePunctuation,
44         /* G_UNICODE_INITIAL_PUNCTUATION = */  InitialQuotePunctuation,
45         /* G_UNICODE_OTHER_PUNCTUATION = */    OtherPunctuation,
46         /* G_UNICODE_OPEN_PUNCTUATION = */     OpenPunctuation,
47         /* G_UNICODE_CURRENCY_SYMBOL = */      CurrencySymbol,
48         /* G_UNICODE_MODIFIER_SYMBOL = */      ModifierSymbol,
49         /* G_UNICODE_MATH_SYMBOL = */          MathSymbol,
50         /* G_UNICODE_OTHER_SYMBOL = */         OtherSymbol,
51         /* G_UNICODE_LINE_SEPARATOR = */       LineSeperator,
52         /* G_UNICODE_PARAGRAPH_SEPARATOR = */  ParagraphSeperator,
53         /* G_UNICODE_SPACE_SEPARATOR = */      SpaceSeperator,
54 };
55
56 double 
57 ves_icall_System_Char_GetNumericValue (gunichar2 c)
58 {
59         return (double)g_unichar_digit_value (c);
60 }
61
62 MonoUnicodeCategory 
63 ves_icall_System_Char_GetUnicodeCategory (gunichar2 c)
64 {
65         return catmap [g_unichar_type (c)];
66 }
67
68 gboolean 
69 ves_icall_System_Char_IsControl (gunichar2 c)
70 {
71         return g_unichar_iscntrl (c);
72 }
73
74 gboolean 
75 ves_icall_System_Char_IsDigit (gunichar2 c)
76 {
77         return g_unichar_isdigit (c);
78 }
79
80 gboolean 
81 ves_icall_System_Char_IsLetter (gunichar2 c)
82 {
83         return g_unichar_isalpha (c);
84 }
85
86 gboolean 
87 ves_icall_System_Char_IsLower (gunichar2 c)
88 {
89         return g_unichar_islower (c);
90 }
91
92 gboolean 
93 ves_icall_System_Char_IsUpper (gunichar2 c)
94 {
95         return g_unichar_isupper (c);
96 }
97
98 gboolean 
99 ves_icall_System_Char_IsNumber (gunichar2 c)
100 {
101         GUnicodeType t = g_unichar_type (c);
102         return t == G_UNICODE_DECIMAL_NUMBER ||
103                 t == G_UNICODE_LETTER_NUMBER ||
104                 t == G_UNICODE_OTHER_NUMBER;
105 }
106
107 gboolean 
108 ves_icall_System_Char_IsPunctuation (gunichar2 c)
109 {
110         return g_unichar_ispunct (c);
111 }
112
113 gboolean 
114 ves_icall_System_Char_IsSeparator (gunichar2 c)
115 {
116         GUnicodeType t = g_unichar_type (c);
117
118         return (t == G_UNICODE_LINE_SEPARATOR ||
119                 t == G_UNICODE_PARAGRAPH_SEPARATOR ||
120                 t == G_UNICODE_SPACE_SEPARATOR);
121 }
122
123 gboolean 
124 ves_icall_System_Char_IsSurrogate (gunichar2 c)
125 {
126         return (g_unichar_type (c) == G_UNICODE_SURROGATE);
127 }
128
129 gboolean 
130 ves_icall_System_Char_IsSymbol (gunichar2 c)
131 {
132         GUnicodeType t = g_unichar_type (c);
133
134         return (t == G_UNICODE_CURRENCY_SYMBOL ||
135                 t == G_UNICODE_MODIFIER_SYMBOL ||
136                 t == G_UNICODE_MATH_SYMBOL ||
137                 t == G_UNICODE_OTHER_SYMBOL);
138 }
139
140 gboolean 
141 ves_icall_System_Char_IsWhiteSpace (gunichar2 c)
142 {
143         return g_unichar_isspace (c);
144 }
145
146 gunichar2
147 ves_icall_System_Char_ToLower (gunichar2 c)
148 {
149         return g_unichar_tolower (c);
150 }
151
152 gunichar2
153 ves_icall_System_Char_ToUpper (gunichar2 c)
154 {
155         return g_unichar_toupper (c);
156 }
157
158 gpointer
159 ves_icall_iconv_new_encoder (MonoString *name, MonoBoolean big_endian)
160 {
161         iconv_t cd;
162         char *n;
163
164         /* FIXME: don't enforce big endian, support old iconv */
165
166         g_assert (name);
167
168         n = mono_string_to_utf8 (name);
169
170         /* force big endian before class libraries are fixed */
171 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
172         big_endian = 1;
173 #endif 
174
175 #ifdef HAVE_NEW_ICONV
176         cd = iconv_open (n, big_endian ? "UTF-16be" : "UTF-16le");
177 #else
178         cd = iconv_open (n, "UTF-16");
179 #endif
180         g_assert (cd != (iconv_t)-1);
181         g_free (n);
182
183         return (gpointer)cd;
184 }
185
186 gpointer
187 ves_icall_iconv_new_decoder (MonoString *name, MonoBoolean big_endian)
188 {
189         iconv_t cd;
190         char *n;
191
192         /* FIXME: don't enforce big endian, support old iconv */
193
194         g_assert (name);
195
196         n = mono_string_to_utf8 (name);
197
198         /* force big endian before class libraries are fixed */
199 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
200         big_endian = 1;
201 #endif 
202
203 #ifdef HAVE_NEW_ICONV
204         cd = iconv_open (big_endian ? "UTF-16be" : "UTF-16le", n);
205 #else
206         cd = iconv_open ("UTF-16", n);
207 #endif
208         g_assert (cd != (iconv_t)-1);
209         g_free (n);
210
211         return (gpointer)cd;
212 }
213
214 void
215 ves_icall_iconv_reset (gpointer converter)
216 {
217         iconv_t cd = (iconv_t)converter;
218
219         g_assert (cd);
220
221         iconv(cd, NULL, NULL, NULL, NULL);      
222 }
223
224 static int
225 iconv_get_length (iconv_t cd, guchar *src, int len, gboolean encode)
226 {
227         guchar buf [512];
228         int res;
229         guchar *outp;
230         guchar *p;
231         guint inbytes_remaining;
232         guint outbytes_remaining;
233         guint outbuf_size;
234         gboolean have_error = FALSE;
235         size_t err;
236         
237         g_assert (cd);
238         g_assert (src);
239
240 #ifndef HAVE_NEW_ICONV
241         if (G_BYTE_ORDER == G_LITTLE_ENDIAN && encode) {
242                 int i;
243                 
244                 src = g_memdup (src, len);
245                 for (i = 0; i < len; i += 2) {
246                         char t = src [i];
247                         src [i] = src [i + 1];
248                         src [i + 1] = t;
249                 }
250         }
251 #endif
252
253         p = src;
254         inbytes_remaining = len;
255         res = 0;
256         
257 again:
258         outbuf_size = 512;
259         outbytes_remaining = outbuf_size;
260         outp = buf;
261
262         err = iconv (cd, (char **)&p, &inbytes_remaining, 
263                      (char **)&outp, &outbytes_remaining);
264
265         if(err == (size_t)-1) {
266                 switch(errno) {
267                 case EINVAL:
268                         /* Incomplete text, do not report an error */
269                         break;
270                 case E2BIG: {
271                         res += outp - buf;
272                         goto again;
273                 }
274                 case EILSEQ:
275                         p++;
276                         inbytes_remaining--;
277                         goto again;
278                 default:
279                         have_error = TRUE;
280                         break;
281                 }
282         }
283   
284         res += outp - buf;
285
286         if((p - src) != len) {
287                 if(!have_error) {
288                         have_error = TRUE;
289                 }
290         }
291
292 #ifndef HAVE_NEW_ICONV
293         if (G_BYTE_ORDER == G_LITTLE_ENDIAN && encode)
294                 g_free (src);
295 #endif
296
297         if (have_error) {
298                 g_assert_not_reached ();
299                 return 0;
300         } else {
301                 return res;
302         }
303 }
304
305 int
306 ves_icall_iconv_get_byte_count (gpointer converter, MonoArray *chars, gint32 idx, gint32 count)
307 {
308         iconv_t cd = (iconv_t)converter;
309         guchar *src;
310         int len;
311
312         g_assert (cd);
313         g_assert (chars);
314         g_assert (mono_array_length (chars) >= (idx + count));
315
316         if (!(len = (mono_array_length (chars) - idx) * 2))
317                 return 0;
318
319         src =  mono_array_addr (chars, guint16, idx);
320
321         return iconv_get_length (cd, src, len, TRUE);
322 }
323
324 static int
325 iconv_convert (iconv_t cd, guchar *src, int len, guchar *dest, int max_len, gboolean encode)
326 {
327         guchar *p, *outp;
328         guint inbytes_remaining;
329         guint outbytes_remaining;
330         guint outbuf_size;
331         gboolean have_error = FALSE;
332         size_t err;
333
334         g_assert (cd);
335         g_assert (src);
336         g_assert (dest);
337
338 #ifndef HAVE_NEW_ICONV
339         if (G_BYTE_ORDER == G_LITTLE_ENDIAN && encode) {
340                 int i;
341                 
342                 src = g_memdup (src, len);
343                 for (i = 0; i < len; i += 2) {
344                         char t = src [i];
345                         src [i] = src [i + 1];
346                         src [i + 1] = t;
347                 }
348         }
349 #endif
350
351         p = src;
352         inbytes_remaining = len;
353         outbuf_size = max_len;
354   
355         outbytes_remaining = outbuf_size;
356         outp = dest;
357
358  again:
359         err = iconv (cd, (char **)&p, &inbytes_remaining, (char **)&outp, &outbytes_remaining);
360
361         if(err == (size_t)-1) {
362                 if (errno == EINVAL) {
363                         /* Incomplete text, do not report an error */
364                 } else if (errno == EILSEQ) {
365                         p++;
366                         inbytes_remaining--;
367                         goto again;
368                 } else {
369                         have_error = TRUE;
370                 }
371         }
372
373         if ((p - src) != len) {
374                 if (!have_error) {
375                         have_error = TRUE;
376                 }
377         }
378
379 #ifndef HAVE_NEW_ICONV
380         if (G_BYTE_ORDER == G_LITTLE_ENDIAN) {
381                 if (encode) {
382                         g_free (src);
383                 } else {
384                         int mb = max_len - outbytes_remaining;
385                         int i;
386                         for (i = 0; i < mb; i+=2) {
387                                 char t = dest [i];
388                                 dest [i] = dest [i + 1];
389                                 dest [i + 1] = t;
390                         }
391                 }
392 }
393 #endif
394         if (have_error) {
395                 g_assert_not_reached ();
396                 return 0;
397         } else {
398                 /* we return the number of bytes written in dest */
399                 return max_len - outbytes_remaining;
400         }
401 }
402
403 int
404 ves_icall_iconv_get_bytes (gpointer converter, MonoArray *chars, gint32 charIndex, gint32 charCount,
405                            MonoArray *bytes, gint32 byteIndex)
406 {
407         iconv_t cd = (iconv_t)converter;
408         guchar *src, *dest;
409         int len, max_len;
410
411         if (!charCount)
412                 return 0;
413
414         g_assert (cd);
415         g_assert (chars);
416         g_assert (bytes);
417         g_assert (mono_array_length (chars) > charIndex);
418         g_assert (mono_array_length (chars) >= (charIndex + charCount));
419         g_assert (mono_array_length (bytes) > byteIndex);
420         g_assert (mono_array_length (chars) >= (byteIndex + charCount));
421
422         if (!(len = (charCount - charIndex) * 2))
423                 return 0;
424
425         src =  mono_array_addr (chars, guint16, charIndex);
426         dest = mono_array_addr (bytes, char, byteIndex);
427
428         max_len = mono_array_length (bytes) - byteIndex;
429
430         return iconv_convert (cd, src, len, dest, max_len, TRUE);
431 }
432
433 int
434 ves_icall_iconv_get_char_count (gpointer converter, MonoArray *bytes, gint32 idx, gint32 count)
435 {
436         iconv_t cd = (iconv_t)converter;
437         guchar *src;
438
439         g_assert (cd);
440         g_assert (bytes);
441         g_assert (mono_array_length (bytes) > idx);
442         g_assert (mono_array_length (bytes) >= (idx + count));
443
444         src =  mono_array_addr (bytes, char, idx);
445
446         /* iconv_get_length () returns the number of bytes */
447         return iconv_get_length (cd, src, (int) count, FALSE) / 2;
448 }
449
450 int
451 ves_icall_iconv_get_chars (gpointer converter, MonoArray *bytes, gint32 byteIndex, gint32 byteCount,
452                            MonoArray *chars, gint32 charIndex)
453 {
454         iconv_t cd = (iconv_t)converter;
455         guchar *src, *dest;
456         int max_len;
457
458         g_assert (cd);
459         g_assert (chars);
460         g_assert (bytes);
461         g_assert (mono_array_length (bytes) > byteIndex);
462         g_assert (mono_array_length (chars) >= (byteIndex + byteCount));
463         g_assert (mono_array_length (chars) > charIndex);
464
465         src =  mono_array_addr (bytes, char, byteIndex);
466         dest = mono_array_addr (chars, guint16, charIndex);
467
468         max_len = (mono_array_length (chars) - charIndex) * 2;
469
470         /* iconv_convert () returns the number of bytes */
471         return iconv_convert (cd, src, (int) byteCount, dest, max_len, FALSE) / 2;
472 }