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