2 * string-icalls.c: String internal calls for the corlib
5 * Patrik Torstensson (patrik.torstensson@labs2.com)
6 * Duncan Mak (duncan@ximian.com)
8 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
9 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
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>
26 /* Internal helper methods */
29 string_icall_is_in_array (MonoArray
*chars
, gint32 arraylength
, gunichar2 chr
);
31 /* This function is redirected to String.CreateString ()
32 by mono_marshal_get_native_wrapper () */
34 ves_icall_System_String_ctor_RedirectToCreateString (void)
36 g_assert_not_reached ();
40 ves_icall_System_String_InternalJoin (MonoString
*separator
, MonoArray
* value
, gint32 sindex
, gint32 count
)
55 insert
= mono_string_chars(separator
);
56 insertlen
= mono_string_length(separator
);
59 for (pos
= sindex
; pos
!= sindex
+ count
; pos
++) {
60 current
= mono_array_get (value
, MonoString
*, pos
);
62 length
+= mono_string_length (current
);
64 if (pos
< sindex
+ count
- 1)
68 ret
= mono_string_new_size( mono_domain_get (), length
);
69 dest
= mono_string_chars(ret
);
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
);
78 memcpy (dest
+ destpos
, src
, srclen
* sizeof(gunichar2
));
82 if (pos
< sindex
+ count
- 1) {
83 memcpy(dest
+ destpos
, insert
, insertlen
* sizeof(gunichar2
));
92 ves_icall_System_String_InternalCopyTo (MonoString
*me
, gint32 sindex
, MonoArray
*dest
, gint32 dindex
, gint32 count
)
94 gunichar2
*destptr
= (gunichar2
*) mono_array_addr(dest
, gunichar2
, dindex
);
95 gunichar2
*src
= mono_string_chars(me
);
99 memcpy(destptr
, src
+ sindex
, sizeof(gunichar2
) * count
);
102 /* System.StringSplitOptions */
104 STRINGSPLITOPTIONS_NONE
= 0,
105 STRINGSPLITOPTIONS_REMOVE_EMPTY_ENTRIES
= 1
106 } StringSplitOptions
;
109 ves_icall_System_String_InternalSplit (MonoString
*me
, MonoArray
*separator
, gint32 count
, gint32 options
)
111 static MonoClass
*String_array
;
115 gint32 arrsize
, srcsize
, splitsize
;
116 gint32 i
, lastpos
, arrpos
;
120 gunichar2
*tmpstrptr
;
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
);
128 MonoClass
*klass
= mono_array_class_get (mono_get_string_class (), 1);
129 mono_memory_barrier ();
130 String_array
= klass
;
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.
139 for (i
= 0; i
!= srcsize
&& splitsize
< count
; i
++) {
140 if (string_icall_is_in_array (separator
, arrsize
, src
[i
]))
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.
151 for (i
= 0; i
!= srcsize
&& splitsize
< count
; i
++) {
152 if (string_icall_is_in_array (separator
, arrsize
, src
[i
])) {
154 } else if (flag
== 0) {
162 /* Nothing but separators */
164 retarr
= mono_array_new_specific (mono_class_vtable (mono_domain_get (), String_array
), 0);
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
);
176 /* otherwise we have to filter out leading & trailing delims */
178 /* find first non-delim char */
179 for (; srcsize
!= 0; srcsize
--, src
++) {
180 if (!string_icall_is_in_array (separator
, arrsize
, src
[0]))
183 /* find last non-delim char */
184 for (; srcsize
!= 0; srcsize
--) {
185 if (!string_icall_is_in_array (separator
, arrsize
, src
[srcsize
- 1]))
188 tmpstr
= mono_string_new_size (mono_domain_get (), srcsize
);
189 tmpstrptr
= mono_string_chars (tmpstr
);
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
);
201 retarr
= mono_array_new_specific (mono_class_vtable (mono_domain_get (), String_array
), splitsize
);
203 for (i
= 0; i
!= srcsize
&& arrpos
!= splitsize
; i
++) {
204 if (string_icall_is_in_array (separator
, arrsize
, src
[i
])) {
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
);
211 memcpy (tmpstrptr
, src
+ lastpos
, tmpstrsize
* sizeof (gunichar2
));
212 mono_array_setref (retarr
, arrpos
, tmpstr
);
215 if (arrpos
== splitsize
- 1) {
216 /* Shortcut the last array element */
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
]))
226 if (count
> splitsize
) {
227 /* Since we have fewer results than our limit, we must remove
228 * trailing delimiters as well.
230 for (; srcsize
!= lastpos
+ 1 ; srcsize
--) {
231 if (!string_icall_is_in_array (separator
, arrsize
, src
[srcsize
- 1]))
237 tmpstrsize
= srcsize
- lastpos
;
238 tmpstr
= mono_string_new_size (mono_domain_get (), tmpstrsize
);
239 tmpstrptr
= mono_string_chars (tmpstr
);
241 memcpy (tmpstrptr
, src
+ lastpos
, tmpstrsize
* sizeof (gunichar2
));
242 mono_array_setref (retarr
, arrpos
, tmpstr
);
244 /* Loop will ALWAYS end here. Test criteria in the FOR loop is technically unnecessary. */
256 string_icall_is_in_array (MonoArray
*chars
, gint32 arraylength
, gunichar2 chr
)
261 for (arrpos
= 0; arrpos
!= arraylength
; arrpos
++) {
262 cmpchar
= mono_array_get(chars
, gunichar2
, arrpos
);
271 ves_icall_System_String_InternalTrim (MonoString
*me
, MonoArray
*chars
, gint32 typ
)
274 gunichar2
*src
, *dest
;
275 gint32 srclen
, newlen
, arrlen
;
276 gint32 i
, lenfirst
, lenlast
;
280 srclen
= mono_string_length(me
);
281 src
= mono_string_chars(me
);
282 arrlen
= mono_array_length(chars
);
287 if (0 == typ
|| 1 == typ
) {
288 for (i
= 0; i
!= srclen
; i
++) {
289 if (string_icall_is_in_array(chars
, arrlen
, src
[i
]))
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
]))
305 newlen
= srclen
- lenfirst
- lenlast
;
306 if (newlen
== srclen
)
309 ret
= mono_string_new_size( mono_domain_get (), newlen
);
310 dest
= mono_string_chars(ret
);
312 memcpy(dest
, src
+ lenfirst
, newlen
*sizeof(gunichar2
));
318 ves_icall_System_String_InternalLastIndexOfAny (MonoString
*me
, MonoArray
*anyOf
, gint32 sindex
, gint32 count
)
327 arraysize
= mono_array_length(anyOf
);
328 src
= mono_string_chars(me
);
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
) )
340 ves_icall_System_String_InternalPad (MonoString
*me
, gint32 width
, gunichar2 chr
, MonoBoolean right
)
351 srclen
= mono_string_length(me
);
352 src
= mono_string_chars(me
);
354 ret
= mono_string_new_size( mono_domain_get (), width
);
355 dest
= mono_string_chars(ret
);
356 fillcount
= width
- srclen
;
359 memcpy(dest
, src
, srclen
* sizeof(gunichar2
));
360 for (i
= srclen
; i
!= width
; i
++)
367 for (i
= 0; i
!= fillcount
; i
++)
370 memcpy(dest
+ fillcount
, src
, srclen
* sizeof(gunichar2
));
376 ves_icall_System_String_InternalAllocateStr (gint32 length
)
380 return mono_string_new_size(mono_domain_get (), length
);
384 ves_icall_System_String_InternalStrcpy_Str (MonoString
*dest
, gint32 destPos
, MonoString
*src
)
391 srcptr
= mono_string_chars (src
);
392 destptr
= mono_string_chars (dest
);
394 g_memmove (destptr
+ destPos
, srcptr
, mono_string_length(src
) * sizeof(gunichar2
));
398 ves_icall_System_String_InternalStrcpy_StrN (MonoString
*dest
, gint32 destPos
, MonoString
*src
, gint32 startPos
, gint32 count
)
405 srcptr
= mono_string_chars (src
);
406 destptr
= mono_string_chars (dest
);
407 g_memmove (destptr
+ destPos
, srcptr
+ startPos
, count
* sizeof(gunichar2
));
411 ves_icall_System_String_InternalStrcpy_Chars (MonoString
*dest
, gint32 destPos
, MonoArray
*src
)
418 srcptr
= mono_array_addr (src
, gunichar2
, 0);
419 destptr
= mono_string_chars (dest
);
421 g_memmove (destptr
+ destPos
, srcptr
, mono_array_length (src
) * sizeof(gunichar2
));
425 ves_icall_System_String_InternalStrcpy_CharsN (MonoString
*dest
, gint32 destPos
, MonoArray
*src
, gint32 startPos
, gint32 count
)
432 srcptr
= mono_array_addr (src
, gunichar2
, 0);
433 destptr
= mono_string_chars (dest
);
435 g_memmove (destptr
+ destPos
, srcptr
+ startPos
, count
* sizeof(gunichar2
));
439 ves_icall_System_String_InternalIntern (MonoString
*str
)
443 return mono_string_intern(str
);
447 ves_icall_System_String_InternalIsInterned (MonoString
*str
)
451 return mono_string_is_interned(str
);
455 ves_icall_System_String_get_Chars (MonoString
*me
, gint32 idx
)
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
];