Tue Jan 15 19:42:50 CET 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 #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: add support big_endian
158
159         g_assert (name);
160
161         n = mono_string_to_utf8 (name);
162
163 #ifdef HAVE_NEW_ICONV
164         cd = iconv_open (n, "UTF-16le");
165 #else
166         cd = iconv_open (n, "UTF-16");
167 #endif
168         g_assert (cd != (iconv_t)-1);
169
170         return (gpointer)cd;
171 }
172
173 gpointer
174 ves_icall_iconv_new_decoder (MonoString *name, MonoBoolean big_endian)
175 {
176         iconv_t cd;
177         char *n;
178
179         // fixme: add support big_endian
180
181         g_assert (name);
182
183         n = mono_string_to_utf8 (name);
184
185 #ifdef HAVE_NEW_ICONV
186         cd = iconv_open ("UTF-16le", n);
187 #else
188         cd = iconv_open ("UTF-16", n);
189 #endif
190         g_assert (cd != (iconv_t)-1);
191
192         return (gpointer)cd;
193 }
194
195 void
196 ves_icall_iconv_reset (gpointer converter)
197 {
198         iconv_t cd = (iconv_t)converter;
199
200         g_assert (cd);
201
202         iconv(cd, NULL, NULL, NULL, NULL);      
203 }
204
205 static int
206 iconv_get_length (iconv_t cd, guchar *src, int len)
207 {
208         guchar buf [512];
209         int i, res;
210         guchar *outp;
211         guchar *p;
212         guint inbytes_remaining;
213         guint outbytes_remaining;
214         guint outbuf_size;
215         gboolean have_error = FALSE;
216         size_t err;
217
218         g_assert (cd);
219         g_assert (src);
220
221 #ifndef HAVE_NEW_ICONV
222         if (G_BYTE_ORDER == G_LITTLE_ENDIAN) {
223                 src = g_memdup (src, len);
224                 for (i = 0; i < len; i += 2) {
225                         char t = src [i];
226                         src [i] = src [i + 1];
227                         src [i + 1] = t;
228                 }
229         }
230 #endif
231
232         p = src;
233         inbytes_remaining = len;
234         res = 0;
235         
236 again:
237         outbuf_size = 512;
238         outbytes_remaining = outbuf_size;
239         outp = buf;
240
241         err = iconv (cd, (const char **)&p, &inbytes_remaining, 
242                      (char **)&outp, &outbytes_remaining);
243
244         if(err == (size_t)-1) {
245                 switch(errno) {
246                 case EINVAL:
247                         /* Incomplete text, do not report an error */
248                         break;
249                 case E2BIG: {
250                         res += outp - buf;
251                         goto again;
252                 }
253                 case EILSEQ:
254                         have_error = TRUE;
255                         break;
256                 default:
257                         have_error = TRUE;
258                         break;
259                 }
260         }
261   
262         res += outp - buf;
263
264         if((p - src) != len) {
265                 if(!have_error) {
266                         have_error = TRUE;
267                 }
268         }
269
270 #ifndef HAVE_NEW_ICONV
271         if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
272                 g_free (src);
273 #endif
274
275         if (have_error) {
276                 g_assert_not_reached ();
277                 return 0;
278         } else {
279                 return res;
280         }
281 }
282
283 int
284 ves_icall_iconv_get_byte_count (gpointer converter, MonoArray *chars, gint32 index, gint32 count)
285 {
286         iconv_t cd = (iconv_t)converter;
287         guchar *src;
288         int len;
289
290         g_assert (cd);
291         g_assert (chars);
292         g_assert (mono_array_length (chars) > index);
293         g_assert (mono_array_length (chars) >= (index + count));
294
295         if (!(len = (mono_array_length (chars) - index) * 2))
296                 return 0;
297
298         src =  mono_array_addr (chars, guint16, index);
299
300         return iconv_get_length (cd, src, len);
301 }
302
303 static int
304 iconv_convert (iconv_t cd, guchar *src, int len, guchar *dest, int max_len)
305 {
306         int i;
307         guchar *p, *outp;
308         guint inbytes_remaining;
309         guint outbytes_remaining;
310         guint outbuf_size;
311         gboolean have_error = FALSE;
312         size_t err;
313
314         g_assert (cd);
315         g_assert (src);
316         g_assert (dest);
317
318 #ifndef HAVE_NEW_ICONV
319         if (G_BYTE_ORDER == G_LITTLE_ENDIAN) {
320                 src = g_memdup (src, len);
321                 for (i = 0; i < len; i += 2) {
322                         char t = src [i];
323                         src [i] = src [i + 1];
324                         src [i + 1] = t;
325                 }
326         }
327 #endif
328
329         p = src;
330         inbytes_remaining = len;
331         outbuf_size = max_len;
332   
333         outbytes_remaining = outbuf_size;
334         outp = dest;
335
336         err = iconv (cd, (const char **)&p, &inbytes_remaining, (char **)&outp, &outbytes_remaining);
337
338         if(err == (size_t)-1) {
339                 if (errno == EINVAL) {
340                         /* Incomplete text, do not report an error */
341                 } else {
342                         have_error = TRUE;
343                 }
344         }
345
346         if ((p - src) != len) {
347                 if (!have_error) {
348                         have_error = TRUE;
349                 }
350         }
351
352 #ifndef HAVE_NEW_ICONV
353         if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
354                 g_free (src);
355 #endif
356         if (have_error) {
357                 g_assert_not_reached ();
358                 return 0;
359         } else {
360                 /* we return the number of bytes written in dest */
361                 return max_len - outbytes_remaining;
362         }
363 }
364
365 int
366 ves_icall_iconv_get_bytes (gpointer converter, MonoArray *chars, gint32 charIndex, gint32 charCount,
367                            MonoArray *bytes, gint32 byteIndex)
368 {
369         iconv_t cd = (iconv_t)converter;
370         guchar *src, *dest;
371         int len, max_len;
372
373         g_assert (cd);
374         g_assert (chars);
375         g_assert (bytes);
376         g_assert (mono_array_length (chars) > charIndex);
377         g_assert (mono_array_length (chars) >= (charIndex + charCount));
378         g_assert (mono_array_length (bytes) > byteIndex);
379         g_assert (mono_array_length (chars) >= (byteIndex + charCount));
380
381         if (!(len = (mono_array_length (chars) - charIndex) * 2))
382                 return 0;
383
384         src =  mono_array_addr (chars, guint16, charIndex);
385         dest = mono_array_addr (bytes, char, byteIndex);
386
387         max_len = mono_array_length (bytes) - byteIndex;
388
389         return iconv_convert (cd, src, len, dest, max_len);
390 }
391
392 int
393 ves_icall_iconv_get_char_count (gpointer converter, MonoArray *bytes, gint32 index, gint32 count)
394 {
395         iconv_t cd = (iconv_t)converter;
396         guchar *src;
397         int len;
398
399         g_assert (cd);
400         g_assert (bytes);
401         g_assert (mono_array_length (bytes) > index);
402         g_assert (mono_array_length (bytes) >= (index + count));
403
404         if (!(len = (mono_array_length (bytes) - index)))
405                 return 0;
406
407         src =  mono_array_addr (bytes, char, index);
408
409         return iconv_get_length (cd, src, len);
410 }
411
412 int
413 ves_icall_iconv_get_chars (gpointer converter, MonoArray *bytes, gint32 byteIndex, gint32 byteCount,
414                            MonoArray *chars, gint32 charIndex)
415 {
416         iconv_t cd = (iconv_t)converter;
417         guchar *src, *dest;
418         int len, max_len;
419
420         g_assert (cd);
421         g_assert (chars);
422         g_assert (bytes);
423         g_assert (mono_array_length (bytes) > byteIndex);
424         g_assert (mono_array_length (chars) >= (byteIndex + byteCount));
425         g_assert (mono_array_length (chars) > charIndex);
426
427         if (!(len = (mono_array_length (bytes) - byteIndex)))
428                 return 0;
429
430         src =  mono_array_addr (bytes, char, byteIndex);
431         dest = mono_array_addr (chars, guint16, charIndex);
432
433         max_len = (mono_array_length (chars) - charIndex) * 2;
434
435         /* iconv_convert () returns the number of bytes */
436         return iconv_convert (cd, src, len, dest, max_len) / 2;
437 }