2009-10-28 Mark Probst <mark.probst@gmail.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  *   Duncan Mak  (duncan@ximian.com)
7  *
8  * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
9  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
10  */
11 #include <config.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <signal.h>
15 #include <string.h>
16 #include "mono/utils/mono-membar.h"
17 #include <mono/metadata/string-icalls.h>
18 #include <mono/metadata/class-internals.h>
19 #include <mono/metadata/appdomain.h>
20 #include <mono/metadata/tabledefs.h>
21 #include <mono/metadata/loader.h>
22 #include <mono/metadata/object.h>
23 #include <mono/metadata/exception.h>
24 #include <mono/metadata/debug-helpers.h>
25
26 /* Internal helper methods */
27
28 static gboolean
29 string_icall_is_in_array (MonoArray *chars, gint32 arraylength, gunichar2 chr);
30
31 /* This function is redirected to String.CreateString ()
32    by mono_marshal_get_native_wrapper () */
33 void
34 ves_icall_System_String_ctor_RedirectToCreateString (void)
35 {
36         g_assert_not_reached ();
37 }
38
39 MonoString * 
40 ves_icall_System_String_InternalJoin (MonoString *separator, MonoArray * value, gint32 sindex, gint32 count)
41 {
42         MonoString * ret;
43         MonoString *current;
44         gint32 length;
45         gint32 pos;
46         gint32 insertlen;
47         gint32 destpos;
48         gint32 srclen;
49         gunichar2 *insert;
50         gunichar2 *dest;
51         gunichar2 *src;
52
53         MONO_ARCH_SAVE_REGS;
54
55         insert = mono_string_chars(separator);
56         insertlen = mono_string_length(separator);
57
58         length = 0;
59         for (pos = sindex; pos != sindex + count; pos++) {
60                 current = mono_array_get (value, MonoString *, pos);
61                 if (current != NULL)
62                         length += mono_string_length (current);
63
64                 if (pos < sindex + count - 1)
65                         length += insertlen;
66         }
67
68         ret = mono_string_new_size( mono_domain_get (), length);
69         dest = mono_string_chars(ret);
70         destpos = 0;
71
72         for (pos = sindex; pos != sindex + count; pos++) {
73                 current = mono_array_get (value, MonoString *, pos);
74                 if (current != NULL) {
75                         src = mono_string_chars (current);
76                         srclen = mono_string_length (current);
77
78                         memcpy (dest + destpos, src, srclen * sizeof(gunichar2));
79                         destpos += srclen;
80                 }
81
82                 if (pos < sindex + count - 1) {
83                         memcpy(dest + destpos, insert, insertlen * sizeof(gunichar2));
84                         destpos += insertlen;
85                 }
86         }
87
88         return ret;
89 }
90
91 void
92 ves_icall_System_String_InternalCopyTo (MonoString *me, gint32 sindex, MonoArray *dest, gint32 dindex, gint32 count)
93 {
94         gunichar2 *destptr = (gunichar2 *) mono_array_addr(dest, gunichar2, dindex);
95         gunichar2 *src =  mono_string_chars(me);
96
97         MONO_ARCH_SAVE_REGS;
98
99         memcpy(destptr, src + sindex, sizeof(gunichar2) * count);
100 }
101
102 /* System.StringSplitOptions */
103 typedef enum {
104         STRINGSPLITOPTIONS_NONE = 0,
105         STRINGSPLITOPTIONS_REMOVE_EMPTY_ENTRIES = 1
106 } StringSplitOptions;
107
108 MonoArray * 
109 ves_icall_System_String_InternalSplit (MonoString *me, MonoArray *separator, gint32 count, gint32 options)
110 {
111         static MonoClass *String_array;
112         MonoString * tmpstr;
113         MonoArray * retarr;
114         gunichar2 *src;
115         gint32 arrsize, srcsize, splitsize;
116         gint32 i, lastpos, arrpos;
117         gint32 tmpstrsize;
118         gint32 remempty;
119         gint32 flag;
120         gunichar2 *tmpstrptr;
121
122         remempty = options & STRINGSPLITOPTIONS_REMOVE_EMPTY_ENTRIES;
123         src = mono_string_chars (me);
124         srcsize = mono_string_length (me);
125         arrsize = mono_array_length (separator);
126
127         if (!String_array) {
128                 MonoClass *klass = mono_array_class_get (mono_get_string_class (), 1);
129                 mono_memory_barrier ();
130                 String_array = klass;
131         }
132
133         splitsize = 1;
134         /* Count the number of elements we will return. Note that this operation
135          * guarantees that we will return exactly splitsize elements, and we will
136          * have enough data to fill each. This allows us to skip some checks later on.
137          */
138         if (remempty == 0) {
139                 for (i = 0; i != srcsize && splitsize < count; i++) {
140                         if (string_icall_is_in_array (separator, arrsize, src [i]))
141                                 splitsize++;
142                 }
143         } else if (count > 1) {
144                 /* Require pattern "Nondelim + Delim + Nondelim" to increment counter.
145                  * Lastpos != 0 means first nondelim found.
146                  * Flag = 0 means last char was delim.
147                  * Efficient, though perhaps confusing.
148                  */
149                 lastpos = 0;
150                 flag = 0;
151                 for (i = 0; i != srcsize && splitsize < count; i++) {
152                         if (string_icall_is_in_array (separator, arrsize, src [i])) {
153                                 flag = 0;
154                         } else if (flag == 0) {
155                                 if (lastpos == 1)
156                                         splitsize++;
157                                 flag = 1;
158                                 lastpos = 1;
159                         }
160                 }
161
162                 /* Nothing but separators */
163                 if (lastpos == 0) {
164                         retarr = mono_array_new_specific (mono_class_vtable (mono_domain_get (), String_array), 0);
165                         return retarr;
166                 }
167         }
168
169         /* if no split chars found return the string */
170         if (splitsize == 1) {
171                 if (remempty == 0 || count == 1) {
172                         /* Copy the whole string */
173                         retarr = mono_array_new_specific (mono_class_vtable (mono_domain_get (), String_array), 1);
174                         mono_array_setref (retarr, 0, me);
175                 } else {
176                         /* otherwise we have to filter out leading & trailing delims */
177
178                         /* find first non-delim char */
179                         for (; srcsize != 0; srcsize--, src++) {
180                                 if (!string_icall_is_in_array (separator, arrsize, src [0]))
181                                         break;
182                         }
183                         /* find last non-delim char */
184                         for (; srcsize != 0; srcsize--) {
185                                 if (!string_icall_is_in_array (separator, arrsize, src [srcsize - 1]))
186                                         break;
187                         }
188                         tmpstr = mono_string_new_size (mono_domain_get (), srcsize);
189                         tmpstrptr = mono_string_chars (tmpstr);
190
191                         memcpy (tmpstrptr, src, srcsize * sizeof (gunichar2));
192                         retarr = mono_array_new_specific (mono_class_vtable (mono_domain_get (), String_array), 1);
193                         mono_array_setref (retarr, 0, tmpstr);
194                 }
195                 return retarr;
196         }
197
198         lastpos = 0;
199         arrpos = 0;
200         
201         retarr = mono_array_new_specific (mono_class_vtable (mono_domain_get (), String_array), splitsize);
202
203         for (i = 0; i != srcsize && arrpos != splitsize; i++) {
204                 if (string_icall_is_in_array (separator, arrsize, src [i])) {
205                         
206                         if (lastpos != i || remempty == 0) {
207                                 tmpstrsize = i - lastpos;
208                                 tmpstr = mono_string_new_size (mono_domain_get (), tmpstrsize);
209                                 tmpstrptr = mono_string_chars (tmpstr);
210
211                                 memcpy (tmpstrptr, src + lastpos, tmpstrsize * sizeof (gunichar2));
212                                 mono_array_setref (retarr, arrpos, tmpstr);
213                                 arrpos++;
214
215                                 if (arrpos == splitsize - 1) {
216                                         /* Shortcut the last array element */
217
218                                         lastpos = i + 1;
219                                         if (remempty != 0) {
220                                                 /* Search for non-delim starting char (guaranteed to find one) Note that loop
221                                                  * condition is only there for safety. It will never actually terminate the loop. */
222                                                 for (; lastpos != srcsize ; lastpos++) {
223                                                         if (!string_icall_is_in_array (separator, arrsize, src [lastpos])) 
224                                                                 break;
225                                                 }
226                                                 if (count > splitsize) {
227                                                         /* Since we have fewer results than our limit, we must remove
228                                                          * trailing delimiters as well. 
229                                                          */
230                                                         for (; srcsize != lastpos + 1 ; srcsize--) {
231                                                                 if (!string_icall_is_in_array (separator, arrsize, src [srcsize - 1])) 
232                                                                         break;
233                                                         }
234                                                 }
235                                         }
236
237                                         tmpstrsize = srcsize - lastpos;
238                                         tmpstr = mono_string_new_size (mono_domain_get (), tmpstrsize);
239                                         tmpstrptr = mono_string_chars (tmpstr);
240
241                                         memcpy (tmpstrptr, src + lastpos, tmpstrsize * sizeof (gunichar2));
242                                         mono_array_setref (retarr, arrpos, tmpstr);
243
244                                         /* Loop will ALWAYS end here. Test criteria in the FOR loop is technically unnecessary. */
245                                         break;
246                                 }
247                         }
248                         lastpos = i + 1;
249                 }
250         }
251
252         return retarr;
253 }
254
255 static gboolean
256 string_icall_is_in_array (MonoArray *chars, gint32 arraylength, gunichar2 chr)
257 {
258         gunichar2 cmpchar;
259         gint32 arrpos;
260
261         for (arrpos = 0; arrpos != arraylength; arrpos++) {
262                 cmpchar = mono_array_get(chars, gunichar2, arrpos);
263                 if (cmpchar == chr)
264                         return TRUE;
265         }
266         
267         return FALSE;
268 }
269
270 MonoString * 
271 ves_icall_System_String_InternalTrim (MonoString *me, MonoArray *chars, gint32 typ)
272 {
273         MonoString * ret;
274         gunichar2 *src, *dest;
275         gint32 srclen, newlen, arrlen;
276         gint32 i, lenfirst, lenlast;
277
278         MONO_ARCH_SAVE_REGS;
279
280         srclen = mono_string_length(me);
281         src = mono_string_chars(me);
282         arrlen = mono_array_length(chars);
283
284         lenfirst = 0;
285         lenlast = 0;
286
287         if (0 == typ || 1 == typ) {
288                 for (i = 0; i != srclen; i++) {
289                         if (string_icall_is_in_array(chars, arrlen, src[i]))
290                                 lenfirst++;
291                         else 
292                                 break;
293                 }
294         }
295
296         if (0 == typ || 2 == typ) {
297                 for (i = srclen - 1; i > lenfirst - 1; i--) {
298                         if (string_icall_is_in_array(chars, arrlen, src[i]))
299                                 lenlast++;
300                         else 
301                                 break;
302                 }
303         }
304
305         newlen = srclen - lenfirst - lenlast;
306         if (newlen == srclen)
307                 return me;
308
309         ret = mono_string_new_size( mono_domain_get (), newlen);
310         dest = mono_string_chars(ret);
311
312         memcpy(dest, src + lenfirst, newlen *sizeof(gunichar2));
313
314         return ret;
315 }
316
317 gint32 
318 ves_icall_System_String_InternalLastIndexOfAny (MonoString *me, MonoArray *anyOf, gint32 sindex, gint32 count)
319 {
320         gint32 pos;
321         gint32 loop;
322         gint32 arraysize;
323         gunichar2 *src;
324
325         MONO_ARCH_SAVE_REGS;
326
327         arraysize = mono_array_length(anyOf);
328         src = mono_string_chars(me);
329
330         for (pos = sindex; pos > sindex - count; pos--) {
331                 for (loop = 0; loop != arraysize; loop++)
332                         if ( src [pos] == mono_array_get(anyOf, gunichar2, loop) )
333                                 return pos;
334         }
335
336         return -1;
337 }
338
339 MonoString *
340 ves_icall_System_String_InternalPad (MonoString *me, gint32 width, gunichar2 chr, MonoBoolean right)
341 {
342         MonoString * ret;
343         gunichar2 *src;
344         gunichar2 *dest;
345         gint32 fillcount;
346         gint32 srclen;
347         gint32 i;
348
349         MONO_ARCH_SAVE_REGS;
350
351         srclen = mono_string_length(me);
352         src = mono_string_chars(me);
353
354         ret = mono_string_new_size( mono_domain_get (), width);
355         dest = mono_string_chars(ret);
356         fillcount = width - srclen;
357
358         if (right) {
359                 memcpy(dest, src, srclen * sizeof(gunichar2));
360                 for (i = srclen; i != width; i++)
361                         dest[i] = chr;
362
363                 return ret;
364         }
365
366         /* left fill */
367         for (i = 0; i != fillcount; i++)
368                 dest[i] = chr;
369
370         memcpy(dest + fillcount, src, srclen * sizeof(gunichar2));
371
372         return ret;
373 }
374
375 MonoString *
376 ves_icall_System_String_InternalAllocateStr (gint32 length)
377 {
378         MONO_ARCH_SAVE_REGS;
379
380         return mono_string_new_size(mono_domain_get (), length);
381 }
382
383 void 
384 ves_icall_System_String_InternalStrcpy_Str (MonoString *dest, gint32 destPos, MonoString *src)
385 {
386         gunichar2 *srcptr;
387         gunichar2 *destptr;
388
389         MONO_ARCH_SAVE_REGS;
390
391         srcptr = mono_string_chars (src);
392         destptr = mono_string_chars (dest);
393
394         g_memmove (destptr + destPos, srcptr, mono_string_length(src) * sizeof(gunichar2));
395 }
396
397 void 
398 ves_icall_System_String_InternalStrcpy_StrN (MonoString *dest, gint32 destPos, MonoString *src, gint32 startPos, gint32 count)
399 {
400         gunichar2 *srcptr;
401         gunichar2 *destptr;
402
403         MONO_ARCH_SAVE_REGS;
404
405         srcptr = mono_string_chars (src);
406         destptr = mono_string_chars (dest);
407         g_memmove (destptr + destPos, srcptr + startPos, count * sizeof(gunichar2));
408 }
409
410 void 
411 ves_icall_System_String_InternalStrcpy_Chars (MonoString *dest, gint32 destPos, MonoArray *src)
412 {
413         gunichar2 *srcptr;
414         gunichar2 *destptr;
415
416         MONO_ARCH_SAVE_REGS;
417
418         srcptr = mono_array_addr (src, gunichar2, 0);
419         destptr = mono_string_chars (dest);
420
421         g_memmove (destptr + destPos, srcptr, mono_array_length (src) * sizeof(gunichar2));
422 }
423
424 void 
425 ves_icall_System_String_InternalStrcpy_CharsN (MonoString *dest, gint32 destPos, MonoArray *src, gint32 startPos, gint32 count)
426 {
427         gunichar2 *srcptr;
428         gunichar2 *destptr;
429
430         MONO_ARCH_SAVE_REGS;
431
432         srcptr = mono_array_addr (src, gunichar2, 0);
433         destptr = mono_string_chars (dest);
434
435         g_memmove (destptr + destPos, srcptr + startPos, count * sizeof(gunichar2));
436 }
437
438 MonoString  *
439 ves_icall_System_String_InternalIntern (MonoString *str)
440 {
441         MONO_ARCH_SAVE_REGS;
442
443         return mono_string_intern(str);
444 }
445
446 MonoString * 
447 ves_icall_System_String_InternalIsInterned (MonoString *str)
448 {
449         MONO_ARCH_SAVE_REGS;
450
451         return mono_string_is_interned(str);
452 }
453
454 gunichar2 
455 ves_icall_System_String_get_Chars (MonoString *me, gint32 idx)
456 {
457         MONO_ARCH_SAVE_REGS;
458
459         if ((idx < 0) || (idx >= mono_string_length (me)))
460                 mono_raise_exception (mono_get_exception_index_out_of_range ());
461         return mono_string_chars(me)[idx];
462 }
463