Workaround from Dick to the string.Replace bug
[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), match_len;
877                 UChar *uret, *match;
878                 
879                 for(pos=usearch_first (search, &ec);
880                     pos!=USEARCH_DONE;
881                     pos=usearch_next (search, &ec)) {
882                         /* ICU usearch currently ignores most of the collator
883                          * attributes :-(
884                          *
885                          * Check the returned match to see if it really
886                          * does match properly...
887                          */
888                         match_len = usearch_getMatchedLength (search);
889                         match=(UChar *)g_malloc0 (sizeof(UChar) * (match_len + 1));
890                         usearch_getMatchedText (search, match, match_len, &ec);
891
892                         if (ucol_strcoll (coll, match, -1, mono_string_chars (old), -1) == UCOL_EQUAL) {
893                                 /* OK, we really did get a match */
894 #ifdef DEBUG
895                                 g_message (G_GNUC_PRETTY_FUNCTION
896                                            ": Got match at %d len %d", pos,
897                                            match_len);
898 #endif
899
900                                 len_delta += (newstr_len - match_len);
901                         } else {
902                                 /* False alarm */
903 #ifdef DEBUG
904                                 g_message (G_GNUC_PRETTY_FUNCTION
905                                            ": Got false match at %d len %d",
906                                            pos, match_len);
907 #endif
908                         }
909                         g_free (match);
910                 }
911 #ifdef DEBUG
912                 g_message (G_GNUC_PRETTY_FUNCTION
913                            ": New string length is %d (delta %d)",
914                            mono_string_length (this)+len_delta, len_delta);
915 #endif
916                 
917                 uret=(UChar *)g_malloc0 (sizeof(UChar) * (mono_string_length (this)+len_delta+2));
918                 
919                 for(oldpos=0, pos=usearch_first (search, &ec);
920                     pos!=USEARCH_DONE;
921                     pos=usearch_next (search, &ec)) {
922                         match_len = usearch_getMatchedLength (search);
923                         match=(UChar *)g_malloc0 (sizeof(UChar) * (match_len + 1));
924                         usearch_getMatchedText (search, match, match_len, &ec);
925
926                         /* Add the unmatched text */
927                         u_strncat (uret, mono_string_chars (this)+oldpos,
928                                    pos-oldpos);
929                         if (ucol_strcoll (coll, match, -1, mono_string_chars (old), -1) == UCOL_EQUAL) {
930                                 /* Then the replacement */
931                                 u_strcat (uret, mono_string_chars (new));
932                         } else {
933                                 /* Then the original, because this is a
934                                  * false match
935                                  */
936                                 u_strncat (uret, mono_string_chars (this)+pos,
937                                            match_len);
938                         }
939                         oldpos=pos+match_len;
940                         g_free (match);
941                 }
942                 
943                 /* Finish off with the trailing unmatched text */
944                 u_strcat (uret, mono_string_chars (this)+oldpos);
945
946                 ret=mono_string_from_utf16 ((gunichar2 *)uret);
947         } else {
948                 g_message (G_GNUC_PRETTY_FUNCTION ": usearch_open error: %s",
949                            u_errorName (ec));
950         }
951
952         usearch_close (search);
953         
954         mono_monitor_exit ((MonoObject *)comp);
955         
956 #ifdef DEBUG
957         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));
958 #endif
959         
960         return(ret);
961 }
962
963 MonoString *ves_icall_System_String_InternalToLower_Comp (MonoString *this, MonoCultureInfo *cult)
964 {
965         MonoString *ret;
966         UChar *udest;
967         UErrorCode ec;
968         char *icu_loc;
969         int32_t len;
970         
971 #ifdef DEBUG
972         g_message (G_GNUC_PRETTY_FUNCTION ": [%s]",
973                    mono_string_to_utf8 (this));
974 #endif
975
976 #ifdef DEBUG
977         g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", cult->lcid);
978 #endif
979
980         if(cult->lcid==0x007F) {
981 #ifdef DEBUG
982                 g_message (G_GNUC_PRETTY_FUNCTION
983                            ": Invariant, using shortcut");
984 #endif
985
986                 return(string_invariant_tolower (this));
987         }
988
989         icu_loc=mono_string_to_icu_locale (cult->icu_name);
990         if(icu_loc==NULL) {
991                 mono_raise_exception ((MonoException *)mono_exception_from_name (mono_defaults.corlib, "System", "SystemException"));
992                 return(NULL);
993         }
994         
995         udest=(UChar *)g_malloc0 (sizeof(UChar)*(mono_string_length (this)+1));
996         
997         /* According to the docs, this might result in a longer or
998          * shorter string than we started with...
999          */
1000
1001         ec=U_ZERO_ERROR;
1002         len=u_strToLower (udest, mono_string_length (this)+1,
1003                           mono_string_chars (this), -1, icu_loc, &ec);
1004         if(ec==U_BUFFER_OVERFLOW_ERROR ||
1005            ec==U_STRING_NOT_TERMINATED_WARNING) {
1006                 g_free (udest);
1007                 udest=(UChar *)g_malloc0 (sizeof(UChar)*(len+1));
1008                 len=u_strToLower (udest, len+1, mono_string_chars (this), -1,
1009                                   icu_loc, &ec);
1010         }
1011
1012         if(U_SUCCESS (ec)) {
1013                 ret=mono_string_from_utf16 ((gunichar2 *)udest);
1014         } else {
1015                 g_message (G_GNUC_PRETTY_FUNCTION ": u_strToLower error: %s",
1016                            u_errorName (ec));
1017                 /* return something */
1018                 ret=this;
1019         }
1020         
1021         g_free (icu_loc);
1022         g_free (udest);
1023         
1024 #ifdef DEBUG
1025         g_message (G_GNUC_PRETTY_FUNCTION ": returning [%s]",
1026                    mono_string_to_utf8 (ret));
1027 #endif
1028
1029         return(ret);
1030 }
1031
1032 MonoString *ves_icall_System_String_InternalToUpper_Comp (MonoString *this, MonoCultureInfo *cult)
1033 {
1034         MonoString *ret;
1035         UChar *udest;
1036         UErrorCode ec;
1037         char *icu_loc;
1038         int32_t len;
1039         
1040 #ifdef DEBUG
1041         g_message (G_GNUC_PRETTY_FUNCTION ": [%s]",
1042                    mono_string_to_utf8 (this));
1043 #endif
1044
1045 #ifdef DEBUG
1046         g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", cult->lcid);
1047 #endif
1048
1049         if(cult->lcid==0x007F) {
1050 #ifdef DEBUG
1051                 g_message (G_GNUC_PRETTY_FUNCTION
1052                            ": Invariant, using shortcut");
1053 #endif
1054
1055                 return(string_invariant_toupper (this));
1056         }
1057
1058         icu_loc=mono_string_to_icu_locale (cult->icu_name);
1059         if(icu_loc==NULL) {
1060                 mono_raise_exception ((MonoException *)mono_exception_from_name (mono_defaults.corlib, "System", "SystemException"));
1061                 return(NULL);
1062         }
1063         
1064         udest=(UChar *)g_malloc0 (sizeof(UChar)*(mono_string_length (this)+1));
1065         
1066         /* According to the docs, this might result in a longer or
1067          * shorter string than we started with...
1068          */
1069
1070         ec=U_ZERO_ERROR;
1071         len=u_strToUpper (udest, mono_string_length (this)+1,
1072                           mono_string_chars (this), -1, icu_loc, &ec);
1073         if(ec==U_BUFFER_OVERFLOW_ERROR ||
1074            ec==U_STRING_NOT_TERMINATED_WARNING) {
1075                 g_free (udest);
1076                 udest=(UChar *)g_malloc0 (sizeof(UChar)*(len+1));
1077                 len=u_strToUpper (udest, len+1, mono_string_chars (this), -1,
1078                                   icu_loc, &ec);
1079         }
1080
1081         if(U_SUCCESS (ec)) {
1082                 ret=mono_string_from_utf16 ((gunichar2 *)udest);
1083         } else {
1084                 g_message (G_GNUC_PRETTY_FUNCTION ": u_strToUpper error: %s",
1085                            u_errorName (ec));
1086                 /* return something */
1087                 ret=this;
1088         }
1089         
1090         g_free (icu_loc);
1091         g_free (udest);
1092         
1093 #ifdef DEBUG
1094         g_message (G_GNUC_PRETTY_FUNCTION ": returning [%s]",
1095                    mono_string_to_utf8 (ret));
1096 #endif
1097         
1098         return(ret);
1099 }
1100
1101 #else /* HAVE_ICU */
1102 void ves_icall_System_Globalization_CultureInfo_construct_internal_locale (MonoCultureInfo *this, MonoString *locale)
1103 {
1104         MONO_ARCH_SAVE_REGS;
1105         
1106         /* Always claim "unknown locale" if we don't have ICU (only
1107          * called for non-invariant locales)
1108          */
1109         mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "ArgumentException"));
1110 }
1111
1112 void ves_icall_System_Globalization_CompareInfo_construct_compareinfo (MonoCompareInfo *comp, MonoString *locale)
1113 {
1114         /* Nothing to do here */
1115 }
1116
1117 int ves_icall_System_Globalization_CompareInfo_internal_compare (MonoCompareInfo *this, MonoString *str1, gint32 off1, gint32 len1, MonoString *str2, gint32 off2, gint32 len2, gint32 options)
1118 {
1119         MONO_ARCH_SAVE_REGS;
1120         
1121         /* Do a normal ascii string compare, as we only know the
1122          * invariant locale if we dont have ICU
1123          */
1124         return(string_invariant_compare (str1, off1, len1, str2, off2, len2,
1125                                          options));
1126 }
1127
1128 void ves_icall_System_Globalization_CompareInfo_free_internal_collator (MonoCompareInfo *this)
1129 {
1130         /* Nothing to do here */
1131 }
1132
1133 void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoCompareInfo *this, MonoSortKey *key, MonoString *source, gint32 options)
1134 {
1135         MonoArray *arr;
1136         gint32 keylen, i;
1137
1138         MONO_ARCH_SAVE_REGS;
1139         
1140         keylen=mono_string_length (source);
1141         
1142         arr=mono_array_new (mono_domain_get (), mono_defaults.byte_class,
1143                             keylen);
1144         for(i=0; i<keylen; i++) {
1145                 mono_array_set (arr, guint8, i, mono_string_chars (source)[i]);
1146         }
1147         
1148         key->key=arr;
1149 }
1150
1151 int ves_icall_System_Globalization_CompareInfo_internal_index (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, MonoString *value, gint32 options, MonoBoolean first)
1152 {
1153         MONO_ARCH_SAVE_REGS;
1154         
1155         return(string_invariant_indexof (source, sindex, count, value, first));
1156 }
1157
1158 int ves_icall_System_Globalization_CompareInfo_internal_index_char (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, gunichar2 value, gint32 options, MonoBoolean first)
1159 {
1160         MONO_ARCH_SAVE_REGS;
1161         
1162         return(string_invariant_indexof_char (source, sindex, count, value,
1163                                               first));
1164 }
1165
1166 int ves_icall_System_Threading_Thread_current_lcid (void)
1167 {
1168         MONO_ARCH_SAVE_REGS;
1169         
1170         /* Invariant */
1171         return(0x007F);
1172 }
1173
1174 MonoString *ves_icall_System_String_InternalReplace_Str_Comp (MonoString *this, MonoString *old, MonoString *new, MonoCompareInfo *comp)
1175 {
1176         MONO_ARCH_SAVE_REGS;
1177         
1178         /* Do a normal ascii string compare and replace, as we only
1179          * know the invariant locale if we dont have ICU
1180          */
1181         return(string_invariant_replace (this, old, new));
1182 }
1183
1184 MonoString *ves_icall_System_String_InternalToLower_Comp (MonoString *this, MonoCultureInfo *cult)
1185 {
1186         MONO_ARCH_SAVE_REGS;
1187         
1188         return(string_invariant_tolower (this));
1189 }
1190
1191 MonoString *ves_icall_System_String_InternalToUpper_Comp (MonoString *this, MonoCultureInfo *cult)
1192 {
1193         MONO_ARCH_SAVE_REGS;
1194         
1195         return(string_invariant_toupper (this));
1196 }
1197
1198 #endif /* HAVE_ICU */
1199
1200 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
1201                                              gint32 options)
1202 {
1203         gint32 result;
1204         GUnicodeType c1type, c2type;
1205
1206         c1type = g_unichar_type (c1);
1207         c2type = g_unichar_type (c2);
1208
1209         if (options & CompareOptions_IgnoreCase) {
1210                 result = (gint32) (c1type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c1) : c1) - (c2type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c2) : c2);
1211         } else if (options & CompareOptions_Ordinal) {
1212                 // Rotor/ms return the full value just not -1 and 1
1213                 return (gint32) c1 - c2;
1214         } else {
1215                 /* No options. Kana, symbol and spacing options don't
1216                  * apply to the invariant culture.
1217                  */
1218                 if (c1type == G_UNICODE_UPPERCASE_LETTER &&
1219                     c2type == G_UNICODE_LOWERCASE_LETTER) {
1220                         return(1);
1221                 }
1222                                         
1223                 if (c1type == G_UNICODE_LOWERCASE_LETTER &&
1224                     c2type == G_UNICODE_UPPERCASE_LETTER) {
1225                         return(-1);
1226                 }
1227                 
1228                 result = (gint32) c1 - c2;
1229         }
1230
1231         return ((result < 0) ? -1 : (result > 0) ? 1 : 0);
1232 }
1233
1234 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
1235                                         gint32 len1, MonoString *str2,
1236                                         gint32 off2, gint32 len2,
1237                                         gint32 options)
1238 {
1239         /* c translation of C# code from old string.cs.. :) */
1240         gint32 length;
1241         gint32 charcmp;
1242         gunichar2 *ustr1;
1243         gunichar2 *ustr2;
1244         gint32 pos;
1245
1246         if(len1 >= len2) {
1247                 length=len1;
1248         } else {
1249                 length=len2;
1250         }
1251
1252         ustr1 = mono_string_chars(str1)+off1;
1253         ustr2 = mono_string_chars(str2)+off2;
1254
1255         pos = 0;
1256
1257         for (pos = 0; pos != length; pos++) {
1258                 if (pos >= len1 || pos >= len2)
1259                         break;
1260
1261                 charcmp = string_invariant_compare_char(ustr1[pos], ustr2[pos],
1262                                                         options);
1263                 if (charcmp != 0) {
1264                         return(charcmp);
1265                 }
1266         }
1267
1268         /* the lesser wins, so if we have looped until length we just
1269          * need to check the last char
1270          */
1271         if (pos == length) {
1272                 return(string_invariant_compare_char(ustr1[pos - 1],
1273                                                      ustr2[pos - 1], options));
1274         }
1275
1276         /* Test if one of the strings has been compared to the end */
1277         if (pos >= len1) {
1278                 if (pos >= len2) {
1279                         return(0);
1280                 } else {
1281                         return(-1);
1282                 }
1283         } else if (pos >= len2) {
1284                 return(1);
1285         }
1286
1287         /* if not, check our last char only.. (can this happen?) */
1288         return(string_invariant_compare_char(ustr1[pos], ustr2[pos], options));
1289 }
1290
1291 static MonoString *string_invariant_replace (MonoString *me,
1292                                              MonoString *oldValue,
1293                                              MonoString *newValue)
1294 {
1295         MonoString *ret;
1296         gunichar2 *src;
1297         gunichar2 *dest=NULL; /* shut gcc up */
1298         gunichar2 *oldstr;
1299         gunichar2 *newstr=NULL; /* shut gcc up here too */
1300         gint32 i, destpos;
1301         gint32 occurr;
1302         gint32 newsize;
1303         gint32 oldstrlen;
1304         gint32 newstrlen;
1305         gint32 srclen;
1306
1307         occurr = 0;
1308         destpos = 0;
1309
1310         oldstr = mono_string_chars(oldValue);
1311         oldstrlen = mono_string_length(oldValue);
1312
1313         if (NULL != newValue) {
1314                 newstr = mono_string_chars(newValue);
1315                 newstrlen = mono_string_length(newValue);
1316         } else
1317                 newstrlen = 0;
1318
1319         src = mono_string_chars(me);
1320         srclen = mono_string_length(me);
1321
1322         if (oldstrlen != newstrlen) {
1323                 for (i = 0; i <= srclen - oldstrlen; i++)
1324                         if (0 == memcmp(src + i, oldstr, oldstrlen * sizeof(gunichar2)))
1325                                 occurr++;
1326                 if (occurr == 0)
1327                         return me;
1328                 newsize = srclen + ((newstrlen - oldstrlen) * occurr);
1329         } else
1330                 newsize = srclen;
1331
1332         ret = NULL;
1333         i = 0;
1334         while (i < srclen) {
1335                 if (0 == memcmp(src + i, oldstr, oldstrlen * sizeof(gunichar2))) {
1336                         if (ret == NULL) {
1337                                 ret = mono_string_new_size( mono_domain_get (), newsize);
1338                                 dest = mono_string_chars(ret);
1339                                 memcpy (dest, src, i * sizeof(gunichar2));
1340                         }
1341                         if (newstrlen > 0) {
1342                                 memcpy(dest + destpos, newstr, newstrlen * sizeof(gunichar2));
1343                                 destpos += newstrlen;
1344                         }
1345                         i += oldstrlen;
1346                         continue;
1347                 } else if (ret != NULL) {
1348                         dest[destpos] = src[i];
1349                 }
1350                 destpos++;
1351                 i++;
1352         }
1353         
1354         if (ret == NULL)
1355                 return me;
1356
1357         return ret;
1358 }
1359
1360 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
1361                                         gint32 count, MonoString *value,
1362                                         MonoBoolean first)
1363 {
1364         gint32 lencmpstr;
1365         gunichar2 *src;
1366         gunichar2 *cmpstr;
1367         gint32 pos,i;
1368         
1369         lencmpstr = mono_string_length(value);
1370         
1371         src = mono_string_chars(source);
1372         cmpstr = mono_string_chars(value);
1373
1374         if(first) {
1375                 count -= lencmpstr;
1376                 for(pos=sindex;pos <= sindex+count;pos++) {
1377                         for(i=0;src[pos+i]==cmpstr[i];) {
1378                                 if(++i==lencmpstr) {
1379                                         return(pos);
1380                                 }
1381                         }
1382                 }
1383                 
1384                 return(-1);
1385         } else {
1386                 for(pos=sindex-lencmpstr+1;pos>sindex-count;pos--) {
1387                         if(memcmp (src+pos, cmpstr,
1388                                    lencmpstr*sizeof(gunichar2))==0) {
1389                                 return(pos);
1390                         }
1391                 }
1392                 
1393                 return(-1);
1394         }
1395 }
1396
1397 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
1398                                              gint32 count, gunichar2 value,
1399                                              MonoBoolean first)
1400 {
1401         gint32 pos;
1402         gunichar2 *src;
1403
1404         src = mono_string_chars(source);
1405         if(first) {
1406                 for (pos = sindex; pos != count + sindex; pos++) {
1407                         if (src [pos] == value) {
1408                                 return(pos);
1409                         }
1410                 }
1411
1412                 return(-1);
1413         } else {
1414                 for (pos = sindex; pos > sindex - count; pos--) {
1415                         if (src [pos] == value)
1416                                 return(pos);
1417                 }
1418
1419                 return(-1);
1420         }
1421 }
1422
1423 static MonoString *string_invariant_tolower (MonoString *this)
1424 {
1425         MonoString *ret;
1426         gunichar2 *src; 
1427         gunichar2 *dest;
1428         gint32 i;
1429
1430         ret = mono_string_new_size(mono_domain_get (),
1431                                    mono_string_length(this));
1432
1433         src = mono_string_chars (this);
1434         dest = mono_string_chars (ret);
1435
1436         for (i = 0; i < mono_string_length (this); ++i) {
1437                 dest[i] = g_unichar_tolower(src[i]);
1438         }
1439
1440         return(ret);
1441 }
1442
1443 static MonoString *string_invariant_toupper (MonoString *this)
1444 {
1445         MonoString *ret;
1446         gunichar2 *src; 
1447         gunichar2 *dest;
1448         guint32 i;
1449
1450         ret = mono_string_new_size(mono_domain_get (),
1451                                    mono_string_length(this));
1452
1453         src = mono_string_chars (this);
1454         dest = mono_string_chars (ret);
1455
1456         for (i = 0; i < mono_string_length (this); ++i) {
1457                 dest[i] = g_unichar_toupper(src[i]);
1458         }
1459
1460         return(ret);
1461 }