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