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 MonoString *string_invariant_tolower (MonoString *this);
47 static MonoString *string_invariant_toupper (MonoString *this);
48 static const CultureInfoEntry* culture_info_entry_from_lcid (int lcid);
51 culture_lcid_locator (const void *a, const void *b)
53 const CultureInfoEntry *aa = a;
54 const CultureInfoEntry *bb = b;
56 return (aa->lcid - bb->lcid);
60 culture_name_locator (const void *a, const void *b)
62 const CultureInfoNameEntry *aa = a;
63 const CultureInfoNameEntry *bb = b;
66 ret = strcmp (aa->name, bb->name);
72 create_group_sizes_array (const gint *gs, gint ml)
77 for (i = 0; i < ml; i++) {
83 ret = mono_array_new (mono_domain_get (),
84 mono_defaults.int32_class, len);
86 for(i = 0; i < len; i++)
87 mono_array_set (ret, gint32, i, gs [i]);
93 create_names_array (const gchar **names, int ml)
102 domain = mono_domain_get ();
104 for (i = 0; i < ml; i++) {
105 if (names [i] == NULL)
110 ret = mono_array_new (mono_domain_get (), mono_defaults.string_class, len);
112 for(i = 0; i < len; i++)
113 mono_array_set (ret, MonoString *, i, mono_string_new (domain, names [i]));
119 ves_icall_System_Globalization_CultureInfo_construct_datetime_format (MonoCultureInfo *this)
122 MonoDateTimeFormatInfo *datetime;
123 const DateTimeFormatEntry *dfe;
127 g_assert (this->datetime_index >= 0);
129 datetime = this->datetime_format;
130 dfe = &datetime_format_entries [this->datetime_index];
132 domain = mono_domain_get ();
134 datetime->AbbreviatedDayNames = create_names_array (dfe->abbreviated_day_names,
136 datetime->AbbreviatedMonthNames = create_names_array (dfe->abbreviated_month_names,
138 datetime->AMDesignator = mono_string_new (domain, dfe->am_designator);
139 datetime->CalendarWeekRule = dfe->calendar_week_rule;
140 datetime->DateSeparator = mono_string_new (domain, dfe->date_separator);
141 datetime->DayNames = create_names_array (dfe->day_names, NUM_DAYS);
142 datetime->FirstDayOfWeek = dfe->first_day_of_week;
143 datetime->FullDateTimePattern = mono_string_new (domain, dfe->full_date_time_pattern);
144 datetime->LongDatePattern = mono_string_new (domain, dfe->long_date_pattern);
145 datetime->LongTimePattern = mono_string_new (domain, dfe->long_time_pattern);
146 datetime->MonthDayPattern = mono_string_new (domain, dfe->month_day_pattern);
147 datetime->MonthNames = create_names_array (dfe->month_names, NUM_MONTHS);
148 datetime->PMDesignator = mono_string_new (domain, dfe->pm_designator);
149 datetime->ShortDatePattern = mono_string_new (domain, dfe->short_date_pattern);
150 datetime->ShortTimePattern = mono_string_new (domain, dfe->short_time_pattern);
151 datetime->TimeSeparator = mono_string_new (domain, dfe->time_separator);
152 datetime->YearMonthPattern = mono_string_new (domain, dfe->year_month_pattern);
157 ves_icall_System_Globalization_CultureInfo_construct_number_format (MonoCultureInfo *this)
160 MonoNumberFormatInfo *number;
161 const NumberFormatEntry *nfe;
165 g_assert (this->number_format != 0);
167 number = this->number_format;
168 nfe = &number_format_entries [this->number_index];
170 domain = mono_domain_get ();
172 number->currencyDecimalDigits = nfe->currency_decimal_digits;
173 number->currencyDecimalSeparator = mono_string_new (domain,
174 nfe->currency_decimal_separator);
175 number->currencyGroupSeparator = mono_string_new (domain,
176 nfe->currency_group_separator);
177 number->currencyGroupSizes = create_group_sizes_array (nfe->currency_group_sizes,
179 number->currencyNegativePattern = nfe->currency_negative_pattern;
180 number->currencyPositivePattern = nfe->currency_positive_pattern;
181 number->currencySymbol = mono_string_new (domain, nfe->currency_symbol);
182 number->naNSymbol = mono_string_new (domain, nfe->nan_symbol);
183 number->negativeInfinitySymbol = mono_string_new (domain,
184 nfe->negative_infinity_symbol);
185 number->negativeSign = mono_string_new (domain, nfe->negative_sign);
186 number->numberDecimalDigits = nfe->number_decimal_digits;
187 number->numberDecimalSeparator = mono_string_new (domain,
188 nfe->number_decimal_separator);
189 number->numberGroupSeparator = mono_string_new (domain, nfe->number_group_separator);
190 number->numberGroupSizes = create_group_sizes_array (nfe->number_group_sizes,
192 number->numberNegativePattern = nfe->number_negative_pattern;
193 number->percentDecimalDigits = nfe->percent_decimal_digits;
194 number->percentDecimalSeparator = mono_string_new (domain,
195 nfe->percent_decimal_separator);
196 number->percentGroupSeparator = mono_string_new (domain,
197 nfe->percent_group_separator);
198 number->percentGroupSizes = create_group_sizes_array (nfe->percent_group_sizes,
200 number->percentNegativePattern = nfe->percent_negative_pattern;
201 number->percentPositivePattern = nfe->percent_positive_pattern;
202 number->percentSymbol = mono_string_new (domain, nfe->percent_symbol);
203 number->perMilleSymbol = mono_string_new (domain, nfe->per_mille_symbol);
204 number->positiveInfinitySymbol = mono_string_new (domain,
205 nfe->positive_infinity_symbol);
206 number->positiveSign = mono_string_new (domain, nfe->positive_sign);
210 construct_culture (MonoCultureInfo *this, const CultureInfoEntry *ci)
212 MonoDomain *domain = mono_domain_get ();
214 this->lcid = ci->lcid;
215 this->name = mono_string_new (domain, ci->name);
216 this->icu_name = mono_string_new (domain, ci->icu_name);
217 this->displayname = mono_string_new (domain, ci->displayname);
218 this->englishname = mono_string_new (domain, ci->englishname);
219 this->nativename = mono_string_new (domain, ci->nativename);
220 this->win3lang = mono_string_new (domain, ci->win3lang);
221 this->iso3lang = mono_string_new (domain, ci->iso3lang);
222 this->iso2lang = mono_string_new (domain, ci->iso2lang);
223 this->parent_lcid = ci->parent_lcid;
224 this->specific_lcid = ci->specific_lcid;
225 this->datetime_index = ci->datetime_format_index;
226 this->number_index = ci->number_format_index;
227 this->calendar_data = ci->calendar_data;
233 construct_culture_from_specific_name (MonoCultureInfo *ci, gchar *name)
235 const CultureInfoEntry *entry;
236 CultureInfoNameEntry key;
237 const CultureInfoNameEntry *ne;
242 ne = bsearch (&key, culture_name_entries, NUM_CULTURE_ENTRIES,
243 sizeof (CultureInfoNameEntry), culture_name_locator);
248 entry = &culture_entries [ne->culture_entry_index];
250 /* try avoiding another lookup, often the culture is its own specific culture */
251 if (entry->lcid != entry->specific_lcid)
252 entry = culture_info_entry_from_lcid (entry->specific_lcid);
254 return construct_culture (ci, entry);
257 static const CultureInfoEntry*
258 culture_info_entry_from_lcid (int lcid)
260 const CultureInfoEntry *ci;
261 CultureInfoEntry key;
264 ci = bsearch (&key, culture_entries, NUM_CULTURE_ENTRIES, sizeof (CultureInfoEntry), culture_lcid_locator);
270 * The following two methods are modified from the ICU source code. (http://oss.software.ibm.com/icu)
271 * Copyright (c) 1995-2003 International Business Machines Corporation and others
272 * All rights reserved.
275 get_posix_locale (void)
277 const gchar* posix_locale = NULL;
279 posix_locale = g_getenv("LC_ALL");
280 if (posix_locale == 0) {
281 posix_locale = g_getenv("LANG");
282 if (posix_locale == 0) {
283 posix_locale = setlocale(LC_ALL, NULL);
287 if (posix_locale == NULL)
290 if ((strcmp ("C", posix_locale) == 0) || (strchr (posix_locale, ' ') != NULL)
291 || (strchr (posix_locale, '/') != NULL)) {
293 * HPUX returns 'C C C C C C C'
294 * Solaris can return /en_US/C/C/C/C/C on the second try.
295 * Maybe we got some garbage.
300 return g_strdup (posix_locale);
304 get_current_locale_name (void)
307 gchar *corrected = NULL;
311 #ifdef PLATFORM_WIN32
312 locale = g_win32_getlocale ();
314 locale = get_posix_locale ();
320 if ((p = strchr (locale, '.')) != NULL) {
321 /* assume new locale can't be larger than old one? */
322 corrected = malloc (strlen (locale));
323 strncpy (corrected, locale, p - locale);
324 corrected [p - locale] = 0;
326 /* do not copy after the @ */
327 if ((p = strchr (corrected, '@')) != NULL)
328 corrected [p - corrected] = 0;
331 /* Note that we scan the *uncorrected* ID. */
332 if ((p = strrchr (locale, '@')) != NULL) {
335 * In Mono we dont handle the '@' modifier because we do
336 * not have any cultures that use it. We just trim it
337 * off of the end of the name.
340 if (corrected == NULL) {
341 corrected = malloc (strlen (locale));
342 strncpy (corrected, locale, p - locale);
343 corrected [p - locale] = 0;
347 if (corrected == NULL)
352 if ((c = strchr (corrected, '_')) != NULL)
355 g_strdown (corrected);
361 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_current_locale (MonoCultureInfo *ci)
368 locale = get_current_locale_name ();
372 ret = construct_culture_from_specific_name (ci, locale);
379 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_lcid (MonoCultureInfo *this,
382 const CultureInfoEntry *ci;
386 ci = culture_info_entry_from_lcid (lcid);
390 return construct_culture (this, ci);
394 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_name (MonoCultureInfo *this,
397 CultureInfoNameEntry key;
398 const CultureInfoNameEntry *ne;
402 key.name = mono_string_to_utf8 (name);
403 ne = bsearch (&key, culture_name_entries, NUM_CULTURE_ENTRIES,
404 sizeof (CultureInfoNameEntry), culture_name_locator);
406 g_free ((gpointer) key.name);
409 g_print ("ne (%s) is null\n", key.name);
413 return construct_culture (this, &culture_entries [ne->culture_entry_index]);
417 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_specific_name (MonoCultureInfo *ci,
425 locale = mono_string_to_utf8 (name);
426 ret = construct_culture_from_specific_name (ci, locale);
433 ves_icall_System_Globalization_CultureInfo_internal_get_cultures (MonoBoolean neutral,
434 MonoBoolean specific, MonoBoolean installed)
438 MonoCultureInfo *culture;
440 const CultureInfoEntry *ci;
446 domain = mono_domain_get ();
449 for (i = 0; i < NUM_CULTURE_ENTRIES; i++) {
450 ci = &culture_entries [i];
451 is_neutral = ((ci->lcid & 0xff00) == 0 || ci->specific_lcid == 0);
452 if ((neutral && is_neutral) || (specific && !is_neutral))
456 class = mono_class_from_name (mono_defaults.corlib,
457 "System.Globalization", "CultureInfo");
458 ret = mono_array_new (domain, class, len);
464 for (i = 0; i < NUM_CULTURE_ENTRIES; i++) {
465 ci = &culture_entries [i];
466 is_neutral = ((ci->lcid & 0xff00) == 0 || ci->specific_lcid == 0);
467 if ((neutral && is_neutral) || (specific && !is_neutral)) {
468 culture = (MonoCultureInfo *) mono_object_new (domain, class);
469 mono_runtime_object_init ((MonoObject *) culture);
470 construct_culture (culture, ci);
471 mono_array_set (ret, MonoCultureInfo *, len++, culture);
479 * Set is_neutral and return TRUE if the culture is found. If it is not found return FALSE.
482 ves_icall_System_Globalization_CultureInfo_internal_is_lcid_neutral (gint lcid, MonoBoolean *is_neutral)
484 const CultureInfoEntry *entry;
488 entry = culture_info_entry_from_lcid (lcid);
493 *is_neutral = (entry->specific_lcid == 0);
500 #include <unicode/utypes.h>
501 #include <unicode/ustring.h>
502 #include <unicode/ures.h>
503 #include <unicode/ucol.h>
504 #include <unicode/usearch.h>
506 static MonoString *monostring_from_resource_index (const UResourceBundle *bundle, int32_t idx)
513 res_str=(gunichar2 *)ures_getStringByIndex (bundle, idx, &res_strlen,
519 return(mono_string_from_utf16 (res_str));
522 static UResourceBundle *open_subbundle (const UResourceBundle *bundle,
523 const char *name, int32_t req_count)
525 UResourceBundle *subbundle;
530 subbundle=ures_getByKey (bundle, name, NULL, &ec);
532 /* Couldn't find the subbundle */
536 count=ures_countArrayItems (bundle, name, &ec);
538 /* Couldn't count the subbundle */
539 ures_close (subbundle);
543 if(count!=req_count) {
545 ures_close (subbundle);
552 static MonoArray *build_array (const UResourceBundle *bundle,
553 const char *resname, int32_t req_count)
556 UResourceBundle *subbundle;
559 subbundle=open_subbundle (bundle, resname, req_count);
560 if(subbundle!=NULL) {
561 arr=mono_array_new(mono_domain_get (),
562 mono_defaults.string_class, req_count);
564 for(i=0; i<req_count; i++) {
565 mono_array_set(arr, MonoString *, i, monostring_from_resource_index (subbundle, i));
568 ures_close (subbundle);
574 static MonoDateTimeFormatInfo *create_DateTimeFormat (const char *locale)
576 MonoDateTimeFormatInfo *new_dtf;
578 UResourceBundle *bundle, *subbundle;
581 class=mono_class_from_name (mono_defaults.corlib,
582 "System.Globalization",
583 "DateTimeFormatInfo");
584 new_dtf=(MonoDateTimeFormatInfo *)mono_object_new (mono_domain_get (),
586 mono_runtime_object_init ((MonoObject *)new_dtf);
590 bundle=ures_open (NULL, locale, &ec);
596 subbundle=open_subbundle (bundle, "AmPmMarkers", 2);
597 if(subbundle!=NULL) {
598 new_dtf->AMDesignator=monostring_from_resource_index (subbundle, 0);
599 new_dtf->PMDesignator=monostring_from_resource_index (subbundle, 1);
601 ures_close (subbundle);
604 /* Date/Time patterns. Don't set FullDateTimePattern. As it
605 * seems to always default to LongDatePattern + " " +
606 * LongTimePattern, let the property accessor deal with it.
608 subbundle=open_subbundle (bundle, "DateTimePatterns", 9);
609 if(subbundle!=NULL) {
610 new_dtf->ShortDatePattern=monostring_from_resource_index (subbundle, 7);
611 new_dtf->LongDatePattern=monostring_from_resource_index (subbundle, 5);
612 new_dtf->ShortTimePattern=monostring_from_resource_index (subbundle, 3);
613 new_dtf->LongTimePattern=monostring_from_resource_index (subbundle, 2);
615 /* RFC1123Pattern, SortableDateTimePattern and
616 * UniversalSortableDateTimePattern all seem to be
617 * constant, and all the same as the invariant default
621 ures_close (subbundle);
625 /* Not sure what to do with these yet, so leave them set to
626 * the invariant default
628 set_field_string (new_dtf, "_DateSeparator", str);
629 set_field_string (new_dtf, "_TimeSeparator", str);
630 set_field_string (new_dtf, "_MonthDayPattern", str);
631 set_field_string (new_dtf, "_YearMonthPattern", str);
634 /* Day names. Luckily both ICU and .net start Sunday at index 0 */
635 new_dtf->DayNames=build_array (bundle, "DayNames", 7);
637 /* Abbreviated day names */
638 new_dtf->AbbreviatedDayNames=build_array (bundle, "DayAbbreviations",
642 new_dtf->MonthNames=build_array (bundle, "MonthNames", 12);
644 /* Abbreviated month names */
645 new_dtf->AbbreviatedMonthNames=build_array (bundle,
646 "MonthAbbreviations", 12);
648 /* TODO: DayOfWeek _FirstDayOfWeek, Calendar _Calendar, CalendarWeekRule _CalendarWeekRule */
655 static MonoNumberFormatInfo *create_NumberFormat (const char *locale)
657 MonoNumberFormatInfo *new_nf;
659 MonoMethodDesc* methodDesc;
661 UResourceBundle *bundle, *subbundle, *table_entries;
664 static char country [7]; //FIXME
665 const UChar *res_str;
668 class=mono_class_from_name (mono_defaults.corlib,
669 "System.Globalization",
671 new_nf=(MonoNumberFormatInfo *)mono_object_new (mono_domain_get (),
673 mono_runtime_object_init ((MonoObject *)new_nf);
677 bundle=ures_open (NULL, locale, &ec);
682 /* Number Elements */
684 subbundle=ures_getByKey (bundle, "NumberElements", NULL, &ec);
686 /* Couldn't find the subbundle */
690 count=ures_countArrayItems (bundle, "NumberElements", &ec);
692 /* Couldn't count the subbundle */
693 ures_close (subbundle);
697 if(subbundle!=NULL) {
698 new_nf->numberDecimalSeparator=monostring_from_resource_index (subbundle, 0);
699 new_nf->numberGroupSeparator=monostring_from_resource_index (subbundle, 1);
700 new_nf->percentDecimalSeparator=monostring_from_resource_index (subbundle, 0);
701 new_nf->percentGroupSeparator=monostring_from_resource_index (subbundle, 1);
702 new_nf->percentSymbol=monostring_from_resource_index (subbundle, 3);
703 new_nf->zeroPattern=monostring_from_resource_index (subbundle, 4);
704 new_nf->digitPattern=monostring_from_resource_index (subbundle, 5);
705 new_nf->negativeSign=monostring_from_resource_index (subbundle, 6);
706 new_nf->perMilleSymbol=monostring_from_resource_index (subbundle, 8);
707 new_nf->positiveInfinitySymbol=monostring_from_resource_index (subbundle, 9);
708 /* we dont have this in CLDR, so copy it from positiveInfinitySymbol */
709 new_nf->negativeInfinitySymbol=monostring_from_resource_index (subbundle, 9);
710 new_nf->naNSymbol=monostring_from_resource_index (subbundle, 10);
711 new_nf->currencyDecimalSeparator=monostring_from_resource_index (subbundle, 0);
712 new_nf->currencyGroupSeparator=monostring_from_resource_index (subbundle, 1);
714 ures_close (subbundle);
717 /* get country name */
719 uloc_getCountry (locale, country, sizeof (country), &ec);
720 if (U_SUCCESS (ec)) {
722 /* find country name in root.CurrencyMap */
723 subbundle = ures_getByKey (bundle, "CurrencyMap", NULL, &ec);
724 if (U_SUCCESS (ec)) {
726 /* get currency id for specified country */
727 table_entries = ures_getByKey (subbundle, country, NULL, &ec);
728 if (U_SUCCESS (ec)) {
729 ures_close (subbundle);
732 res_str = ures_getStringByIndex (
733 table_entries, 0, &res_strlen, &ec);
735 /* now we have currency id string */
736 ures_close (table_entries);
738 u_UCharsToChars (res_str, country,
742 /* find currency string in locale data */
743 subbundle = ures_getByKey (
744 bundle, "Currencies",
747 if (U_SUCCESS (ec)) {
749 /* find currency symbol under specified currency id */
750 table_entries = ures_getByKey (subbundle, country, NULL, &ec);
751 if (U_SUCCESS (ec)) {
752 /* get the first string only,
753 * the second is international currency symbol (not used)*/
754 new_nf->currencySymbol=monostring_from_resource_index (table_entries, 0);
755 ures_close (table_entries);
757 ures_close (subbundle);
765 subbundle=open_subbundle (bundle, "NumberPatterns", 4);
766 if(subbundle!=NULL) {
767 new_nf->decimalFormats=monostring_from_resource_index (subbundle, 0);
768 new_nf->currencyFormats=monostring_from_resource_index (subbundle, 1);
769 new_nf->percentFormats=monostring_from_resource_index (subbundle, 2);
770 ures_close (subbundle);
772 /* calls InitPatterns to parse the patterns
774 methodDesc = mono_method_desc_new (
775 "System.Globalization.NumberFormatInfo:InitPatterns()",
777 method = mono_method_desc_search_in_class (methodDesc, class);
779 mono_runtime_invoke (method, new_nf, NULL, NULL);
781 g_warning (G_GNUC_PRETTY_FUNCTION ": Runtime mismatch with class lib! (Looking for System.Globalization.NumberFormatInfo:InitPatterns())");
790 static char *mono_string_to_icu_locale (MonoString *locale)
793 char *passed_locale, *icu_locale=NULL;
794 int32_t loc_len, ret;
796 passed_locale=mono_string_to_utf8 (locale);
799 ret=uloc_getName (passed_locale, NULL, 0, &ec);
800 if(ec==U_BUFFER_OVERFLOW_ERROR) {
803 icu_locale=(char *)g_malloc0 (sizeof(char)*loc_len);
804 ret=uloc_getName (passed_locale, icu_locale, loc_len, &ec);
806 g_free (passed_locale);
811 void ves_icall_System_Globalization_CultureInfo_construct_internal_locale (MonoCultureInfo *this, MonoString *locale)
817 int32_t str_len, ret;
821 icu_locale=mono_string_to_icu_locale (locale);
822 if(icu_locale==NULL) {
823 /* Something went wrong */
824 mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "SystemException"));
828 /* Fill in the static fields */
830 /* TODO: Calendar, InstalledUICulture, OptionalCalendars,
834 str_len=256; /* Should be big enough for anything */
835 str=(char *)g_malloc0 (sizeof(char)*str_len);
836 ustr=(UChar *)g_malloc0 (sizeof(UChar)*str_len);
840 ret=uloc_getDisplayName (icu_locale, "en", ustr, str_len, &ec);
841 if(U_SUCCESS (ec) && ret<str_len) {
842 this->englishname=mono_string_from_utf16 ((gunichar2 *)ustr);
845 ret=uloc_getDisplayName (icu_locale, uloc_getDefault (), ustr, str_len,
847 if(U_SUCCESS (ec) && ret<str_len) {
848 this->displayname=mono_string_from_utf16 ((gunichar2 *)ustr);
851 ret=uloc_getDisplayName (icu_locale, icu_locale, ustr, str_len, &ec);
852 if(U_SUCCESS (ec) && ret<str_len) {
853 this->nativename=mono_string_from_utf16 ((gunichar2 *)ustr);
856 this->iso3lang=mono_string_new_wrapper (uloc_getISO3Language (icu_locale));
858 ret=uloc_getLanguage (icu_locale, str, str_len, &ec);
859 if(U_SUCCESS (ec) && ret<str_len) {
860 this->iso2lang=mono_string_new_wrapper (str);
863 this->datetime_format=create_DateTimeFormat (icu_locale);
864 this->number_format=create_NumberFormat (icu_locale);
871 void ves_icall_System_Globalization_CompareInfo_construct_compareinfo (MonoCompareInfo *comp, MonoString *locale)
880 g_message (G_GNUC_PRETTY_FUNCTION ": Constructing collator for locale [%s]", mono_string_to_utf8 (locale));
883 icu_locale=mono_string_to_icu_locale (locale);
884 if(icu_locale==NULL) {
885 /* Something went wrong */
886 mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "SystemException"));
891 coll=ucol_open (icu_locale, &ec);
893 comp->ICU_collator=coll;
895 comp->ICU_collator=NULL;
901 /* Set up the collator to reflect the options required. Some of these
902 * options clash, as they adjust the collator strength level. Try to
903 * make later checks reduce the strength level, and attempt to take
904 * previous options into account.
906 * Don't bother to check the error returns when setting the
907 * attributes, as a failure here is hardly grounds to error out.
909 static void set_collator_options (UCollator *coll, gint32 options)
911 UErrorCode ec=U_ZERO_ERROR;
913 /* Set up the defaults */
914 ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE,
916 ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_OFF, &ec);
918 /* Do this first so other options will override the quaternary
919 * level strength setting if necessary
921 if(!(options & CompareOptions_IgnoreKanaType)) {
922 ucol_setAttribute (coll, UCOL_HIRAGANA_QUATERNARY_MODE,
924 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_QUATERNARY, &ec);
927 /* Word sort, the default */
928 if(!(options & CompareOptions_StringSort)) {
929 ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING,
931 /* Tertiary strength is the default, but it might have
932 * been set to quaternary above. (We don't want that
933 * here, because that will order all the punctuation
934 * first instead of just ignoring it.)
936 * Unfortunately, tertiary strength with
937 * ALTERNATE_HANDLING==SHIFTED means that '/' and '@'
938 * compare to equal, which has the nasty side effect
939 * of killing mcs :-( (We can't specify a
940 * culture-insensitive compare, because
941 * String.StartsWith doesn't have that option.)
943 * ALTERNATE_HANDLING==SHIFTED is needed to accomplish
944 * the word-sorting-ignoring-punctuation feature. So
945 * we have to live with the slightly mis-ordered
946 * punctuation and a working mcs...
948 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_QUATERNARY, &ec);
951 if(options & CompareOptions_IgnoreCase) {
952 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_SECONDARY, &ec);
953 ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE, &ec);
956 if(options & CompareOptions_IgnoreWidth) {
957 /* Kana width is a tertiary strength difference. This
958 * will totally break the !IgnoreKanaType option
960 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_SECONDARY, &ec);
963 if(options & CompareOptions_IgnoreNonSpace) {
964 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_PRIMARY, &ec);
965 /* We can still compare case even when just checking
968 if(!(options & CompareOptions_IgnoreCase) ||
969 !(options & CompareOptions_IgnoreWidth)) {
970 /* Not sure if CASE_LEVEL handles kana width
972 ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_ON,
977 if(options & CompareOptions_IgnoreSymbols) {
978 /* Don't know what to do here */
981 if(options == CompareOptions_Ordinal) {
982 /* This one is handled elsewhere */
986 gint32 ves_icall_System_Globalization_CompareInfo_internal_compare (MonoCompareInfo *this, MonoString *str1, gint32 off1, gint32 len1, MonoString *str2, gint32 off2, gint32 len2, gint32 options)
989 UCollationResult result;
994 g_message (G_GNUC_PRETTY_FUNCTION ": Comparing [%s] and [%s]", mono_string_to_utf8 (str1), mono_string_to_utf8 (str2));
997 coll=this->ICU_collator;
1000 g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", this->lcid);
1003 if(coll==NULL || this->lcid==0x007F ||
1004 options & CompareOptions_Ordinal) {
1006 g_message (G_GNUC_PRETTY_FUNCTION ": No collator or invariant, using shortcut");
1009 return(string_invariant_compare (str1, off1, len1, str2, off2,
1013 if (!mono_monitor_try_enter ((MonoObject *)this, INFINITE))
1016 set_collator_options (coll, options);
1018 result=ucol_strcoll (coll, mono_string_chars (str1)+off1, len1,
1019 mono_string_chars (str2)+off2, len2);
1021 mono_monitor_exit ((MonoObject *)this);
1024 g_message (G_GNUC_PRETTY_FUNCTION ": Comparison of [%s] and [%s] returning %d", mono_string_to_utf8 (str1), mono_string_to_utf8 (str2), result);
1030 void ves_icall_System_Globalization_CompareInfo_free_internal_collator (MonoCompareInfo *this)
1034 MONO_ARCH_SAVE_REGS;
1036 coll=this->ICU_collator;
1042 void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoCompareInfo *this, MonoSortKey *key, MonoString *source, gint32 options)
1049 MONO_ARCH_SAVE_REGS;
1051 coll=this->ICU_collator;
1053 mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "SystemException"));
1057 if (!mono_monitor_try_enter ((MonoObject *)this, INFINITE))
1060 set_collator_options (coll, options);
1062 keylen=ucol_getSortKey (coll, mono_string_chars (source), -1, NULL, 0);
1063 keybuf=g_malloc (sizeof(char)* keylen);
1064 ucol_getSortKey (coll, mono_string_chars (source), -1, keybuf, keylen);
1066 mono_monitor_exit ((MonoObject *)this);
1068 arr=mono_array_new (mono_domain_get (), mono_defaults.byte_class,
1070 for(i=0; i<keylen; i++) {
1071 mono_array_set (arr, guint8, i, keybuf[i]);
1079 int ves_icall_System_Globalization_CompareInfo_internal_index (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, MonoString *value, gint32 options, MonoBoolean first)
1084 UStringSearch *search;
1087 MONO_ARCH_SAVE_REGS;
1090 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);
1093 coll=this->ICU_collator;
1096 g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", this->lcid);
1099 if(coll==NULL || this->lcid==0x007F ||
1100 options & CompareOptions_Ordinal) {
1102 g_message (G_GNUC_PRETTY_FUNCTION ": No collator or invariant, using shortcut");
1105 return(string_invariant_indexof (source, sindex, count, value,
1109 usrcstr=g_malloc0 (sizeof(UChar)*(count+1));
1111 memcpy (usrcstr, mono_string_chars (source)+sindex,
1112 sizeof(UChar)*count);
1114 memcpy (usrcstr, mono_string_chars (source)+sindex-count+1,
1115 sizeof(UChar)*count);
1118 if (!mono_monitor_try_enter ((MonoObject *)this, INFINITE))
1123 /* Need to set the collator to a fairly weak level, so that it
1124 * treats characters that can be written differently as
1125 * identical (eg "ß" and "ss", "æ" and "ae" or "ä" etc.) Note
1126 * that this means that the search string and the original
1127 * text might have differing lengths.
1129 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_PRIMARY, &ec);
1131 /* Still notice case differences though (normally a tertiary
1134 ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_ON, &ec);
1136 /* Don't ignore some codepoints */
1137 ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE,
1140 search=usearch_openFromCollator (mono_string_chars (value), -1, usrcstr, -1, coll, NULL,
1142 if(U_SUCCESS (ec)) {
1144 pos=usearch_first (search, &ec);
1146 pos=usearch_last (search, &ec);
1149 if(pos!=USEARCH_DONE) {
1151 g_message (G_GNUC_PRETTY_FUNCTION
1152 ": Got match at %d (sindex %d) len %d", pos,
1153 sindex, usearch_getMatchedLength (search));
1159 pos+=(sindex-count+1);
1164 g_message (G_GNUC_PRETTY_FUNCTION ": usearch_open error: %s",
1168 usearch_close (search);
1170 mono_monitor_exit ((MonoObject *)this);
1177 int ves_icall_System_Globalization_CompareInfo_internal_index_char (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, gunichar2 value, gint32 options, MonoBoolean first)
1180 UChar *usrcstr, uvalstr[2]={0, 0};
1182 UStringSearch *search;
1185 MONO_ARCH_SAVE_REGS;
1188 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);
1191 coll=this->ICU_collator;
1194 g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", this->lcid);
1197 if(coll==NULL || this->lcid==0x007F ||
1198 options & CompareOptions_Ordinal) {
1200 g_message (G_GNUC_PRETTY_FUNCTION ": No collator or invariant, using shortcut");
1203 return(string_invariant_indexof_char (source, sindex, count,
1207 usrcstr=g_malloc0 (sizeof(UChar)*(count+1));
1209 memcpy (usrcstr, mono_string_chars (source)+sindex,
1210 sizeof(UChar)*count);
1212 memcpy (usrcstr, mono_string_chars (source)+sindex-count+1,
1213 sizeof(UChar)*count);
1217 if (!mono_monitor_try_enter ((MonoObject *)this, INFINITE))
1222 /* Need to set the collator to a fairly weak level, so that it
1223 * treats characters that can be written differently as
1224 * identical (eg "ß" and "ss", "æ" and "ae" or "ä" etc.) Note
1225 * that this means that the search string and the original
1226 * text might have differing lengths.
1228 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_PRIMARY, &ec);
1230 /* Still notice case differences though (normally a tertiary
1233 ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_ON, &ec);
1235 /* Don't ignore some codepoints */
1236 ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE,
1239 search=usearch_openFromCollator (uvalstr, -1, usrcstr, -1, coll, NULL,
1241 if(U_SUCCESS (ec)) {
1243 pos=usearch_first (search, &ec);
1245 pos=usearch_last (search, &ec);
1248 if(pos!=USEARCH_DONE) {
1250 g_message (G_GNUC_PRETTY_FUNCTION
1251 ": Got match at %d (sindex %d) len %d", pos,
1252 sindex, usearch_getMatchedLength (search));
1258 pos+=(sindex-count+1);
1263 g_message (G_GNUC_PRETTY_FUNCTION ": usearch_open error: %s",
1267 usearch_close (search);
1269 mono_monitor_exit ((MonoObject *)this);
1276 int ves_icall_System_Threading_Thread_current_lcid (void)
1278 MONO_ARCH_SAVE_REGS;
1280 return(uloc_getLCID (uloc_getDefault ()));
1283 MonoString *ves_icall_System_String_InternalReplace_Str_Comp (MonoString *this, MonoString *old, MonoString *new, MonoCompareInfo *comp)
1285 MonoString *ret=NULL;
1288 UStringSearch *search;
1290 MONO_ARCH_SAVE_REGS;
1293 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));
1296 coll=comp->ICU_collator;
1299 g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", comp->lcid);
1302 if(coll==NULL || comp->lcid==0x007F) {
1304 g_message (G_GNUC_PRETTY_FUNCTION ": No collator or invariant, using shortcut");
1307 return(string_invariant_replace (this, old, new));
1310 if (!mono_monitor_try_enter ((MonoObject *)comp, INFINITE))
1315 /* Need to set the collator to a fairly weak level, so that it
1316 * treats characters that can be written differently as
1317 * identical (eg "ß" and "ss", "æ" and "ae" or "ä" etc.) Note
1318 * that this means that the search string and the original
1319 * text might have differing lengths.
1321 ucol_setAttribute (coll, UCOL_STRENGTH, UCOL_PRIMARY, &ec);
1323 /* Still notice case differences though (normally a tertiary
1326 ucol_setAttribute (coll, UCOL_CASE_LEVEL, UCOL_ON, &ec);
1328 /* Don't ignore some codepoints */
1329 ucol_setAttribute (coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE,
1332 search=usearch_openFromCollator (mono_string_chars (old), -1,
1333 mono_string_chars (this), -1, coll,
1335 if(U_SUCCESS (ec)) {
1336 int pos, oldpos, len_delta=0;
1337 int32_t newstr_len=mono_string_length (new), match_len;
1338 UChar *uret, *match;
1340 for(pos=usearch_first (search, &ec);
1342 pos=usearch_next (search, &ec)) {
1343 /* ICU usearch currently ignores most of the collator
1346 * Check the returned match to see if it really
1347 * does match properly...
1349 match_len = usearch_getMatchedLength (search);
1350 match=(UChar *)g_malloc0 (sizeof(UChar) * (match_len + 1));
1351 usearch_getMatchedText (search, match, match_len, &ec);
1353 if (ucol_strcoll (coll, match, -1, mono_string_chars (old), -1) == UCOL_EQUAL) {
1354 /* OK, we really did get a match */
1356 g_message (G_GNUC_PRETTY_FUNCTION
1357 ": Got match at %d len %d", pos,
1361 len_delta += (newstr_len - match_len);
1365 g_message (G_GNUC_PRETTY_FUNCTION
1366 ": Got false match at %d len %d",
1373 g_message (G_GNUC_PRETTY_FUNCTION
1374 ": New string length is %d (delta %d)",
1375 mono_string_length (this)+len_delta, len_delta);
1378 uret=(UChar *)g_malloc0 (sizeof(UChar) * (mono_string_length (this)+len_delta+2));
1380 for(oldpos=0, pos=usearch_first (search, &ec);
1382 pos=usearch_next (search, &ec)) {
1383 match_len = usearch_getMatchedLength (search);
1384 match=(UChar *)g_malloc0 (sizeof(UChar) * (match_len + 1));
1385 usearch_getMatchedText (search, match, match_len, &ec);
1387 /* Add the unmatched text */
1388 u_strncat (uret, mono_string_chars (this)+oldpos,
1390 if (ucol_strcoll (coll, match, -1, mono_string_chars (old), -1) == UCOL_EQUAL) {
1391 /* Then the replacement */
1392 u_strcat (uret, mono_string_chars (new));
1394 /* Then the original, because this is a
1397 u_strncat (uret, mono_string_chars (this)+pos,
1400 oldpos=pos+match_len;
1404 /* Finish off with the trailing unmatched text */
1405 u_strcat (uret, mono_string_chars (this)+oldpos);
1407 ret=mono_string_from_utf16 ((gunichar2 *)uret);
1409 g_message (G_GNUC_PRETTY_FUNCTION ": usearch_open error: %s",
1413 usearch_close (search);
1415 mono_monitor_exit ((MonoObject *)comp);
1418 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));
1424 MonoString *ves_icall_System_String_InternalToLower_Comp (MonoString *this, MonoCultureInfo *cult)
1432 MONO_ARCH_SAVE_REGS;
1435 g_message (G_GNUC_PRETTY_FUNCTION ": [%s]",
1436 mono_string_to_utf8 (this));
1440 g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", cult->lcid);
1443 icu_loc=mono_string_to_icu_locale (cult->icu_name);
1445 mono_raise_exception ((MonoException *)mono_exception_from_name (mono_defaults.corlib, "System", "SystemException"));
1449 udest=(UChar *)g_malloc0 (sizeof(UChar)*(mono_string_length (this)+1));
1451 /* According to the docs, this might result in a longer or
1452 * shorter string than we started with...
1456 len=u_strToLower (udest, mono_string_length (this)+1,
1457 mono_string_chars (this), -1, icu_loc, &ec);
1458 if(ec==U_BUFFER_OVERFLOW_ERROR ||
1459 ec==U_STRING_NOT_TERMINATED_WARNING) {
1461 udest=(UChar *)g_malloc0 (sizeof(UChar)*(len+1));
1462 len=u_strToLower (udest, len+1, mono_string_chars (this), -1,
1466 if(U_SUCCESS (ec)) {
1467 ret=mono_string_from_utf16 ((gunichar2 *)udest);
1469 g_message (G_GNUC_PRETTY_FUNCTION ": u_strToLower error: %s",
1471 /* return something */
1479 g_message (G_GNUC_PRETTY_FUNCTION ": returning [%s]",
1480 mono_string_to_utf8 (ret));
1486 MonoString *ves_icall_System_String_InternalToUpper_Comp (MonoString *this, MonoCultureInfo *cult)
1494 MONO_ARCH_SAVE_REGS;
1497 g_message (G_GNUC_PRETTY_FUNCTION ": [%s]",
1498 mono_string_to_utf8 (this));
1502 g_message (G_GNUC_PRETTY_FUNCTION ": LCID is %d", cult->lcid);
1505 icu_loc=mono_string_to_icu_locale (cult->icu_name);
1507 mono_raise_exception ((MonoException *)mono_exception_from_name (mono_defaults.corlib, "System", "SystemException"));
1511 udest=(UChar *)g_malloc0 (sizeof(UChar)*(mono_string_length (this)+1));
1513 /* According to the docs, this might result in a longer or
1514 * shorter string than we started with...
1518 len=u_strToUpper (udest, mono_string_length (this)+1,
1519 mono_string_chars (this), -1, icu_loc, &ec);
1520 if(ec==U_BUFFER_OVERFLOW_ERROR ||
1521 ec==U_STRING_NOT_TERMINATED_WARNING) {
1523 udest=(UChar *)g_malloc0 (sizeof(UChar)*(len+1));
1524 len=u_strToUpper (udest, len+1, mono_string_chars (this), -1,
1528 if(U_SUCCESS (ec)) {
1529 ret=mono_string_from_utf16 ((gunichar2 *)udest);
1531 g_message (G_GNUC_PRETTY_FUNCTION ": u_strToUpper error: %s",
1533 /* return something */
1541 g_message (G_GNUC_PRETTY_FUNCTION ": returning [%s]",
1542 mono_string_to_utf8 (ret));
1548 gunichar2 ves_icall_System_Char_InternalToUpper_Comp (gunichar2 c, MonoCultureInfo *cult)
1555 MONO_ARCH_SAVE_REGS;
1557 icu_loc=mono_string_to_icu_locale (cult->icu_name);
1559 mono_raise_exception ((MonoException *)mono_exception_from_name (mono_defaults.corlib, "System", "SystemException"));
1564 len=u_strToUpper (&udest, 1, &c, 1, icu_loc, &ec);
1566 if(U_SUCCESS (ec) && len==1) {
1569 /* return something */
1575 gunichar2 ves_icall_System_Char_InternalToLower_Comp (gunichar2 c, MonoCultureInfo *cult)
1582 MONO_ARCH_SAVE_REGS;
1584 icu_loc=mono_string_to_icu_locale (cult->icu_name);
1586 mono_raise_exception ((MonoException *)mono_exception_from_name (mono_defaults.corlib, "System", "SystemException"));
1591 len=u_strToLower (&udest, 1, &c, 1, icu_loc, &ec);
1593 if(U_SUCCESS (ec) && len==1) {
1596 /* return something */
1601 #else /* HAVE_ICU */
1602 void ves_icall_System_Globalization_CultureInfo_construct_internal_locale (MonoCultureInfo *this, MonoString *locale)
1604 MONO_ARCH_SAVE_REGS;
1606 /* Always claim "unknown locale" if we don't have ICU (only
1607 * called for non-invariant locales)
1609 mono_raise_exception((MonoException *)mono_exception_from_name(mono_defaults.corlib, "System", "ArgumentException"));
1612 void ves_icall_System_Globalization_CompareInfo_construct_compareinfo (MonoCompareInfo *comp, MonoString *locale)
1614 /* Nothing to do here */
1617 int ves_icall_System_Globalization_CompareInfo_internal_compare (MonoCompareInfo *this, MonoString *str1, gint32 off1, gint32 len1, MonoString *str2, gint32 off2, gint32 len2, gint32 options)
1619 MONO_ARCH_SAVE_REGS;
1621 /* Do a normal ascii string compare, as we only know the
1622 * invariant locale if we dont have ICU
1624 return(string_invariant_compare (str1, off1, len1, str2, off2, len2,
1628 void ves_icall_System_Globalization_CompareInfo_free_internal_collator (MonoCompareInfo *this)
1630 /* Nothing to do here */
1633 void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoCompareInfo *this, MonoSortKey *key, MonoString *source, gint32 options)
1638 MONO_ARCH_SAVE_REGS;
1640 keylen=mono_string_length (source);
1642 arr=mono_array_new (mono_domain_get (), mono_defaults.byte_class,
1644 for(i=0; i<keylen; i++) {
1645 mono_array_set (arr, guint8, i, mono_string_chars (source)[i]);
1651 int ves_icall_System_Globalization_CompareInfo_internal_index (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, MonoString *value, gint32 options, MonoBoolean first)
1653 MONO_ARCH_SAVE_REGS;
1655 return(string_invariant_indexof (source, sindex, count, value, first));
1658 int ves_icall_System_Globalization_CompareInfo_internal_index_char (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, gunichar2 value, gint32 options, MonoBoolean first)
1660 MONO_ARCH_SAVE_REGS;
1662 return(string_invariant_indexof_char (source, sindex, count, value,
1666 int ves_icall_System_Threading_Thread_current_lcid (void)
1668 MONO_ARCH_SAVE_REGS;
1674 MonoString *ves_icall_System_String_InternalReplace_Str_Comp (MonoString *this, MonoString *old, MonoString *new, MonoCompareInfo *comp)
1676 MONO_ARCH_SAVE_REGS;
1678 /* Do a normal ascii string compare and replace, as we only
1679 * know the invariant locale if we dont have ICU
1681 return(string_invariant_replace (this, old, new));
1684 MonoString *ves_icall_System_String_InternalToLower_Comp (MonoString *this, MonoCultureInfo *cult)
1686 MONO_ARCH_SAVE_REGS;
1688 return(string_invariant_tolower (this));
1691 MonoString *ves_icall_System_String_InternalToUpper_Comp (MonoString *this, MonoCultureInfo *cult)
1693 MONO_ARCH_SAVE_REGS;
1695 return(string_invariant_toupper (this));
1698 gunichar2 ves_icall_System_Char_InternalToUpper_Comp (gunichar2 c, MonoCultureInfo *cult)
1700 MONO_ARCH_SAVE_REGS;
1702 return g_unichar_toupper (c);
1706 gunichar2 ves_icall_System_Char_InternalToLower_Comp (gunichar2 c, MonoCultureInfo *cult)
1708 MONO_ARCH_SAVE_REGS;
1710 return g_unichar_tolower (c);
1713 #endif /* HAVE_ICU */
1715 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
1719 GUnicodeType c1type, c2type;
1721 c1type = g_unichar_type (c1);
1722 c2type = g_unichar_type (c2);
1724 if (options & CompareOptions_IgnoreCase) {
1725 result = (gint32) (c1type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c1) : c1) - (c2type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c2) : c2);
1726 } else if (options & CompareOptions_Ordinal) {
1727 // Rotor/ms return the full value just not -1 and 1
1728 return (gint32) c1 - c2;
1730 /* No options. Kana, symbol and spacing options don't
1731 * apply to the invariant culture.
1733 if (c1type == G_UNICODE_UPPERCASE_LETTER &&
1734 c2type == G_UNICODE_LOWERCASE_LETTER) {
1738 if (c1type == G_UNICODE_LOWERCASE_LETTER &&
1739 c2type == G_UNICODE_UPPERCASE_LETTER) {
1743 result = (gint32) c1 - c2;
1746 return ((result < 0) ? -1 : (result > 0) ? 1 : 0);
1749 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
1750 gint32 len1, MonoString *str2,
1751 gint32 off2, gint32 len2,
1754 /* c translation of C# code from old string.cs.. :) */
1767 ustr1 = mono_string_chars(str1)+off1;
1768 ustr2 = mono_string_chars(str2)+off2;
1772 for (pos = 0; pos != length; pos++) {
1773 if (pos >= len1 || pos >= len2)
1776 charcmp = string_invariant_compare_char(ustr1[pos], ustr2[pos],
1783 /* the lesser wins, so if we have looped until length we just
1784 * need to check the last char
1786 if (pos == length) {
1787 return(string_invariant_compare_char(ustr1[pos - 1],
1788 ustr2[pos - 1], options));
1791 /* Test if one of the strings has been compared to the end */
1798 } else if (pos >= len2) {
1802 /* if not, check our last char only.. (can this happen?) */
1803 return(string_invariant_compare_char(ustr1[pos], ustr2[pos], options));
1806 static MonoString *string_invariant_replace (MonoString *me,
1807 MonoString *oldValue,
1808 MonoString *newValue)
1812 gunichar2 *dest=NULL; /* shut gcc up */
1814 gunichar2 *newstr=NULL; /* shut gcc up here too */
1825 oldstr = mono_string_chars(oldValue);
1826 oldstrlen = mono_string_length(oldValue);
1828 if (NULL != newValue) {
1829 newstr = mono_string_chars(newValue);
1830 newstrlen = mono_string_length(newValue);
1834 src = mono_string_chars(me);
1835 srclen = mono_string_length(me);
1837 if (oldstrlen != newstrlen) {
1839 while (i <= srclen - oldstrlen) {
1840 if (0 == memcmp(src + i, oldstr, oldstrlen * sizeof(gunichar2))) {
1849 newsize = srclen + ((newstrlen - oldstrlen) * occurr);
1855 while (i < srclen) {
1856 if (0 == memcmp(src + i, oldstr, oldstrlen * sizeof(gunichar2))) {
1858 ret = mono_string_new_size( mono_domain_get (), newsize);
1859 dest = mono_string_chars(ret);
1860 memcpy (dest, src, i * sizeof(gunichar2));
1862 if (newstrlen > 0) {
1863 memcpy(dest + destpos, newstr, newstrlen * sizeof(gunichar2));
1864 destpos += newstrlen;
1868 } else if (ret != NULL) {
1869 dest[destpos] = src[i];
1881 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
1882 gint32 count, MonoString *value,
1890 lencmpstr = mono_string_length(value);
1892 src = mono_string_chars(source);
1893 cmpstr = mono_string_chars(value);
1897 for(pos=sindex;pos <= sindex+count;pos++) {
1898 for(i=0;src[pos+i]==cmpstr[i];) {
1899 if(++i==lencmpstr) {
1907 for(pos=sindex-lencmpstr+1;pos>sindex-count;pos--) {
1908 if(memcmp (src+pos, cmpstr,
1909 lencmpstr*sizeof(gunichar2))==0) {
1918 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
1919 gint32 count, gunichar2 value,
1925 src = mono_string_chars(source);
1927 for (pos = sindex; pos != count + sindex; pos++) {
1928 if (src [pos] == value) {
1935 for (pos = sindex; pos > sindex - count; pos--) {
1936 if (src [pos] == value)
1944 static MonoString *string_invariant_tolower (MonoString *this)
1951 ret = mono_string_new_size(mono_domain_get (),
1952 mono_string_length(this));
1954 src = mono_string_chars (this);
1955 dest = mono_string_chars (ret);
1957 for (i = 0; i < mono_string_length (this); ++i) {
1958 dest[i] = g_unichar_tolower(src[i]);
1964 static MonoString *string_invariant_toupper (MonoString *this)
1971 ret = mono_string_new_size(mono_domain_get (),
1972 mono_string_length(this));
1974 src = mono_string_chars (this);
1975 dest = mono_string_chars (ret);
1977 for (i = 0; i < mono_string_length (this); ++i) {
1978 dest[i] = g_unichar_toupper(src[i]);