2003-12-20 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mono / metadata / locales.c
1 /*
2  * locales.c: Culture-sensitive handling
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *      Mohammad DAMT (mdamt@cdl2000.com)
7  *
8  * (C) 2003 Ximian, Inc.
9  * (C) 2003 PT Cakram Datalingga Duaribu  http://www.cdl2000.com
10  */
11
12 #include <config.h>
13 #include <glib.h>
14 #include <string.h>
15
16 #include <mono/metadata/debug-helpers.h>
17 #include <mono/metadata/object.h>
18 #include <mono/metadata/appdomain.h>
19 #include <mono/metadata/exception.h>
20 #include <mono/metadata/monitor.h>
21 #include <mono/metadata/locales.h>
22
23 #undef DEBUG
24
25 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
26                                              gint32 options);
27 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
28                                         gint32 len1, MonoString *str2,
29                                         gint32 off2, gint32 len2,
30                                         gint32 options);
31 static MonoString *string_invariant_replace (MonoString *me,
32                                              MonoString *oldValue,
33                                              MonoString *newValue);
34 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
35                                         gint32 count, MonoString *value,
36                                         MonoBoolean first);
37 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
38                                              gint32 count, gunichar2 value,
39                                              MonoBoolean first);
40 static MonoString *string_invariant_tolower (MonoString *this);
41 static MonoString *string_invariant_toupper (MonoString *this);
42
43 #ifdef HAVE_ICU
44
45 #include <unicode/utypes.h>
46 #include <unicode/ustring.h>
47 #include <unicode/ures.h>
48 #include <unicode/ucol.h>
49 #include <unicode/usearch.h>
50
51 static MonoString *monostring_from_resource_index (const UResourceBundle *bundle, int32_t idx)
52 {
53         gunichar2 *res_str;
54         int32_t res_strlen;
55         UErrorCode ec;
56         
57         ec=U_ZERO_ERROR;
58         res_str=(gunichar2 *)ures_getStringByIndex (bundle, idx, &res_strlen,
59                                                    &ec);
60         if(U_FAILURE (ec)) {
61                 return(NULL);
62         }
63
64         return(mono_string_from_utf16 (res_str));
65 }
66
67 static UResourceBundle *open_subbundle (const UResourceBundle *bundle,
68                                         const char *name, int32_t req_count)
69 {
70         UResourceBundle *subbundle;
71         UErrorCode ec;
72         int32_t count;
73         
74         ec=U_ZERO_ERROR;
75         subbundle=ures_getByKey (bundle, name, NULL, &ec);
76         if(U_FAILURE (ec)) {
77                 /* Couldn't find the subbundle */
78                 return(NULL);
79         }
80         
81         count=ures_countArrayItems (bundle, name, &ec);
82         if(U_FAILURE (ec)) {
83                 /* Couldn't count the subbundle */
84                 ures_close (subbundle);
85                 return(NULL);
86         }
87         
88         if(count!=req_count) {
89                 /* Bummer */
90                 ures_close (subbundle);
91                 return(NULL);
92         }
93
94         return(subbundle);
95 }
96
97 static MonoArray *build_array (const UResourceBundle *bundle,
98                                const char *resname, int32_t req_count)
99 {
100         MonoArray *arr=NULL;
101         UResourceBundle *subbundle;
102         int i;
103         
104         subbundle=open_subbundle (bundle, resname, req_count);
105         if(subbundle!=NULL) {
106                 arr=mono_array_new(mono_domain_get (),
107                                    mono_defaults.string_class, req_count);
108                 
109                 for(i=0; i<req_count; i++) {
110                         mono_array_set(arr, MonoString *, i, monostring_from_resource_index (subbundle, i));
111                 }
112
113                 ures_close (subbundle);
114         }
115
116         return(arr);
117 }
118
119 static MonoDateTimeFormatInfo *create_DateTimeFormat (const char *locale)
120 {
121         MonoDateTimeFormatInfo *new_dtf;
122         MonoClass *class;
123         UResourceBundle *bundle, *subbundle;
124         UErrorCode ec;
125         
126         class=mono_class_from_name (mono_defaults.corlib,
127                                     "System.Globalization",
128                                     "DateTimeFormatInfo");
129         new_dtf=(MonoDateTimeFormatInfo *)mono_object_new (mono_domain_get (),
130                                                            class);
131         mono_runtime_object_init ((MonoObject *)new_dtf);
132         
133         ec=U_ZERO_ERROR;
134
135         bundle=ures_open (NULL, locale, &ec);
136         if(U_FAILURE (ec)) {
137                 goto error1;
138         }
139         
140         /* AM/PM markers */
141         subbundle=open_subbundle (bundle, "AmPmMarkers", 2);
142         if(subbundle!=NULL) {
143                 new_dtf->AMDesignator=monostring_from_resource_index (subbundle, 0);
144                 new_dtf->PMDesignator=monostring_from_resource_index (subbundle, 1);
145                 
146                 ures_close (subbundle);
147         }
148         
149         /* Date/Time patterns.  Don't set FullDateTimePattern.  As it
150          * seems to always default to LongDatePattern + " " +
151          * LongTimePattern, let the property accessor deal with it.
152          */
153         subbundle=open_subbundle (bundle, "DateTimePatterns", 9);
154         if(subbundle!=NULL) {
155                 new_dtf->ShortDatePattern=monostring_from_resource_index (subbundle, 7);
156                 new_dtf->LongDatePattern=monostring_from_resource_index (subbundle, 5);
157                 new_dtf->ShortTimePattern=monostring_from_resource_index (subbundle, 3);
158                 new_dtf->LongTimePattern=monostring_from_resource_index (subbundle, 2);
159
160                 /* RFC1123Pattern, SortableDateTimePattern and
161                  * UniversalSortableDateTimePattern all seem to be
162                  * constant, and all the same as the invariant default
163                  * set in the ctor
164                  */
165         
166                 ures_close (subbundle);
167         }
168         
169 #if 0
170         /* Not sure what to do with these yet, so leave them set to
171          * the invariant default
172          */
173         set_field_string (new_dtf, "_DateSeparator", str);
174         set_field_string (new_dtf, "_TimeSeparator", str);
175         set_field_string (new_dtf, "_MonthDayPattern", str);
176         set_field_string (new_dtf, "_YearMonthPattern", str);
177 #endif
178
179         /* Day names.  Luckily both ICU and .net start Sunday at index 0 */
180         new_dtf->DayNames=build_array (bundle, "DayNames", 7);
181
182         /* Abbreviated day names */
183         new_dtf->AbbreviatedDayNames=build_array (bundle, "DayAbbreviations",
184                                                   7);
185
186         /* Month names */
187         new_dtf->MonthNames=build_array (bundle, "MonthNames", 12);
188         
189         /* Abbreviated month names */
190         new_dtf->AbbreviatedMonthNames=build_array (bundle,
191                                                     "MonthAbbreviations", 12);
192
193         /* TODO: DayOfWeek _FirstDayOfWeek, Calendar _Calendar, CalendarWeekRule _CalendarWeekRule */
194
195         ures_close (bundle);
196 error1:
197         return(new_dtf);
198 }
199
200 static MonoNumberFormatInfo *create_NumberFormat (const char *locale)
201 {
202         MonoNumberFormatInfo *new_nf;
203         MonoClass *class;
204         MonoMethodDesc* methodDesc;
205         MonoMethod *method;
206         UResourceBundle *bundle, *subbundle, *table_entries;
207         UErrorCode ec;
208         int32_t count;
209         static char country [7]; //FIXME
210         const UChar *res_str;
211         int32_t res_strlen;
212
213         class=mono_class_from_name (mono_defaults.corlib,
214                                     "System.Globalization",
215                                     "NumberFormatInfo");
216         new_nf=(MonoNumberFormatInfo *)mono_object_new (mono_domain_get (),
217                                                         class);
218         mono_runtime_object_init ((MonoObject *)new_nf);
219
220         ec=U_ZERO_ERROR;
221
222         bundle=ures_open (NULL, locale, &ec);
223         if(U_FAILURE (ec)) {
224                 goto error1;
225         }
226
227         /* Number Elements */
228         ec=U_ZERO_ERROR;
229         subbundle=ures_getByKey (bundle, "NumberElements", NULL, &ec);
230         if(U_FAILURE (ec)) {
231                 /* Couldn't find the subbundle */
232                 goto error1;
233         }
234                 
235         count=ures_countArrayItems (bundle, "NumberElements", &ec);
236         if(U_FAILURE (ec)) {
237                 /* Couldn't count the subbundle */
238                 ures_close (subbundle);
239                 goto error1;
240         }
241
242         if(subbundle!=NULL) {
243                 new_nf->numberDecimalSeparator=monostring_from_resource_index (subbundle, 0);
244                 new_nf->numberGroupSeparator=monostring_from_resource_index (subbundle, 1);
245                 new_nf->percentDecimalSeparator=monostring_from_resource_index (subbundle, 0);
246                 new_nf->percentGroupSeparator=monostring_from_resource_index (subbundle, 1);
247                 new_nf->percentSymbol=monostring_from_resource_index (subbundle, 3);
248                 new_nf->zeroPattern=monostring_from_resource_index (subbundle, 4);
249                 new_nf->digitPattern=monostring_from_resource_index (subbundle, 5);
250                 new_nf->negativeSign=monostring_from_resource_index (subbundle, 6);
251                 new_nf->perMilleSymbol=monostring_from_resource_index (subbundle, 8);
252                 new_nf->positiveInfinitySymbol=monostring_from_resource_index (subbundle, 9);
253                 /* we dont have this in CLDR, so copy it from positiveInfinitySymbol */
254                 new_nf->negativeInfinitySymbol=monostring_from_resource_index (subbundle, 9);
255                 new_nf->naNSymbol=monostring_from_resource_index (subbundle, 10);
256                 new_nf->currencyDecimalSeparator=monostring_from_resource_index (subbundle, 0);
257                 new_nf->currencyGroupSeparator=monostring_from_resource_index (subbundle, 1);
258
259                 ures_close (subbundle);
260         }
261  
262         /* get country name */
263         ec = U_ZERO_ERROR;
264         uloc_getCountry (locale, country, sizeof (country), &ec);
265         if (U_SUCCESS (ec)) {                                           
266                 ec = U_ZERO_ERROR;
267                 /* find country name in root.CurrencyMap */
268                 subbundle = ures_getByKey (bundle, "CurrencyMap", NULL, &ec);
269                 if (U_SUCCESS (ec)) {
270                         ec = U_ZERO_ERROR;
271                         /* get currency id for specified country */
272                         table_entries = ures_getByKey (subbundle, country, NULL, &ec);
273                         if (U_SUCCESS (ec)) {
274                                 ures_close (subbundle);
275                                 ec = U_ZERO_ERROR;
276                                 
277                                 res_str = ures_getStringByIndex (
278                                         table_entries, 0, &res_strlen, &ec);                            
279                                 if(U_SUCCESS (ec)) {
280                                         /* now we have currency id string */
281                                         ures_close (table_entries);
282                                         ec = U_ZERO_ERROR;
283                                         u_UCharsToChars (res_str, country,
284                                                          sizeof (country));
285                                         if(U_SUCCESS (ec)) {
286                                                 ec = U_ZERO_ERROR;
287                                                 /* find currency string in locale data */
288                                                 subbundle = ures_getByKey (
289                                                         bundle, "Currencies",
290                                                         NULL, &ec);
291                                                         
292                                                 if (U_SUCCESS (ec)) {
293                                                         ec = U_ZERO_ERROR;
294                                                         /* find currency symbol under specified currency id */
295                                                         table_entries = ures_getByKey (subbundle, country, NULL, &ec);
296                                                         if (U_SUCCESS (ec)) {
297                                                                 /* get the first string only, 
298                                                                  * the second is international currency symbol (not used)*/
299                                                                 new_nf->currencySymbol=monostring_from_resource_index (table_entries, 0);
300                                                                 ures_close (table_entries);
301                                                         }
302                                                         ures_close (subbundle);
303                                                 }               
304                                         }
305                                 }
306                         }
307                 }
308         }
309
310         subbundle=open_subbundle (bundle, "NumberPatterns", 4);
311         if(subbundle!=NULL) {
312                 new_nf->decimalFormats=monostring_from_resource_index (subbundle, 0);
313                 new_nf->currencyFormats=monostring_from_resource_index (subbundle, 1);
314                 new_nf->percentFormats=monostring_from_resource_index (subbundle, 2);
315                 ures_close (subbundle);
316                 
317                 /* calls InitPatterns to parse the patterns
318                  */
319                 methodDesc = mono_method_desc_new (
320                         "System.Globalization.NumberFormatInfo:InitPatterns()",
321                         TRUE);
322                 method = mono_method_desc_search_in_class (methodDesc, class);
323                 if(method!=NULL) {
324                         mono_runtime_invoke (method, new_nf, NULL, NULL);
325                 } else {
326                         g_warning (G_GNUC_PRETTY_FUNCTION ": Runtime mismatch with class lib! (Looking for System.Globalization.NumberFormatInfo:InitPatterns())");
327                 }
328         }
329
330         ures_close (bundle);
331 error1:
332         return(new_nf);
333 }
334
335 static char *mono_string_to_icu_locale (MonoString *locale)
336 {
337         UErrorCode ec;
338         char *passed_locale, *icu_locale=NULL;
339         int32_t loc_len, ret;
340
341         passed_locale=mono_string_to_utf8 (locale);
342         
343         ec=U_ZERO_ERROR;
344         ret=uloc_getName (passed_locale, NULL, 0, &ec);
345         if(ec==U_BUFFER_OVERFLOW_ERROR) {
346                 ec=U_ZERO_ERROR;
347                 loc_len=ret+1;
348                 icu_locale=(char *)g_malloc0 (sizeof(char)*loc_len);
349                 ret=uloc_getName (passed_locale, icu_locale, loc_len, &ec);
350         }
351         g_free (passed_locale);
352         
353         return(icu_locale);
354 }
355
356 void ves_icall_System_Globalization_CultureInfo_construct_internal_locale (MonoCultureInfo *this, MonoString *locale)
357 {
358         UChar *ustr;
359         char *str;
360         UErrorCode ec;
361         char *icu_locale;
362         int32_t str_len, ret;
363         
364         MONO_ARCH_SAVE_REGS;
365         
366         icu_locale=mono_string_to_icu_locale (locale);
367         if(icu_locale==NULL) {
368                 /* Something went wrong */
369                 mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "SystemException"));
370                 return;
371         }
372         
373         /* Fill in the static fields */
374
375         /* TODO: Calendar, InstalledUICulture, OptionalCalendars,
376          * TextInfo
377          */
378
379         str_len=256;    /* Should be big enough for anything */
380         str=(char *)g_malloc0 (sizeof(char)*str_len);
381         ustr=(UChar *)g_malloc0 (sizeof(UChar)*str_len);
382         
383         ec=U_ZERO_ERROR;
384         
385         ret=uloc_getDisplayName (icu_locale, "en", ustr, str_len, &ec);
386         if(U_SUCCESS (ec) && ret<str_len) {
387                 this->englishname=mono_string_from_utf16 ((gunichar2 *)ustr);
388         }
389         
390         ret=uloc_getDisplayName (icu_locale, uloc_getDefault (), ustr, str_len,
391                                  &ec);
392         if(U_SUCCESS (ec) && ret<str_len) {
393                 this->displayname=mono_string_from_utf16 ((gunichar2 *)ustr);
394         }
395         
396         ret=uloc_getDisplayName (icu_locale, icu_locale, ustr, str_len, &ec);
397         if(U_SUCCESS (ec) && ret<str_len) {
398                 this->nativename=mono_string_from_utf16 ((gunichar2 *)ustr);
399         }
400
401         this->iso3lang=mono_string_new_wrapper (uloc_getISO3Language (icu_locale));
402
403         ret=uloc_getLanguage (icu_locale, str, str_len, &ec);
404         if(U_SUCCESS (ec) && ret<str_len) {
405                 this->iso2lang=mono_string_new_wrapper (str);
406         }
407
408         this->datetime_format=create_DateTimeFormat (icu_locale);
409         this->number_format=create_NumberFormat (icu_locale);
410  
411         g_free (str);
412         g_free (ustr);
413         g_free (icu_locale);
414 }
415
416 void ves_icall_System_Globalization_CompareInfo_construct_compareinfo (MonoCompareInfo *comp, MonoString *locale)
417 {
418         UCollator *coll;
419         UErrorCode ec;
420         char *icu_locale;
421         
422         MONO_ARCH_SAVE_REGS;
423         
424 #ifdef DEBUG
425         g_message (G_GNUC_PRETTY_FUNCTION ": Constructing collator for locale [%s]", mono_string_to_utf8 (locale));
426 #endif
427
428         icu_locale=mono_string_to_icu_locale (locale);
429         if(icu_locale==NULL) {
430                 /* Something went wrong */
431                 mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "SystemException"));
432                 return;
433         }
434
435         ec=U_ZERO_ERROR;
436         coll=ucol_open (icu_locale, &ec);
437         if(U_SUCCESS (ec)) {
438                 comp->ICU_collator=coll;
439         } else {
440                 comp->ICU_collator=NULL;
441         }
442
443         g_free (icu_locale);
444 }
445
446 /* Set up the collator to reflect the options required.  Some of these
447  * options clash, as they adjust the collator strength level.  Try to
448  * make later checks reduce the strength level, and attempt to take
449  * previous options into account.
450  *
451  * Don't bother to check the error returns when setting the
452  * attributes, as a failure here is hardly grounds to error out.
453  */
454 static void set_collator_options (UCollator *coll, gint32 options)
455 {
456         UErrorCode ec=U_ZERO_ERROR;
457         
458         /* Set up the defaults */
459         ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE,
460                            &ec);
461         ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_OFF, &ec);
462         
463         /* Do this first so other options will override the quaternary
464          * level strength setting if necessary
465          */
466         if(!(options & CompareOptions_IgnoreKanaType)) {
467                 ucol_setAttribute (coll, UCOL_HIRAGANA_QUATERNARY_MODE,
468                                    UCOL_ON, &ec);
469                 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_QUATERNARY, &ec);
470         }
471
472         /* Word sort, the default */
473         if(!(options & CompareOptions_StringSort)) {
474                 ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING,
475                                    UCOL_SHIFTED, &ec);
476                 /* Tertiary strength is the default, but it might have
477                  * been set to quaternary above.  (We don't want that
478                  * here, because that will order all the punctuation
479                  * first instead of just ignoring it.)
480                  *
481                  * Unfortunately, tertiary strength with
482                  * ALTERNATE_HANDLING==SHIFTED means that '/' and '@'
483                  * compare to equal, which has the nasty side effect
484                  * of killing mcs :-( (We can't specify a
485                  * culture-insensitive compare, because
486                  * String.StartsWith doesn't have that option.)
487                  *
488                  * ALTERNATE_HANDLING==SHIFTED is needed to accomplish
489                  * the word-sorting-ignoring-punctuation feature.  So
490                  * we have to live with the slightly mis-ordered
491                  * punctuation and a working mcs...
492                  */
493                 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_QUATERNARY, &ec);
494         }
495
496         if(options & CompareOptions_IgnoreCase) {
497                 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_SECONDARY, &ec);
498         }
499
500         if(options & CompareOptions_IgnoreWidth) {
501                 /* Kana width is a tertiary strength difference.  This
502                  * will totally break the !IgnoreKanaType option
503                  */
504                 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_SECONDARY, &ec);
505         }
506                 
507         if(options & CompareOptions_IgnoreNonSpace) {
508                 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_PRIMARY, &ec);
509                 /* We can still compare case even when just checking
510                  * primary strength
511                  */
512                 if(!(options & CompareOptions_IgnoreCase) ||
513                    !(options & CompareOptions_IgnoreWidth)) {
514                         /* Not sure if CASE_LEVEL handles kana width
515                          */
516                         ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_ON,
517                                            &ec);
518                 }
519         }
520
521         if(options & CompareOptions_IgnoreSymbols) {
522                 /* Don't know what to do here */
523         }
524
525         if(options == CompareOptions_Ordinal) {
526                 /* This one is handled elsewhere */
527         }
528 }
529
530 gint32 ves_icall_System_Globalization_CompareInfo_internal_compare (MonoCompareInfo *this, MonoString *str1, gint32 off1, gint32 len1, MonoString *str2, gint32 off2, gint32 len2, gint32 options)
531 {
532         UCollator *coll;
533         UCollationResult result;
534         
535         MONO_ARCH_SAVE_REGS;
536         
537 #ifdef DEBUG
538         g_message (G_GNUC_PRETTY_FUNCTION ": Comparing [%s] and [%s]", mono_string_to_utf8 (str1), mono_string_to_utf8 (str2));
539 #endif
540
541         coll=this->ICU_collator;
542
543 #ifdef DEBUG
544         g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", this->lcid);
545 #endif
546         
547         if(coll==NULL || this->lcid==0x007F ||
548            options & CompareOptions_Ordinal) {
549 #ifdef DEBUG
550                 g_message (G_GNUC_PRETTY_FUNCTION ": No collator or invariant, using shortcut");
551 #endif
552
553                 return(string_invariant_compare (str1, off1, len1, str2, off2,
554                                                  len2, options));
555         }
556         
557         mono_monitor_try_enter ((MonoObject *)this, INFINITE);
558         
559         set_collator_options (coll, options);
560                         
561         result=ucol_strcoll (coll, mono_string_chars (str1)+off1, len1,
562                              mono_string_chars (str2)+off2, len2);
563
564         mono_monitor_exit ((MonoObject *)this);
565
566 #ifdef DEBUG
567         g_message (G_GNUC_PRETTY_FUNCTION ": Comparison of [%s] and [%s] returning %d", mono_string_to_utf8 (str1), mono_string_to_utf8 (str2), result);
568 #endif
569         
570         return(result);
571 }
572
573 void ves_icall_System_Globalization_CompareInfo_free_internal_collator (MonoCompareInfo *this)
574 {
575         UCollator *coll;
576         
577         MONO_ARCH_SAVE_REGS;
578         
579         coll=this->ICU_collator;
580         if(coll!=NULL) {
581                 ucol_close (coll);
582         }
583 }
584
585 void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoCompareInfo *this, MonoSortKey *key, MonoString *source, gint32 options)
586 {
587         UCollator *coll;
588         MonoArray *arr;
589         char *keybuf;
590         int32_t keylen, i;
591         
592         MONO_ARCH_SAVE_REGS;
593         
594         coll=this->ICU_collator;
595         if(coll==NULL) {
596                 mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "SystemException"));
597                 return;
598         }
599         
600         mono_monitor_try_enter ((MonoObject *)this, INFINITE);
601         
602         set_collator_options (coll, options);
603
604         keylen=ucol_getSortKey (coll, mono_string_chars (source), -1, NULL, 0);
605         keybuf=g_malloc (sizeof(char)* keylen);
606         ucol_getSortKey (coll, mono_string_chars (source), -1, keybuf, keylen);
607
608         mono_monitor_exit ((MonoObject *)this);
609         
610         arr=mono_array_new (mono_domain_get (), mono_defaults.byte_class,
611                             keylen);
612         for(i=0; i<keylen; i++) {
613                 mono_array_set (arr, guint8, i, keybuf[i]);
614         }
615         
616         key->key=arr;
617
618         g_free (keybuf);
619 }
620
621 int ves_icall_System_Globalization_CompareInfo_internal_index (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, MonoString *value, gint32 options, MonoBoolean first)
622 {
623         UCollator *coll;
624         UChar *usrcstr;
625         UErrorCode ec;
626         UStringSearch *search;
627         int32_t pos= -1;
628         
629         MONO_ARCH_SAVE_REGS;
630         
631 #ifdef DEBUG
632         g_message (G_GNUC_PRETTY_FUNCTION ": Finding %s [%s] in [%s] (sindex %d,count %d)", first?"first":"last", mono_string_to_utf8 (value), mono_string_to_utf8 (source), sindex, count);
633 #endif
634
635         coll=this->ICU_collator;
636
637 #ifdef DEBUG
638         g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", this->lcid);
639 #endif
640         
641         if(coll==NULL || this->lcid==0x007F ||
642            options & CompareOptions_Ordinal) {
643 #ifdef DEBUG
644                 g_message (G_GNUC_PRETTY_FUNCTION ": No collator or invariant, using shortcut");
645 #endif
646
647                 return(string_invariant_indexof (source, sindex, count, value,
648                                                  first));
649         }
650         
651         usrcstr=g_malloc0 (sizeof(UChar)*(count+1));
652         if(first) {
653                 memcpy (usrcstr, mono_string_chars (source)+sindex,
654                         sizeof(UChar)*count);
655         } else {
656                 memcpy (usrcstr, mono_string_chars (source)+sindex-count+1,
657                         sizeof(UChar)*count);
658         }
659         
660         mono_monitor_try_enter ((MonoObject *)this, INFINITE);
661         
662         ec=U_ZERO_ERROR;
663         
664         /* Need to set the collator to a fairly weak level, so that it
665          * treats characters that can be written differently as
666          * identical (eg "ß" and "ss", "æ" and "ae" or "ä" etc.)  Note
667          * that this means that the search string and the original
668          * text might have differing lengths.
669          */
670         ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_PRIMARY, &ec);
671
672         /* Still notice case differences though (normally a tertiary
673          * difference)
674          */
675         ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_ON, &ec);
676
677         /* Don't ignore some codepoints */
678         ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE,
679                            &ec);
680                         
681         search=usearch_openFromCollator (mono_string_chars (value), -1, usrcstr, -1, coll, NULL,
682                                          &ec);
683         if(U_SUCCESS (ec)) {
684                 if(first) {
685                         pos=usearch_first (search, &ec);
686                 } else {
687                         pos=usearch_last (search, &ec);
688                 }
689
690                 if(pos!=USEARCH_DONE) {
691 #ifdef DEBUG
692                         g_message (G_GNUC_PRETTY_FUNCTION
693                                    ": Got match at %d (sindex %d) len %d", pos,
694                                    sindex, usearch_getMatchedLength (search));
695 #endif
696                         if(sindex>0) {
697                                 if(first) {
698                                         pos+=sindex;
699                                 } else {
700                                         pos+=(sindex-count+1);
701                                 }
702                         }
703                 }
704         } else {
705                 g_message (G_GNUC_PRETTY_FUNCTION ": usearch_open error: %s",
706                            u_errorName (ec));
707         }
708
709         usearch_close (search);
710         
711         mono_monitor_exit ((MonoObject *)this);
712         
713         g_free (usrcstr);
714
715         return(pos);
716 }
717
718 int ves_icall_System_Globalization_CompareInfo_internal_index_char (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, gunichar2 value, gint32 options, MonoBoolean first)
719 {
720         UCollator *coll;
721         UChar *usrcstr, uvalstr[2]={0, 0};
722         UErrorCode ec;
723         UStringSearch *search;
724         int32_t pos= -1;
725         
726         MONO_ARCH_SAVE_REGS;
727         
728 #ifdef DEBUG
729         g_message (G_GNUC_PRETTY_FUNCTION ": Finding %s 0x%0x in [%s] (sindex %d,count %d)", first?"first":"last", value, mono_string_to_utf8 (source), sindex, count);
730 #endif
731
732         coll=this->ICU_collator;
733
734 #ifdef DEBUG
735         g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", this->lcid);
736 #endif
737         
738         if(coll==NULL || this->lcid==0x007F ||
739            options & CompareOptions_Ordinal) {
740 #ifdef DEBUG
741                 g_message (G_GNUC_PRETTY_FUNCTION ": No collator or invariant, using shortcut");
742 #endif
743
744                 return(string_invariant_indexof_char (source, sindex, count,
745                                                       value, first));
746         }
747         
748         usrcstr=g_malloc0 (sizeof(UChar)*(count+1));
749         if(first) {
750                 memcpy (usrcstr, mono_string_chars (source)+sindex,
751                         sizeof(UChar)*count);
752         } else {
753                 memcpy (usrcstr, mono_string_chars (source)+sindex-count+1,
754                         sizeof(UChar)*count);
755         }
756         uvalstr[0]=value;
757         
758         mono_monitor_try_enter ((MonoObject *)this, INFINITE);
759         
760         ec=U_ZERO_ERROR;
761         
762         /* Need to set the collator to a fairly weak level, so that it
763          * treats characters that can be written differently as
764          * identical (eg "ß" and "ss", "æ" and "ae" or "ä" etc.)  Note
765          * that this means that the search string and the original
766          * text might have differing lengths.
767          */
768         ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_PRIMARY, &ec);
769
770         /* Still notice case differences though (normally a tertiary
771          * difference)
772          */
773         ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_ON, &ec);
774
775         /* Don't ignore some codepoints */
776         ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE,
777                            &ec);
778                         
779         search=usearch_openFromCollator (uvalstr, -1, usrcstr, -1, coll, NULL,
780                                          &ec);
781         if(U_SUCCESS (ec)) {
782                 if(first) {
783                         pos=usearch_first (search, &ec);
784                 } else {
785                         pos=usearch_last (search, &ec);
786                 }
787
788                 if(pos!=USEARCH_DONE) {
789 #ifdef DEBUG
790                         g_message (G_GNUC_PRETTY_FUNCTION
791                                    ": Got match at %d (sindex %d) len %d", pos,
792                                    sindex, usearch_getMatchedLength (search));
793 #endif
794                         if(sindex>0) {
795                                 if(first) {
796                                         pos+=sindex;
797                                 } else {
798                                         pos+=(sindex-count+1);
799                                 }
800                         }
801                 }
802         } else {
803                 g_message (G_GNUC_PRETTY_FUNCTION ": usearch_open error: %s",
804                            u_errorName (ec));
805         }
806
807         usearch_close (search);
808         
809         mono_monitor_exit ((MonoObject *)this);
810         
811         g_free (uvalstr);
812
813         return(pos);
814 }
815
816 int ves_icall_System_Threading_Thread_current_lcid (void)
817 {
818         MONO_ARCH_SAVE_REGS;
819         
820         return(uloc_getLCID (uloc_getDefault ()));
821 }
822
823 MonoString *ves_icall_System_String_InternalReplace_Str_Comp (MonoString *this, MonoString *old, MonoString *new, MonoCompareInfo *comp)
824 {
825         MonoString *ret=NULL;
826         UCollator *coll;
827         UErrorCode ec;
828         UStringSearch *search;
829         
830         MONO_ARCH_SAVE_REGS;
831         
832 #ifdef DEBUG
833         g_message (G_GNUC_PRETTY_FUNCTION ": Replacing [%s] with [%s] in [%s]", mono_string_to_utf8 (old), mono_string_to_utf8 (new), mono_string_to_utf8 (this));
834 #endif
835
836         coll=comp->ICU_collator;
837
838 #ifdef DEBUG
839         g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", comp->lcid);
840 #endif
841         
842         if(coll==NULL || comp->lcid==0x007F) {
843 #ifdef DEBUG
844                 g_message (G_GNUC_PRETTY_FUNCTION ": No collator or invariant, using shortcut");
845 #endif
846
847                 return(string_invariant_replace (this, old, new));
848         }
849         
850         mono_monitor_try_enter ((MonoObject *)comp, INFINITE);
851         
852         ec=U_ZERO_ERROR;
853         
854         /* Need to set the collator to a fairly weak level, so that it
855          * treats characters that can be written differently as
856          * identical (eg "ß" and "ss", "æ" and "ae" or "ä" etc.)  Note
857          * that this means that the search string and the original
858          * text might have differing lengths.
859          */
860         ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_PRIMARY, &ec);
861
862         /* Still notice case differences though (normally a tertiary
863          * difference)
864          */
865         ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_ON, &ec);
866
867         /* Don't ignore some codepoints */
868         ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE,
869                            &ec);
870                         
871         search=usearch_openFromCollator (mono_string_chars (old), -1,
872                                          mono_string_chars (this), -1, coll,
873                                          NULL, &ec);
874         if(U_SUCCESS (ec)) {
875                 int pos, oldpos, len_delta=0;
876                 int32_t newstr_len=mono_string_length (new);
877                 UChar *uret;
878                 
879                 for(pos=usearch_first (search, &ec);
880                     pos!=USEARCH_DONE;
881                     pos=usearch_next (search, &ec)) {
882 #ifdef DEBUG
883                         g_message (G_GNUC_PRETTY_FUNCTION
884                                    ": Got match at %d len %d", pos,
885                                    usearch_getMatchedLength (search));
886 #endif
887
888                         len_delta += (newstr_len -
889                                       usearch_getMatchedLength (search));
890                 }
891 #ifdef DEBUG
892                 g_message (G_GNUC_PRETTY_FUNCTION
893                            ": New string length is %d (delta %d)",
894                            mono_string_length (this)+len_delta, len_delta);
895 #endif
896                 
897                 uret=(UChar *)g_malloc0 (sizeof(UChar) * (mono_string_length (this)+len_delta+2));
898                 
899                 for(oldpos=0, pos=usearch_first (search, &ec);
900                     pos!=USEARCH_DONE;
901                     pos=usearch_next (search, &ec)) {
902                         /* Add the unmatched text */
903                         u_strncat (uret, mono_string_chars (this)+oldpos,
904                                    pos-oldpos);
905                         /* Then the replacement */
906                         u_strcat (uret, mono_string_chars (new));
907                         oldpos=pos+usearch_getMatchedLength (search);
908                 }
909                 
910                 /* Finish off with the trailing unmatched text */
911                 u_strcat (uret, mono_string_chars (this)+oldpos);
912
913                 ret=mono_string_from_utf16 ((gunichar2 *)uret);
914         } else {
915                 g_message (G_GNUC_PRETTY_FUNCTION ": usearch_open error: %s",
916                            u_errorName (ec));
917         }
918
919         usearch_close (search);
920         
921         mono_monitor_exit ((MonoObject *)comp);
922         
923 #ifdef DEBUG
924         g_message (G_GNUC_PRETTY_FUNCTION ": Replacing [%s] with [%s] in [%s] returns [%s]", mono_string_to_utf8 (old), mono_string_to_utf8 (new), mono_string_to_utf8 (this), mono_string_to_utf8 (ret));
925 #endif
926         
927         return(ret);
928 }
929
930 MonoString *ves_icall_System_String_InternalToLower_Comp (MonoString *this, MonoCultureInfo *cult)
931 {
932         MonoString *ret;
933         UChar *udest;
934         UErrorCode ec;
935         char *icu_loc;
936         int32_t len;
937         
938 #ifdef DEBUG
939         g_message (G_GNUC_PRETTY_FUNCTION ": [%s]",
940                    mono_string_to_utf8 (this));
941 #endif
942
943 #ifdef DEBUG
944         g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", cult->lcid);
945 #endif
946
947         if(cult->lcid==0x007F) {
948 #ifdef DEBUG
949                 g_message (G_GNUC_PRETTY_FUNCTION
950                            ": Invariant, using shortcut");
951 #endif
952
953                 return(string_invariant_tolower (this));
954         }
955
956         icu_loc=mono_string_to_icu_locale (cult->icu_name);
957         if(icu_loc==NULL) {
958                 mono_raise_exception ((MonoException *)mono_exception_from_name (mono_defaults.corlib, "System", "SystemException"));
959                 return(NULL);
960         }
961         
962         udest=(UChar *)g_malloc0 (sizeof(UChar)*(mono_string_length (this)+1));
963         
964         /* According to the docs, this might result in a longer or
965          * shorter string than we started with...
966          */
967
968         ec=U_ZERO_ERROR;
969         len=u_strToLower (udest, mono_string_length (this)+1,
970                           mono_string_chars (this), -1, icu_loc, &ec);
971         if(ec==U_BUFFER_OVERFLOW_ERROR ||
972            ec==U_STRING_NOT_TERMINATED_WARNING) {
973                 g_free (udest);
974                 udest=(UChar *)g_malloc0 (sizeof(UChar)*(len+1));
975                 len=u_strToLower (udest, len+1, mono_string_chars (this), -1,
976                                   icu_loc, &ec);
977         }
978
979         if(U_SUCCESS (ec)) {
980                 ret=mono_string_from_utf16 ((gunichar2 *)udest);
981         } else {
982                 g_message (G_GNUC_PRETTY_FUNCTION ": u_strToLower error: %s",
983                            u_errorName (ec));
984                 /* return something */
985                 ret=this;
986         }
987         
988         g_free (icu_loc);
989         g_free (udest);
990         
991 #ifdef DEBUG
992         g_message (G_GNUC_PRETTY_FUNCTION ": returning [%s]",
993                    mono_string_to_utf8 (ret));
994 #endif
995
996         return(ret);
997 }
998
999 MonoString *ves_icall_System_String_InternalToUpper_Comp (MonoString *this, MonoCultureInfo *cult)
1000 {
1001         MonoString *ret;
1002         UChar *udest;
1003         UErrorCode ec;
1004         char *icu_loc;
1005         int32_t len;
1006         
1007 #ifdef DEBUG
1008         g_message (G_GNUC_PRETTY_FUNCTION ": [%s]",
1009                    mono_string_to_utf8 (this));
1010 #endif
1011
1012 #ifdef DEBUG
1013         g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", cult->lcid);
1014 #endif
1015
1016         if(cult->lcid==0x007F) {
1017 #ifdef DEBUG
1018                 g_message (G_GNUC_PRETTY_FUNCTION
1019                            ": Invariant, using shortcut");
1020 #endif
1021
1022                 return(string_invariant_toupper (this));
1023         }
1024
1025         icu_loc=mono_string_to_icu_locale (cult->icu_name);
1026         if(icu_loc==NULL) {
1027                 mono_raise_exception ((MonoException *)mono_exception_from_name (mono_defaults.corlib, "System", "SystemException"));
1028                 return(NULL);
1029         }
1030         
1031         udest=(UChar *)g_malloc0 (sizeof(UChar)*(mono_string_length (this)+1));
1032         
1033         /* According to the docs, this might result in a longer or
1034          * shorter string than we started with...
1035          */
1036
1037         ec=U_ZERO_ERROR;
1038         len=u_strToUpper (udest, mono_string_length (this)+1,
1039                           mono_string_chars (this), -1, icu_loc, &ec);
1040         if(ec==U_BUFFER_OVERFLOW_ERROR ||
1041            ec==U_STRING_NOT_TERMINATED_WARNING) {
1042                 g_free (udest);
1043                 udest=(UChar *)g_malloc0 (sizeof(UChar)*(len+1));
1044                 len=u_strToUpper (udest, len+1, mono_string_chars (this), -1,
1045                                   icu_loc, &ec);
1046         }
1047
1048         if(U_SUCCESS (ec)) {
1049                 ret=mono_string_from_utf16 ((gunichar2 *)udest);
1050         } else {
1051                 g_message (G_GNUC_PRETTY_FUNCTION ": u_strToUpper error: %s",
1052                            u_errorName (ec));
1053                 /* return something */
1054                 ret=this;
1055         }
1056         
1057         g_free (icu_loc);
1058         g_free (udest);
1059         
1060 #ifdef DEBUG
1061         g_message (G_GNUC_PRETTY_FUNCTION ": returning [%s]",
1062                    mono_string_to_utf8 (ret));
1063 #endif
1064         
1065         return(ret);
1066 }
1067
1068 #else /* HAVE_ICU */
1069 void ves_icall_System_Globalization_CultureInfo_construct_internal_locale (MonoCultureInfo *this, MonoString *locale)
1070 {
1071         MONO_ARCH_SAVE_REGS;
1072         
1073         /* Always claim "unknown locale" if we don't have ICU (only
1074          * called for non-invariant locales)
1075          */
1076         mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "ArgumentException"));
1077 }
1078
1079 void ves_icall_System_Globalization_CompareInfo_construct_compareinfo (MonoCompareInfo *comp, MonoString *locale)
1080 {
1081         /* Nothing to do here */
1082 }
1083
1084 int ves_icall_System_Globalization_CompareInfo_internal_compare (MonoCompareInfo *this, MonoString *str1, gint32 off1, gint32 len1, MonoString *str2, gint32 off2, gint32 len2, gint32 options)
1085 {
1086         MONO_ARCH_SAVE_REGS;
1087         
1088         /* Do a normal ascii string compare, as we only know the
1089          * invariant locale if we dont have ICU
1090          */
1091         return(string_invariant_compare (str1, off1, len1, str2, off2, len2,
1092                                          options));
1093 }
1094
1095 void ves_icall_System_Globalization_CompareInfo_free_internal_collator (MonoCompareInfo *this)
1096 {
1097         /* Nothing to do here */
1098 }
1099
1100 void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoCompareInfo *this, MonoSortKey *key, MonoString *source, gint32 options)
1101 {
1102         MonoArray *arr;
1103         gint32 keylen, i;
1104
1105         MONO_ARCH_SAVE_REGS;
1106         
1107         keylen=mono_string_length (source);
1108         
1109         arr=mono_array_new (mono_domain_get (), mono_defaults.byte_class,
1110                             keylen);
1111         for(i=0; i<keylen; i++) {
1112                 mono_array_set (arr, guint8, i, mono_string_chars (source)[i]);
1113         }
1114         
1115         key->key=arr;
1116 }
1117
1118 int ves_icall_System_Globalization_CompareInfo_internal_index (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, MonoString *value, gint32 options, MonoBoolean first)
1119 {
1120         MONO_ARCH_SAVE_REGS;
1121         
1122         return(string_invariant_indexof (source, sindex, count, value, first));
1123 }
1124
1125 int ves_icall_System_Globalization_CompareInfo_internal_index_char (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, gunichar2 value, gint32 options, MonoBoolean first)
1126 {
1127         MONO_ARCH_SAVE_REGS;
1128         
1129         return(string_invariant_indexof_char (source, sindex, count, value,
1130                                               first));
1131 }
1132
1133 int ves_icall_System_Threading_Thread_current_lcid (void)
1134 {
1135         MONO_ARCH_SAVE_REGS;
1136         
1137         /* Invariant */
1138         return(0x007F);
1139 }
1140
1141 MonoString *ves_icall_System_String_InternalReplace_Str_Comp (MonoString *this, MonoString *old, MonoString *new, MonoCompareInfo *comp)
1142 {
1143         MONO_ARCH_SAVE_REGS;
1144         
1145         /* Do a normal ascii string compare and replace, as we only
1146          * know the invariant locale if we dont have ICU
1147          */
1148         return(string_invariant_replace (this, old, new));
1149 }
1150
1151 MonoString *ves_icall_System_String_InternalToLower_Comp (MonoString *this, MonoCultureInfo *cult)
1152 {
1153         MONO_ARCH_SAVE_REGS;
1154         
1155         return(string_invariant_tolower (this));
1156 }
1157
1158 MonoString *ves_icall_System_String_InternalToUpper_Comp (MonoString *this, MonoCultureInfo *cult)
1159 {
1160         MONO_ARCH_SAVE_REGS;
1161         
1162         return(string_invariant_toupper (this));
1163 }
1164
1165 #endif /* HAVE_ICU */
1166
1167 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
1168                                              gint32 options)
1169 {
1170         gint32 result;
1171         GUnicodeType c1type, c2type;
1172
1173         c1type = g_unichar_type (c1);
1174         c2type = g_unichar_type (c2);
1175
1176         if (options & CompareOptions_IgnoreCase) {
1177                 result = (gint32) (c1type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c1) : c1) - (c2type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c2) : c2);
1178         } else if (options & CompareOptions_Ordinal) {
1179                 // Rotor/ms return the full value just not -1 and 1
1180                 return (gint32) c1 - c2;
1181         } else {
1182                 /* No options. Kana, symbol and spacing options don't
1183                  * apply to the invariant culture.
1184                  */
1185                 if (c1type == G_UNICODE_UPPERCASE_LETTER &&
1186                     c2type == G_UNICODE_LOWERCASE_LETTER) {
1187                         return(1);
1188                 }
1189                                         
1190                 if (c1type == G_UNICODE_LOWERCASE_LETTER &&
1191                     c2type == G_UNICODE_UPPERCASE_LETTER) {
1192                         return(-1);
1193                 }
1194                 
1195                 result = (gint32) c1 - c2;
1196         }
1197
1198         return ((result < 0) ? -1 : (result > 0) ? 1 : 0);
1199 }
1200
1201 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
1202                                         gint32 len1, MonoString *str2,
1203                                         gint32 off2, gint32 len2,
1204                                         gint32 options)
1205 {
1206         /* c translation of C# code from old string.cs.. :) */
1207         gint32 length;
1208         gint32 charcmp;
1209         gunichar2 *ustr1;
1210         gunichar2 *ustr2;
1211         gint32 pos;
1212
1213         if(len1 >= len2) {
1214                 length=len1;
1215         } else {
1216                 length=len2;
1217         }
1218
1219         ustr1 = mono_string_chars(str1)+off1;
1220         ustr2 = mono_string_chars(str2)+off2;
1221
1222         pos = 0;
1223
1224         for (pos = 0; pos != length; pos++) {
1225                 if (pos >= len1 || pos >= len2)
1226                         break;
1227
1228                 charcmp = string_invariant_compare_char(ustr1[pos], ustr2[pos],
1229                                                         options);
1230                 if (charcmp != 0) {
1231                         return(charcmp);
1232                 }
1233         }
1234
1235         /* the lesser wins, so if we have looped until length we just
1236          * need to check the last char
1237          */
1238         if (pos == length) {
1239                 return(string_invariant_compare_char(ustr1[pos - 1],
1240                                                      ustr2[pos - 1], options));
1241         }
1242
1243         /* Test if one of the strings has been compared to the end */
1244         if (pos >= len1) {
1245                 if (pos >= len2) {
1246                         return(0);
1247                 } else {
1248                         return(-1);
1249                 }
1250         } else if (pos >= len2) {
1251                 return(1);
1252         }
1253
1254         /* if not, check our last char only.. (can this happen?) */
1255         return(string_invariant_compare_char(ustr1[pos], ustr2[pos], options));
1256 }
1257
1258 static MonoString *string_invariant_replace (MonoString *me,
1259                                              MonoString *oldValue,
1260                                              MonoString *newValue)
1261 {
1262         MonoString *ret;
1263         gunichar2 *src;
1264         gunichar2 *dest=NULL; /* shut gcc up */
1265         gunichar2 *oldstr;
1266         gunichar2 *newstr=NULL; /* shut gcc up here too */
1267         gint32 i, destpos;
1268         gint32 occurr;
1269         gint32 newsize;
1270         gint32 oldstrlen;
1271         gint32 newstrlen;
1272         gint32 srclen;
1273
1274         occurr = 0;
1275         destpos = 0;
1276
1277         oldstr = mono_string_chars(oldValue);
1278         oldstrlen = mono_string_length(oldValue);
1279
1280         if (NULL != newValue) {
1281                 newstr = mono_string_chars(newValue);
1282                 newstrlen = mono_string_length(newValue);
1283         } else
1284                 newstrlen = 0;
1285
1286         src = mono_string_chars(me);
1287         srclen = mono_string_length(me);
1288
1289         if (oldstrlen != newstrlen) {
1290                 for (i = 0; i <= srclen - oldstrlen; i++)
1291                         if (0 == memcmp(src + i, oldstr, oldstrlen * sizeof(gunichar2)))
1292                                 occurr++;
1293                 if (occurr == 0)
1294                         return me;
1295                 newsize = srclen + ((newstrlen - oldstrlen) * occurr);
1296         } else
1297                 newsize = srclen;
1298
1299         ret = NULL;
1300         i = 0;
1301         while (i < srclen) {
1302                 if (0 == memcmp(src + i, oldstr, oldstrlen * sizeof(gunichar2))) {
1303                         if (ret == NULL) {
1304                                 ret = mono_string_new_size( mono_domain_get (), newsize);
1305                                 dest = mono_string_chars(ret);
1306                                 memcpy (dest, src, i * sizeof(gunichar2));
1307                         }
1308                         if (newstrlen > 0) {
1309                                 memcpy(dest + destpos, newstr, newstrlen * sizeof(gunichar2));
1310                                 destpos += newstrlen;
1311                         }
1312                         i += oldstrlen;
1313                         continue;
1314                 } else if (ret != NULL) {
1315                         dest[destpos] = src[i];
1316                 }
1317                 destpos++;
1318                 i++;
1319         }
1320         
1321         if (ret == NULL)
1322                 return me;
1323
1324         return ret;
1325 }
1326
1327 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
1328                                         gint32 count, MonoString *value,
1329                                         MonoBoolean first)
1330 {
1331         gint32 lencmpstr;
1332         gunichar2 *src;
1333         gunichar2 *cmpstr;
1334         gint32 pos,i;
1335         
1336         lencmpstr = mono_string_length(value);
1337         
1338         src = mono_string_chars(source);
1339         cmpstr = mono_string_chars(value);
1340
1341         if(first) {
1342                 count -= lencmpstr;
1343                 for(pos=sindex;pos <= sindex+count;pos++) {
1344                         for(i=0;src[pos+i]==cmpstr[i];) {
1345                                 if(++i==lencmpstr) {
1346                                         return(pos);
1347                                 }
1348                         }
1349                 }
1350                 
1351                 return(-1);
1352         } else {
1353                 for(pos=sindex-lencmpstr+1;pos>sindex-count;pos--) {
1354                         if(memcmp (src+pos, cmpstr,
1355                                    lencmpstr*sizeof(gunichar2))==0) {
1356                                 return(pos);
1357                         }
1358                 }
1359                 
1360                 return(-1);
1361         }
1362 }
1363
1364 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
1365                                              gint32 count, gunichar2 value,
1366                                              MonoBoolean first)
1367 {
1368         gint32 pos;
1369         gunichar2 *src;
1370
1371         src = mono_string_chars(source);
1372         if(first) {
1373                 for (pos = sindex; pos != count + sindex; pos++) {
1374                         if (src [pos] == value) {
1375                                 return(pos);
1376                         }
1377                 }
1378
1379                 return(-1);
1380         } else {
1381                 for (pos = sindex; pos > sindex - count; pos--) {
1382                         if (src [pos] == value)
1383                                 return(pos);
1384                 }
1385
1386                 return(-1);
1387         }
1388 }
1389
1390 static MonoString *string_invariant_tolower (MonoString *this)
1391 {
1392         MonoString *ret;
1393         gunichar2 *src; 
1394         gunichar2 *dest;
1395         gint32 i;
1396
1397         ret = mono_string_new_size(mono_domain_get (),
1398                                    mono_string_length(this));
1399
1400         src = mono_string_chars (this);
1401         dest = mono_string_chars (ret);
1402
1403         for (i = 0; i < mono_string_length (this); ++i) {
1404                 dest[i] = g_unichar_tolower(src[i]);
1405         }
1406
1407         return(ret);
1408 }
1409
1410 static MonoString *string_invariant_toupper (MonoString *this)
1411 {
1412         MonoString *ret;
1413         gunichar2 *src; 
1414         gunichar2 *dest;
1415         guint32 i;
1416
1417         ret = mono_string_new_size(mono_domain_get (),
1418                                    mono_string_length(this));
1419
1420         src = mono_string_chars (this);
1421         dest = mono_string_chars (ret);
1422
1423         for (i = 0; i < mono_string_length (this); ++i) {
1424                 dest[i] = g_unichar_toupper(src[i]);
1425         }
1426
1427         return(ret);
1428 }