* culture-info.h: Make defines more consistent, add calendar data
[mono.git] / mono / metadata / string-icalls.c
1 /*
2  * string-icalls.c: String internal calls for the corlib
3  *
4  * Author:
5  *   Patrik Torstensson (patrik.torstensson@labs2.com)
6  *   Duncan Mak  (duncan@ximian.com)
7  *
8  * (C) 2001 Ximian, Inc.
9  */
10 #include <config.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <signal.h>
14 #include <string.h>
15 #include <mono/metadata/string-icalls.h>
16 #include <mono/metadata/appdomain.h>
17 #include <mono/metadata/tabledefs.h>
18 #include <mono/metadata/loader.h>
19 #include <mono/metadata/object.h>
20 #include <mono/metadata/unicode.h>
21 #include <mono/metadata/exception.h>
22
23 /* Internal helper methods */
24
25 static gboolean
26 string_icall_is_in_array (MonoArray *chars, gint32 arraylength, gunichar2 chr);
27
28 static gint32
29 string_icall_cmp_char (gunichar2 c1, gunichar2 c2, gint32 mode);
30
31 MonoString *
32 ves_icall_System_String_ctor_charp (gpointer dummy, gunichar2 *value)
33 {
34         gint32 i, length;
35         MonoDomain *domain;
36
37         MONO_ARCH_SAVE_REGS;
38
39         domain = mono_domain_get ();
40
41         if (value == NULL)
42                 length = 0;
43         else {
44                 for (i = 0; *(value + i) != '\0'; i++);
45                 length = i;
46         }
47
48         return mono_string_new_utf16 (domain, value, length);
49 }
50
51 MonoString *
52 ves_icall_System_String_ctor_char_int (gpointer dummy, gunichar2 value, gint32 count)
53 {
54         MonoDomain *domain;
55         MonoString *res;
56         gunichar2 *chars;
57         gint32 i;
58
59         MONO_ARCH_SAVE_REGS;
60
61         if (count < 0)
62                 mono_raise_exception (mono_get_exception_argument_out_of_range ("count"));
63
64         domain = mono_domain_get ();
65         res = mono_string_new_size (domain, count);
66
67         chars = mono_string_chars (res);
68         for (i = 0; i < count; i++)
69                 chars [i] = value;
70         
71         return res;
72 }
73
74 MonoString *
75 ves_icall_System_String_ctor_charp_int_int (gpointer dummy, gunichar2 *value, gint32 sindex, gint32 length)
76 {
77         gunichar2 *begin;
78         MonoDomain * domain;
79         
80         MONO_ARCH_SAVE_REGS;
81
82         domain = mono_domain_get ();
83
84         if ((value == NULL) && (length != 0))
85                 mono_raise_exception (mono_get_exception_argument_out_of_range ("Out of range"));
86
87         if ((sindex < 0) || (length < 0))
88                 mono_raise_exception (mono_get_exception_argument_out_of_range ("Out of range"));
89         
90         if (length == 0) {      /* fixme: return String.Empty here */
91                 g_warning ("string doesn't yet support empy strings in char* constructor");
92                 g_assert_not_reached ();
93         }
94         
95         begin = (gunichar2 *) (value + sindex);
96
97         return mono_string_new_utf16 (domain, begin, length);
98 }
99
100 MonoString *
101 ves_icall_System_String_ctor_sbytep (gpointer dummy, gint8 *value)
102 {
103         MonoDomain *domain;
104         
105         MONO_ARCH_SAVE_REGS;
106
107         domain = mono_domain_get ();
108
109         if (NULL == value) {    /* fixme: return String.Empty here */
110                 g_warning ("string doesn't yet support empy strings in char* constructor");
111                 g_assert_not_reached ();
112         }
113
114         return mono_string_new (domain, (const char *) value);
115 }
116
117 MonoString *
118 ves_icall_System_String_ctor_sbytep_int_int (gpointer dummy, gint8 *value, gint32 sindex, gint32 length)
119 {
120         guchar *begin;
121         MonoDomain *domain;
122         MonoString *res;
123         gunichar2 *chars;
124         int i;
125         
126         MONO_ARCH_SAVE_REGS;
127
128         domain = mono_domain_get ();
129
130         if ((value == NULL) && (length != 0))
131                 mono_raise_exception (mono_get_exception_argument_out_of_range ("Out of range"));
132
133         if ((sindex < 0) || (length < 0))
134                 mono_raise_exception (mono_get_exception_argument_out_of_range ("Out of range"));
135
136         begin = (guchar *) (value + sindex);
137         res = mono_string_new_size (domain, length);
138         chars = mono_string_chars (res);
139         for (i = 0; i < length; ++i)
140                 chars [i] = begin [i];
141
142         return res;
143 }
144
145 MonoString *
146 ves_icall_System_String_ctor_chara (gpointer dummy, MonoArray *value)
147 {
148         MonoDomain *domain;
149
150         MONO_ARCH_SAVE_REGS;
151
152         domain = mono_domain_get ();
153
154         if (value == NULL)
155                 return mono_string_new_utf16 (domain, NULL, 0);
156         else
157                 return mono_string_new_utf16 (domain, (gunichar2 *) mono_array_addr(value, gunichar2, 0),  value->max_length);
158 }
159
160 MonoString *
161 ves_icall_System_String_ctor_chara_int_int (gpointer dummy, MonoArray *value, 
162                                          gint32 sindex, gint32 length)
163 {
164         MonoDomain *domain;
165
166         MONO_ARCH_SAVE_REGS;
167
168         if (value == NULL)
169                 mono_raise_exception (mono_get_exception_argument_null ("value"));
170         if (sindex < 0)
171                 mono_raise_exception (mono_get_exception_argument_out_of_range ("startIndex"));         
172         if (length < 0)
173                 mono_raise_exception (mono_get_exception_argument_out_of_range ("length"));
174         if (sindex + length > mono_array_length (value))
175                 mono_raise_exception (mono_get_exception_argument_out_of_range ("Out of range"));
176
177         domain = mono_domain_get ();
178         
179         return mono_string_new_utf16 (domain, (gunichar2 *) mono_array_addr(value, gunichar2, sindex), length);
180 }
181
182 MonoString *
183 ves_icall_System_String_ctor_encoding (gpointer dummy, gint8 *value, gint32 sindex, 
184                                     gint32 length, MonoObject *enc)
185 {
186         MONO_ARCH_SAVE_REGS;
187
188         g_warning("string.ctor with encoding obj unimplemented");
189         g_assert_not_reached ();
190         return NULL;
191 }
192
193 MonoString * 
194 ves_icall_System_String_InternalJoin (MonoString *separator, MonoArray * value, gint32 sindex, gint32 count)
195 {
196         MonoString * ret;
197         MonoString *current;
198         gint32 length;
199         gint32 pos;
200         gint32 insertlen;
201         gint32 destpos;
202         gint32 srclen;
203         gunichar2 *insert;
204         gunichar2 *dest;
205         gunichar2 *src;
206
207         MONO_ARCH_SAVE_REGS;
208
209         insert = mono_string_chars(separator);
210         insertlen = mono_string_length(separator);
211
212         length = 0;
213         for (pos = sindex; pos != sindex + count; pos++) {
214                 current = mono_array_get (value, MonoString *, pos);
215                 if (current != NULL)
216                         length += mono_string_length (current);
217
218                 if (pos < sindex + count - 1)
219                         length += insertlen;
220         }
221
222         ret = mono_string_new_size( mono_domain_get (), length);
223         dest = mono_string_chars(ret);
224         destpos = 0;
225
226         for (pos = sindex; pos != sindex + count; pos++) {
227                 current = mono_array_get (value, MonoString *, pos);
228                 if (current != NULL) {
229                         src = mono_string_chars (current);
230                         srclen = mono_string_length (current);
231
232                         memcpy (dest + destpos, src, srclen * sizeof(gunichar2));
233                         destpos += srclen;
234                 }
235
236                 if (pos < sindex + count - 1) {
237                         memcpy(dest + destpos, insert, insertlen * sizeof(gunichar2));
238                         destpos += insertlen;
239                 }
240         }
241
242         return ret;
243 }
244
245 MonoString * 
246 ves_icall_System_String_InternalInsert (MonoString *me, gint32 sindex, MonoString *value)
247 {
248         MonoString * ret;
249         gunichar2 *src;
250         gunichar2 *insertsrc;
251         gunichar2 *dest;
252         gint32 srclen;
253         gint32 insertlen;
254
255         MONO_ARCH_SAVE_REGS;
256
257         src = mono_string_chars(me);
258         srclen = mono_string_length(me);
259
260         insertsrc = mono_string_chars(value);
261         insertlen = mono_string_length(value);
262
263         ret = mono_string_new_size( mono_domain_get (), srclen + insertlen);
264         dest = mono_string_chars(ret);
265
266         memcpy(dest, src, sindex * sizeof(gunichar2));
267         memcpy(dest + sindex, insertsrc, insertlen * sizeof(gunichar2));
268         memcpy(dest + sindex + insertlen, src + sindex, (srclen - sindex) * sizeof(gunichar2));
269
270         return ret;
271 }
272
273 MonoString * 
274 ves_icall_System_String_InternalReplace_Char (MonoString *me, gunichar2 oldChar, gunichar2 newChar)
275 {
276         MonoString *ret;
277         gunichar2 *src;
278         gunichar2 *dest;
279         gint32 i, srclen;
280
281         MONO_ARCH_SAVE_REGS;
282
283         src = mono_string_chars(me);
284         srclen = mono_string_length(me);
285
286         ret = mono_string_new_size( mono_domain_get (), srclen);
287         dest = mono_string_chars(ret);
288
289         for (i = 0; i != srclen; i++) {
290                 if (src[i] == oldChar)
291                         dest[i] = newChar;
292                 else
293                         dest[i] = src[i];
294         }
295
296         return ret;
297 }
298
299 MonoString * 
300 ves_icall_System_String_InternalRemove (MonoString *me, gint32 sindex, gint32 count)
301 {
302         MonoString * ret;
303         gint32 srclen;
304         gunichar2 *dest;
305         gunichar2 *src;
306
307         MONO_ARCH_SAVE_REGS;
308
309         srclen = mono_string_length(me);
310         ret = mono_string_new_size( mono_domain_get (), srclen - count);
311
312         src = mono_string_chars(me);
313         dest = mono_string_chars(ret);
314
315         memcpy(dest, src, sindex * sizeof(gunichar2));
316         memcpy(dest + sindex, src + sindex + count, (srclen - count - sindex) * sizeof(gunichar2));
317
318         return ret;
319 }
320
321 void
322 ves_icall_System_String_InternalCopyTo (MonoString *me, gint32 sindex, MonoArray *dest, gint32 dindex, gint32 count)
323 {
324         gunichar2 *destptr = (gunichar2 *) mono_array_addr(dest, gunichar2, dindex);
325         gunichar2 *src =  mono_string_chars(me);
326
327         MONO_ARCH_SAVE_REGS;
328
329         memcpy(destptr, src + sindex, sizeof(gunichar2) * count);
330 }
331
332 MonoArray * 
333 ves_icall_System_String_InternalSplit (MonoString *me, MonoArray *separator, gint32 count)
334 {
335         MonoString * tmpstr;
336         MonoArray * retarr;
337         gunichar2 *src;
338         gint32 arrsize, srcsize, splitsize;
339         gint32 i, lastpos, arrpos;
340         gint32 tmpstrsize;
341         gunichar2 *tmpstrptr;
342
343         gunichar2 cmpchar;
344
345         MONO_ARCH_SAVE_REGS;
346
347         src = mono_string_chars(me);
348         srcsize = mono_string_length(me);
349         arrsize = mono_array_length(separator);
350
351         cmpchar = mono_array_get(separator, gunichar2, 0);
352
353         splitsize = 0;
354         for (i = 0; i != srcsize && splitsize < count; i++) {
355                 if (string_icall_is_in_array(separator, arrsize, src[i]))
356                         splitsize++;
357         }
358
359         lastpos = 0;
360         arrpos = 0;
361
362         /* if no split chars found return the string */
363         if (splitsize == 0) {
364                 retarr = mono_array_new(mono_domain_get(), mono_defaults.string_class, 1);
365                 mono_array_set(retarr, MonoString *, 0, me);
366
367                 return retarr;
368         }
369
370         if (splitsize != count)
371                 splitsize++;
372
373         retarr = mono_array_new(mono_domain_get(), mono_defaults.string_class, splitsize);
374         for (i = 0; i != srcsize && arrpos != count; i++) {
375                 if (string_icall_is_in_array(separator, arrsize, src[i])) {
376                         if (arrpos == count - 1)
377                                 tmpstrsize = srcsize - lastpos;
378                         else
379                                 tmpstrsize = i - lastpos;
380
381                         tmpstr = mono_string_new_size( mono_domain_get (), tmpstrsize);
382                         tmpstrptr = mono_string_chars(tmpstr);
383
384                         memcpy(tmpstrptr, src + lastpos, tmpstrsize * sizeof(gunichar2));
385                         mono_array_set(retarr, MonoString *, arrpos, tmpstr);
386                         arrpos++;
387                         lastpos = i + 1;
388                 }
389         }
390
391         if (arrpos < count) {
392                 tmpstrsize = srcsize - lastpos;
393                 tmpstr = mono_string_new_size( mono_domain_get (), tmpstrsize);
394                 tmpstrptr = mono_string_chars(tmpstr);
395
396                 memcpy(tmpstrptr, src + lastpos, tmpstrsize * sizeof(gunichar2));
397                 mono_array_set(retarr, MonoString *, arrpos, tmpstr);
398         }
399
400         return retarr;
401 }
402
403 static gboolean
404 string_icall_is_in_array (MonoArray *chars, gint32 arraylength, gunichar2 chr)
405 {
406         gunichar2 cmpchar;
407         gint32 arrpos;
408
409         for (arrpos = 0; arrpos != arraylength; arrpos++) {
410                 cmpchar = mono_array_get(chars, gunichar2, arrpos);
411                 if (cmpchar == chr)
412                         return TRUE;
413         }
414         
415         return FALSE;
416 }
417
418 MonoString * 
419 ves_icall_System_String_InternalTrim (MonoString *me, MonoArray *chars, gint32 typ)
420 {
421         MonoString * ret;
422         gunichar2 *src, *dest;
423         gint32 srclen, newlen, arrlen;
424         gint32 i, lenfirst, lenlast;
425
426         MONO_ARCH_SAVE_REGS;
427
428         srclen = mono_string_length(me);
429         src = mono_string_chars(me);
430         arrlen = mono_array_length(chars);
431
432         lenfirst = 0;
433         lenlast = 0;
434
435         if (0 == typ || 1 == typ) {
436                 for (i = 0; i != srclen; i++) {
437                         if (string_icall_is_in_array(chars, arrlen, src[i]))
438                                 lenfirst++;
439                         else 
440                                 break;
441                 }
442         }
443
444         if (0 == typ || 2 == typ) {
445                 for (i = srclen - 1; i > lenfirst - 1; i--) {
446                         if (string_icall_is_in_array(chars, arrlen, src[i]))
447                                 lenlast++;
448                         else 
449                                 break;
450                 }
451         }
452
453         newlen = srclen - lenfirst - lenlast;
454         if (newlen == srclen)
455                 return me;
456
457         ret = mono_string_new_size( mono_domain_get (), newlen);
458         dest = mono_string_chars(ret);
459
460         memcpy(dest, src + lenfirst, newlen *sizeof(gunichar2));
461
462         return ret;
463 }
464
465 gint32 
466 ves_icall_System_String_InternalIndexOfAny (MonoString *me, MonoArray *arr, gint32 sindex, gint32 count)
467 {
468         gint32 pos;
469         gint32 loop;
470         gint32 arraysize;
471         gunichar2 *src;
472
473         MONO_ARCH_SAVE_REGS;
474
475         arraysize = mono_array_length(arr);
476         src = mono_string_chars(me);
477
478         for (pos = sindex; pos != count + sindex; pos++) {
479                 for (loop = 0; loop != arraysize; loop++)
480                         if ( src [pos] == mono_array_get(arr, gunichar2, loop) )
481                                 return pos;
482         }
483
484         return -1;
485 }
486
487 gint32 
488 ves_icall_System_String_InternalLastIndexOf_Char (MonoString *me, gunichar2 value, gint32 sindex, gint32 count)
489 {
490         gint32 pos;
491         gunichar2 *src;
492
493         MONO_ARCH_SAVE_REGS;
494
495         src = mono_string_chars(me);
496         for (pos = sindex; pos > sindex - count; pos--) {
497                 if (src [pos] == value)
498                         return pos;
499         }
500
501         return -1;
502 }
503
504 gint32 
505 ves_icall_System_String_InternalLastIndexOf_Str (MonoString *me, MonoString *value, gint32 sindex, gint32 count)
506 {
507         gint32 lencmpstr;
508         gint32 pos;
509         gunichar2 *src;
510         gunichar2 *cmpstr;
511
512         MONO_ARCH_SAVE_REGS;
513
514         lencmpstr = mono_string_length(value);
515
516         src = mono_string_chars(me);
517         cmpstr = mono_string_chars(value);
518
519         for (pos = sindex - lencmpstr + 1; pos > sindex - count; pos--) {
520                 if (0 == memcmp(src + pos, cmpstr, lencmpstr * sizeof(gunichar2)))
521                         return pos;
522         }
523
524         return -1;
525 }
526
527 gint32 
528 ves_icall_System_String_InternalLastIndexOfAny (MonoString *me, MonoArray *anyOf, gint32 sindex, gint32 count)
529 {
530         gint32 pos;
531         gint32 loop;
532         gint32 arraysize;
533         gunichar2 *src;
534
535         MONO_ARCH_SAVE_REGS;
536
537         arraysize = mono_array_length(anyOf);
538         src = mono_string_chars(me);
539
540         for (pos = sindex; pos > sindex - count; pos--) {
541                 for (loop = 0; loop != arraysize; loop++)
542                         if ( src [pos] == mono_array_get(anyOf, gunichar2, loop) )
543                                 return pos;
544         }
545
546         return -1;
547 }
548
549 MonoString *
550 ves_icall_System_String_InternalPad (MonoString *me, gint32 width, gunichar2 chr, MonoBoolean right)
551 {
552         MonoString * ret;
553         gunichar2 *src;
554         gunichar2 *dest;
555         gint32 fillcount;
556         gint32 srclen;
557         gint32 i;
558
559         MONO_ARCH_SAVE_REGS;
560
561         srclen = mono_string_length(me);
562         src = mono_string_chars(me);
563
564         ret = mono_string_new_size( mono_domain_get (), width);
565         dest = mono_string_chars(ret);
566         fillcount = width - srclen;
567
568         if (right) {
569                 memcpy(dest, src, srclen * sizeof(gunichar2));
570                 for (i = srclen; i != width; i++)
571                         dest[i] = chr;
572
573                 return ret;
574         }
575
576         /* left fill */
577         for (i = 0; i != fillcount; i++)
578                 dest[i] = chr;
579
580         memcpy(dest + fillcount, src, srclen * sizeof(gunichar2));
581
582         return ret;
583 }
584
585 MonoString *
586 ves_icall_System_String_InternalAllocateStr (gint32 length)
587 {
588         MONO_ARCH_SAVE_REGS;
589
590         return mono_string_new_size(mono_domain_get (), length);
591 }
592
593 void 
594 ves_icall_System_String_InternalStrcpy_Str (MonoString *dest, gint32 destPos, MonoString *src)
595 {
596         gunichar2 *srcptr;
597         gunichar2 *destptr;
598
599         MONO_ARCH_SAVE_REGS;
600
601         srcptr = mono_string_chars (src);
602         destptr = mono_string_chars (dest);
603
604         g_memmove (destptr + destPos, srcptr, mono_string_length(src) * sizeof(gunichar2));
605 }
606
607 void 
608 ves_icall_System_String_InternalStrcpy_StrN (MonoString *dest, gint32 destPos, MonoString *src, gint32 startPos, gint32 count)
609 {
610         gunichar2 *srcptr;
611         gunichar2 *destptr;
612
613         MONO_ARCH_SAVE_REGS;
614
615         srcptr = mono_string_chars (src);
616         destptr = mono_string_chars (dest);
617         g_memmove (destptr + destPos, srcptr + startPos, count * sizeof(gunichar2));
618 }
619
620 MonoString  *
621 ves_icall_System_String_InternalIntern (MonoString *str)
622 {
623         MONO_ARCH_SAVE_REGS;
624
625         return mono_string_intern(str);
626 }
627
628 MonoString * 
629 ves_icall_System_String_InternalIsInterned (MonoString *str)
630 {
631         MONO_ARCH_SAVE_REGS;
632
633         return mono_string_is_interned(str);
634 }
635
636 gint32
637 ves_icall_System_String_GetHashCode (MonoString *me)
638 {
639         int i, h = 0;
640         gunichar2 *data = mono_string_chars (me);
641
642         MONO_ARCH_SAVE_REGS;
643
644         for (i = 0; i < mono_string_length (me); ++i)
645                 h = (h << 5) - h + data [i];
646
647         return h;
648 }
649
650 gunichar2 
651 ves_icall_System_String_get_Chars (MonoString *me, gint32 idx)
652 {
653         MONO_ARCH_SAVE_REGS;
654
655         if ((idx < 0) || (idx >= mono_string_length (me)))
656                 mono_raise_exception (mono_get_exception_index_out_of_range ());
657         return mono_string_chars(me)[idx];
658 }
659
660 void
661 ves_icall_System_String_InternalCharCopy (gunichar2 *src, gunichar2 *dest, gint32 count)
662 {
663         MONO_ARCH_SAVE_REGS;
664
665         memcpy (dest, src, sizeof (gunichar2) * count);
666 }
667
668 /*
669  * @mode:
670  * 0 = StringCompareModeDirect
671  * 1 = StringCompareModeCaseInsensitive
672  * 2 = StringCompareModeOrdinal
673  */
674 static gint32 
675 string_icall_cmp_char (gunichar2 c1, gunichar2 c2, gint32 mode)
676 {
677         gint32 result;
678         GUnicodeType c1type, c2type;
679
680         c1type = g_unichar_type (c1);
681         c2type = g_unichar_type (c2);
682
683         switch (mode) {
684         case 0: 
685                 /* TODO: compare with culture info */
686                 if (c1type == G_UNICODE_UPPERCASE_LETTER && c2type == G_UNICODE_LOWERCASE_LETTER)
687                         return 1;
688                                         
689                 if (c1type == G_UNICODE_LOWERCASE_LETTER && c2type == G_UNICODE_UPPERCASE_LETTER)
690                         return -1;
691         
692                 result = (gint32) c1 - c2;
693                 break;
694         case 1: 
695                 result = (gint32) (c1type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c1) : c1) - 
696                                   (c2type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c2) : c2);
697                 break;
698         case 2:
699                 // Rotor/ms return the full value just not -1 and 1
700                 return (gint32) c1 - c2; break;
701         }
702
703         return ((result < 0) ? -1 : (result > 0) ? 1 : 0);
704 }