2010-03-12 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / metadata / locales.c
blob928ce29026652761704311239dfa7c84fa1b7d43
1 /*
2 * locales.c: Culture-sensitive handling
4 * Author:
5 * Dick Porter (dick@ximian.com)
6 * Mohammad DAMT (mdamt@cdl2000.com)
8 * Copyright 2003 Ximian, Inc (http://www.ximian.com)
9 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
10 * (C) 2003 PT Cakram Datalingga Duaribu http://www.cdl2000.com
13 #include <config.h>
14 #include <glib.h>
15 #include <string.h>
17 #include <mono/metadata/debug-helpers.h>
18 #include <mono/metadata/object.h>
19 #include <mono/metadata/appdomain.h>
20 #include <mono/metadata/exception.h>
21 #include <mono/metadata/monitor.h>
22 #include <mono/metadata/locales.h>
23 #include <mono/metadata/culture-info.h>
24 #include <mono/metadata/culture-info-tables.h>
25 #include <mono/metadata/normalization-tables.h>
28 #include <locale.h>
29 #if defined(__APPLE__)
30 #include <CoreFoundation/CoreFoundation.h>
31 #endif
33 #undef DEBUG
35 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
36 gint32 options);
37 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
38 gint32 len1, MonoString *str2,
39 gint32 off2, gint32 len2,
40 gint32 options);
41 static MonoString *string_invariant_replace (MonoString *me,
42 MonoString *oldValue,
43 MonoString *newValue);
44 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
45 gint32 count, MonoString *value,
46 MonoBoolean first);
47 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
48 gint32 count, gunichar2 value,
49 MonoBoolean first);
51 static const CultureInfoEntry* culture_info_entry_from_lcid (int lcid);
53 static const RegionInfoEntry* region_info_entry_from_lcid (int lcid);
55 static int
56 culture_lcid_locator (const void *a, const void *b)
58 const CultureInfoEntry *aa = a;
59 const CultureInfoEntry *bb = b;
61 return (aa->lcid - bb->lcid);
64 static int
65 region_lcid_locator (const void *a, const void *b)
67 const int *lcid = a;
68 const CultureInfoEntry *bb = b;
70 return *lcid - bb->lcid;
73 static int
74 culture_name_locator (const void *a, const void *b)
76 const char *aa = a;
77 const CultureInfoNameEntry *bb = b;
78 int ret;
80 ret = strcmp (aa, idx2string (bb->name));
82 return ret;
85 static int
86 region_name_locator (const void *a, const void *b)
88 const char *aa = a;
89 const RegionInfoNameEntry *bb = b;
90 int ret;
92 ret = strcmp (aa, idx2string (bb->name));
94 return ret;
97 static MonoArray*
98 create_group_sizes_array (const gint *gs, gint ml)
100 MonoArray *ret;
101 int i, len = 0;
103 for (i = 0; i < ml; i++) {
104 if (gs [i] == -1)
105 break;
106 len++;
109 ret = mono_array_new_cached (mono_domain_get (),
110 mono_get_int32_class (), len);
112 for(i = 0; i < len; i++)
113 mono_array_set (ret, gint32, i, gs [i]);
115 return ret;
118 static MonoArray*
119 create_names_array_idx (const guint16 *names, int ml)
121 MonoArray *ret;
122 MonoDomain *domain;
123 int i, len = 0;
125 if (names == NULL)
126 return NULL;
128 domain = mono_domain_get ();
130 for (i = 0; i < ml; i++) {
131 if (names [i] == 0)
132 break;
133 len++;
136 ret = mono_array_new_cached (mono_domain_get (), mono_get_string_class (), len);
138 for(i = 0; i < len; i++)
139 mono_array_setref (ret, i, mono_string_new (domain, idx2string (names [i])));
141 return ret;
144 void
145 ves_icall_System_Globalization_CultureInfo_construct_datetime_format (MonoCultureInfo *this)
147 MonoDomain *domain;
148 MonoDateTimeFormatInfo *datetime;
149 const DateTimeFormatEntry *dfe;
151 MONO_ARCH_SAVE_REGS;
153 g_assert (this->datetime_index >= 0);
155 datetime = this->datetime_format;
156 dfe = &datetime_format_entries [this->datetime_index];
158 domain = mono_domain_get ();
160 datetime->readOnly = this->is_read_only;
161 MONO_OBJECT_SETREF (datetime, AbbreviatedDayNames, create_names_array_idx (dfe->abbreviated_day_names,
162 NUM_DAYS));
163 MONO_OBJECT_SETREF (datetime, AbbreviatedMonthNames, create_names_array_idx (dfe->abbreviated_month_names,
164 NUM_MONTHS));
165 MONO_OBJECT_SETREF (datetime, AMDesignator, mono_string_new (domain, idx2string (dfe->am_designator)));
166 datetime->CalendarWeekRule = dfe->calendar_week_rule;
167 MONO_OBJECT_SETREF (datetime, DateSeparator, mono_string_new (domain, idx2string (dfe->date_separator)));
168 MONO_OBJECT_SETREF (datetime, DayNames, create_names_array_idx (dfe->day_names, NUM_DAYS));
169 datetime->FirstDayOfWeek = dfe->first_day_of_week;
170 MONO_OBJECT_SETREF (datetime, FullDateTimePattern, mono_string_new (domain, idx2string (dfe->full_date_time_pattern)));
171 MONO_OBJECT_SETREF (datetime, LongDatePattern, mono_string_new (domain, idx2string (dfe->long_date_pattern)));
172 MONO_OBJECT_SETREF (datetime, LongTimePattern, mono_string_new (domain, idx2string (dfe->long_time_pattern)));
173 MONO_OBJECT_SETREF (datetime, MonthDayPattern, mono_string_new (domain, idx2string (dfe->month_day_pattern)));
174 MONO_OBJECT_SETREF (datetime, MonthNames, create_names_array_idx (dfe->month_names, NUM_MONTHS));
175 MONO_OBJECT_SETREF (datetime, PMDesignator, mono_string_new (domain, idx2string (dfe->pm_designator)));
176 MONO_OBJECT_SETREF (datetime, ShortDatePattern, mono_string_new (domain, idx2string (dfe->short_date_pattern)));
177 MONO_OBJECT_SETREF (datetime, ShortTimePattern, mono_string_new (domain, idx2string (dfe->short_time_pattern)));
178 MONO_OBJECT_SETREF (datetime, TimeSeparator, mono_string_new (domain, idx2string (dfe->time_separator)));
179 MONO_OBJECT_SETREF (datetime, YearMonthPattern, mono_string_new (domain, idx2string (dfe->year_month_pattern)));
180 MONO_OBJECT_SETREF (datetime, ShortDatePatterns, create_names_array_idx (dfe->short_date_patterns,
181 NUM_SHORT_DATE_PATTERNS));
182 MONO_OBJECT_SETREF (datetime, LongDatePatterns, create_names_array_idx (dfe->long_date_patterns,
183 NUM_LONG_DATE_PATTERNS));
184 MONO_OBJECT_SETREF (datetime, ShortTimePatterns, create_names_array_idx (dfe->short_time_patterns,
185 NUM_SHORT_TIME_PATTERNS));
186 MONO_OBJECT_SETREF (datetime, LongTimePatterns, create_names_array_idx (dfe->long_time_patterns,
187 NUM_LONG_TIME_PATTERNS));
191 void
192 ves_icall_System_Globalization_CultureInfo_construct_number_format (MonoCultureInfo *this)
194 MonoDomain *domain;
195 MonoNumberFormatInfo *number;
196 const NumberFormatEntry *nfe;
198 MONO_ARCH_SAVE_REGS;
200 g_assert (this->number_format != 0);
201 if (this->number_index < 0)
202 return;
204 number = this->number_format;
205 nfe = &number_format_entries [this->number_index];
207 domain = mono_domain_get ();
209 number->readOnly = this->is_read_only;
210 number->currencyDecimalDigits = nfe->currency_decimal_digits;
211 MONO_OBJECT_SETREF (number, currencyDecimalSeparator, mono_string_new (domain,
212 idx2string (nfe->currency_decimal_separator)));
213 MONO_OBJECT_SETREF (number, currencyGroupSeparator, mono_string_new (domain,
214 idx2string (nfe->currency_group_separator)));
215 MONO_OBJECT_SETREF (number, currencyGroupSizes, create_group_sizes_array (nfe->currency_group_sizes,
216 GROUP_SIZE));
217 number->currencyNegativePattern = nfe->currency_negative_pattern;
218 number->currencyPositivePattern = nfe->currency_positive_pattern;
219 MONO_OBJECT_SETREF (number, currencySymbol, mono_string_new (domain, idx2string (nfe->currency_symbol)));
220 MONO_OBJECT_SETREF (number, naNSymbol, mono_string_new (domain, idx2string (nfe->nan_symbol)));
221 MONO_OBJECT_SETREF (number, negativeInfinitySymbol, mono_string_new (domain,
222 idx2string (nfe->negative_infinity_symbol)));
223 MONO_OBJECT_SETREF (number, negativeSign, mono_string_new (domain, idx2string (nfe->negative_sign)));
224 number->numberDecimalDigits = nfe->number_decimal_digits;
225 MONO_OBJECT_SETREF (number, numberDecimalSeparator, mono_string_new (domain,
226 idx2string (nfe->number_decimal_separator)));
227 MONO_OBJECT_SETREF (number, numberGroupSeparator, mono_string_new (domain, idx2string (nfe->number_group_separator)));
228 MONO_OBJECT_SETREF (number, numberGroupSizes, create_group_sizes_array (nfe->number_group_sizes,
229 GROUP_SIZE));
230 number->numberNegativePattern = nfe->number_negative_pattern;
231 number->percentDecimalDigits = nfe->percent_decimal_digits;
232 MONO_OBJECT_SETREF (number, percentDecimalSeparator, mono_string_new (domain,
233 idx2string (nfe->percent_decimal_separator)));
234 MONO_OBJECT_SETREF (number, percentGroupSeparator, mono_string_new (domain,
235 idx2string (nfe->percent_group_separator)));
236 MONO_OBJECT_SETREF (number, percentGroupSizes, create_group_sizes_array (nfe->percent_group_sizes,
237 GROUP_SIZE));
238 number->percentNegativePattern = nfe->percent_negative_pattern;
239 number->percentPositivePattern = nfe->percent_positive_pattern;
240 MONO_OBJECT_SETREF (number, percentSymbol, mono_string_new (domain, idx2string (nfe->percent_symbol)));
241 MONO_OBJECT_SETREF (number, perMilleSymbol, mono_string_new (domain, idx2string (nfe->per_mille_symbol)));
242 MONO_OBJECT_SETREF (number, positiveInfinitySymbol, mono_string_new (domain,
243 idx2string (nfe->positive_infinity_symbol)));
244 MONO_OBJECT_SETREF (number, positiveSign, mono_string_new (domain, idx2string (nfe->positive_sign)));
247 static MonoBoolean
248 construct_culture (MonoCultureInfo *this, const CultureInfoEntry *ci)
250 MonoDomain *domain = mono_domain_get ();
252 this->lcid = ci->lcid;
253 MONO_OBJECT_SETREF (this, name, mono_string_new (domain, idx2string (ci->name)));
254 MONO_OBJECT_SETREF (this, icu_name, mono_string_new (domain, idx2string (ci->icu_name)));
255 MONO_OBJECT_SETREF (this, displayname, mono_string_new (domain, idx2string (ci->displayname)));
256 MONO_OBJECT_SETREF (this, englishname, mono_string_new (domain, idx2string (ci->englishname)));
257 MONO_OBJECT_SETREF (this, nativename, mono_string_new (domain, idx2string (ci->nativename)));
258 MONO_OBJECT_SETREF (this, win3lang, mono_string_new (domain, idx2string (ci->win3lang)));
259 MONO_OBJECT_SETREF (this, iso3lang, mono_string_new (domain, idx2string (ci->iso3lang)));
260 MONO_OBJECT_SETREF (this, iso2lang, mono_string_new (domain, idx2string (ci->iso2lang)));
261 MONO_OBJECT_SETREF (this, territory, mono_string_new (domain, idx2string (ci->territory)));
262 this->parent_lcid = ci->parent_lcid;
263 this->specific_lcid = ci->specific_lcid;
264 this->datetime_index = ci->datetime_format_index;
265 this->number_index = ci->number_format_index;
266 this->calendar_data = ci->calendar_data;
267 this->text_info_data = &ci->text_info;
269 return TRUE;
272 static MonoBoolean
273 construct_region (MonoRegionInfo *this, const RegionInfoEntry *ri)
275 MonoDomain *domain = mono_domain_get ();
277 this->region_id = ri->region_id;
278 MONO_OBJECT_SETREF (this, iso2name, mono_string_new (domain, idx2string (ri->iso2name)));
279 MONO_OBJECT_SETREF (this, iso3name, mono_string_new (domain, idx2string (ri->iso3name)));
280 MONO_OBJECT_SETREF (this, win3name, mono_string_new (domain, idx2string (ri->win3name)));
281 MONO_OBJECT_SETREF (this, english_name, mono_string_new (domain, idx2string (ri->english_name)));
282 MONO_OBJECT_SETREF (this, currency_symbol, mono_string_new (domain, idx2string (ri->currency_symbol)));
283 MONO_OBJECT_SETREF (this, iso_currency_symbol, mono_string_new (domain, idx2string (ri->iso_currency_symbol)));
284 MONO_OBJECT_SETREF (this, currency_english_name, mono_string_new (domain, idx2string (ri->currency_english_name)));
286 return TRUE;
289 static gboolean
290 construct_culture_from_specific_name (MonoCultureInfo *ci, gchar *name)
292 const CultureInfoEntry *entry;
293 const CultureInfoNameEntry *ne;
295 MONO_ARCH_SAVE_REGS;
297 ne = bsearch (name, culture_name_entries, NUM_CULTURE_ENTRIES,
298 sizeof (CultureInfoNameEntry), culture_name_locator);
300 if (ne == NULL)
301 return FALSE;
303 entry = &culture_entries [ne->culture_entry_index];
305 /* try avoiding another lookup, often the culture is its own specific culture */
306 if (entry->lcid != entry->specific_lcid)
307 entry = culture_info_entry_from_lcid (entry->specific_lcid);
309 if (entry)
310 return construct_culture (ci, entry);
311 else
312 return FALSE;
315 static const CultureInfoEntry*
316 culture_info_entry_from_lcid (int lcid)
318 const CultureInfoEntry *ci;
319 CultureInfoEntry key;
321 key.lcid = lcid;
322 ci = bsearch (&key, culture_entries, NUM_CULTURE_ENTRIES, sizeof (CultureInfoEntry), culture_lcid_locator);
324 return ci;
327 static const RegionInfoEntry*
328 region_info_entry_from_lcid (int lcid)
330 const RegionInfoEntry *entry;
331 const CultureInfoEntry *ne;
333 MONO_ARCH_SAVE_REGS;
335 ne = bsearch (&lcid, culture_entries, NUM_CULTURE_ENTRIES,
336 sizeof (CultureInfoEntry), region_lcid_locator);
338 if (ne == NULL)
339 return FALSE;
341 entry = &region_entries [ne->region_entry_index];
343 return entry;
347 * The following two methods are modified from the ICU source code. (http://oss.software.ibm.com/icu)
348 * Copyright (c) 1995-2003 International Business Machines Corporation and others
349 * All rights reserved.
351 static gchar*
352 get_posix_locale (void)
354 const gchar* posix_locale = NULL;
356 posix_locale = g_getenv("LC_ALL");
357 if (posix_locale == 0) {
358 posix_locale = g_getenv("LANG");
359 if (posix_locale == 0) {
360 posix_locale = setlocale(LC_ALL, NULL);
364 if (posix_locale == NULL)
365 return NULL;
367 if ((strcmp ("C", posix_locale) == 0) || (strchr (posix_locale, ' ') != NULL)
368 || (strchr (posix_locale, '/') != NULL)) {
370 * HPUX returns 'C C C C C C C'
371 * Solaris can return /en_US/C/C/C/C/C on the second try.
372 * Maybe we got some garbage.
374 return NULL;
377 return g_strdup (posix_locale);
380 #if defined (__APPLE__)
381 static gchar*
382 get_darwin_locale (void)
384 const gchar *darwin_locale = NULL;
385 gchar *cur_locale = NULL;
386 int i;
387 CFLocaleRef locale = NULL;
388 CFStringRef locale_cfstr = NULL;
390 locale = CFLocaleCopyCurrent ();
392 if (locale) {
393 locale_cfstr = CFLocaleGetIdentifier (locale);
395 if (locale_cfstr) {
396 darwin_locale = CFStringGetCStringPtr (locale_cfstr, kCFStringEncodingMacRoman);
398 cur_locale = g_strdup (darwin_locale);
400 for (i = 0; i < strlen (cur_locale); i++)
401 if (cur_locale [i] == '_')
402 cur_locale [i] = '-';
406 CFRelease (locale);
409 return cur_locale;
411 #endif
413 static gchar*
414 get_current_locale_name (void)
416 gchar *locale;
417 gchar *corrected = NULL;
418 const gchar *p;
419 gchar *c;
421 #ifdef HOST_WIN32
422 locale = g_win32_getlocale ();
423 #elif defined (__APPLE__)
424 locale = get_darwin_locale ();
425 if (!locale)
426 locale = get_posix_locale ();
427 #else
428 locale = get_posix_locale ();
429 #endif
431 if (locale == NULL)
432 return NULL;
434 if ((p = strchr (locale, '.')) != NULL) {
435 /* assume new locale can't be larger than old one? */
436 corrected = g_malloc (strlen (locale));
437 strncpy (corrected, locale, p - locale);
438 corrected [p - locale] = 0;
440 /* do not copy after the @ */
441 if ((p = strchr (corrected, '@')) != NULL)
442 corrected [p - corrected] = 0;
445 /* Note that we scan the *uncorrected* ID. */
446 if ((p = strrchr (locale, '@')) != NULL) {
449 * In Mono we dont handle the '@' modifier because we do
450 * not have any cultures that use it. We just trim it
451 * off of the end of the name.
454 if (corrected == NULL) {
455 corrected = g_malloc (strlen (locale));
456 strncpy (corrected, locale, p - locale);
457 corrected [p - locale] = 0;
461 if (corrected == NULL)
462 corrected = locale;
463 else
464 g_free (locale);
466 if ((c = strchr (corrected, '_')) != NULL)
467 *c = '-';
469 c = corrected;
470 corrected = g_ascii_strdown (c, -1);
471 g_free (c);
473 return corrected;
476 MonoBoolean
477 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_current_locale (MonoCultureInfo *ci)
479 gchar *locale;
480 gboolean ret;
482 MONO_ARCH_SAVE_REGS;
484 locale = get_current_locale_name ();
485 if (locale == NULL)
486 return FALSE;
488 ret = construct_culture_from_specific_name (ci, locale);
489 g_free (locale);
490 ci->is_read_only = TRUE;
491 ci->use_user_override = TRUE;
493 return ret;
496 MonoBoolean
497 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_lcid (MonoCultureInfo *this,
498 gint lcid)
500 const CultureInfoEntry *ci;
502 MONO_ARCH_SAVE_REGS;
504 ci = culture_info_entry_from_lcid (lcid);
505 if(ci == NULL)
506 return FALSE;
508 return construct_culture (this, ci);
511 MonoBoolean
512 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_name (MonoCultureInfo *this,
513 MonoString *name)
515 const CultureInfoNameEntry *ne;
516 char *n;
518 MONO_ARCH_SAVE_REGS;
520 n = mono_string_to_utf8 (name);
521 ne = bsearch (n, culture_name_entries, NUM_CULTURE_ENTRIES,
522 sizeof (CultureInfoNameEntry), culture_name_locator);
524 if (ne == NULL) {
525 /*g_print ("ne (%s) is null\n", n);*/
526 g_free (n);
527 return FALSE;
529 g_free (n);
531 return construct_culture (this, &culture_entries [ne->culture_entry_index]);
534 MonoBoolean
535 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_specific_name (MonoCultureInfo *ci,
536 MonoString *name)
538 gchar *locale;
539 gboolean ret;
541 MONO_ARCH_SAVE_REGS;
543 locale = mono_string_to_utf8 (name);
544 ret = construct_culture_from_specific_name (ci, locale);
545 g_free (locale);
547 return ret;
550 MonoBoolean
551 ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_lcid (MonoRegionInfo *this,
552 gint lcid)
554 const RegionInfoEntry *ri;
556 MONO_ARCH_SAVE_REGS;
558 ri = region_info_entry_from_lcid (lcid);
559 if(ri == NULL)
560 return FALSE;
562 return construct_region (this, ri);
565 MonoBoolean
566 ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_name (MonoRegionInfo *this,
567 MonoString *name)
569 const RegionInfoNameEntry *ne;
570 char *n;
572 MONO_ARCH_SAVE_REGS;
574 n = mono_string_to_utf8 (name);
575 ne = bsearch (n, region_name_entries, NUM_REGION_ENTRIES,
576 sizeof (RegionInfoNameEntry), region_name_locator);
578 if (ne == NULL) {
579 /*g_print ("ne (%s) is null\n", n);*/
580 g_free (n);
581 return FALSE;
583 g_free (n);
585 return construct_region (this, &region_entries [ne->region_entry_index]);
588 MonoArray*
589 ves_icall_System_Globalization_CultureInfo_internal_get_cultures (MonoBoolean neutral,
590 MonoBoolean specific, MonoBoolean installed)
592 MonoArray *ret;
593 MonoClass *class;
594 MonoCultureInfo *culture;
595 MonoDomain *domain;
596 const CultureInfoEntry *ci;
597 gint i, len;
598 gboolean is_neutral;
600 MONO_ARCH_SAVE_REGS;
602 domain = mono_domain_get ();
604 len = 0;
605 for (i = 0; i < NUM_CULTURE_ENTRIES; i++) {
606 ci = &culture_entries [i];
607 is_neutral = ((ci->lcid & 0xff00) == 0 || ci->specific_lcid == 0);
608 if ((neutral && is_neutral) || (specific && !is_neutral))
609 len++;
612 class = mono_class_from_name (mono_get_corlib (),
613 "System.Globalization", "CultureInfo");
615 /* The InvariantCulture is not in culture_entries */
616 /* We reserve the first slot in the array for it */
617 if (neutral)
618 len++;
620 ret = mono_array_new (domain, class, len);
622 if (len == 0)
623 return ret;
625 len = 0;
626 if (neutral)
627 mono_array_setref (ret, len++, NULL);
629 for (i = 0; i < NUM_CULTURE_ENTRIES; i++) {
630 ci = &culture_entries [i];
631 is_neutral = ((ci->lcid & 0xff00) == 0 || ci->specific_lcid == 0);
632 if ((neutral && is_neutral) || (specific && !is_neutral)) {
633 culture = (MonoCultureInfo *) mono_object_new (domain, class);
634 mono_runtime_object_init ((MonoObject *) culture);
635 construct_culture (culture, ci);
636 culture->use_user_override = TRUE;
637 mono_array_setref (ret, len++, culture);
641 return ret;
645 * ves_icall_System_Globalization_CultureInfo_internal_is_lcid_neutral:
647 * Set is_neutral and return TRUE if the culture is found. If it is not found return FALSE.
649 MonoBoolean
650 ves_icall_System_Globalization_CultureInfo_internal_is_lcid_neutral (gint lcid, MonoBoolean *is_neutral)
652 const CultureInfoEntry *entry;
654 MONO_ARCH_SAVE_REGS;
656 entry = culture_info_entry_from_lcid (lcid);
658 if (entry == NULL)
659 return FALSE;
661 *is_neutral = (entry->specific_lcid == 0);
663 return TRUE;
666 void ves_icall_System_Globalization_CompareInfo_construct_compareinfo (MonoCompareInfo *comp, MonoString *locale)
668 /* Nothing to do here */
671 int ves_icall_System_Globalization_CompareInfo_internal_compare (MonoCompareInfo *this, MonoString *str1, gint32 off1, gint32 len1, MonoString *str2, gint32 off2, gint32 len2, gint32 options)
673 MONO_ARCH_SAVE_REGS;
675 /* Do a normal ascii string compare, as we only know the
676 * invariant locale if we dont have ICU
678 return(string_invariant_compare (str1, off1, len1, str2, off2, len2,
679 options));
682 void ves_icall_System_Globalization_CompareInfo_free_internal_collator (MonoCompareInfo *this)
684 /* Nothing to do here */
687 void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoCompareInfo *this, MonoSortKey *key, MonoString *source, gint32 options)
689 MonoArray *arr;
690 gint32 keylen, i;
692 MONO_ARCH_SAVE_REGS;
694 keylen=mono_string_length (source);
696 arr=mono_array_new (mono_domain_get (), mono_get_byte_class (),
697 keylen);
698 for(i=0; i<keylen; i++) {
699 mono_array_set (arr, guint8, i, mono_string_chars (source)[i]);
702 MONO_OBJECT_SETREF (key, key, arr);
705 int ves_icall_System_Globalization_CompareInfo_internal_index (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, MonoString *value, gint32 options, MonoBoolean first)
707 MONO_ARCH_SAVE_REGS;
709 return(string_invariant_indexof (source, sindex, count, value, first));
712 int ves_icall_System_Globalization_CompareInfo_internal_index_char (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, gunichar2 value, gint32 options, MonoBoolean first)
714 MONO_ARCH_SAVE_REGS;
716 return(string_invariant_indexof_char (source, sindex, count, value,
717 first));
720 int ves_icall_System_Threading_Thread_current_lcid (void)
722 MONO_ARCH_SAVE_REGS;
724 /* Invariant */
725 return(0x007F);
728 MonoString *ves_icall_System_String_InternalReplace_Str_Comp (MonoString *this, MonoString *old, MonoString *new, MonoCompareInfo *comp)
730 MONO_ARCH_SAVE_REGS;
732 /* Do a normal ascii string compare and replace, as we only
733 * know the invariant locale if we dont have ICU
735 return(string_invariant_replace (this, old, new));
738 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
739 gint32 options)
741 gint32 result;
743 /* Ordinal can not be mixed with other options, and must return the difference, not only -1, 0, 1 */
744 if (options & CompareOptions_Ordinal)
745 return (gint32) c1 - c2;
747 if (options & CompareOptions_IgnoreCase) {
748 GUnicodeType c1type, c2type;
750 c1type = g_unichar_type (c1);
751 c2type = g_unichar_type (c2);
753 result = (gint32) (c1type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c1) : c1) -
754 (c2type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c2) : c2);
755 } else {
757 * No options. Kana, symbol and spacing options don't
758 * apply to the invariant culture.
762 * FIXME: here we must use the information from c1type and c2type
763 * to find out the proper collation, even on the InvariantCulture, the
764 * sorting is not done by computing the unicode values, but their
765 * actual sort order.
767 result = (gint32) c1 - c2;
770 return ((result < 0) ? -1 : (result > 0) ? 1 : 0);
773 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
774 gint32 len1, MonoString *str2,
775 gint32 off2, gint32 len2,
776 gint32 options)
778 /* c translation of C# code from old string.cs.. :) */
779 gint32 length;
780 gint32 charcmp;
781 gunichar2 *ustr1;
782 gunichar2 *ustr2;
783 gint32 pos;
785 if(len1 >= len2) {
786 length=len1;
787 } else {
788 length=len2;
791 ustr1 = mono_string_chars(str1)+off1;
792 ustr2 = mono_string_chars(str2)+off2;
794 pos = 0;
796 for (pos = 0; pos != length; pos++) {
797 if (pos >= len1 || pos >= len2)
798 break;
800 charcmp = string_invariant_compare_char(ustr1[pos], ustr2[pos],
801 options);
802 if (charcmp != 0) {
803 return(charcmp);
807 /* the lesser wins, so if we have looped until length we just
808 * need to check the last char
810 if (pos == length) {
811 return(string_invariant_compare_char(ustr1[pos - 1],
812 ustr2[pos - 1], options));
815 /* Test if one of the strings has been compared to the end */
816 if (pos >= len1) {
817 if (pos >= len2) {
818 return(0);
819 } else {
820 return(-1);
822 } else if (pos >= len2) {
823 return(1);
826 /* if not, check our last char only.. (can this happen?) */
827 return(string_invariant_compare_char(ustr1[pos], ustr2[pos], options));
830 static MonoString *string_invariant_replace (MonoString *me,
831 MonoString *oldValue,
832 MonoString *newValue)
834 MonoString *ret;
835 gunichar2 *src;
836 gunichar2 *dest=NULL; /* shut gcc up */
837 gunichar2 *oldstr;
838 gunichar2 *newstr=NULL; /* shut gcc up here too */
839 gint32 i, destpos;
840 gint32 occurr;
841 gint32 newsize;
842 gint32 oldstrlen;
843 gint32 newstrlen;
844 gint32 srclen;
846 occurr = 0;
847 destpos = 0;
849 oldstr = mono_string_chars(oldValue);
850 oldstrlen = mono_string_length(oldValue);
852 if (NULL != newValue) {
853 newstr = mono_string_chars(newValue);
854 newstrlen = mono_string_length(newValue);
855 } else
856 newstrlen = 0;
858 src = mono_string_chars(me);
859 srclen = mono_string_length(me);
861 if (oldstrlen != newstrlen) {
862 i = 0;
863 while (i <= srclen - oldstrlen) {
864 if (0 == memcmp(src + i, oldstr, oldstrlen * sizeof(gunichar2))) {
865 occurr++;
866 i += oldstrlen;
868 else
869 i ++;
871 if (occurr == 0)
872 return me;
873 newsize = srclen + ((newstrlen - oldstrlen) * occurr);
874 } else
875 newsize = srclen;
877 ret = NULL;
878 i = 0;
879 while (i < srclen) {
880 if (0 == memcmp(src + i, oldstr, oldstrlen * sizeof(gunichar2))) {
881 if (ret == NULL) {
882 ret = mono_string_new_size( mono_domain_get (), newsize);
883 dest = mono_string_chars(ret);
884 memcpy (dest, src, i * sizeof(gunichar2));
886 if (newstrlen > 0) {
887 memcpy(dest + destpos, newstr, newstrlen * sizeof(gunichar2));
888 destpos += newstrlen;
890 i += oldstrlen;
891 continue;
892 } else if (ret != NULL) {
893 dest[destpos] = src[i];
895 destpos++;
896 i++;
899 if (ret == NULL)
900 return me;
902 return ret;
905 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
906 gint32 count, MonoString *value,
907 MonoBoolean first)
909 gint32 lencmpstr;
910 gunichar2 *src;
911 gunichar2 *cmpstr;
912 gint32 pos,i;
914 lencmpstr = mono_string_length(value);
916 src = mono_string_chars(source);
917 cmpstr = mono_string_chars(value);
919 if(first) {
920 count -= lencmpstr;
921 for(pos=sindex;pos <= sindex+count;pos++) {
922 for(i=0;src[pos+i]==cmpstr[i];) {
923 if(++i==lencmpstr) {
924 return(pos);
929 return(-1);
930 } else {
931 for(pos=sindex-lencmpstr+1;pos>sindex-count;pos--) {
932 if(memcmp (src+pos, cmpstr,
933 lencmpstr*sizeof(gunichar2))==0) {
934 return(pos);
938 return(-1);
942 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
943 gint32 count, gunichar2 value,
944 MonoBoolean first)
946 gint32 pos;
947 gunichar2 *src;
949 src = mono_string_chars(source);
950 if(first) {
951 for (pos = sindex; pos != count + sindex; pos++) {
952 if (src [pos] == value) {
953 return(pos);
957 return(-1);
958 } else {
959 for (pos = sindex; pos > sindex - count; pos--) {
960 if (src [pos] == value)
961 return(pos);
964 return(-1);
968 void load_normalization_resource (guint8 **argProps,
969 guint8 **argMappedChars,
970 guint8 **argCharMapIndex,
971 guint8 **argHelperIndex,
972 guint8 **argMapIdxToComposite,
973 guint8 **argCombiningClass)
975 *argProps = (guint8*)props;
976 *argMappedChars = (guint8*) mappedChars;
977 *argCharMapIndex = (guint8*) charMapIndex;
978 *argHelperIndex = (guint8*) helperIndex;
979 *argMapIdxToComposite = (guint8*) mapIdxToComposite;
980 *argCombiningClass = (guint8*)combiningClass;