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