2 * locales.c: Culture-sensitive handling
5 * Dick Porter (dick@ximian.com)
6 * Mohammad DAMT (mdamt@cdl2000.com)
8 * (C) 2003 Ximian, Inc.
9 * (C) 2003 PT Cakram Datalingga Duaribu http://www.cdl2000.com
16 #include <mono/metadata/debug-helpers.h>
17 #include <mono/metadata/object.h>
18 #include <mono/metadata/appdomain.h>
19 #include <mono/metadata/exception.h>
20 #include <mono/metadata/monitor.h>
21 #include <mono/metadata/locales.h>
22 #include <mono/metadata/culture-info.h>
23 #include <mono/metadata/culture-info-tables.h>
30 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
32 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
33 gint32 len1, MonoString *str2,
34 gint32 off2, gint32 len2,
36 static MonoString *string_invariant_replace (MonoString *me,
38 MonoString *newValue);
39 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
40 gint32 count, MonoString *value,
42 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
43 gint32 count, gunichar2 value,
46 static const CultureInfoEntry* culture_info_entry_from_lcid (int lcid);
48 static const RegionInfoEntry* region_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 region_lcid_locator (const void *a, const void *b)
63 const CultureInfoEntry *bb = b;
65 return *lcid - bb->lcid;
69 culture_name_locator (const void *a, const void *b)
72 const CultureInfoNameEntry *bb = b;
75 ret = strcmp (aa, idx2string (bb->name));
81 region_name_locator (const void *a, const void *b)
84 const RegionInfoNameEntry *bb = b;
87 ret = strcmp (aa, idx2string (bb->name));
93 create_group_sizes_array (const gint *gs, gint ml)
98 for (i = 0; i < ml; i++) {
104 ret = mono_array_new (mono_domain_get (),
105 mono_get_int32_class (), len);
107 for(i = 0; i < len; i++)
108 mono_array_set (ret, gint32, i, gs [i]);
114 create_names_array_idx (const guint16 *names, int ml)
123 domain = mono_domain_get ();
125 for (i = 0; i < ml; i++) {
131 ret = mono_array_new (mono_domain_get (), mono_get_string_class (), len);
133 for(i = 0; i < len; i++)
134 mono_array_setref (ret, i, mono_string_new (domain, idx2string (names [i])));
140 ves_icall_System_Globalization_CultureInfo_construct_datetime_format (MonoCultureInfo *this)
143 MonoDateTimeFormatInfo *datetime;
144 const DateTimeFormatEntry *dfe;
148 g_assert (this->datetime_index >= 0);
150 datetime = this->datetime_format;
151 dfe = &datetime_format_entries [this->datetime_index];
153 domain = mono_domain_get ();
155 MONO_OBJECT_SETREF (datetime, AbbreviatedDayNames, create_names_array_idx (dfe->abbreviated_day_names,
157 MONO_OBJECT_SETREF (datetime, AbbreviatedMonthNames, create_names_array_idx (dfe->abbreviated_month_names,
159 MONO_OBJECT_SETREF (datetime, AMDesignator, mono_string_new (domain, idx2string (dfe->am_designator)));
160 datetime->CalendarWeekRule = dfe->calendar_week_rule;
161 MONO_OBJECT_SETREF (datetime, DateSeparator, mono_string_new (domain, idx2string (dfe->date_separator)));
162 MONO_OBJECT_SETREF (datetime, DayNames, create_names_array_idx (dfe->day_names, NUM_DAYS));
163 datetime->FirstDayOfWeek = dfe->first_day_of_week;
164 MONO_OBJECT_SETREF (datetime, FullDateTimePattern, mono_string_new (domain, idx2string (dfe->full_date_time_pattern)));
165 MONO_OBJECT_SETREF (datetime, LongDatePattern, mono_string_new (domain, idx2string (dfe->long_date_pattern)));
166 MONO_OBJECT_SETREF (datetime, LongTimePattern, mono_string_new (domain, idx2string (dfe->long_time_pattern)));
167 MONO_OBJECT_SETREF (datetime, MonthDayPattern, mono_string_new (domain, idx2string (dfe->month_day_pattern)));
168 MONO_OBJECT_SETREF (datetime, MonthNames, create_names_array_idx (dfe->month_names, NUM_MONTHS));
169 MONO_OBJECT_SETREF (datetime, PMDesignator, mono_string_new (domain, idx2string (dfe->pm_designator)));
170 MONO_OBJECT_SETREF (datetime, ShortDatePattern, mono_string_new (domain, idx2string (dfe->short_date_pattern)));
171 MONO_OBJECT_SETREF (datetime, ShortTimePattern, mono_string_new (domain, idx2string (dfe->short_time_pattern)));
172 MONO_OBJECT_SETREF (datetime, TimeSeparator, mono_string_new (domain, idx2string (dfe->time_separator)));
173 MONO_OBJECT_SETREF (datetime, YearMonthPattern, mono_string_new (domain, idx2string (dfe->year_month_pattern)));
174 MONO_OBJECT_SETREF (datetime, ShortDatePatterns, create_names_array_idx (dfe->short_date_patterns,
175 NUM_SHORT_DATE_PATTERNS));
176 MONO_OBJECT_SETREF (datetime, LongDatePatterns, create_names_array_idx (dfe->long_date_patterns,
177 NUM_LONG_DATE_PATTERNS));
178 MONO_OBJECT_SETREF (datetime, ShortTimePatterns, create_names_array_idx (dfe->short_time_patterns,
179 NUM_SHORT_TIME_PATTERNS));
180 MONO_OBJECT_SETREF (datetime, LongTimePatterns, create_names_array_idx (dfe->long_time_patterns,
181 NUM_LONG_TIME_PATTERNS));
186 ves_icall_System_Globalization_CultureInfo_construct_number_format (MonoCultureInfo *this)
189 MonoNumberFormatInfo *number;
190 const NumberFormatEntry *nfe;
194 g_assert (this->number_format != 0);
196 number = this->number_format;
197 nfe = &number_format_entries [this->number_index];
199 domain = mono_domain_get ();
201 number->currencyDecimalDigits = nfe->currency_decimal_digits;
202 MONO_OBJECT_SETREF (number, currencyDecimalSeparator, mono_string_new (domain,
203 idx2string (nfe->currency_decimal_separator)));
204 MONO_OBJECT_SETREF (number, currencyGroupSeparator, mono_string_new (domain,
205 idx2string (nfe->currency_group_separator)));
206 MONO_OBJECT_SETREF (number, currencyGroupSizes, create_group_sizes_array (nfe->currency_group_sizes,
208 number->currencyNegativePattern = nfe->currency_negative_pattern;
209 number->currencyPositivePattern = nfe->currency_positive_pattern;
210 MONO_OBJECT_SETREF (number, currencySymbol, mono_string_new (domain, idx2string (nfe->currency_symbol)));
211 MONO_OBJECT_SETREF (number, naNSymbol, mono_string_new (domain, idx2string (nfe->nan_symbol)));
212 MONO_OBJECT_SETREF (number, negativeInfinitySymbol, mono_string_new (domain,
213 idx2string (nfe->negative_infinity_symbol)));
214 MONO_OBJECT_SETREF (number, negativeSign, mono_string_new (domain, idx2string (nfe->negative_sign)));
215 number->numberDecimalDigits = nfe->number_decimal_digits;
216 MONO_OBJECT_SETREF (number, numberDecimalSeparator, mono_string_new (domain,
217 idx2string (nfe->number_decimal_separator)));
218 MONO_OBJECT_SETREF (number, numberGroupSeparator, mono_string_new (domain, idx2string (nfe->number_group_separator)));
219 MONO_OBJECT_SETREF (number, numberGroupSizes, create_group_sizes_array (nfe->number_group_sizes,
221 number->numberNegativePattern = nfe->number_negative_pattern;
222 number->percentDecimalDigits = nfe->percent_decimal_digits;
223 MONO_OBJECT_SETREF (number, percentDecimalSeparator, mono_string_new (domain,
224 idx2string (nfe->percent_decimal_separator)));
225 MONO_OBJECT_SETREF (number, percentGroupSeparator, mono_string_new (domain,
226 idx2string (nfe->percent_group_separator)));
227 MONO_OBJECT_SETREF (number, percentGroupSizes, create_group_sizes_array (nfe->percent_group_sizes,
229 number->percentNegativePattern = nfe->percent_negative_pattern;
230 number->percentPositivePattern = nfe->percent_positive_pattern;
231 MONO_OBJECT_SETREF (number, percentSymbol, mono_string_new (domain, idx2string (nfe->percent_symbol)));
232 MONO_OBJECT_SETREF (number, perMilleSymbol, mono_string_new (domain, idx2string (nfe->per_mille_symbol)));
233 MONO_OBJECT_SETREF (number, positiveInfinitySymbol, mono_string_new (domain,
234 idx2string (nfe->positive_infinity_symbol)));
235 MONO_OBJECT_SETREF (number, positiveSign, mono_string_new (domain, idx2string (nfe->positive_sign)));
239 construct_culture (MonoCultureInfo *this, const CultureInfoEntry *ci)
241 MonoDomain *domain = mono_domain_get ();
243 this->lcid = ci->lcid;
244 MONO_OBJECT_SETREF (this, name, mono_string_new (domain, idx2string (ci->name)));
245 MONO_OBJECT_SETREF (this, icu_name, mono_string_new (domain, idx2string (ci->icu_name)));
246 MONO_OBJECT_SETREF (this, displayname, mono_string_new (domain, idx2string (ci->displayname)));
247 MONO_OBJECT_SETREF (this, englishname, mono_string_new (domain, idx2string (ci->englishname)));
248 MONO_OBJECT_SETREF (this, nativename, mono_string_new (domain, idx2string (ci->nativename)));
249 MONO_OBJECT_SETREF (this, win3lang, mono_string_new (domain, idx2string (ci->win3lang)));
250 MONO_OBJECT_SETREF (this, iso3lang, mono_string_new (domain, idx2string (ci->iso3lang)));
251 MONO_OBJECT_SETREF (this, iso2lang, mono_string_new (domain, idx2string (ci->iso2lang)));
252 this->parent_lcid = ci->parent_lcid;
253 this->specific_lcid = ci->specific_lcid;
254 this->datetime_index = ci->datetime_format_index;
255 this->number_index = ci->number_format_index;
256 this->calendar_data = ci->calendar_data;
257 this->text_info_data = &ci->text_info;
263 construct_region (MonoRegionInfo *this, const RegionInfoEntry *ri)
265 MonoDomain *domain = mono_domain_get ();
267 this->region_id = ri->region_id;
268 MONO_OBJECT_SETREF (this, iso2name, mono_string_new (domain, idx2string (ri->iso2name)));
269 MONO_OBJECT_SETREF (this, iso3name, mono_string_new (domain, idx2string (ri->iso3name)));
270 MONO_OBJECT_SETREF (this, win3name, mono_string_new (domain, idx2string (ri->win3name)));
271 MONO_OBJECT_SETREF (this, english_name, mono_string_new (domain, idx2string (ri->english_name)));
272 MONO_OBJECT_SETREF (this, currency_symbol, mono_string_new (domain, idx2string (ri->currency_symbol)));
273 MONO_OBJECT_SETREF (this, iso_currency_symbol, mono_string_new (domain, idx2string (ri->iso_currency_symbol)));
274 MONO_OBJECT_SETREF (this, currency_english_name, mono_string_new (domain, idx2string (ri->currency_english_name)));
280 construct_culture_from_specific_name (MonoCultureInfo *ci, gchar *name)
282 const CultureInfoEntry *entry;
283 const CultureInfoNameEntry *ne;
287 ne = bsearch (name, culture_name_entries, NUM_CULTURE_ENTRIES,
288 sizeof (CultureInfoNameEntry), culture_name_locator);
293 entry = &culture_entries [ne->culture_entry_index];
295 /* try avoiding another lookup, often the culture is its own specific culture */
296 if (entry->lcid != entry->specific_lcid)
297 entry = culture_info_entry_from_lcid (entry->specific_lcid);
299 return construct_culture (ci, entry);
303 construct_region_from_specific_name (MonoRegionInfo *ri, gchar *name)
305 const RegionInfoEntry *entry;
306 const RegionInfoNameEntry *ne;
310 ne = bsearch (name, region_name_entries, NUM_REGION_ENTRIES,
311 sizeof (RegionInfoNameEntry), region_name_locator);
316 entry = ®ion_entries [ne->region_entry_index];
318 return construct_region (ri, entry);
321 static const CultureInfoEntry*
322 culture_info_entry_from_lcid (int lcid)
324 const CultureInfoEntry *ci;
325 CultureInfoEntry key;
328 ci = bsearch (&key, culture_entries, NUM_CULTURE_ENTRIES, sizeof (CultureInfoEntry), culture_lcid_locator);
333 static const RegionInfoEntry*
334 region_info_entry_from_lcid (int lcid)
336 const RegionInfoEntry *entry;
337 const CultureInfoEntry *ne;
341 ne = bsearch (&lcid, culture_entries, NUM_CULTURE_ENTRIES,
342 sizeof (CultureInfoEntry), region_lcid_locator);
347 entry = ®ion_entries [ne->region_entry_index];
353 * The following two methods are modified from the ICU source code. (http://oss.software.ibm.com/icu)
354 * Copyright (c) 1995-2003 International Business Machines Corporation and others
355 * All rights reserved.
358 get_posix_locale (void)
360 const gchar* posix_locale = NULL;
362 posix_locale = g_getenv("LC_ALL");
363 if (posix_locale == 0) {
364 posix_locale = g_getenv("LANG");
365 if (posix_locale == 0) {
366 posix_locale = setlocale(LC_ALL, NULL);
370 if (posix_locale == NULL)
373 if ((strcmp ("C", posix_locale) == 0) || (strchr (posix_locale, ' ') != NULL)
374 || (strchr (posix_locale, '/') != NULL)) {
376 * HPUX returns 'C C C C C C C'
377 * Solaris can return /en_US/C/C/C/C/C on the second try.
378 * Maybe we got some garbage.
383 return g_strdup (posix_locale);
387 get_current_locale_name (void)
390 gchar *corrected = NULL;
394 #ifdef PLATFORM_WIN32
395 locale = g_win32_getlocale ();
397 locale = get_posix_locale ();
403 if ((p = strchr (locale, '.')) != NULL) {
404 /* assume new locale can't be larger than old one? */
405 corrected = malloc (strlen (locale));
406 strncpy (corrected, locale, p - locale);
407 corrected [p - locale] = 0;
409 /* do not copy after the @ */
410 if ((p = strchr (corrected, '@')) != NULL)
411 corrected [p - corrected] = 0;
414 /* Note that we scan the *uncorrected* ID. */
415 if ((p = strrchr (locale, '@')) != NULL) {
418 * In Mono we dont handle the '@' modifier because we do
419 * not have any cultures that use it. We just trim it
420 * off of the end of the name.
423 if (corrected == NULL) {
424 corrected = malloc (strlen (locale));
425 strncpy (corrected, locale, p - locale);
426 corrected [p - locale] = 0;
430 if (corrected == NULL)
435 if ((c = strchr (corrected, '_')) != NULL)
438 g_strdown (corrected);
444 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_current_locale (MonoCultureInfo *ci)
451 locale = get_current_locale_name ();
455 ret = construct_culture_from_specific_name (ci, locale);
462 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_lcid (MonoCultureInfo *this,
465 const CultureInfoEntry *ci;
469 ci = culture_info_entry_from_lcid (lcid);
473 return construct_culture (this, ci);
477 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_name (MonoCultureInfo *this,
480 const CultureInfoNameEntry *ne;
485 n = mono_string_to_utf8 (name);
486 ne = bsearch (n, culture_name_entries, NUM_CULTURE_ENTRIES,
487 sizeof (CultureInfoNameEntry), culture_name_locator);
490 /*g_print ("ne (%s) is null\n", n);*/
496 return construct_culture (this, &culture_entries [ne->culture_entry_index]);
500 ves_icall_System_Globalization_CultureInfo_construct_internal_locale_from_specific_name (MonoCultureInfo *ci,
508 locale = mono_string_to_utf8 (name);
509 ret = construct_culture_from_specific_name (ci, locale);
516 ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_lcid (MonoRegionInfo *this,
519 const RegionInfoEntry *ri;
523 ri = region_info_entry_from_lcid (lcid);
527 return construct_region (this, ri);
531 ves_icall_System_Globalization_RegionInfo_construct_internal_region_from_name (MonoRegionInfo *this,
534 const RegionInfoNameEntry *ne;
539 n = mono_string_to_utf8 (name);
540 ne = bsearch (n, region_name_entries, NUM_REGION_ENTRIES,
541 sizeof (RegionInfoNameEntry), region_name_locator);
544 /*g_print ("ne (%s) is null\n", n);*/
550 return construct_region (this, ®ion_entries [ne->region_entry_index]);
554 ves_icall_System_Globalization_CultureInfo_internal_get_cultures (MonoBoolean neutral,
555 MonoBoolean specific, MonoBoolean installed)
559 MonoCultureInfo *culture;
561 const CultureInfoEntry *ci;
567 domain = mono_domain_get ();
570 for (i = 0; i < NUM_CULTURE_ENTRIES; i++) {
571 ci = &culture_entries [i];
572 is_neutral = ((ci->lcid & 0xff00) == 0 || ci->specific_lcid == 0);
573 if ((neutral && is_neutral) || (specific && !is_neutral))
577 class = mono_class_from_name (mono_get_corlib (),
578 "System.Globalization", "CultureInfo");
580 /* The InvariantCulture is not in culture_entries */
581 /* We reserve the first slot in the array for it */
585 ret = mono_array_new (domain, class, len);
592 mono_array_setref (ret, len++, NULL);
594 for (i = 0; i < NUM_CULTURE_ENTRIES; i++) {
595 ci = &culture_entries [i];
596 is_neutral = ((ci->lcid & 0xff00) == 0 || ci->specific_lcid == 0);
597 if ((neutral && is_neutral) || (specific && !is_neutral)) {
598 culture = (MonoCultureInfo *) mono_object_new (domain, class);
599 mono_runtime_object_init ((MonoObject *) culture);
600 construct_culture (culture, ci);
601 mono_array_setref (ret, len++, culture);
609 * ves_icall_System_Globalization_CultureInfo_internal_is_lcid_neutral:
611 * Set is_neutral and return TRUE if the culture is found. If it is not found return FALSE.
614 ves_icall_System_Globalization_CultureInfo_internal_is_lcid_neutral (gint lcid, MonoBoolean *is_neutral)
616 const CultureInfoEntry *entry;
620 entry = culture_info_entry_from_lcid (lcid);
625 *is_neutral = (entry->specific_lcid == 0);
630 void ves_icall_System_Globalization_CultureInfo_construct_internal_locale (MonoCultureInfo *this, MonoString *locale)
634 /* Always claim "unknown locale" if we don't have ICU (only
635 * called for non-invariant locales)
637 mono_raise_exception((MonoException *)mono_exception_from_name(mono_get_corlib (), "System", "ArgumentException"));
640 void ves_icall_System_Globalization_CompareInfo_construct_compareinfo (MonoCompareInfo *comp, MonoString *locale)
642 /* Nothing to do here */
645 int ves_icall_System_Globalization_CompareInfo_internal_compare (MonoCompareInfo *this, MonoString *str1, gint32 off1, gint32 len1, MonoString *str2, gint32 off2, gint32 len2, gint32 options)
649 /* Do a normal ascii string compare, as we only know the
650 * invariant locale if we dont have ICU
652 return(string_invariant_compare (str1, off1, len1, str2, off2, len2,
656 void ves_icall_System_Globalization_CompareInfo_free_internal_collator (MonoCompareInfo *this)
658 /* Nothing to do here */
661 void ves_icall_System_Globalization_CompareInfo_assign_sortkey (MonoCompareInfo *this, MonoSortKey *key, MonoString *source, gint32 options)
668 keylen=mono_string_length (source);
670 arr=mono_array_new (mono_domain_get (), mono_get_byte_class (),
672 for(i=0; i<keylen; i++) {
673 mono_array_set (arr, guint8, i, mono_string_chars (source)[i]);
676 MONO_OBJECT_SETREF (key, key, arr);
679 int ves_icall_System_Globalization_CompareInfo_internal_index (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, MonoString *value, gint32 options, MonoBoolean first)
683 return(string_invariant_indexof (source, sindex, count, value, first));
686 int ves_icall_System_Globalization_CompareInfo_internal_index_char (MonoCompareInfo *this, MonoString *source, gint32 sindex, gint32 count, gunichar2 value, gint32 options, MonoBoolean first)
690 return(string_invariant_indexof_char (source, sindex, count, value,
694 int ves_icall_System_Threading_Thread_current_lcid (void)
702 MonoString *ves_icall_System_String_InternalReplace_Str_Comp (MonoString *this, MonoString *old, MonoString *new, MonoCompareInfo *comp)
706 /* Do a normal ascii string compare and replace, as we only
707 * know the invariant locale if we dont have ICU
709 return(string_invariant_replace (this, old, new));
712 static gint32 string_invariant_compare_char (gunichar2 c1, gunichar2 c2,
716 GUnicodeType c1type, c2type;
718 /* Ordinal can not be mixed with other options, and must return the difference, not only -1, 0, 1 */
719 if (options & CompareOptions_Ordinal)
720 return (gint32) c1 - c2;
722 c1type = g_unichar_type (c1);
723 c2type = g_unichar_type (c2);
725 if (options & CompareOptions_IgnoreCase) {
726 result = (gint32) (c1type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c1) : c1) -
727 (c2type != G_UNICODE_LOWERCASE_LETTER ? g_unichar_tolower(c2) : c2);
730 * No options. Kana, symbol and spacing options don't
731 * apply to the invariant culture.
735 * FIXME: here we must use the information from c1type and c2type
736 * to find out the proper collation, even on the InvariantCulture, the
737 * sorting is not done by computing the unicode values, but their
740 result = (gint32) c1 - c2;
743 return ((result < 0) ? -1 : (result > 0) ? 1 : 0);
746 static gint32 string_invariant_compare (MonoString *str1, gint32 off1,
747 gint32 len1, MonoString *str2,
748 gint32 off2, gint32 len2,
751 /* c translation of C# code from old string.cs.. :) */
764 ustr1 = mono_string_chars(str1)+off1;
765 ustr2 = mono_string_chars(str2)+off2;
769 for (pos = 0; pos != length; pos++) {
770 if (pos >= len1 || pos >= len2)
773 charcmp = string_invariant_compare_char(ustr1[pos], ustr2[pos],
780 /* the lesser wins, so if we have looped until length we just
781 * need to check the last char
784 return(string_invariant_compare_char(ustr1[pos - 1],
785 ustr2[pos - 1], options));
788 /* Test if one of the strings has been compared to the end */
795 } else if (pos >= len2) {
799 /* if not, check our last char only.. (can this happen?) */
800 return(string_invariant_compare_char(ustr1[pos], ustr2[pos], options));
803 static MonoString *string_invariant_replace (MonoString *me,
804 MonoString *oldValue,
805 MonoString *newValue)
809 gunichar2 *dest=NULL; /* shut gcc up */
811 gunichar2 *newstr=NULL; /* shut gcc up here too */
822 oldstr = mono_string_chars(oldValue);
823 oldstrlen = mono_string_length(oldValue);
825 if (NULL != newValue) {
826 newstr = mono_string_chars(newValue);
827 newstrlen = mono_string_length(newValue);
831 src = mono_string_chars(me);
832 srclen = mono_string_length(me);
834 if (oldstrlen != newstrlen) {
836 while (i <= srclen - oldstrlen) {
837 if (0 == memcmp(src + i, oldstr, oldstrlen * sizeof(gunichar2))) {
846 newsize = srclen + ((newstrlen - oldstrlen) * occurr);
853 if (0 == memcmp(src + i, oldstr, oldstrlen * sizeof(gunichar2))) {
855 ret = mono_string_new_size( mono_domain_get (), newsize);
856 dest = mono_string_chars(ret);
857 memcpy (dest, src, i * sizeof(gunichar2));
860 memcpy(dest + destpos, newstr, newstrlen * sizeof(gunichar2));
861 destpos += newstrlen;
865 } else if (ret != NULL) {
866 dest[destpos] = src[i];
878 static gint32 string_invariant_indexof (MonoString *source, gint32 sindex,
879 gint32 count, MonoString *value,
887 lencmpstr = mono_string_length(value);
889 src = mono_string_chars(source);
890 cmpstr = mono_string_chars(value);
894 for(pos=sindex;pos <= sindex+count;pos++) {
895 for(i=0;src[pos+i]==cmpstr[i];) {
904 for(pos=sindex-lencmpstr+1;pos>sindex-count;pos--) {
905 if(memcmp (src+pos, cmpstr,
906 lencmpstr*sizeof(gunichar2))==0) {
915 static gint32 string_invariant_indexof_char (MonoString *source, gint32 sindex,
916 gint32 count, gunichar2 value,
922 src = mono_string_chars(source);
924 for (pos = sindex; pos != count + sindex; pos++) {
925 if (src [pos] == value) {
932 for (pos = sindex; pos > sindex - count; pos--) {
933 if (src [pos] == value)