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