2 * locales.c: Culture-sensitive handling
5 * Dick Porter (dick@ximian.com)
6 * Mohammad DAMT (mdamt@cdl2000.com)
8 * (C) 2003 Ximian, Inc.
9 * (C) 2003 PT Cakram Datalingga Duaribu http://www.cdl2000.com
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 #include <mono/metadata/culture-info.h>
23 #include <mono/metadata/culture-info-tables.h>
30 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
32 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
33 gint32 len1, MonoString *str2,
34 gint32 off2, gint32 len2,
36 static MonoString *string_invariant_replace (MonoString *me,
38 MonoString *newValue);
39 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
40 gint32 count, MonoString *value,
42 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
43 gint32 count, gunichar2 value,
46 static const CultureInfoEntry* culture_info_entry_from_lcid (int lcid);
49 culture_lcid_locator (const void *a, const void *b)
51 const CultureInfoEntry *aa = a;
52 const CultureInfoEntry *bb = b;
54 return (aa->lcid - bb->lcid);
58 culture_name_locator (const void *a, const void *b)
61 const CultureInfoNameEntry *bb = b;
64 ret = strcmp (aa, idx2string (bb->name));
70 create_group_sizes_array (const gint *gs, gint ml)
75 for (i = 0; i < ml; i++) {
81 ret = mono_array_new (mono_domain_get (),
82 mono_get_int32_class (), len);
84 for(i = 0; i < len; i++)
85 mono_array_set (ret, gint32, i, gs [i]);
91 create_names_array_idx (const guint16 *names, int ml)
100 domain = mono_domain_get ();
102 for (i = 0; i < ml; i++) {
108 ret = mono_array_new (mono_domain_get (), mono_get_string_class (), len);
110 for(i = 0; i < len; i++)
111 mono_array_set (ret, MonoString *, i, mono_string_new (domain, idx2string (names [i])));
117 ves_icall_System_Globalization_CultureInfo_construct_datetime_format (MonoCultureInfo *this)
120 MonoDateTimeFormatInfo *datetime;
121 const DateTimeFormatEntry *dfe;
125 g_assert (this->datetime_index >= 0);
127 datetime = this->datetime_format;
128 dfe = &datetime_format_entries [this->datetime_index];
130 domain = mono_domain_get ();
132 datetime->AbbreviatedDayNames = create_names_array_idx (dfe->abbreviated_day_names,
134 datetime->AbbreviatedMonthNames = create_names_array_idx (dfe->abbreviated_month_names,
136 datetime->AMDesignator = mono_string_new (domain, idx2string (dfe->am_designator));
137 datetime->CalendarWeekRule = dfe->calendar_week_rule;
138 datetime->DateSeparator = mono_string_new (domain, idx2string (dfe->date_separator));
139 datetime->DayNames = create_names_array_idx (dfe->day_names, NUM_DAYS);
140 datetime->FirstDayOfWeek = dfe->first_day_of_week;
141 datetime->FullDateTimePattern = mono_string_new (domain, idx2string (dfe->full_date_time_pattern));
142 datetime->LongDatePattern = mono_string_new (domain, idx2string (dfe->long_date_pattern));
143 datetime->LongTimePattern = mono_string_new (domain, idx2string (dfe->long_time_pattern));
144 datetime->MonthDayPattern = mono_string_new (domain, idx2string (dfe->month_day_pattern));
145 datetime->MonthNames = create_names_array_idx (dfe->month_names, NUM_MONTHS);
146 datetime->PMDesignator = mono_string_new (domain, idx2string (dfe->pm_designator));
147 datetime->ShortDatePattern = mono_string_new (domain, idx2string (dfe->short_date_pattern));
148 datetime->ShortTimePattern = mono_string_new (domain, idx2string (dfe->short_time_pattern));
149 datetime->TimeSeparator = mono_string_new (domain, idx2string (dfe->time_separator));
150 datetime->YearMonthPattern = mono_string_new (domain, idx2string (dfe->year_month_pattern));
151 datetime->ShortDatePatterns = create_names_array_idx (dfe->short_date_patterns,
152 NUM_SHORT_DATE_PATTERNS);
153 datetime->LongDatePatterns = create_names_array_idx (dfe->long_date_patterns,
154 NUM_LONG_DATE_PATTERNS);
155 datetime->ShortTimePatterns = create_names_array_idx (dfe->short_time_patterns,
156 NUM_SHORT_TIME_PATTERNS);
157 datetime->LongTimePatterns = create_names_array_idx (dfe->long_time_patterns,
158 NUM_LONG_TIME_PATTERNS);
163 ves_icall_System_Globalization_CultureInfo_construct_number_format (MonoCultureInfo *this)
166 MonoNumberFormatInfo *number;
167 const NumberFormatEntry *nfe;
171 g_assert (this->number_format != 0);
173 number = this->number_format;
174 nfe = &number_format_entries [this->number_index];
176 domain = mono_domain_get ();
178 number->currencyDecimalDigits = nfe->currency_decimal_digits;
179 number->currencyDecimalSeparator = mono_string_new (domain,
180 idx2string (nfe->currency_decimal_separator));
181 number->currencyGroupSeparator = mono_string_new (domain,
182 idx2string (nfe->currency_group_separator));
183 number->currencyGroupSizes = create_group_sizes_array (nfe->currency_group_sizes,
185 number->currencyNegativePattern = nfe->currency_negative_pattern;
186 number->currencyPositivePattern = nfe->currency_positive_pattern;
187 number->currencySymbol = mono_string_new (domain, idx2string (nfe->currency_symbol));
188 number->naNSymbol = mono_string_new (domain, idx2string (nfe->nan_symbol));
189 number->negativeInfinitySymbol = mono_string_new (domain,
190 idx2string (nfe->negative_infinity_symbol));
191 number->negativeSign = mono_string_new (domain, idx2string (nfe->negative_sign));
192 number->numberDecimalDigits = nfe->number_decimal_digits;
193 number->numberDecimalSeparator = mono_string_new (domain,
194 idx2string (nfe->number_decimal_separator));
195 number->numberGroupSeparator = mono_string_new (domain, idx2string (nfe->number_group_separator));
196 number->numberGroupSizes = create_group_sizes_array (nfe->number_group_sizes,
198 number->numberNegativePattern = nfe->number_negative_pattern;
199 number->percentDecimalDigits = nfe->percent_decimal_digits;
200 number->percentDecimalSeparator = mono_string_new (domain,
201 idx2string (nfe->percent_decimal_separator));
202 number->percentGroupSeparator = mono_string_new (domain,
203 idx2string (nfe->percent_group_separator));
204 number->percentGroupSizes = create_group_sizes_array (nfe->percent_group_sizes,
206 number->percentNegativePattern = nfe->percent_negative_pattern;
207 number->percentPositivePattern = nfe->percent_positive_pattern;
208 number->percentSymbol = mono_string_new (domain, idx2string (nfe->percent_symbol));
209 number->perMilleSymbol = mono_string_new (domain, idx2string (nfe->per_mille_symbol));
210 number->positiveInfinitySymbol = mono_string_new (domain,
211 idx2string (nfe->positive_infinity_symbol));
212 number->positiveSign = mono_string_new (domain, idx2string (nfe->positive_sign));
216 construct_culture (MonoCultureInfo *this, const CultureInfoEntry *ci)
218 MonoDomain *domain = mono_domain_get ();
220 this->lcid = ci->lcid;
221 this->name = mono_string_new (domain, idx2string (ci->name));
222 this->icu_name = mono_string_new (domain, idx2string (ci->icu_name));
223 this->displayname = mono_string_new (domain, idx2string (ci->displayname));
224 this->englishname = mono_string_new (domain, idx2string (ci->englishname));
225 this->nativename = mono_string_new (domain, idx2string (ci->nativename));
226 this->win3lang = mono_string_new (domain, idx2string (ci->win3lang));
227 this->iso3lang = mono_string_new (domain, idx2string (ci->iso3lang));
228 this->iso2lang = mono_string_new (domain, idx2string (ci->iso2lang));
229 this->parent_lcid = ci->parent_lcid;
230 this->specific_lcid = ci->specific_lcid;
231 this->datetime_index = ci->datetime_format_index;
232 this->number_index = ci->number_format_index;
233 this->calendar_data = ci->calendar_data;
234 this->text_info_data = &ci->text_info;
240 construct_culture_from_specific_name (MonoCultureInfo *ci, gchar *name)
242 const CultureInfoEntry *entry;
243 const CultureInfoNameEntry *ne;
247 ne = bsearch (name, culture_name_entries, NUM_CULTURE_ENTRIES,
248 sizeof (CultureInfoNameEntry), culture_name_locator);
253 entry = &culture_entries [ne->culture_entry_index];
255 /* try avoiding another lookup, often the culture is its own specific culture */
256 if (entry->lcid != entry->specific_lcid)
257 entry = culture_info_entry_from_lcid (entry->specific_lcid);
259 return construct_culture (ci, entry);
262 static const CultureInfoEntry*
263 culture_info_entry_from_lcid (int lcid)
265 const CultureInfoEntry *ci;
266 CultureInfoEntry key;
269 ci = bsearch (&key, culture_entries, NUM_CULTURE_ENTRIES, sizeof (CultureInfoEntry), culture_lcid_locator);
275 * The following two methods are modified from the ICU source code. (http://oss.software.ibm.com/icu)
276 * Copyright (c) 1995-2003 International Business Machines Corporation and others
277 * All rights reserved.
280 get_posix_locale (void)
282 const gchar* posix_locale = NULL;
284 posix_locale = g_getenv("LC_ALL");
285 if (posix_locale == 0) {
286 posix_locale = g_getenv("LANG");
287 if (posix_locale == 0) {
288 posix_locale = setlocale(LC_ALL, NULL);
292 if (posix_locale == NULL)
295 if ((strcmp ("C", posix_locale) == 0) || (strchr (posix_locale, ' ') != NULL)
296 || (strchr (posix_locale, '/') != NULL)) {
298 * HPUX returns 'C C C C C C C'
299 * Solaris can return /en_US/C/C/C/C/C on the second try.
300 * Maybe we got some garbage.
305 return g_strdup (posix_locale);
309 get_current_locale_name (void)
312 gchar *corrected = NULL;
316 #ifdef PLATFORM_WIN32
317 locale = g_win32_getlocale ();
319 locale = get_posix_locale ();
325 if ((p = strchr (locale, '.')) != NULL) {
326 /* assume new locale can't be larger than old one? */
327 corrected = malloc (strlen (locale));
328 strncpy (corrected, locale, p - locale);
329 corrected [p - locale] = 0;
331 /* do not copy after the @ */
332 if ((p = strchr (corrected, '@')) != NULL)
333 corrected [p - corrected] = 0;
336 /* Note that we scan the *uncorrected* ID. */
337 if ((p = strrchr (locale, '@')) != NULL) {
340 * In Mono we dont handle the '@' modifier because we do
341 * not have any cultures that use it. We just trim it
342 * off of the end of the name.
345 if (corrected == NULL) {
346 corrected = malloc (strlen (locale));
347 strncpy (corrected, locale, p - locale);
348 corrected [p - locale] = 0;
352 if (corrected == NULL)
357 if ((c = strchr (corrected, '_')) != NULL)
360 g_strdown (corrected);
366 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_current_locale (MonoCultureInfo *ci)
373 locale = get_current_locale_name ();
377 ret = construct_culture_from_specific_name (ci, locale);
384 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_lcid (MonoCultureInfo *this,
387 const CultureInfoEntry *ci;
391 ci = culture_info_entry_from_lcid (lcid);
395 return construct_culture (this, ci);
399 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_name (MonoCultureInfo *this,
402 const CultureInfoNameEntry *ne;
407 n = mono_string_to_utf8 (name);
408 ne = bsearch (n, culture_name_entries, NUM_CULTURE_ENTRIES,
409 sizeof (CultureInfoNameEntry), culture_name_locator);
412 g_print ("ne (%s) is null\n", n);
418 return construct_culture (this, &culture_entries [ne->culture_entry_index]);
422 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_specific_name (MonoCultureInfo *ci,
430 locale = mono_string_to_utf8 (name);
431 ret = construct_culture_from_specific_name (ci, locale);
438 ves_icall_System_Globalization_CultureInfo_internal_get_cultures (MonoBoolean neutral,
439 MonoBoolean specific, MonoBoolean installed)
443 MonoCultureInfo *culture;
445 const CultureInfoEntry *ci;
451 domain = mono_domain_get ();
454 for (i = 0; i < NUM_CULTURE_ENTRIES; i++) {
455 ci = &culture_entries [i];
456 is_neutral = ((ci->lcid & 0xff00) == 0 || ci->specific_lcid == 0);
457 if ((neutral && is_neutral) || (specific && !is_neutral))
461 class = mono_class_from_name (mono_get_corlib (),
462 "System.Globalization", "CultureInfo");
463 ret = mono_array_new (domain, class, len);
469 for (i = 0; i < NUM_CULTURE_ENTRIES; i++) {
470 ci = &culture_entries [i];
471 is_neutral = ((ci->lcid & 0xff00) == 0 || ci->specific_lcid == 0);
472 if ((neutral && is_neutral) || (specific && !is_neutral)) {
473 culture = (MonoCultureInfo *) mono_object_new (domain, class);
474 mono_runtime_object_init ((MonoObject *) culture);
475 construct_culture (culture, ci);
476 mono_array_set (ret, MonoCultureInfo *, len++, culture);
484 * ves_icall_System_Globalization_CultureInfo_internal_is_lcid_neutral:
486 * Set is_neutral and return TRUE if the culture is found. If it is not found return FALSE.
489 ves_icall_System_Globalization_CultureInfo_internal_is_lcid_neutral (gint lcid, MonoBoolean *is_neutral)
491 const CultureInfoEntry *entry;
495 entry = culture_info_entry_from_lcid (lcid);
500 *is_neutral = (entry->specific_lcid == 0);
507 #include <unicode/utypes.h>
508 #include <unicode/ustring.h>
509 #include <unicode/ures.h>
510 #include <unicode/ucol.h>
511 #include <unicode/usearch.h>
513 static MonoString *monostring_from_resource_index (const UResourceBundle *bundle, int32_t idx)
520 res_str=(gunichar2 *)ures_getStringByIndex (bundle, idx, &res_strlen,
526 return(mono_string_from_utf16 (res_str));
529 static UResourceBundle *open_subbundle (const UResourceBundle *bundle,
530 const char *name, int32_t req_count)
532 UResourceBundle *subbundle;
537 subbundle=ures_getByKey (bundle, name, NULL, &ec);
539 /* Couldn't find the subbundle */
543 count=ures_countArrayItems (bundle, name, &ec);
545 /* Couldn't count the subbundle */
546 ures_close (subbundle);
550 if(count!=req_count) {
552 ures_close (subbundle);
559 static MonoArray *build_array (const UResourceBundle *bundle,
560 const char *resname, int32_t req_count)
563 UResourceBundle *subbundle;
566 subbundle=open_subbundle (bundle, resname, req_count);
567 if(subbundle!=NULL) {
568 arr=mono_array_new(mono_domain_get (),
569 mono_get_string_class (), req_count);
571 for(i=0; i<req_count; i++) {
572 mono_array_set(arr, MonoString *, i, monostring_from_resource_index (subbundle, i));
575 ures_close (subbundle);
581 static MonoDateTimeFormatInfo *create_DateTimeFormat (const char *locale)
583 MonoDateTimeFormatInfo *new_dtf;
585 UResourceBundle *bundle, *subbundle;
588 class=mono_class_from_name (mono_get_corlib (),
589 "System.Globalization",
590 "DateTimeFormatInfo");
591 new_dtf=(MonoDateTimeFormatInfo *)mono_object_new (mono_domain_get (),
593 mono_runtime_object_init ((MonoObject *)new_dtf);
597 bundle=ures_open (NULL, locale, &ec);
603 subbundle=open_subbundle (bundle, "AmPmMarkers", 2);
604 if(subbundle!=NULL) {
605 new_dtf->AMDesignator=monostring_from_resource_index (subbundle, 0);
606 new_dtf->PMDesignator=monostring_from_resource_index (subbundle, 1);
608 ures_close (subbundle);
611 /* Date/Time patterns. Don't set FullDateTimePattern. As it
612 * seems to always default to LongDatePattern + " " +
613 * LongTimePattern, let the property accessor deal with it.
615 subbundle=open_subbundle (bundle, "DateTimePatterns", 9);
616 if(subbundle!=NULL) {
617 new_dtf->ShortDatePattern=monostring_from_resource_index (subbundle, 7);
618 new_dtf->LongDatePattern=monostring_from_resource_index (subbundle, 5);
619 new_dtf->ShortTimePattern=monostring_from_resource_index (subbundle, 3);
620 new_dtf->LongTimePattern=monostring_from_resource_index (subbundle, 2);
622 /* RFC1123Pattern, SortableDateTimePattern and
623 * UniversalSortableDateTimePattern all seem to be
624 * constant, and all the same as the invariant default
628 ures_close (subbundle);
632 /* Not sure what to do with these yet, so leave them set to
633 * the invariant default
635 set_field_string (new_dtf, "_DateSeparator", str);
636 set_field_string (new_dtf, "_TimeSeparator", str);
637 set_field_string (new_dtf, "_MonthDayPattern", str);
638 set_field_string (new_dtf, "_YearMonthPattern", str);
641 /* Day names. Luckily both ICU and .net start Sunday at index 0 */
642 new_dtf->DayNames=build_array (bundle, "DayNames", 7);
644 /* Abbreviated day names */
645 new_dtf->AbbreviatedDayNames=build_array (bundle, "DayAbbreviations",
649 new_dtf->MonthNames=build_array (bundle, "MonthNames", 12);
651 /* Abbreviated month names */
652 new_dtf->AbbreviatedMonthNames=build_array (bundle,
653 "MonthAbbreviations", 12);
655 /* TODO: DayOfWeek _FirstDayOfWeek, Calendar _Calendar, CalendarWeekRule _CalendarWeekRule */
662 static MonoNumberFormatInfo *create_NumberFormat (const char *locale)
664 MonoNumberFormatInfo *new_nf;
666 MonoMethodDesc* methodDesc;
668 UResourceBundle *bundle, *subbundle, *table_entries;
671 static char country [7]; /* FIXME */
672 const UChar *res_str;
675 class=mono_class_from_name (mono_get_corlib (),
676 "System.Globalization",
678 new_nf=(MonoNumberFormatInfo *)mono_object_new (mono_domain_get (),
680 mono_runtime_object_init ((MonoObject *)new_nf);
684 bundle=ures_open (NULL, locale, &ec);
689 /* Number Elements */
691 subbundle=ures_getByKey (bundle, "NumberElements", NULL, &ec);
693 /* Couldn't find the subbundle */
697 count=ures_countArrayItems (bundle, "NumberElements", &ec);
699 /* Couldn't count the subbundle */
700 ures_close (subbundle);
704 if(subbundle!=NULL) {
705 new_nf->numberDecimalSeparator=monostring_from_resource_index (subbundle, 0);
706 new_nf->numberGroupSeparator=monostring_from_resource_index (subbundle, 1);
707 new_nf->percentDecimalSeparator=monostring_from_resource_index (subbundle, 0);
708 new_nf->percentGroupSeparator=monostring_from_resource_index (subbundle, 1);
709 new_nf->percentSymbol=monostring_from_resource_index (subbundle, 3);
710 new_nf->zeroPattern=monostring_from_resource_index (subbundle, 4);
711 new_nf->digitPattern=monostring_from_resource_index (subbundle, 5);
712 new_nf->negativeSign=monostring_from_resource_index (subbundle, 6);
713 new_nf->perMilleSymbol=monostring_from_resource_index (subbundle, 8);
714 new_nf->positiveInfinitySymbol=monostring_from_resource_index (subbundle, 9);
715 /* we dont have this in CLDR, so copy it from positiveInfinitySymbol */
716 new_nf->negativeInfinitySymbol=monostring_from_resource_index (subbundle, 9);
717 new_nf->naNSymbol=monostring_from_resource_index (subbundle, 10);
718 new_nf->currencyDecimalSeparator=monostring_from_resource_index (subbundle, 0);
719 new_nf->currencyGroupSeparator=monostring_from_resource_index (subbundle, 1);
721 ures_close (subbundle);
724 /* get country name */
726 uloc_getCountry (locale, country, sizeof (country), &ec);
727 if (U_SUCCESS (ec)) {
729 /* find country name in root.CurrencyMap */
730 subbundle = ures_getByKey (bundle, "CurrencyMap", NULL, &ec);
731 if (U_SUCCESS (ec)) {
733 /* get currency id for specified country */
734 table_entries = ures_getByKey (subbundle, country, NULL, &ec);
735 if (U_SUCCESS (ec)) {
736 ures_close (subbundle);
739 res_str = ures_getStringByIndex (
740 table_entries, 0, &res_strlen, &ec);
742 /* now we have currency id string */
743 ures_close (table_entries);
745 u_UCharsToChars (res_str, country,
749 /* find currency string in locale data */
750 subbundle = ures_getByKey (
751 bundle, "Currencies",
754 if (U_SUCCESS (ec)) {
756 /* find currency symbol under specified currency id */
757 table_entries = ures_getByKey (subbundle, country, NULL, &ec);
758 if (U_SUCCESS (ec)) {
759 /* get the first string only,
760 * the second is international currency symbol (not used)*/
761 new_nf->currencySymbol=monostring_from_resource_index (table_entries, 0);
762 ures_close (table_entries);
764 ures_close (subbundle);
772 subbundle=open_subbundle (bundle, "NumberPatterns", 4);
773 if(subbundle!=NULL) {
774 new_nf->decimalFormats=monostring_from_resource_index (subbundle, 0);
775 new_nf->currencyFormats=monostring_from_resource_index (subbundle, 1);
776 new_nf->percentFormats=monostring_from_resource_index (subbundle, 2);
777 ures_close (subbundle);
779 /* calls InitPatterns to parse the patterns
781 methodDesc = mono_method_desc_new (
782 "System.Globalization.NumberFormatInfo:InitPatterns()",
784 method = mono_method_desc_search_in_class (methodDesc, class);
786 mono_runtime_invoke (method, new_nf, NULL, NULL);
788 g_warning (G_GNUC_PRETTY_FUNCTION ": Runtime mismatch with class lib! (Looking for System.Globalization.NumberFormatInfo:InitPatterns())");
797 static char *mono_string_to_icu_locale (MonoString *locale)
800 char *passed_locale, *icu_locale=NULL;
801 int32_t loc_len, ret;
803 passed_locale=mono_string_to_utf8 (locale);
806 ret=uloc_getName (passed_locale, NULL, 0, &ec);
807 if(ec==U_BUFFER_OVERFLOW_ERROR) {
810 icu_locale=(char *)g_malloc0 (sizeof(char)*loc_len);
811 ret=uloc_getName (passed_locale, icu_locale, loc_len, &ec);
813 g_free (passed_locale);
818 void ves_icall_System_Globalization_CultureInfo_construct_internal_locale (MonoCultureInfo *this, MonoString *locale)
824 int32_t str_len, ret;
828 icu_locale=mono_string_to_icu_locale (locale);
829 if(icu_locale==NULL) {
830 /* Something went wrong */
831 mono_raise_exception((MonoException *)mono_exception_from_name(mono_get_corlib (), "System", "SystemException"));
835 /* Fill in the static fields */
837 /* TODO: Calendar, InstalledUICulture, OptionalCalendars,
841 str_len=256; /* Should be big enough for anything */
842 str=(char *)g_malloc0 (sizeof(char)*str_len);
843 ustr=(UChar *)g_malloc0 (sizeof(UChar)*str_len);
847 ret=uloc_getDisplayName (icu_locale, "en", ustr, str_len, &ec);
848 if(U_SUCCESS (ec) && ret<str_len) {
849 this->englishname=mono_string_from_utf16 ((gunichar2 *)ustr);
852 ret=uloc_getDisplayName (icu_locale, uloc_getDefault (), ustr, str_len,
854 if(U_SUCCESS (ec) && ret<str_len) {
855 this->displayname=mono_string_from_utf16 ((gunichar2 *)ustr);
858 ret=uloc_getDisplayName (icu_locale, icu_locale, ustr, str_len, &ec);
859 if(U_SUCCESS (ec) && ret<str_len) {
860 this->nativename=mono_string_from_utf16 ((gunichar2 *)ustr);
863 this->iso3lang=mono_string_new_wrapper (uloc_getISO3Language (icu_locale));
865 ret=uloc_getLanguage (icu_locale, str, str_len, &ec);
866 if(U_SUCCESS (ec) && ret<str_len) {
867 this->iso2lang=mono_string_new_wrapper (str);
870 this->datetime_format=create_DateTimeFormat (icu_locale);
871 this->number_format=create_NumberFormat (icu_locale);
878 void ves_icall_System_Globalization_CompareInfo_construct_compareinfo (MonoCompareInfo *comp, MonoString *locale)
887 g_message (G_GNUC_PRETTY_FUNCTION ": Constructing collator for locale [%s]", mono_string_to_utf8 (locale));
890 icu_locale=mono_string_to_icu_locale (locale);
891 if(icu_locale==NULL) {
892 /* Something went wrong */
893 mono_raise_exception((MonoException *)mono_exception_from_name(mono_get_corlib (), "System", "SystemException"));
898 coll=ucol_open (icu_locale, &ec);
900 comp->ICU_collator=coll;
902 comp->ICU_collator=NULL;
908 /* Set up the collator to reflect the options required. Some of these
909 * options clash, as they adjust the collator strength level. Try to
910 * make later checks reduce the strength level, and attempt to take
911 * previous options into account.
913 * Don't bother to check the error returns when setting the
914 * attributes, as a failure here is hardly grounds to error out.
916 static void set_collator_options (UCollator *coll, gint32 options)
918 UErrorCode ec=U_ZERO_ERROR;
920 /* Set up the defaults */
921 ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE,
923 ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_OFF, &ec);
925 /* Do this first so other options will override the quaternary
926 * level strength setting if necessary
928 if(!(options & CompareOptions_IgnoreKanaType)) {
929 ucol_setAttribute (coll, UCOL_HIRAGANA_QUATERNARY_MODE,
931 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_QUATERNARY, &ec);
934 /* Word sort, the default */
935 if(!(options & CompareOptions_StringSort)) {
936 ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING,
938 /* Tertiary strength is the default, but it might have
939 * been set to quaternary above. (We don't want that
940 * here, because that will order all the punctuation
941 * first instead of just ignoring it.)
943 * Unfortunately, tertiary strength with
944 * ALTERNATE_HANDLING==SHIFTED means that '/' and '@'
945 * compare to equal, which has the nasty side effect
946 * of killing mcs :-( (We can't specify a
947 * culture-insensitive compare, because
948 * String.StartsWith doesn't have that option.)
950 * ALTERNATE_HANDLING==SHIFTED is needed to accomplish
951 * the word-sorting-ignoring-punctuation feature. So
952 * we have to live with the slightly mis-ordered
953 * punctuation and a working mcs...
955 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_QUATERNARY, &ec);
958 if(options & CompareOptions_IgnoreCase) {
959 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_SECONDARY, &ec);
960 ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE, &ec);
963 if(options & CompareOptions_IgnoreWidth) {
964 /* Kana width is a tertiary strength difference. This
965 * will totally break the !IgnoreKanaType option
967 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_SECONDARY, &ec);
970 if(options & CompareOptions_IgnoreNonSpace) {
971 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_PRIMARY, &ec);
972 /* We can still compare case even when just checking
975 if(!(options & CompareOptions_IgnoreCase) ||
976 !(options & CompareOptions_IgnoreWidth)) {
977 /* Not sure if CASE_LEVEL handles kana width
979 ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_ON,
984 if(options & CompareOptions_IgnoreSymbols) {
985 /* Don't know what to do here */
988 if(options == CompareOptions_Ordinal) {
989 /* This one is handled elsewhere */
993 gint32 ves_icall_System_Globalization_CompareInfo_internal_compare (MonoCompareInfo *this, MonoString *str1, gint32 off1, gint32 len1, MonoString *str2, gint32 off2, gint32 len2, gint32 options)
996 UCollationResult result;
1001 g_message (G_GNUC_PRETTY_FUNCTION ": Comparing [%s] and [%s]", mono_string_to_utf8 (str1), mono_string_to_utf8 (str2));
1004 coll=this->ICU_collator;
1007 g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", this->lcid);
1010 if(coll==NULL || this->lcid==0x007F ||
1011 options & CompareOptions_Ordinal) {
1013 g_message (G_GNUC_PRETTY_FUNCTION ": No collator or invariant, using shortcut");
1016 return(string_invariant_compare (str1, off1, len1, str2, off2,
1020 if (!mono_monitor_enter ((MonoObject *)this))
1023 set_collator_options (coll, options);
1025 result=ucol_strcoll (coll, mono_string_chars (str1)+off1, len1,
1026 mono_string_chars (str2)+off2, len2);
1028 mono_monitor_exit ((MonoObject *)this);
1031 g_message (G_GNUC_PRETTY_FUNCTION ": Comparison of [%s] and [%s] returning %d", mono_string_to_utf8 (str1), mono_string_to_utf8 (str2), result);
1037 void ves_icall_System_Globalization_CompareInfo_free_internal_collator (MonoCompareInfo *this)
1041 MONO_ARCH_SAVE_REGS;
1043 coll=this->ICU_collator;
1045 this->ICU_collator = NULL;
1050 void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoCompareInfo *this, MonoSortKey *key, MonoString *source, gint32 options)
1057 MONO_ARCH_SAVE_REGS;
1059 coll=this->ICU_collator;
1061 mono_raise_exception((MonoException *)mono_exception_from_name(mono_get_corlib (), "System", "SystemException"));
1065 if (!mono_monitor_enter ((MonoObject *)this))
1068 set_collator_options (coll, options);
1070 keylen=ucol_getSortKey (coll, mono_string_chars (source),
1071 mono_string_length (source), NULL, 0);
1072 keybuf=g_malloc (sizeof(char)* keylen);
1073 ucol_getSortKey (coll, mono_string_chars (source),
1074 mono_string_length (source), keybuf, keylen);
1076 mono_monitor_exit ((MonoObject *)this);
1078 arr=mono_array_new (mono_domain_get (), mono_get_byte_class (),
1080 for(i=0; i<keylen; i++) {
1081 mono_array_set (arr, guint8, i, keybuf[i]);
1089 int ves_icall_System_Globalization_CompareInfo_internal_index (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, MonoString *value, gint32 options, MonoBoolean first)
1094 UStringSearch *search;
1097 MONO_ARCH_SAVE_REGS;
1100 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);
1103 coll=this->ICU_collator;
1106 g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", this->lcid);
1109 if(coll==NULL || this->lcid==0x007F ||
1110 options & CompareOptions_Ordinal) {
1112 g_message (G_GNUC_PRETTY_FUNCTION ": No collator or invariant, using shortcut");
1115 return(string_invariant_indexof (source, sindex, count, value,
1119 usrcstr=g_malloc0 (sizeof(UChar)*(count+1));
1121 memcpy (usrcstr, mono_string_chars (source)+sindex,
1122 sizeof(UChar)*count);
1124 memcpy (usrcstr, mono_string_chars (source)+sindex-count+1,
1125 sizeof(UChar)*count);
1128 if (!mono_monitor_enter ((MonoObject *)this))
1133 /* Need to set the collator to a fairly weak level, so that it
1134 * treats characters that can be written differently as
1135 * identical (eg "ß" and "ss", "æ" and "ae" or "ä" etc.) Note
1136 * that this means that the search string and the original
1137 * text might have differing lengths.
1139 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_PRIMARY, &ec);
1141 /* Still notice case differences though (normally a tertiary
1144 ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_ON, &ec);
1146 /* Don't ignore some codepoints */
1147 ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE,
1150 search=usearch_openFromCollator (mono_string_chars (value),
1151 mono_string_length (value),
1152 usrcstr, count, coll, NULL, &ec);
1153 if(U_SUCCESS (ec)) {
1155 pos=usearch_first (search, &ec);
1157 pos=usearch_last (search, &ec);
1160 while (pos!=USEARCH_DONE) {
1163 UCollationResult uret;
1166 g_message (G_GNUC_PRETTY_FUNCTION
1167 ": Got potential match at %d (sindex %d) len %d", pos, sindex, usearch_getMatchedLength (search));
1170 /* ICU usearch currently ignores most of the
1171 * collator attributes :-(
1173 * Check the returned match to see if it
1174 * really does match properly...
1176 match_len = usearch_getMatchedLength (search);
1177 match=(UChar *)g_malloc0 (sizeof(UChar) * (match_len + 1));
1178 usearch_getMatchedText (search, match, match_len, &ec);
1180 uret = ucol_strcoll (coll, match, match_len,
1181 mono_string_chars (value),
1182 mono_string_length (value));
1185 if (uret == UCOL_EQUAL) {
1186 /* OK, we really did get a match */
1188 g_message (G_GNUC_PRETTY_FUNCTION
1189 ": Got match at %d len %d", pos,
1197 pos+=(sindex-count+1);
1204 /* False alarm, keep looking */
1206 pos=usearch_next (search, &ec);
1208 pos=usearch_previous (search, &ec);
1212 g_message (G_GNUC_PRETTY_FUNCTION ": usearch_open error: %s",
1216 usearch_close (search);
1218 mono_monitor_exit ((MonoObject *)this);
1225 int ves_icall_System_Globalization_CompareInfo_internal_index_char (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, gunichar2 value, gint32 options, MonoBoolean first)
1228 UChar *usrcstr, uvalstr[2]={0, 0};
1230 UStringSearch *search;
1233 MONO_ARCH_SAVE_REGS;
1236 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);
1239 coll=this->ICU_collator;
1242 g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", this->lcid);
1245 if(coll==NULL || this->lcid==0x007F ||
1246 options & CompareOptions_Ordinal) {
1248 g_message (G_GNUC_PRETTY_FUNCTION ": No collator or invariant, using shortcut");
1251 return(string_invariant_indexof_char (source, sindex, count,
1255 usrcstr=g_malloc0 (sizeof(UChar)*(count+1));
1257 memcpy (usrcstr, mono_string_chars (source)+sindex,
1258 sizeof(UChar)*count);
1260 memcpy (usrcstr, mono_string_chars (source)+sindex-count+1,
1261 sizeof(UChar)*count);
1265 if (!mono_monitor_enter ((MonoObject *)this))
1270 /* Need to set the collator to a fairly weak level, so that it
1271 * treats characters that can be written differently as
1272 * identical (eg "ß" and "ss", "æ" and "ae" or "ä" etc.) Note
1273 * that this means that the search string and the original
1274 * text might have differing lengths.
1276 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_PRIMARY, &ec);
1278 /* Still notice case differences though (normally a tertiary
1281 ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_ON, &ec);
1283 /* Don't ignore some codepoints */
1284 ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE,
1287 search=usearch_openFromCollator (uvalstr, 1, usrcstr, count, coll,
1289 if(U_SUCCESS (ec)) {
1291 pos=usearch_first (search, &ec);
1293 pos=usearch_last (search, &ec);
1296 while (pos!=USEARCH_DONE) {
1299 UCollationResult uret;
1302 g_message (G_GNUC_PRETTY_FUNCTION
1303 ": Got potential match at %d (sindex %d) len %d", pos, sindex, usearch_getMatchedLength (search));
1306 /* ICU usearch currently ignores most of the
1307 * collator attributes :-(
1309 * Check the returned match to see if it
1310 * really does match properly...
1312 match_len = usearch_getMatchedLength (search);
1313 match=(UChar *)g_malloc0 (sizeof(UChar) * (match_len + 1));
1314 usearch_getMatchedText (search, match, match_len, &ec);
1316 uret = ucol_strcoll (coll, match, match_len, uvalstr,
1320 if (uret == UCOL_EQUAL) {
1321 /* OK, we really did get a match */
1323 g_message (G_GNUC_PRETTY_FUNCTION
1324 ": Got match at %d len %d", pos,
1332 pos+=(sindex-count+1);
1339 /* False alarm, keep looking */
1341 pos=usearch_next (search, &ec);
1343 pos=usearch_previous (search, &ec);
1347 g_message (G_GNUC_PRETTY_FUNCTION ": usearch_open error: %s",
1351 usearch_close (search);
1353 mono_monitor_exit ((MonoObject *)this);
1360 int ves_icall_System_Threading_Thread_current_lcid (void)
1362 MONO_ARCH_SAVE_REGS;
1364 return(uloc_getLCID (uloc_getDefault ()));
1367 MonoString *ves_icall_System_String_InternalReplace_Str_Comp (MonoString *this, MonoString *old, MonoString *new, MonoCompareInfo *comp)
1369 MonoString *ret=NULL;
1372 UStringSearch *search;
1374 MONO_ARCH_SAVE_REGS;
1377 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));
1380 coll=comp->ICU_collator;
1383 g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", comp->lcid);
1386 if(coll==NULL || comp->lcid==0x007F) {
1388 g_message (G_GNUC_PRETTY_FUNCTION ": No collator or invariant, using shortcut");
1391 return(string_invariant_replace (this, old, new));
1394 if (!mono_monitor_enter ((MonoObject *)comp))
1399 /* Need to set the collator to a fairly weak level, so that it
1400 * treats characters that can be written differently as
1401 * identical (eg "ß" and "ss", "æ" and "ae" or "ä" etc.) Note
1402 * that this means that the search string and the original
1403 * text might have differing lengths.
1405 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_PRIMARY, &ec);
1407 /* Still notice case differences though (normally a tertiary
1410 ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_ON, &ec);
1412 /* Don't ignore some codepoints */
1413 ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE,
1416 search=usearch_openFromCollator (mono_string_chars (old),
1417 mono_string_length (old),
1418 mono_string_chars (this),
1419 mono_string_length (this),
1421 if(U_SUCCESS (ec)) {
1422 int pos, oldpos, len_delta=0;
1423 int32_t newstr_len=mono_string_length (new), match_len;
1424 UChar *uret, *match;
1426 for(pos=usearch_first (search, &ec);
1428 pos=usearch_next (search, &ec)) {
1429 /* ICU usearch currently ignores most of the collator
1432 * Check the returned match to see if it really
1433 * does match properly...
1435 match_len = usearch_getMatchedLength (search);
1437 if(match_len == 0) {
1441 match=(UChar *)g_malloc0 (sizeof(UChar) * (match_len + 1));
1442 usearch_getMatchedText (search, match, match_len, &ec);
1444 if (ucol_strcoll (coll, match, match_len,
1445 mono_string_chars (old),
1446 mono_string_length (old)) == UCOL_EQUAL) {
1447 /* OK, we really did get a match */
1449 g_message (G_GNUC_PRETTY_FUNCTION
1450 ": Got match at %d len %d", pos,
1454 len_delta += (newstr_len - match_len);
1458 g_message (G_GNUC_PRETTY_FUNCTION
1459 ": Got false match at %d len %d",
1466 g_message (G_GNUC_PRETTY_FUNCTION
1467 ": New string length is %d (delta %d)",
1468 mono_string_length (this)+len_delta, len_delta);
1471 uret=(UChar *)g_malloc0 (sizeof(UChar) * (mono_string_length (this)+len_delta+2));
1473 for(oldpos=0, pos=usearch_first (search, &ec);
1475 pos=usearch_next (search, &ec)) {
1476 match_len = usearch_getMatchedLength (search);
1478 if (match_len == 0) {
1482 match=(UChar *)g_malloc0 (sizeof(UChar) * (match_len + 1));
1483 usearch_getMatchedText (search, match, match_len, &ec);
1485 /* Add the unmatched text */
1486 u_strncat (uret, mono_string_chars (this)+oldpos,
1488 if (ucol_strcoll (coll, match, match_len,
1489 mono_string_chars (old),
1490 mono_string_length (old)) == UCOL_EQUAL) {
1491 /* Then the replacement */
1492 u_strcat (uret, mono_string_chars (new));
1494 /* Then the original, because this is a
1497 u_strncat (uret, mono_string_chars (this)+pos,
1500 oldpos=pos+match_len;
1504 /* Finish off with the trailing unmatched text */
1505 u_strcat (uret, mono_string_chars (this)+oldpos);
1507 ret=mono_string_from_utf16 ((gunichar2 *)uret);
1509 g_message (G_GNUC_PRETTY_FUNCTION ": usearch_open error: %s",
1513 usearch_close (search);
1515 mono_monitor_exit ((MonoObject *)comp);
1518 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));
1524 #else /* HAVE_ICU */
1526 void ves_icall_System_Globalization_CultureInfo_construct_internal_locale (MonoCultureInfo *this, MonoString *locale)
1528 MONO_ARCH_SAVE_REGS;
1530 /* Always claim "unknown locale" if we don't have ICU (only
1531 * called for non-invariant locales)
1533 mono_raise_exception((MonoException *)mono_exception_from_name(mono_get_corlib (), "System", "ArgumentException"));
1536 void ves_icall_System_Globalization_CompareInfo_construct_compareinfo (MonoCompareInfo *comp, MonoString *locale)
1538 /* Nothing to do here */
1541 int ves_icall_System_Globalization_CompareInfo_internal_compare (MonoCompareInfo *this, MonoString *str1, gint32 off1, gint32 len1, MonoString *str2, gint32 off2, gint32 len2, gint32 options)
1543 MONO_ARCH_SAVE_REGS;
1545 /* Do a normal ascii string compare, as we only know the
1546 * invariant locale if we dont have ICU
1548 return(string_invariant_compare (str1, off1, len1, str2, off2, len2,
1552 void ves_icall_System_Globalization_CompareInfo_free_internal_collator (MonoCompareInfo *this)
1554 /* Nothing to do here */
1557 void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoCompareInfo *this, MonoSortKey *key, MonoString *source, gint32 options)
1562 MONO_ARCH_SAVE_REGS;
1564 keylen=mono_string_length (source);
1566 arr=mono_array_new (mono_domain_get (), mono_get_byte_class (),
1568 for(i=0; i<keylen; i++) {
1569 mono_array_set (arr, guint8, i, mono_string_chars (source)[i]);
1575 int ves_icall_System_Globalization_CompareInfo_internal_index (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, MonoString *value, gint32 options, MonoBoolean first)
1577 MONO_ARCH_SAVE_REGS;
1579 return(string_invariant_indexof (source, sindex, count, value, first));
1582 int ves_icall_System_Globalization_CompareInfo_internal_index_char (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, gunichar2 value, gint32 options, MonoBoolean first)
1584 MONO_ARCH_SAVE_REGS;
1586 return(string_invariant_indexof_char (source, sindex, count, value,
1590 int ves_icall_System_Threading_Thread_current_lcid (void)
1592 MONO_ARCH_SAVE_REGS;
1598 MonoString *ves_icall_System_String_InternalReplace_Str_Comp (MonoString *this, MonoString *old, MonoString *new, MonoCompareInfo *comp)
1600 MONO_ARCH_SAVE_REGS;
1602 /* Do a normal ascii string compare and replace, as we only
1603 * know the invariant locale if we dont have ICU
1605 return(string_invariant_replace (this, old, new));
1608 #endif /* HAVE_ICU */
1610 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
1614 GUnicodeType c1type, c2type;
1616 /* Ordinal can not be mixed with other options, and must return the difference, not only -1, 0, 1 */
1617 if (options & CompareOptions_Ordinal)
1618 return (gint32) c1 - c2;
1620 c1type = g_unichar_type (c1);
1621 c2type = g_unichar_type (c2);
1623 if (options & CompareOptions_IgnoreCase) {
1624 result = (gint32) (c1type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c1) : c1) -
1625 (c2type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c2) : c2);
1628 * No options. Kana, symbol and spacing options don't
1629 * apply to the invariant culture.
1633 * FIXME: here we must use the information from c1type and c2type
1634 * to find out the proper collation, even on the InvariantCulture, the
1635 * sorting is not done by computing the unicode values, but their
1636 * actual sort order.
1638 result = (gint32) c1 - c2;
1641 return ((result < 0) ? -1 : (result > 0) ? 1 : 0);
1644 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
1645 gint32 len1, MonoString *str2,
1646 gint32 off2, gint32 len2,
1649 /* c translation of C# code from old string.cs.. :) */
1662 ustr1 = mono_string_chars(str1)+off1;
1663 ustr2 = mono_string_chars(str2)+off2;
1667 for (pos = 0; pos != length; pos++) {
1668 if (pos >= len1 || pos >= len2)
1671 charcmp = string_invariant_compare_char(ustr1[pos], ustr2[pos],
1678 /* the lesser wins, so if we have looped until length we just
1679 * need to check the last char
1681 if (pos == length) {
1682 return(string_invariant_compare_char(ustr1[pos - 1],
1683 ustr2[pos - 1], options));
1686 /* Test if one of the strings has been compared to the end */
1693 } else if (pos >= len2) {
1697 /* if not, check our last char only.. (can this happen?) */
1698 return(string_invariant_compare_char(ustr1[pos], ustr2[pos], options));
1701 static MonoString *string_invariant_replace (MonoString *me,
1702 MonoString *oldValue,
1703 MonoString *newValue)
1707 gunichar2 *dest=NULL; /* shut gcc up */
1709 gunichar2 *newstr=NULL; /* shut gcc up here too */
1720 oldstr = mono_string_chars(oldValue);
1721 oldstrlen = mono_string_length(oldValue);
1723 if (NULL != newValue) {
1724 newstr = mono_string_chars(newValue);
1725 newstrlen = mono_string_length(newValue);
1729 src = mono_string_chars(me);
1730 srclen = mono_string_length(me);
1732 if (oldstrlen != newstrlen) {
1734 while (i <= srclen - oldstrlen) {
1735 if (0 == memcmp(src + i, oldstr, oldstrlen * sizeof(gunichar2))) {
1744 newsize = srclen + ((newstrlen - oldstrlen) * occurr);
1750 while (i < srclen) {
1751 if (0 == memcmp(src + i, oldstr, oldstrlen * sizeof(gunichar2))) {
1753 ret = mono_string_new_size( mono_domain_get (), newsize);
1754 dest = mono_string_chars(ret);
1755 memcpy (dest, src, i * sizeof(gunichar2));
1757 if (newstrlen > 0) {
1758 memcpy(dest + destpos, newstr, newstrlen * sizeof(gunichar2));
1759 destpos += newstrlen;
1763 } else if (ret != NULL) {
1764 dest[destpos] = src[i];
1776 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
1777 gint32 count, MonoString *value,
1785 lencmpstr = mono_string_length(value);
1787 src = mono_string_chars(source);
1788 cmpstr = mono_string_chars(value);
1792 for(pos=sindex;pos <= sindex+count;pos++) {
1793 for(i=0;src[pos+i]==cmpstr[i];) {
1794 if(++i==lencmpstr) {
1802 for(pos=sindex-lencmpstr+1;pos>sindex-count;pos--) {
1803 if(memcmp (src+pos, cmpstr,
1804 lencmpstr*sizeof(gunichar2))==0) {
1813 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
1814 gint32 count, gunichar2 value,
1820 src = mono_string_chars(source);
1822 for (pos = sindex; pos != count + sindex; pos++) {
1823 if (src [pos] == value) {
1830 for (pos = sindex; pos > sindex - count; pos--) {
1831 if (src [pos] == value)