[Profiler] Use the correct function to increment the refcount
[mono.git] / mono / metadata / locales.c
1 /*
2  * locales.c: Culture-sensitive handling
3  *
4  * Authors:
5  *      Dick Porter (dick@ximian.com)
6  *      Mohammad DAMT (mdamt@cdl2000.com)
7  *      Marek Safar (marek.safar@gmail.com)
8  *
9  * Copyright 2003 Ximian, Inc (http://www.ximian.com)
10  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
11  * (C) 2003 PT Cakram Datalingga Duaribu  http://www.cdl2000.com
12  * Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
13  */
14
15 #include <config.h>
16 #include <glib.h>
17 #include <string.h>
18
19 #include <mono/metadata/debug-helpers.h>
20 #include <mono/metadata/object.h>
21 #include <mono/metadata/appdomain.h>
22 #include <mono/metadata/exception.h>
23 #include <mono/metadata/monitor.h>
24 #include <mono/metadata/locales.h>
25 #include <mono/metadata/culture-info.h>
26 #include <mono/metadata/culture-info-tables.h>
27 #include <mono/utils/bsearch.h>
28
29 #ifndef DISABLE_NORMALIZATION
30 #include <mono/metadata/normalization-tables.h>
31 #endif
32
33 #include <locale.h>
34 #if defined(__APPLE__)
35 #include <CoreFoundation/CoreFoundation.h>
36 #endif
37
38 #undef DEBUG
39
40 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
41                                              gint32 options);
42 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
43                                         gint32 len1, MonoString *str2,
44                                         gint32 off2, gint32 len2,
45                                         gint32 options);
46 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
47                                         gint32 count, MonoString *value,
48                                         MonoBoolean first);
49 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
50                                              gint32 count, gunichar2 value,
51                                              MonoBoolean first);
52
53 static const CultureInfoEntry* culture_info_entry_from_lcid (int lcid);
54
55 static const RegionInfoEntry* region_info_entry_from_lcid (int lcid);
56
57 /* Lazy class loading functions */
58 static GENERATE_GET_CLASS_WITH_CACHE (culture_info, System.Globalization, CultureInfo)
59
60 static int
61 culture_lcid_locator (const void *a, const void *b)
62 {
63         const int *lcid = (const int *)a;
64         const CultureInfoEntry *bb = (const CultureInfoEntry *)b;
65
66         return *lcid - bb->lcid;
67 }
68
69 static int
70 culture_name_locator (const void *a, const void *b)
71 {
72         const char *aa = (const char *)a;
73         const CultureInfoNameEntry *bb = (const CultureInfoNameEntry *)b;
74         int ret;
75         
76         ret = strcmp (aa, idx2string (bb->name));
77
78         return ret;
79 }
80
81 static int
82 region_name_locator (const void *a, const void *b)
83 {
84         const char *aa = (const char *)a;
85         const RegionInfoNameEntry *bb = (const RegionInfoNameEntry *)b;
86         int ret;
87         
88         ret = strcmp (aa, idx2string (bb->name));
89
90         return ret;
91 }
92
93 static MonoArray*
94 create_group_sizes_array (const gint *gs, gint ml)
95 {
96         MonoArray *ret;
97         int i, len = 0;
98
99         for (i = 0; i < ml; i++) {
100                 if (gs [i] == -1)
101                         break;
102                 len++;
103         }
104         
105         ret = mono_array_new_cached (mono_domain_get (),
106                         mono_get_int32_class (), len);
107
108         for(i = 0; i < len; i++)
109                 mono_array_set (ret, gint32, i, gs [i]);
110
111         return ret;
112 }
113
114 static MonoArray*
115 create_names_array_idx (const guint16 *names, int ml)
116 {
117         MonoArray *ret;
118         MonoDomain *domain;
119         int i;
120
121         if (names == NULL)
122                 return NULL;
123
124         domain = mono_domain_get ();
125
126         ret = mono_array_new_cached (mono_domain_get (), mono_get_string_class (), ml);
127
128         for(i = 0; i < ml; i++)
129                 mono_array_setref (ret, i, mono_string_new (domain, idx2string (names [i])));
130
131         return ret;
132 }
133
134 static MonoArray*
135 create_names_array_idx_dynamic (const guint16 *names, int ml)
136 {
137         MonoArray *ret;
138         MonoDomain *domain;
139         int i, len = 0;
140
141         if (names == NULL)
142                 return NULL;
143
144         domain = mono_domain_get ();
145
146         for (i = 0; i < ml; i++) {
147                 if (names [i] == 0)
148                         break;
149                 len++;
150         }
151
152         ret = mono_array_new_cached (mono_domain_get (), mono_get_string_class (), len);
153
154         for(i = 0; i < len; i++)
155                 mono_array_setref (ret, i, mono_string_new (domain, idx2string (names [i])));
156
157         return ret;
158 }
159
160 MonoBoolean
161 ves_icall_System_Globalization_CalendarData_fill_calendar_data (MonoCalendarData *this_obj, MonoString *name, gint32 calendar_index)
162 {
163         MonoDomain *domain;
164         const DateTimeFormatEntry *dfe;
165         const CultureInfoNameEntry *ne;
166         const CultureInfoEntry *ci;
167         char *n;
168
169         n = mono_string_to_utf8 (name);
170         ne = (const CultureInfoNameEntry *)mono_binary_search (n, culture_name_entries, NUM_CULTURE_ENTRIES,
171                         sizeof (CultureInfoNameEntry), culture_name_locator);
172         g_free (n);
173         if (ne == NULL) {
174                 return FALSE;
175         }
176
177         ci = &culture_entries [ne->culture_entry_index];
178         dfe = &datetime_format_entries [ci->datetime_format_index];
179
180         domain = mono_domain_get ();
181
182         MONO_OBJECT_SETREF (this_obj, NativeName, mono_string_new (domain, idx2string (ci->nativename)));
183         MONO_OBJECT_SETREF (this_obj, ShortDatePatterns, create_names_array_idx_dynamic (dfe->short_date_patterns,
184                         NUM_SHORT_DATE_PATTERNS));
185         MONO_OBJECT_SETREF (this_obj, YearMonthPatterns, create_names_array_idx_dynamic (dfe->year_month_patterns,
186                         NUM_YEAR_MONTH_PATTERNS));
187
188         MONO_OBJECT_SETREF (this_obj, LongDatePatterns, create_names_array_idx_dynamic (dfe->long_date_patterns,
189                         NUM_LONG_DATE_PATTERNS));
190         MONO_OBJECT_SETREF (this_obj, MonthDayPattern, mono_string_new (domain, idx2string (dfe->month_day_pattern)));
191
192         MONO_OBJECT_SETREF (this_obj, DayNames, create_names_array_idx (dfe->day_names, NUM_DAYS));
193         MONO_OBJECT_SETREF (this_obj, AbbreviatedDayNames, create_names_array_idx (dfe->abbreviated_day_names, 
194                         NUM_DAYS));
195         MONO_OBJECT_SETREF (this_obj, SuperShortDayNames, create_names_array_idx (dfe->shortest_day_names, NUM_DAYS));
196         MONO_OBJECT_SETREF (this_obj, MonthNames, create_names_array_idx (dfe->month_names, NUM_MONTHS));
197         MONO_OBJECT_SETREF (this_obj, AbbreviatedMonthNames, create_names_array_idx (dfe->abbreviated_month_names,
198                         NUM_MONTHS));
199         MONO_OBJECT_SETREF (this_obj, GenitiveMonthNames, create_names_array_idx (dfe->month_genitive_names, NUM_MONTHS));
200         MONO_OBJECT_SETREF (this_obj, GenitiveAbbreviatedMonthNames, create_names_array_idx (dfe->abbreviated_month_genitive_names, NUM_MONTHS));
201
202         return TRUE;
203 }
204
205 void
206 ves_icall_System_Globalization_CultureData_fill_culture_data (MonoCultureData *this_obj, gint32 datetime_index)
207 {
208         MonoDomain *domain;
209         const DateTimeFormatEntry *dfe;
210
211         g_assert (datetime_index >= 0);
212
213         dfe = &datetime_format_entries [datetime_index];
214
215         domain = mono_domain_get ();
216
217         MONO_OBJECT_SETREF (this_obj, AMDesignator, mono_string_new (domain, idx2string (dfe->am_designator)));
218         MONO_OBJECT_SETREF (this_obj, PMDesignator, mono_string_new (domain, idx2string (dfe->pm_designator)));
219         MONO_OBJECT_SETREF (this_obj, TimeSeparator, mono_string_new (domain, idx2string (dfe->time_separator)));
220         MONO_OBJECT_SETREF (this_obj, LongTimePatterns, create_names_array_idx_dynamic (dfe->long_time_patterns,
221                         NUM_LONG_TIME_PATTERNS));
222         MONO_OBJECT_SETREF (this_obj, ShortTimePatterns, create_names_array_idx_dynamic (dfe->short_time_patterns,
223                         NUM_SHORT_TIME_PATTERNS));
224         this_obj->FirstDayOfWeek = dfe->first_day_of_week;
225         this_obj->CalendarWeekRule = dfe->calendar_week_rule;
226 }
227
228 void
229 ves_icall_System_Globalization_CultureData_fill_number_data (MonoNumberFormatInfo* number, gint32 number_index)
230 {
231         MonoDomain *domain;
232         const NumberFormatEntry *nfe;
233
234         g_assert (number_index >= 0);
235
236         nfe = &number_format_entries [number_index];
237
238         domain = mono_domain_get ();
239
240         number->currencyDecimalDigits = nfe->currency_decimal_digits;
241         MONO_OBJECT_SETREF (number, currencyDecimalSeparator, mono_string_new (domain,
242                         idx2string (nfe->currency_decimal_separator)));
243         MONO_OBJECT_SETREF (number, currencyGroupSeparator, mono_string_new (domain,
244                         idx2string (nfe->currency_group_separator)));
245         MONO_OBJECT_SETREF (number, currencyGroupSizes, create_group_sizes_array (nfe->currency_group_sizes,
246                         GROUP_SIZE));
247         number->currencyNegativePattern = nfe->currency_negative_pattern;
248         number->currencyPositivePattern = nfe->currency_positive_pattern;
249         MONO_OBJECT_SETREF (number, currencySymbol, mono_string_new (domain, idx2string (nfe->currency_symbol)));
250         MONO_OBJECT_SETREF (number, naNSymbol, mono_string_new (domain, idx2string (nfe->nan_symbol)));
251         MONO_OBJECT_SETREF (number, negativeInfinitySymbol, mono_string_new (domain,
252                         idx2string (nfe->negative_infinity_symbol)));
253         MONO_OBJECT_SETREF (number, negativeSign, mono_string_new (domain, idx2string (nfe->negative_sign)));
254         number->numberDecimalDigits = nfe->number_decimal_digits;
255         MONO_OBJECT_SETREF (number, numberDecimalSeparator, mono_string_new (domain,
256                         idx2string (nfe->number_decimal_separator)));
257         MONO_OBJECT_SETREF (number, numberGroupSeparator, mono_string_new (domain, idx2string (nfe->number_group_separator)));
258         MONO_OBJECT_SETREF (number, numberGroupSizes, create_group_sizes_array (nfe->number_group_sizes,
259                         GROUP_SIZE));
260         number->numberNegativePattern = nfe->number_negative_pattern;
261         number->percentNegativePattern = nfe->percent_negative_pattern;
262         number->percentPositivePattern = nfe->percent_positive_pattern;
263         MONO_OBJECT_SETREF (number, percentSymbol, mono_string_new (domain, idx2string (nfe->percent_symbol)));
264         MONO_OBJECT_SETREF (number, perMilleSymbol, mono_string_new (domain, idx2string (nfe->per_mille_symbol)));
265         MONO_OBJECT_SETREF (number, positiveInfinitySymbol, mono_string_new (domain,
266                         idx2string (nfe->positive_infinity_symbol)));
267         MONO_OBJECT_SETREF (number, positiveSign, mono_string_new (domain, idx2string (nfe->positive_sign)));
268 }
269
270 static MonoBoolean
271 construct_culture (MonoCultureInfo *this_obj, const CultureInfoEntry *ci)
272 {
273         MonoDomain *domain = mono_domain_get ();
274
275         this_obj->lcid = ci->lcid;
276         MONO_OBJECT_SETREF (this_obj, name, mono_string_new (domain, idx2string (ci->name)));
277         MONO_OBJECT_SETREF (this_obj, englishname, mono_string_new (domain, idx2string (ci->englishname)));
278         MONO_OBJECT_SETREF (this_obj, nativename, mono_string_new (domain, idx2string (ci->nativename)));
279         MONO_OBJECT_SETREF (this_obj, win3lang, mono_string_new (domain, idx2string (ci->win3lang)));
280         MONO_OBJECT_SETREF (this_obj, iso3lang, mono_string_new (domain, idx2string (ci->iso3lang)));
281         MONO_OBJECT_SETREF (this_obj, iso2lang, mono_string_new (domain, idx2string (ci->iso2lang)));
282
283         // It's null for neutral cultures
284         if (ci->territory > 0)
285                 MONO_OBJECT_SETREF (this_obj, territory, mono_string_new (domain, idx2string (ci->territory)));
286         MONO_OBJECT_SETREF (this_obj, native_calendar_names, create_names_array_idx (ci->native_calendar_names, NUM_CALENDARS));
287         this_obj->parent_lcid = ci->parent_lcid;
288         this_obj->datetime_index = ci->datetime_format_index;
289         this_obj->number_index = ci->number_format_index;
290         this_obj->calendar_type = ci->calendar_type;
291         this_obj->text_info_data = &ci->text_info;
292         
293         return TRUE;
294 }
295
296 static MonoBoolean
297 construct_region (MonoRegionInfo *this_obj, const RegionInfoEntry *ri)
298 {
299         MonoDomain *domain = mono_domain_get ();
300
301         this_obj->geo_id = ri->geo_id;
302         MONO_OBJECT_SETREF (this_obj, iso2name, mono_string_new (domain, idx2string (ri->iso2name)));
303         MONO_OBJECT_SETREF (this_obj, iso3name, mono_string_new (domain, idx2string (ri->iso3name)));
304         MONO_OBJECT_SETREF (this_obj, win3name, mono_string_new (domain, idx2string (ri->win3name)));
305         MONO_OBJECT_SETREF (this_obj, english_name, mono_string_new (domain, idx2string (ri->english_name)));
306         MONO_OBJECT_SETREF (this_obj, native_name, mono_string_new (domain, idx2string (ri->native_name)));
307         MONO_OBJECT_SETREF (this_obj, currency_symbol, mono_string_new (domain, idx2string (ri->currency_symbol)));
308         MONO_OBJECT_SETREF (this_obj, iso_currency_symbol, mono_string_new (domain, idx2string (ri->iso_currency_symbol)));
309         MONO_OBJECT_SETREF (this_obj, currency_english_name, mono_string_new (domain, idx2string (ri->currency_english_name)));
310         MONO_OBJECT_SETREF (this_obj, currency_native_name, mono_string_new (domain, idx2string (ri->currency_native_name)));
311         
312         return TRUE;
313 }
314
315 static const CultureInfoEntry*
316 culture_info_entry_from_lcid (int lcid)
317 {
318         const CultureInfoEntry *ci;
319
320         ci = (const CultureInfoEntry *)mono_binary_search (&lcid, culture_entries, NUM_CULTURE_ENTRIES, sizeof (CultureInfoEntry), culture_lcid_locator);
321
322         return ci;
323 }
324
325 static const RegionInfoEntry*
326 region_info_entry_from_lcid (int lcid)
327 {
328         const RegionInfoEntry *entry;
329         const CultureInfoEntry *ne;
330
331         ne = (const CultureInfoEntry *)mono_binary_search (&lcid, culture_entries, NUM_CULTURE_ENTRIES, sizeof (CultureInfoEntry), culture_lcid_locator);
332
333         if (ne == NULL)
334                 return FALSE;
335
336         entry = &region_entries [ne->region_entry_index];
337
338         return entry;
339 }
340
341 #if defined (__APPLE__)
342 static gchar*
343 get_darwin_locale (void)
344 {
345         static gchar *darwin_locale = NULL;
346         CFLocaleRef locale = NULL;
347         CFStringRef locale_language = NULL;
348         CFStringRef locale_country = NULL;
349         CFStringRef locale_script = NULL;
350         CFStringRef locale_cfstr = NULL;
351         CFIndex bytes_converted;
352         CFIndex bytes_written;
353         CFIndex len;
354         int i;
355
356         if (darwin_locale != NULL)
357                 return g_strdup (darwin_locale);
358
359         locale = CFLocaleCopyCurrent ();
360
361         if (locale) {
362                 locale_language = CFLocaleGetValue (locale, kCFLocaleLanguageCode);
363                 if (locale_language != NULL && CFStringGetBytes(locale_language, CFRangeMake (0, CFStringGetLength (locale_language)), kCFStringEncodingMacRoman, 0, FALSE, NULL, 0, &bytes_converted) > 0) {
364                         len = bytes_converted + 1;
365
366                         locale_country = CFLocaleGetValue (locale, kCFLocaleCountryCode);
367                         if (locale_country != NULL && CFStringGetBytes (locale_country, CFRangeMake (0, CFStringGetLength (locale_country)), kCFStringEncodingMacRoman, 0, FALSE, NULL, 0, &bytes_converted) > 0) {
368                                 len += bytes_converted + 1;
369
370                                 locale_script = CFLocaleGetValue (locale, kCFLocaleScriptCode);
371                                 if (locale_script != NULL && CFStringGetBytes (locale_script, CFRangeMake (0, CFStringGetLength (locale_script)), kCFStringEncodingMacRoman, 0, FALSE, NULL, 0, &bytes_converted) > 0) {
372                                         len += bytes_converted + 1;
373                                 }
374
375                                 darwin_locale = (char *) malloc (len + 1);
376                                 CFStringGetBytes (locale_language, CFRangeMake (0, CFStringGetLength (locale_language)), kCFStringEncodingMacRoman, 0, FALSE, (UInt8 *) darwin_locale, len, &bytes_converted);
377
378                                 darwin_locale[bytes_converted] = '-';
379                                 bytes_written = bytes_converted + 1;
380                                 if (locale_script != NULL && CFStringGetBytes (locale_script, CFRangeMake (0, CFStringGetLength (locale_script)), kCFStringEncodingMacRoman, 0, FALSE, (UInt8 *) &darwin_locale[bytes_written], len - bytes_written, &bytes_converted) > 0) {
381                                         darwin_locale[bytes_written + bytes_converted] = '-';
382                                         bytes_written += bytes_converted + 1;
383                                 }
384
385                                 CFStringGetBytes (locale_country, CFRangeMake (0, CFStringGetLength (locale_country)), kCFStringEncodingMacRoman, 0, FALSE, (UInt8 *) &darwin_locale[bytes_written], len - bytes_written, &bytes_converted);
386                                 darwin_locale[bytes_written + bytes_converted] = '\0';
387                         }
388                 }
389
390                 if (darwin_locale == NULL) {
391                         locale_cfstr = CFLocaleGetIdentifier (locale);
392
393                         if (locale_cfstr) {
394                                 len = CFStringGetMaximumSizeForEncoding (CFStringGetLength (locale_cfstr), kCFStringEncodingMacRoman) + 1;
395                                 darwin_locale = (char *) malloc (len);
396                                 if (!CFStringGetCString (locale_cfstr, darwin_locale, len, kCFStringEncodingMacRoman)) {
397                                         free (darwin_locale);
398                                         CFRelease (locale);
399                                         darwin_locale = NULL;
400                                         return NULL;
401                                 }
402
403                                 for (i = 0; i < strlen (darwin_locale); i++)
404                                         if (darwin_locale [i] == '_')
405                                                 darwin_locale [i] = '-';
406                         }                       
407                 }
408
409                 CFRelease (locale);
410         }
411
412         return g_strdup (darwin_locale);
413 }
414 #endif
415
416 static char *
417 get_posix_locale (void)
418 {
419         const char *locale;
420
421         locale = g_getenv ("LC_ALL");
422         if (locale == NULL) {
423                 locale = g_getenv ("LANG");
424                 if (locale == NULL)
425                         locale = setlocale (LC_ALL, NULL);
426         }
427         if (locale == NULL)
428                 return NULL;
429
430         /* Skip English-only locale 'C' */
431         if (strcmp (locale, "C") == 0)
432                 return NULL;
433
434         return g_strdup (locale);
435 }
436
437
438 static gchar *
439 get_current_locale_name (void)
440 {
441         char *locale;
442         char *p, *ret;
443                 
444 #ifdef HOST_WIN32
445         locale = g_win32_getlocale ();
446 #elif defined (__APPLE__)       
447         locale = get_darwin_locale ();
448         if (!locale)
449                 locale = get_posix_locale ();
450 #else
451         locale = get_posix_locale ();
452 #endif
453
454         if (locale == NULL)
455                 return NULL;
456
457         p = strchr (locale, '.');
458         if (p != NULL)
459                 *p = 0;
460         p = strchr (locale, '@');
461         if (p != NULL)
462                 *p = 0;
463         p = strchr (locale, '_');
464         if (p != NULL)
465                 *p = '-';
466
467         ret = g_ascii_strdown (locale, -1);
468         g_free (locale);
469
470         return ret;
471 }
472
473 MonoString*
474 ves_icall_System_Globalization_CultureInfo_get_current_locale_name (void)
475 {
476         gchar *locale;
477         MonoString* ret;
478         MonoDomain *domain;
479
480         locale = get_current_locale_name ();
481         if (locale == NULL)
482                 return NULL;
483
484         domain = mono_domain_get ();
485         ret = mono_string_new (domain, locale);
486         g_free (locale);
487
488         return ret;
489 }
490
491 MonoBoolean
492 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_lcid (MonoCultureInfo *this_obj,
493                 gint lcid)
494 {
495         const CultureInfoEntry *ci;
496         
497         ci = culture_info_entry_from_lcid (lcid);
498         if(ci == NULL)
499                 return FALSE;
500
501         return construct_culture (this_obj, ci);
502 }
503
504 MonoBoolean
505 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_name (MonoCultureInfo *this_obj,
506                 MonoString *name)
507 {
508         const CultureInfoNameEntry *ne;
509         char *n;
510         
511         n = mono_string_to_utf8 (name);
512         ne = (const CultureInfoNameEntry *)mono_binary_search (n, culture_name_entries, NUM_CULTURE_ENTRIES,
513                         sizeof (CultureInfoNameEntry), culture_name_locator);
514
515         if (ne == NULL) {
516                 /*g_print ("ne (%s) is null\n", n);*/
517                 g_free (n);
518                 return FALSE;
519         }
520         g_free (n);
521
522         return construct_culture (this_obj, &culture_entries [ne->culture_entry_index]);
523 }
524 /*
525 MonoBoolean
526 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_specific_name (MonoCultureInfo *ci,
527                 MonoString *name)
528 {
529         gchar *locale;
530         gboolean ret;
531
532         locale = mono_string_to_utf8 (name);
533         ret = construct_culture_from_specific_name (ci, locale);
534         g_free (locale);
535
536         return ret;
537 }
538 */
539 MonoBoolean
540 ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_lcid (MonoRegionInfo *this_obj,
541                 gint lcid)
542 {
543         const RegionInfoEntry *ri;
544         
545         ri = region_info_entry_from_lcid (lcid);
546         if(ri == NULL)
547                 return FALSE;
548
549         return construct_region (this_obj, ri);
550 }
551
552 MonoBoolean
553 ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_name (MonoRegionInfo *this_obj,
554                 MonoString *name)
555 {
556         const RegionInfoNameEntry *ne;
557         char *n;
558         
559         n = mono_string_to_utf8 (name);
560         ne = (const RegionInfoNameEntry *)mono_binary_search (n, region_name_entries, NUM_REGION_ENTRIES,
561                 sizeof (RegionInfoNameEntry), region_name_locator);
562
563         if (ne == NULL) {
564                 /*g_print ("ne (%s) is null\n", n);*/
565                 g_free (n);
566                 return FALSE;
567         }
568         g_free (n);
569
570         return construct_region (this_obj, &region_entries [ne->region_entry_index]);
571 }
572
573 MonoArray*
574 ves_icall_System_Globalization_CultureInfo_internal_get_cultures (MonoBoolean neutral,
575                 MonoBoolean specific, MonoBoolean installed)
576 {
577         MonoError error;
578         MonoArray *ret;
579         MonoClass *klass;
580         MonoCultureInfo *culture;
581         MonoDomain *domain;
582         const CultureInfoEntry *ci;
583         gint i, len;
584         gboolean is_neutral;
585
586         domain = mono_domain_get ();
587
588         len = 0;
589         for (i = 0; i < NUM_CULTURE_ENTRIES; i++) {
590                 ci = &culture_entries [i];
591                 is_neutral = ci->territory == 0;
592                 if ((neutral && is_neutral) || (specific && !is_neutral))
593                         len++;
594         }
595
596         klass = mono_class_get_culture_info_class ();
597
598         /* The InvariantCulture is not in culture_entries */
599         /* We reserve the first slot in the array for it */
600         if (neutral)
601                 len++;
602
603         ret = mono_array_new (domain, klass, len);
604
605         if (len == 0)
606                 return ret;
607
608         len = 0;
609         if (neutral)
610                 mono_array_setref (ret, len++, NULL);
611
612         for (i = 0; i < NUM_CULTURE_ENTRIES; i++) {
613                 ci = &culture_entries [i];
614                 is_neutral = ci->territory == 0;
615                 if ((neutral && is_neutral) || (specific && !is_neutral)) {
616                         culture = (MonoCultureInfo *) mono_object_new_checked (domain, klass, &error);
617                         mono_error_raise_exception (&error);
618                         mono_runtime_object_init ((MonoObject *) culture);
619                         construct_culture (culture, ci);
620                         culture->use_user_override = TRUE;
621                         mono_array_setref (ret, len++, culture);
622                 }
623         }
624
625         return ret;
626 }
627
628 int ves_icall_System_Globalization_CompareInfo_internal_compare (MonoCompareInfo *this_obj, MonoString *str1, gint32 off1, gint32 len1, MonoString *str2, gint32 off2, gint32 len2, gint32 options)
629 {
630         /* Do a normal ascii string compare, as we only know the
631          * invariant locale if we dont have ICU
632          */
633         return(string_invariant_compare (str1, off1, len1, str2, off2, len2,
634                                          options));
635 }
636
637 void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoCompareInfo *this_obj, MonoSortKey *key, MonoString *source, gint32 options)
638 {
639         MonoArray *arr;
640         gint32 keylen, i;
641
642         keylen=mono_string_length (source);
643         
644         arr=mono_array_new (mono_domain_get (), mono_get_byte_class (),
645                             keylen);
646         for(i=0; i<keylen; i++) {
647                 mono_array_set (arr, guint8, i, mono_string_chars (source)[i]);
648         }
649         
650         MONO_OBJECT_SETREF (key, key, arr);
651 }
652
653 int ves_icall_System_Globalization_CompareInfo_internal_index (MonoCompareInfo *this_obj, MonoString *source, gint32 sindex, gint32 count, MonoString *value, gint32 options, MonoBoolean first)
654 {
655         return(string_invariant_indexof (source, sindex, count, value, first));
656 }
657
658 int ves_icall_System_Globalization_CompareInfo_internal_index_char (MonoCompareInfo *this_obj, MonoString *source, gint32 sindex, gint32 count, gunichar2 value, gint32 options, MonoBoolean first)
659 {
660         return(string_invariant_indexof_char (source, sindex, count, value,
661                                               first));
662 }
663
664 int ves_icall_System_Threading_Thread_current_lcid (void)
665 {
666         /* Invariant */
667         return(0x007F);
668 }
669
670 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
671                                              gint32 options)
672 {
673         gint32 result;
674
675         /* Ordinal can not be mixed with other options, and must return the difference, not only -1, 0, 1 */
676         if (options & CompareOptions_Ordinal) 
677                 return (gint32) c1 - c2;
678         
679         if (options & CompareOptions_IgnoreCase) {
680                 GUnicodeType c1type, c2type;
681
682                 c1type = g_unichar_type (c1);
683                 c2type = g_unichar_type (c2);
684         
685                 result = (gint32) (c1type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c1) : c1) -
686                         (c2type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c2) : c2);
687         } else {
688                 /*
689                  * No options. Kana, symbol and spacing options don't
690                  * apply to the invariant culture.
691                  */
692
693                 /*
694                  * FIXME: here we must use the information from c1type and c2type
695                  * to find out the proper collation, even on the InvariantCulture, the
696                  * sorting is not done by computing the unicode values, but their
697                  * actual sort order.
698                  */
699                 result = (gint32) c1 - c2;
700         }
701
702         return ((result < 0) ? -1 : (result > 0) ? 1 : 0);
703 }
704
705 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
706                                         gint32 len1, MonoString *str2,
707                                         gint32 off2, gint32 len2,
708                                         gint32 options)
709 {
710         /* c translation of C# code from old string.cs.. :) */
711         gint32 length;
712         gint32 charcmp;
713         gunichar2 *ustr1;
714         gunichar2 *ustr2;
715         gint32 pos;
716
717         if(len1 >= len2) {
718                 length=len1;
719         } else {
720                 length=len2;
721         }
722
723         ustr1 = mono_string_chars(str1)+off1;
724         ustr2 = mono_string_chars(str2)+off2;
725
726         pos = 0;
727
728         for (pos = 0; pos != length; pos++) {
729                 if (pos >= len1 || pos >= len2)
730                         break;
731
732                 charcmp = string_invariant_compare_char(ustr1[pos], ustr2[pos],
733                                                         options);
734                 if (charcmp != 0) {
735                         return(charcmp);
736                 }
737         }
738
739         /* the lesser wins, so if we have looped until length we just
740          * need to check the last char
741          */
742         if (pos == length) {
743                 return(string_invariant_compare_char(ustr1[pos - 1],
744                                                      ustr2[pos - 1], options));
745         }
746
747         /* Test if one of the strings has been compared to the end */
748         if (pos >= len1) {
749                 if (pos >= len2) {
750                         return(0);
751                 } else {
752                         return(-1);
753                 }
754         } else if (pos >= len2) {
755                 return(1);
756         }
757
758         /* if not, check our last char only.. (can this happen?) */
759         return(string_invariant_compare_char(ustr1[pos], ustr2[pos], options));
760 }
761
762 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
763                                         gint32 count, MonoString *value,
764                                         MonoBoolean first)
765 {
766         gint32 lencmpstr;
767         gunichar2 *src;
768         gunichar2 *cmpstr;
769         gint32 pos,i;
770         
771         lencmpstr = mono_string_length(value);
772         
773         src = mono_string_chars(source);
774         cmpstr = mono_string_chars(value);
775
776         if(first) {
777                 count -= lencmpstr;
778                 for(pos=sindex;pos <= sindex+count;pos++) {
779                         for(i=0;src[pos+i]==cmpstr[i];) {
780                                 if(++i==lencmpstr) {
781                                         return(pos);
782                                 }
783                         }
784                 }
785                 
786                 return(-1);
787         } else {
788                 for(pos=sindex-lencmpstr+1;pos>sindex-count;pos--) {
789                         if(memcmp (src+pos, cmpstr,
790                                    lencmpstr*sizeof(gunichar2))==0) {
791                                 return(pos);
792                         }
793                 }
794                 
795                 return(-1);
796         }
797 }
798
799 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
800                                              gint32 count, gunichar2 value,
801                                              MonoBoolean first)
802 {
803         gint32 pos;
804         gunichar2 *src;
805
806         src = mono_string_chars(source);
807         if(first) {
808                 for (pos = sindex; pos != count + sindex; pos++) {
809                         if (src [pos] == value) {
810                                 return(pos);
811                         }
812                 }
813
814                 return(-1);
815         } else {
816                 for (pos = sindex; pos > sindex - count; pos--) {
817                         if (src [pos] == value)
818                                 return(pos);
819                 }
820
821                 return(-1);
822         }
823 }
824
825 void ves_icall_System_Text_Normalization_load_normalization_resource (guint8 **argProps,
826                                                                                                                                           guint8 **argMappedChars,
827                                                                                                                                           guint8 **argCharMapIndex,
828                                                                                                                                           guint8 **argHelperIndex,
829                                                                                                                                           guint8 **argMapIdxToComposite,
830                                                                                                                                           guint8 **argCombiningClass)
831 {
832 #ifdef DISABLE_NORMALIZATION
833         mono_set_pending_exception (mono_get_exception_not_supported ("This runtime has been compiled without string normalization support."));
834         return;
835 #else
836         *argProps = (guint8*)props;
837         *argMappedChars = (guint8*) mappedChars;
838         *argCharMapIndex = (guint8*) charMapIndex;
839         *argHelperIndex = (guint8*) helperIndex;
840         *argMapIdxToComposite = (guint8*) mapIdxToComposite;
841         *argCombiningClass = (guint8*)combiningClass;
842 #endif
843 }
844
845