diff options
Diffstat (limited to 'src/mscorlib/src/System/Globalization')
79 files changed, 11027 insertions, 25071 deletions
diff --git a/src/mscorlib/src/System/Globalization/BidiCategory.cs b/src/mscorlib/src/System/Globalization/BidiCategory.cs index 1041776424..f37c44b385 100644 --- a/src/mscorlib/src/System/Globalization/BidiCategory.cs +++ b/src/mscorlib/src/System/Globalization/BidiCategory.cs @@ -9,9 +9,12 @@ ** ** ============================================================*/ -namespace System.Globalization { + +namespace System.Globalization +{ [Serializable] - internal enum BidiCategory { + internal enum BidiCategory + { LeftToRight = 0, LeftToRightEmbedding = 1, LeftToRightOverride = 2, @@ -32,7 +35,7 @@ namespace System.Globalization { Whitespace = 17, OtherNeutrals = 18, LeftToRightIsolate = 19, - RightToLeftIsolate = 20, + RightToLeftIsolate = 20, FirstStrongIsolate = 21, PopDirectionIsolate = 22, } diff --git a/src/mscorlib/src/System/Globalization/Calendar.cs b/src/mscorlib/src/System/Globalization/Calendar.cs index 1ff795ddc5..c23e1088c1 100644 --- a/src/mscorlib/src/System/Globalization/Calendar.cs +++ b/src/mscorlib/src/System/Globalization/Calendar.cs @@ -2,14 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace System.Globalization { - using System; - using System.Runtime.CompilerServices; - using System.Globalization; - using System.Runtime.Versioning; - using System.Diagnostics; - using System.Diagnostics.Contracts; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Runtime.Serialization; +namespace System.Globalization +{ // This abstract class represents a calendar. A calendar reckons time in // divisions such as weeks, months and years. The number, length and start of // the divisions vary in each calendar. @@ -31,69 +29,49 @@ namespace System.Globalization { // since most of the calendars (or all?) have the same way of calcuating hour/minute/second. [Serializable] - public abstract class Calendar : ICloneable + public abstract partial class Calendar : ICloneable { - // Number of 100ns (10E-7 second) ticks per time unit - internal const long TicksPerMillisecond = 10000; - internal const long TicksPerSecond = TicksPerMillisecond * 1000; - internal const long TicksPerMinute = TicksPerSecond * 60; - internal const long TicksPerHour = TicksPerMinute * 60; - internal const long TicksPerDay = TicksPerHour * 24; + internal const long TicksPerMillisecond = 10000; + internal const long TicksPerSecond = TicksPerMillisecond * 1000; + internal const long TicksPerMinute = TicksPerSecond * 60; + internal const long TicksPerHour = TicksPerMinute * 60; + internal const long TicksPerDay = TicksPerHour * 24; // Number of milliseconds per time unit - internal const int MillisPerSecond = 1000; - internal const int MillisPerMinute = MillisPerSecond * 60; - internal const int MillisPerHour = MillisPerMinute * 60; - internal const int MillisPerDay = MillisPerHour * 24; + internal const int MillisPerSecond = 1000; + internal const int MillisPerMinute = MillisPerSecond * 60; + internal const int MillisPerHour = MillisPerMinute * 60; + internal const int MillisPerDay = MillisPerHour * 24; // Number of days in a non-leap year - internal const int DaysPerYear = 365; + internal const int DaysPerYear = 365; // Number of days in 4 years - internal const int DaysPer4Years = DaysPerYear * 4 + 1; + internal const int DaysPer4Years = DaysPerYear * 4 + 1; // Number of days in 100 years - internal const int DaysPer100Years = DaysPer4Years * 25 - 1; + internal const int DaysPer100Years = DaysPer4Years * 25 - 1; // Number of days in 400 years - internal const int DaysPer400Years = DaysPer100Years * 4 + 1; + internal const int DaysPer400Years = DaysPer100Years * 4 + 1; // Number of days from 1/1/0001 to 1/1/10000 - internal const int DaysTo10000 = DaysPer400Years * 25 - 366; - - internal const long MaxMillis = (long)DaysTo10000 * MillisPerDay; - - // - // Calendar ID Values. This is used to get data from calendar.nlp. - // The order of calendar ID means the order of data items in the table. - // - - internal const int CAL_GREGORIAN = 1 ; // Gregorian (localized) calendar - internal const int CAL_GREGORIAN_US = 2 ; // Gregorian (U.S.) calendar - internal const int CAL_JAPAN = 3 ; // Japanese Emperor Era calendar - internal const int CAL_TAIWAN = 4 ; // Taiwan Era calendar - internal const int CAL_KOREA = 5 ; // Korean Tangun Era calendar - internal const int CAL_HIJRI = 6 ; // Hijri (Arabic Lunar) calendar - internal const int CAL_THAI = 7 ; // Thai calendar - internal const int CAL_HEBREW = 8 ; // Hebrew (Lunar) calendar - internal const int CAL_GREGORIAN_ME_FRENCH = 9 ; // Gregorian Middle East French calendar - internal const int CAL_GREGORIAN_ARABIC = 10; // Gregorian Arabic calendar - internal const int CAL_GREGORIAN_XLIT_ENGLISH = 11; // Gregorian Transliterated English calendar - internal const int CAL_GREGORIAN_XLIT_FRENCH = 12; - internal const int CAL_JULIAN = 13; - internal const int CAL_JAPANESELUNISOLAR = 14; - internal const int CAL_CHINESELUNISOLAR = 15; - internal const int CAL_SAKA = 16; // reserved to match Office but not implemented in our code - internal const int CAL_LUNAR_ETO_CHN = 17; // reserved to match Office but not implemented in our code - internal const int CAL_LUNAR_ETO_KOR = 18; // reserved to match Office but not implemented in our code - internal const int CAL_LUNAR_ETO_ROKUYOU = 19; // reserved to match Office but not implemented in our code - internal const int CAL_KOREANLUNISOLAR = 20; - internal const int CAL_TAIWANLUNISOLAR = 21; - internal const int CAL_PERSIAN = 22; - internal const int CAL_UMALQURA = 23; - - internal int m_currentEraValue = -1; - - [System.Runtime.Serialization.OptionalField(VersionAdded = 2)] - private bool m_isReadOnly = false; + internal const int DaysTo10000 = DaysPer400Years * 25 - 366; + + internal const long MaxMillis = (long)DaysTo10000 * MillisPerDay; + + private int _currentEraValue = -1; + + [OptionalField(VersionAdded = 2)] + private bool _isReadOnly = false; + +#if CORECLR + internal const CalendarId CAL_HEBREW = CalendarId.HEBREW; + internal const CalendarId CAL_HIJRI = CalendarId.HIJRI; + internal const CalendarId CAL_JAPAN = CalendarId.JAPAN; + internal const CalendarId CAL_JULIAN = CalendarId.JULIAN; + internal const CalendarId CAL_TAIWAN = CalendarId.TAIWAN; + internal const CalendarId CAL_UMALQURA = CalendarId.UMALQURA; + internal const CalendarId CAL_PERSIAN = CalendarId.PERSIAN; +#endif // The minimum supported DateTime range for the calendar. @@ -115,19 +93,27 @@ namespace System.Globalization { } } + public virtual CalendarAlgorithmType AlgorithmType + { + get + { + return CalendarAlgorithmType.Unknown; + } + } - - - protected Calendar() { + protected Calendar() + { //Do-nothing constructor. } /// // This can not be abstract, otherwise no one can create a subclass of Calendar. // - internal virtual int ID { - get { - return (-1); + internal virtual CalendarId ID + { + get + { + return CalendarId.UNINITIALIZED_VALUE; } } @@ -135,21 +121,11 @@ namespace System.Globalization { // Return the Base calendar ID for calendars that didn't have defined data in calendarData // - internal virtual int BaseCalendarID + internal virtual CalendarId BaseCalendarID { get { return ID; } } - // Returns the type of the calendar. - // - public virtual CalendarAlgorithmType AlgorithmType - { - get - { - return CalendarAlgorithmType.Unknown; - } - } - //////////////////////////////////////////////////////////////////////// // // IsReadOnly @@ -159,7 +135,7 @@ namespace System.Globalization { //////////////////////////////////////////////////////////////////////// public bool IsReadOnly { - get { return (m_isReadOnly); } + get { return (_isReadOnly); } } //////////////////////////////////////////////////////////////////////// @@ -169,13 +145,13 @@ namespace System.Globalization { // Is the implementation of ICloneable. // //////////////////////////////////////////////////////////////////////// - public virtual Object Clone() + public virtual object Clone() { object o = MemberwiseClone(); - ((Calendar) o).SetReadOnlyState(false); + ((Calendar)o).SetReadOnlyState(false); return (o); } - + //////////////////////////////////////////////////////////////////////// // // ReadOnly @@ -184,29 +160,29 @@ namespace System.Globalization { // readonly. // //////////////////////////////////////////////////////////////////////// - public static Calendar ReadOnly(Calendar calendar) + public static Calendar ReadOnly(Calendar calendar) { - if (calendar == null) { throw new ArgumentNullException(nameof(calendar)); } + if (calendar == null) { throw new ArgumentNullException(nameof(calendar)); } Contract.EndContractBlock(); - if (calendar.IsReadOnly) { return (calendar); } - + if (calendar.IsReadOnly) { return (calendar); } + Calendar clonedCalendar = (Calendar)(calendar.MemberwiseClone()); clonedCalendar.SetReadOnlyState(true); - + return (clonedCalendar); } internal void VerifyWritable() { - if (m_isReadOnly) + if (_isReadOnly) { - throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly")); + throw new InvalidOperationException(SR.InvalidOperation_ReadOnly); } } internal void SetReadOnlyState(bool readOnly) { - m_isReadOnly = readOnly; + _isReadOnly = readOnly; } @@ -219,14 +195,17 @@ namespace System.Globalization { ** The value is from calendar.nlp. ============================================================================*/ - internal virtual int CurrentEraValue { - get { + internal virtual int CurrentEraValue + { + get + { // The following code assumes that the current era value can not be -1. - if (m_currentEraValue == -1) { - Debug.Assert(BaseCalendarID > 0, "[Calendar.CurrentEraValue] Expected ID > 0"); - m_currentEraValue = CalendarData.GetCalendarData(BaseCalendarID).iCurrentEra; + if (_currentEraValue == -1) + { + Debug.Assert(BaseCalendarID != CalendarId.UNINITIALIZED_VALUE, "[Calendar.CurrentEraValue] Expected a real calendar ID"); + _currentEraValue = CalendarData.GetCalendarData(BaseCalendarID).iCurrentEra; } - return (m_currentEraValue); + return (_currentEraValue); } } @@ -236,16 +215,19 @@ namespace System.Globalization { internal int twoDigitYearMax = -1; - internal static void CheckAddResult(long ticks, DateTime minValue, DateTime maxValue) { - if (ticks < minValue.Ticks || ticks > maxValue.Ticks) { + internal static void CheckAddResult(long ticks, DateTime minValue, DateTime maxValue) + { + if (ticks < minValue.Ticks || ticks > maxValue.Ticks) + { throw new ArgumentException( - String.Format(CultureInfo.InvariantCulture, Environment.GetResourceString("Argument_ResultCalendarRange"), - minValue, maxValue)); + String.Format(CultureInfo.InvariantCulture, SR.Format(SR.Argument_ResultCalendarRange, + minValue, maxValue))); } Contract.EndContractBlock(); } - internal DateTime Add(DateTime time, double value, int scale) { + internal DateTime Add(DateTime time, double value, int scale) + { // From ECMA CLI spec, Partition III, section 3.27: // // If overflow occurs converting a floating-point type to an integer, or if the floating-point value @@ -256,7 +238,7 @@ namespace System.Globalization { double tempMillis = (value * scale + (value >= 0 ? 0.5 : -0.5)); if (!((tempMillis > -(double)MaxMillis) && (tempMillis < (double)MaxMillis))) { - throw new ArgumentOutOfRangeException(nameof(value), Environment.GetResourceString("ArgumentOutOfRange_AddValue")); + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_AddValue); } long millis = (long)tempMillis; @@ -272,7 +254,8 @@ namespace System.Globalization { // argument is permitted to be negative. // - public virtual DateTime AddMilliseconds(DateTime time, double milliseconds) { + public virtual DateTime AddMilliseconds(DateTime time, double milliseconds) + { return (Add(time, milliseconds, 1)); } @@ -284,7 +267,8 @@ namespace System.Globalization { // value argument is permitted to be negative. // - public virtual DateTime AddDays(DateTime time, int days) { + public virtual DateTime AddDays(DateTime time, int days) + { return (Add(time, days, MillisPerDay)); } @@ -295,7 +279,8 @@ namespace System.Globalization { // value argument is permitted to be negative. // - public virtual DateTime AddHours(DateTime time, int hours) { + public virtual DateTime AddHours(DateTime time, int hours) + { return (Add(time, hours, MillisPerHour)); } @@ -307,7 +292,8 @@ namespace System.Globalization { // value argument is permitted to be negative. // - public virtual DateTime AddMinutes(DateTime time, int minutes) { + public virtual DateTime AddMinutes(DateTime time, int minutes) + { return (Add(time, minutes, MillisPerMinute)); } @@ -339,7 +325,8 @@ namespace System.Globalization { // value argument is permitted to be negative. // - public virtual DateTime AddSeconds(DateTime time, int seconds) { + public virtual DateTime AddSeconds(DateTime time, int seconds) + { return Add(time, seconds, MillisPerSecond); } @@ -348,7 +335,8 @@ namespace System.Globalization { // value argument is permitted to be negative. // - public virtual DateTime AddWeeks(DateTime time, int weeks) { + public virtual DateTime AddWeeks(DateTime time, int weeks) + { return (AddDays(time, weeks * 7)); } @@ -425,7 +413,8 @@ namespace System.Globalization { ============================================================================*/ - public abstract int[] Eras { + public abstract int[] Eras + { get; } @@ -434,7 +423,8 @@ namespace System.Globalization { // integer between 0 and 23. // - public virtual int GetHour(DateTime time) { + public virtual int GetHour(DateTime time) + { return ((int)((time.Ticks / TicksPerHour) % 24)); } @@ -442,7 +432,8 @@ namespace System.Globalization { // is an integer between 0 and 999. // - public virtual double GetMilliseconds(DateTime time) { + public virtual double GetMilliseconds(DateTime time) + { return (double)((time.Ticks / TicksPerMillisecond) % 1000); } @@ -450,7 +441,8 @@ namespace System.Globalization { // an integer between 0 and 59. // - public virtual int GetMinute(DateTime time) { + public virtual int GetMinute(DateTime time) + { return ((int)((time.Ticks / TicksPerMinute) % 60)); } @@ -475,7 +467,8 @@ namespace System.Globalization { // an integer between 0 and 59. // - public virtual int GetSecond(DateTime time) { + public virtual int GetSecond(DateTime time) + { return ((int)((time.Ticks / TicksPerSecond) % 60)); } @@ -516,7 +509,8 @@ namespace System.Globalization { ** So Week of year = (GetDayOfYear(time) + offset - 1) / 7 + 1 ============================================================================*/ - internal int GetFirstDayWeekOfYear(DateTime time, int firstDayOfWeek) { + internal int GetFirstDayWeekOfYear(DateTime time, int firstDayOfWeek) + { int dayOfYear = GetDayOfYear(time) - 1; // Make the day of year to be 0-based, so that 1/1 is day 0. // Calculate the day of week for the first day of the year. // dayOfWeek - (dayOfYear % 7) is the day of week for the first day of this year. Note that @@ -527,7 +521,8 @@ namespace System.Globalization { return ((dayOfYear + offset) / 7 + 1); } - private int GetWeekOfYearFullDays(DateTime time, int firstDayOfWeek, int fullDays) { + private int GetWeekOfYearFullDays(DateTime time, int firstDayOfWeek, int fullDays) + { int dayForJan1; int offset; int day; @@ -576,11 +571,12 @@ namespace System.Globalization { // Calculate the day of year for specified time by taking offset into account. // day = dayOfYear - offset; - if (day >= 0) { + if (day >= 0) + { // // If the day of year value is greater than zero, get the week of year. // - return (day/7 + 1); + return (day / 7 + 1); } // // Otherwise, the specified time falls on the week of previous year. @@ -643,13 +639,15 @@ namespace System.Globalization { public virtual int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek) { - if ((int)firstDayOfWeek < 0 || (int)firstDayOfWeek > 6) { + if ((int)firstDayOfWeek < 0 || (int)firstDayOfWeek > 6) + { throw new ArgumentOutOfRangeException( - nameof(firstDayOfWeek), Environment.GetResourceString("ArgumentOutOfRange_Range", + nameof(firstDayOfWeek), SR.Format(SR.ArgumentOutOfRange_Range, DayOfWeek.Sunday, DayOfWeek.Saturday)); } Contract.EndContractBlock(); - switch (rule) { + switch (rule) + { case CalendarWeekRule.FirstDay: return (GetFirstDayWeekOfYear(time, (int)firstDayOfWeek)); case CalendarWeekRule.FirstFullWeek: @@ -658,9 +656,8 @@ namespace System.Globalization { return (GetWeekOfYearFullDays(time, (int)firstDayOfWeek, 4)); } throw new ArgumentOutOfRangeException( - nameof(rule), Environment.GetResourceString("ArgumentOutOfRange_Range", + nameof(rule), SR.Format(SR.ArgumentOutOfRange_Range, CalendarWeekRule.FirstDay, CalendarWeekRule.FirstFourDayWeek)); - } // Returns the year part of the specified DateTime. The returned value is an @@ -688,7 +685,8 @@ namespace System.Globalization { // month is a leap month, or false if not. // - public virtual bool IsLeapMonth(int year, int month) { + public virtual bool IsLeapMonth(int year, int month) + { return (IsLeapMonth(year, month, CurrentEra)); } @@ -717,7 +715,7 @@ namespace System.Globalization { return 0; int monthsCount = GetMonthsInYear(year, era); - for (int month=1; month<=monthsCount; month++) + for (int month = 1; month <= monthsCount; month++) { if (IsLeapMonth(year, month, era)) return month; @@ -744,7 +742,7 @@ namespace System.Globalization { // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid. // - public virtual DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond) + public virtual DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond) { return (ToDateTime(year, month, day, hour, minute, second, millisecond, CurrentEra)); } @@ -754,30 +752,35 @@ namespace System.Globalization { public abstract DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era); - internal virtual Boolean TryToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era, out DateTime result) { + internal virtual Boolean TryToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era, out DateTime result) + { result = DateTime.MinValue; - try { + try + { result = ToDateTime(year, month, day, hour, minute, second, millisecond, era); return true; } - catch (ArgumentException) { + catch (ArgumentException) + { return false; } } - - internal virtual bool IsValidYear(int year, int era) { + + internal virtual bool IsValidYear(int year, int era) + { return (year >= GetYear(MinSupportedDateTime) && year <= GetYear(MaxSupportedDateTime)); } - - internal virtual bool IsValidMonth(int year, int month, int era) { + + internal virtual bool IsValidMonth(int year, int month, int era) + { return (IsValidYear(year, era) && month >= 1 && month <= GetMonthsInYear(year, era)); } - + internal virtual bool IsValidDay(int year, int month, int day, int era) { return (IsValidMonth(year, month, era) && day >= 1 && day <= GetDaysInMonth(year, month, era)); } - + // Returns and assigns the maximum value to represent a two digit year. This // value is the upper boundary of a 100 year range that allows a two digit year @@ -805,14 +808,17 @@ namespace System.Globalization { // then a two digit value of 30 will get converted to 1930 while a two digit // value of 29 will get converted to 2029. - public virtual int ToFourDigitYear(int year) { - if (year < 0) { + public virtual int ToFourDigitYear(int year) + { + if (year < 0) + { throw new ArgumentOutOfRangeException(nameof(year), - Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + SR.ArgumentOutOfRange_NeedNonNegNum); } Contract.EndContractBlock(); - if (year < 100) { - return ((TwoDigitYearMax/100 - ( year > TwoDigitYearMax % 100 ? 1 : 0))*100 + year); + if (year < 100) + { + return ((TwoDigitYearMax / 100 - (year > TwoDigitYearMax % 100 ? 1 : 0)) * 100 + year); } // If the year value is above 100, just return the year value. Don't have to do // the TwoDigitYearMax comparison. @@ -823,30 +829,29 @@ namespace System.Globalization { // Will check the if the parameters are valid. internal static long TimeToTicks(int hour, int minute, int second, int millisecond) { - if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >=0 && second < 60) + if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >= 0 && second < 60) { - if (millisecond < 0 || millisecond >= MillisPerSecond) { + if (millisecond < 0 || millisecond >= MillisPerSecond) + { throw new ArgumentOutOfRangeException( nameof(millisecond), String.Format( CultureInfo.InvariantCulture, - Environment.GetResourceString("ArgumentOutOfRange_Range"), 0, MillisPerSecond - 1)); + SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1))); } - return TimeSpan.TimeToTicks(hour, minute, second) + millisecond * TicksPerMillisecond; + return InternalGlobalizationHelper.TimeToTicks(hour, minute, second) + millisecond * TicksPerMillisecond; } - throw new ArgumentOutOfRangeException(null, Environment.GetResourceString("ArgumentOutOfRange_BadHourMinuteSecond")); + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadHourMinuteSecond); } - internal static int GetSystemTwoDigitYearSetting(int CalID, int defaultYearValue) + internal static int GetSystemTwoDigitYearSetting(CalendarId CalID, int defaultYearValue) { - // Call nativeGetTwoDigitYearMax - int twoDigitYearMax = CalendarData.nativeGetTwoDigitYearMax(CalID); + int twoDigitYearMax = CalendarData.GetTwoDigitYearMax(CalID); if (twoDigitYearMax < 0) { twoDigitYearMax = defaultYearValue; } return (twoDigitYearMax); } - } } diff --git a/src/mscorlib/src/System/Globalization/CalendarAlgorithmType.cs b/src/mscorlib/src/System/Globalization/CalendarAlgorithmType.cs deleted file mode 100644 index 33b5035a8d..0000000000 --- a/src/mscorlib/src/System/Globalization/CalendarAlgorithmType.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Globalization { - using System; - - public enum CalendarAlgorithmType { - Unknown = 0, // This is the default value to return in the Calendar base class. - SolarCalendar = 1, // Solar-base calendar, such as GregorianCalendar, jaoaneseCalendar, JulianCalendar, etc. - // Solar calendars are based on the solar year and seasons. - LunarCalendar = 2, // Lunar-based calendar, such as Hijri and UmAlQuraCalendar. - // Lunar calendars are based on the path of the moon. The seasons are not accurately represented. - LunisolarCalendar = 3 // Lunisolar-based calendar which use leap month rule, such as HebrewCalendar and Asian Lunisolar calendars. - // Lunisolar calendars are based on the cycle of the moon, but consider the seasons as a secondary consideration, - // so they align with the seasons as well as lunar events. - - } -} diff --git a/src/mscorlib/src/System/Globalization/CalendarData.Unix.cs b/src/mscorlib/src/System/Globalization/CalendarData.Unix.cs new file mode 100644 index 0000000000..319f66ae8c --- /dev/null +++ b/src/mscorlib/src/System/Globalization/CalendarData.Unix.cs @@ -0,0 +1,339 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Security; +using System.Text; + +namespace System.Globalization +{ + // needs to be kept in sync with CalendarDataType in System.Globalization.Native + internal enum CalendarDataType + { + Uninitialized = 0, + NativeName = 1, + MonthDay = 2, + ShortDates = 3, + LongDates = 4, + YearMonths = 5, + DayNames = 6, + AbbrevDayNames = 7, + MonthNames = 8, + AbbrevMonthNames = 9, + SuperShortDayNames = 10, + MonthGenitiveNames = 11, + AbbrevMonthGenitiveNames = 12, + EraNames = 13, + AbbrevEraNames = 14, + } + + internal partial class CalendarData + { + private bool LoadCalendarDataFromSystem(String localeName, CalendarId calendarId) + { + bool result = true; + result &= GetCalendarInfo(localeName, calendarId, CalendarDataType.NativeName, out this.sNativeName); + result &= GetCalendarInfo(localeName, calendarId, CalendarDataType.MonthDay, out this.sMonthDay); + this.sMonthDay = NormalizeDatePattern(this.sMonthDay); + + result &= EnumDatePatterns(localeName, calendarId, CalendarDataType.ShortDates, out this.saShortDates); + result &= EnumDatePatterns(localeName, calendarId, CalendarDataType.LongDates, out this.saLongDates); + result &= EnumDatePatterns(localeName, calendarId, CalendarDataType.YearMonths, out this.saYearMonths); + result &= EnumCalendarInfo(localeName, calendarId, CalendarDataType.DayNames, out this.saDayNames); + result &= EnumCalendarInfo(localeName, calendarId, CalendarDataType.AbbrevDayNames, out this.saAbbrevDayNames); + result &= EnumCalendarInfo(localeName, calendarId, CalendarDataType.SuperShortDayNames, out this.saSuperShortDayNames); + result &= EnumMonthNames(localeName, calendarId, CalendarDataType.MonthNames, out this.saMonthNames); + result &= EnumMonthNames(localeName, calendarId, CalendarDataType.AbbrevMonthNames, out this.saAbbrevMonthNames); + result &= EnumMonthNames(localeName, calendarId, CalendarDataType.MonthGenitiveNames, out this.saMonthGenitiveNames); + result &= EnumMonthNames(localeName, calendarId, CalendarDataType.AbbrevMonthGenitiveNames, out this.saAbbrevMonthGenitiveNames); + result &= EnumEraNames(localeName, calendarId, CalendarDataType.EraNames, out this.saEraNames); + result &= EnumEraNames(localeName, calendarId, CalendarDataType.AbbrevEraNames, out this.saAbbrevEraNames); + + return result; + } + + internal static int GetTwoDigitYearMax(CalendarId calendarId) + { + // There is no user override for this value on Linux or in ICU. + // So just return -1 to use the hard-coded defaults. + return -1; + } + + // Call native side to figure out which calendars are allowed + internal static int GetCalendars(string localeName, bool useUserOverride, CalendarId[] calendars) + { + Debug.Assert(!GlobalizationMode.Invariant); + + // NOTE: there are no 'user overrides' on Linux + int count = Interop.GlobalizationInterop.GetCalendars(localeName, calendars, calendars.Length); + + // ensure there is at least 1 calendar returned + if (count == 0 && calendars.Length > 0) + { + calendars[0] = CalendarId.GREGORIAN; + count = 1; + } + + return count; + } + + private static bool SystemSupportsTaiwaneseCalendar() + { + return true; + } + + // PAL Layer ends here + + private static bool GetCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType dataType, out string calendarString) + { + Debug.Assert(!GlobalizationMode.Invariant); + + return Interop.CallStringMethod( + (locale, calId, type, stringBuilder) => + Interop.GlobalizationInterop.GetCalendarInfo( + locale, + calId, + type, + stringBuilder, + stringBuilder.Capacity), + localeName, + calendarId, + dataType, + out calendarString); + } + + private static bool EnumDatePatterns(string localeName, CalendarId calendarId, CalendarDataType dataType, out string[] datePatterns) + { + datePatterns = null; + + CallbackContext callbackContext = new CallbackContext(); + callbackContext.DisallowDuplicates = true; + bool result = EnumCalendarInfo(localeName, calendarId, dataType, callbackContext); + if (result) + { + List<string> datePatternsList = callbackContext.Results; + + datePatterns = new string[datePatternsList.Count]; + for (int i = 0; i < datePatternsList.Count; i++) + { + datePatterns[i] = NormalizeDatePattern(datePatternsList[i]); + } + } + + return result; + } + + /// <summary> + /// The ICU date format characters are not exactly the same as the .NET date format characters. + /// NormalizeDatePattern will take in an ICU date pattern and return the equivalent .NET date pattern. + /// </summary> + /// <remarks> + /// see Date Field Symbol Table in http://userguide.icu-project.org/formatparse/datetime + /// and https://msdn.microsoft.com/en-us/library/8kb3ddd4(v=vs.110).aspx + /// </remarks> + private static string NormalizeDatePattern(string input) + { + StringBuilder destination = StringBuilderCache.Acquire(input.Length); + + int index = 0; + while (index < input.Length) + { + switch (input[index]) + { + case '\'': + // single quotes escape characters, like 'de' in es-SP + // so read verbatim until the next single quote + destination.Append(input[index++]); + while (index < input.Length) + { + char current = input[index++]; + destination.Append(current); + if (current == '\'') + { + break; + } + } + break; + case 'E': + case 'e': + case 'c': + // 'E' in ICU is the day of the week, which maps to 3 or 4 'd's in .NET + // 'e' in ICU is the local day of the week, which has no representation in .NET, but + // maps closest to 3 or 4 'd's in .NET + // 'c' in ICU is the stand-alone day of the week, which has no representation in .NET, but + // maps closest to 3 or 4 'd's in .NET + NormalizeDayOfWeek(input, destination, ref index); + break; + case 'L': + case 'M': + // 'L' in ICU is the stand-alone name of the month, + // which maps closest to 'M' in .NET since it doesn't support stand-alone month names in patterns + // 'M' in both ICU and .NET is the month, + // but ICU supports 5 'M's, which is the super short month name + int occurrences = CountOccurrences(input, input[index], ref index); + if (occurrences > 4) + { + // 5 'L's or 'M's in ICU is the super short name, which maps closest to MMM in .NET + occurrences = 3; + } + destination.Append('M', occurrences); + break; + case 'G': + // 'G' in ICU is the era, which maps to 'g' in .NET + occurrences = CountOccurrences(input, 'G', ref index); + + // it doesn't matter how many 'G's, since .NET only supports 'g' or 'gg', and they + // have the same meaning + destination.Append('g'); + break; + case 'y': + // a single 'y' in ICU is the year with no padding or trimming. + // a single 'y' in .NET is the year with 1 or 2 digits + // so convert any single 'y' to 'yyyy' + occurrences = CountOccurrences(input, 'y', ref index); + if (occurrences == 1) + { + occurrences = 4; + } + destination.Append('y', occurrences); + break; + default: + const string unsupportedDateFieldSymbols = "YuUrQqwWDFg"; + Debug.Assert(unsupportedDateFieldSymbols.IndexOf(input[index]) == -1, + string.Format(CultureInfo.InvariantCulture, + "Encountered an unexpected date field symbol '{0}' from ICU which has no known corresponding .NET equivalent.", + input[index])); + + destination.Append(input[index++]); + break; + } + } + + return StringBuilderCache.GetStringAndRelease(destination); + } + + private static void NormalizeDayOfWeek(string input, StringBuilder destination, ref int index) + { + char dayChar = input[index]; + int occurrences = CountOccurrences(input, dayChar, ref index); + occurrences = Math.Max(occurrences, 3); + if (occurrences > 4) + { + // 5 and 6 E/e/c characters in ICU is the super short names, which maps closest to ddd in .NET + occurrences = 3; + } + + destination.Append('d', occurrences); + } + + private static int CountOccurrences(string input, char value, ref int index) + { + int startIndex = index; + while (index < input.Length && input[index] == value) + { + index++; + } + + return index - startIndex; + } + + private static bool EnumMonthNames(string localeName, CalendarId calendarId, CalendarDataType dataType, out string[] monthNames) + { + monthNames = null; + + CallbackContext callbackContext = new CallbackContext(); + bool result = EnumCalendarInfo(localeName, calendarId, dataType, callbackContext); + if (result) + { + // the month-name arrays are expected to have 13 elements. If ICU only returns 12, add an + // extra empty string to fill the array. + if (callbackContext.Results.Count == 12) + { + callbackContext.Results.Add(string.Empty); + } + + monthNames = callbackContext.Results.ToArray(); + } + + return result; + } + + private static bool EnumEraNames(string localeName, CalendarId calendarId, CalendarDataType dataType, out string[] eraNames) + { + bool result = EnumCalendarInfo(localeName, calendarId, dataType, out eraNames); + + // .NET expects that only the Japanese calendars have more than 1 era. + // So for other calendars, only return the latest era. + if (calendarId != CalendarId.JAPAN && calendarId != CalendarId.JAPANESELUNISOLAR && eraNames.Length > 0) + { + string[] latestEraName = new string[] { eraNames[eraNames.Length - 1] }; + eraNames = latestEraName; + } + + return result; + } + + internal static bool EnumCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType dataType, out string[] calendarData) + { + calendarData = null; + + CallbackContext callbackContext = new CallbackContext(); + bool result = EnumCalendarInfo(localeName, calendarId, dataType, callbackContext); + if (result) + { + calendarData = callbackContext.Results.ToArray(); + } + + return result; + } + + private static bool EnumCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType dataType, CallbackContext callbackContext) + { + GCHandle context = GCHandle.Alloc(callbackContext); + try + { + return Interop.GlobalizationInterop.EnumCalendarInfo(EnumCalendarInfoCallback, localeName, calendarId, dataType, (IntPtr)context); + } + finally + { + context.Free(); + } + } + + private static void EnumCalendarInfoCallback(string calendarString, IntPtr context) + { + CallbackContext callbackContext = (CallbackContext)((GCHandle)context).Target; + + if (callbackContext.DisallowDuplicates) + { + foreach (string existingResult in callbackContext.Results) + { + if (string.Equals(calendarString, existingResult, StringComparison.Ordinal)) + { + // the value is already in the results, so don't add it again + return; + } + } + } + + callbackContext.Results.Add(calendarString); + } + + private class CallbackContext + { + private List<string> _results = new List<string>(); + + public CallbackContext() + { + } + + public List<string> Results { get { return _results; } } + + public bool DisallowDuplicates { get; set; } + } + } +} diff --git a/src/mscorlib/src/System/Globalization/CalendarData.Windows.cs b/src/mscorlib/src/System/Globalization/CalendarData.Windows.cs new file mode 100644 index 0000000000..51a2727c7d --- /dev/null +++ b/src/mscorlib/src/System/Globalization/CalendarData.Windows.cs @@ -0,0 +1,502 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Diagnostics.Contracts; +using System.Collections.Generic; + +namespace System.Globalization +{ +#if CORECLR + using IntList = List<int>; + using StringList = List<string>; +#else + using IntList = LowLevelList<int>; + using StringList = LowLevelList<string>; +#endif + + internal partial class CalendarData + { + private bool LoadCalendarDataFromSystem(String localeName, CalendarId calendarId) + { + Debug.Assert(!GlobalizationMode.Invariant); + + bool ret = true; + + uint useOverrides = this.bUseUserOverrides ? 0 : CAL_NOUSEROVERRIDE; + + // + // Windows doesn't support some calendars right now, so remap those. + // + switch (calendarId) + { + case CalendarId.JAPANESELUNISOLAR: // Data looks like Japanese + calendarId = CalendarId.JAPAN; + break; + case CalendarId.JULIAN: // Data looks like gregorian US + case CalendarId.CHINESELUNISOLAR: // Algorithmic, so actual data is irrelevent + case CalendarId.SAKA: // reserved to match Office but not implemented in our code, so data is irrelevent + case CalendarId.LUNAR_ETO_CHN: // reserved to match Office but not implemented in our code, so data is irrelevent + case CalendarId.LUNAR_ETO_KOR: // reserved to match Office but not implemented in our code, so data is irrelevent + case CalendarId.LUNAR_ETO_ROKUYOU: // reserved to match Office but not implemented in our code, so data is irrelevent + case CalendarId.KOREANLUNISOLAR: // Algorithmic, so actual data is irrelevent + case CalendarId.TAIWANLUNISOLAR: // Algorithmic, so actual data is irrelevent + calendarId = CalendarId.GREGORIAN_US; + break; + } + + // + // Special handling for some special calendar due to OS limitation. + // This includes calendar like Taiwan calendar, UmAlQura calendar, etc. + // + CheckSpecialCalendar(ref calendarId, ref localeName); + + // Numbers + ret &= CallGetCalendarInfoEx(localeName, calendarId, CAL_ITWODIGITYEARMAX | useOverrides, out this.iTwoDigitYearMax); + + // Strings + ret &= CallGetCalendarInfoEx(localeName, calendarId, CAL_SCALNAME, out this.sNativeName); + ret &= CallGetCalendarInfoEx(localeName, calendarId, CAL_SMONTHDAY | useOverrides, out this.sMonthDay); + + // String Arrays + // Formats + ret &= CallEnumCalendarInfo(localeName, calendarId, CAL_SSHORTDATE, LOCALE_SSHORTDATE | useOverrides, out this.saShortDates); + ret &= CallEnumCalendarInfo(localeName, calendarId, CAL_SLONGDATE, LOCALE_SLONGDATE | useOverrides, out this.saLongDates); + + // Get the YearMonth pattern. + ret &= CallEnumCalendarInfo(localeName, calendarId, CAL_SYEARMONTH, LOCALE_SYEARMONTH, out this.saYearMonths); + + // Day & Month Names + // These are all single calType entries, 1 per day, so we have to make 7 or 13 calls to collect all the names + + // Day + // Note that we're off-by-one since managed starts on sunday and windows starts on monday + ret &= GetCalendarDayInfo(localeName, calendarId, CAL_SDAYNAME7, out this.saDayNames); + ret &= GetCalendarDayInfo(localeName, calendarId, CAL_SABBREVDAYNAME7, out this.saAbbrevDayNames); + + // Month names + ret &= GetCalendarMonthInfo(localeName, calendarId, CAL_SMONTHNAME1, out this.saMonthNames); + ret &= GetCalendarMonthInfo(localeName, calendarId, CAL_SABBREVMONTHNAME1, out this.saAbbrevMonthNames); + + // + // The following LCTYPE are not supported in some platforms. If the call fails, + // don't return a failure. + // + GetCalendarDayInfo(localeName, calendarId, CAL_SSHORTESTDAYNAME7, out this.saSuperShortDayNames); + + // Gregorian may have genitive month names + if (calendarId == CalendarId.GREGORIAN) + { + GetCalendarMonthInfo(localeName, calendarId, CAL_SMONTHNAME1 | CAL_RETURN_GENITIVE_NAMES, out this.saMonthGenitiveNames); + GetCalendarMonthInfo(localeName, calendarId, CAL_SABBREVMONTHNAME1 | CAL_RETURN_GENITIVE_NAMES, out this.saAbbrevMonthGenitiveNames); + } + + // Calendar Parts Names + // This doesn't get always get localized names for gregorian (not available in windows < 7) + // so: eg: coreclr on win < 7 won't get these + CallEnumCalendarInfo(localeName, calendarId, CAL_SERASTRING, 0, out this.saEraNames); + CallEnumCalendarInfo(localeName, calendarId, CAL_SABBREVERASTRING, 0, out this.saAbbrevEraNames); + + // + // Calendar Era Info + // Note that calendar era data (offsets, etc) is hard coded for each calendar since this + // data is implementation specific and not dynamic (except perhaps Japanese) + // + + // Clean up the escaping of the formats + this.saShortDates = CultureData.ReescapeWin32Strings(this.saShortDates); + this.saLongDates = CultureData.ReescapeWin32Strings(this.saLongDates); + this.saYearMonths = CultureData.ReescapeWin32Strings(this.saYearMonths); + this.sMonthDay = CultureData.ReescapeWin32String(this.sMonthDay); + + return ret; + } + + // Get native two digit year max + internal static int GetTwoDigitYearMax(CalendarId calendarId) + { + if (GlobalizationMode.Invariant) + { + return Invariant.iTwoDigitYearMax; + } + + int twoDigitYearMax = -1; + + if (!CallGetCalendarInfoEx(null, calendarId, (uint)CAL_ITWODIGITYEARMAX, out twoDigitYearMax)) + { + twoDigitYearMax = -1; + } + + return twoDigitYearMax; + } + + // Call native side to figure out which calendars are allowed + internal static int GetCalendars(String localeName, bool useUserOverride, CalendarId[] calendars) + { + Debug.Assert(!GlobalizationMode.Invariant); + + EnumCalendarsData data = new EnumCalendarsData(); + data.userOverride = 0; + data.calendars = new IntList(); + + // First call GetLocaleInfo if necessary + if (useUserOverride) + { + // They want user overrides, see if the user calendar matches the input calendar + int userCalendar = CultureData.GetLocaleInfoExInt(localeName, LOCALE_ICALENDARTYPE); + + // If we got a default, then use it as the first calendar + if (userCalendar != 0) + { + data.userOverride = userCalendar; + data.calendars.Add(userCalendar); + } + } + + GCHandle contextHandle = GCHandle.Alloc(data); + try + { + // Now call the enumeration API. Work is done by our callback function +#if CORECLR + Interop.Kernel32.EnumCalendarInfoExEx(EnumCalendarsCallback, localeName, ENUM_ALL_CALENDARS, null, CAL_ICALINTVALUE, (IntPtr)contextHandle); +#else + IntPtr callback = AddrofIntrinsics.AddrOf<Func<IntPtr, uint, IntPtr, IntPtr, Interop.BOOL>>(EnumCalendarsCallback); + Interop.Kernel32.EnumCalendarInfoExEx(callback, localeName, ENUM_ALL_CALENDARS, null, CAL_ICALINTVALUE, (IntPtr)contextHandle); +#endif + } + finally + { + contextHandle.Free(); + } + + // Copy to the output array + for (int i = 0; i < Math.Min(calendars.Length, data.calendars.Count); i++) + calendars[i] = (CalendarId)data.calendars[i]; + + // Now we have a list of data, return the count + return data.calendars.Count; + } + + private static bool SystemSupportsTaiwaneseCalendar() + { + Debug.Assert(!GlobalizationMode.Invariant); + + string data; + // Taiwanese calendar get listed as one of the optional zh-TW calendars only when having zh-TW UI + return CallGetCalendarInfoEx("zh-TW", CalendarId.TAIWAN, CAL_SCALNAME, out data); + } + + // PAL Layer ends here + + private const uint CAL_RETURN_NUMBER = 0x20000000; + private const uint CAL_RETURN_GENITIVE_NAMES = 0x10000000; + private const uint CAL_NOUSEROVERRIDE = 0x80000000; + private const uint CAL_SCALNAME = 0x00000002; + private const uint CAL_SMONTHDAY = 0x00000038; + private const uint CAL_SSHORTDATE = 0x00000005; + private const uint CAL_SLONGDATE = 0x00000006; + private const uint CAL_SYEARMONTH = 0x0000002f; + private const uint CAL_SDAYNAME7 = 0x0000000d; + private const uint CAL_SABBREVDAYNAME7 = 0x00000014; + private const uint CAL_SMONTHNAME1 = 0x00000015; + private const uint CAL_SABBREVMONTHNAME1 = 0x00000022; + private const uint CAL_SSHORTESTDAYNAME7 = 0x00000037; + private const uint CAL_SERASTRING = 0x00000004; + private const uint CAL_SABBREVERASTRING = 0x00000039; + private const uint CAL_ICALINTVALUE = 0x00000001; + private const uint CAL_ITWODIGITYEARMAX = 0x00000030; + + private const uint ENUM_ALL_CALENDARS = 0xffffffff; + + private const uint LOCALE_SSHORTDATE = 0x0000001F; + private const uint LOCALE_SLONGDATE = 0x00000020; + private const uint LOCALE_SYEARMONTH = 0x00001006; + private const uint LOCALE_ICALENDARTYPE = 0x00001009; + + //////////////////////////////////////////////////////////////////////// + // + // For calendars like Gregorain US/Taiwan/UmAlQura, they are not available + // in all OS or all localized versions of OS. + // If OS does not support these calendars, we will fallback by using the + // appropriate fallback calendar and locale combination to retrieve data from OS. + // + // Parameters: + // __deref_inout pCalendarInt: + // Pointer to the calendar ID. This will be updated to new fallback calendar ID if needed. + // __in_out pLocaleNameStackBuffer + // Pointer to the StackSString object which holds the locale name to be checked. + // This will be updated to new fallback locale name if needed. + // + //////////////////////////////////////////////////////////////////////// + private static void CheckSpecialCalendar(ref CalendarId calendar, ref string localeName) + { + string data; + + // Gregorian-US isn't always available in the OS, however it is the same for all locales + switch (calendar) + { + case CalendarId.GREGORIAN_US: + // See if this works + if (!CallGetCalendarInfoEx(localeName, calendar, CAL_SCALNAME, out data)) + { + // Failed, set it to a locale (fa-IR) that's alway has Gregorian US available in the OS + localeName = "fa-IR"; + } + // See if that works + if (!CallGetCalendarInfoEx(localeName, calendar, CAL_SCALNAME, out data)) + { + // Failed again, just use en-US with the gregorian calendar + localeName = "en-US"; + calendar = CalendarId.GREGORIAN; + } + break; + case CalendarId.TAIWAN: + // Taiwan calendar data is not always in all language version of OS due to Geopolical reasons. + // It is only available in zh-TW localized versions of Windows. + // Let's check if OS supports it. If not, fallback to Greogrian localized for Taiwan calendar. + if (!SystemSupportsTaiwaneseCalendar()) + { + calendar = CalendarId.GREGORIAN; + } + break; + } + } + + private static bool CallGetCalendarInfoEx(string localeName, CalendarId calendar, uint calType, out int data) + { + return (Interop.Kernel32.GetCalendarInfoEx(localeName, (uint)calendar, IntPtr.Zero, calType | CAL_RETURN_NUMBER, IntPtr.Zero, 0, out data) != 0); + } + + private static unsafe bool CallGetCalendarInfoEx(string localeName, CalendarId calendar, uint calType, out string data) + { + const int BUFFER_LENGTH = 80; + + // The maximum size for values returned from GetCalendarInfoEx is 80 characters. + char* buffer = stackalloc char[BUFFER_LENGTH]; + + int ret = Interop.Kernel32.GetCalendarInfoEx(localeName, (uint)calendar, IntPtr.Zero, calType, (IntPtr)buffer, BUFFER_LENGTH, IntPtr.Zero); + if (ret > 0) + { + if (buffer[ret - 1] == '\0') + { + ret--; // don't include the null termination in the string + } + data = new string(buffer, 0, ret); + return true; + } + data = ""; + return false; + } + + // Context for EnumCalendarInfoExEx callback. + private class EnumData + { + public string userOverride; + public StringList strings; + } + + // EnumCalendarInfoExEx callback itself. +#if !CORECLR + [NativeCallable(CallingConvention = CallingConvention.StdCall)] +#endif + private static unsafe Interop.BOOL EnumCalendarInfoCallback(IntPtr lpCalendarInfoString, uint calendar, IntPtr pReserved, IntPtr lParam) + { + EnumData context = (EnumData)((GCHandle)lParam).Target; + try + { + string calendarInfo = new string((char*)lpCalendarInfoString); + + // If we had a user override, check to make sure this differs + if (context.userOverride != calendarInfo) + context.strings.Add(calendarInfo); + + return Interop.BOOL.TRUE; + } + catch (Exception) + { + return Interop.BOOL.FALSE; + } + } + + private static unsafe bool CallEnumCalendarInfo(string localeName, CalendarId calendar, uint calType, uint lcType, out string[] data) + { + EnumData context = new EnumData(); + context.userOverride = null; + context.strings = new StringList(); + // First call GetLocaleInfo if necessary + if (((lcType != 0) && ((lcType & CAL_NOUSEROVERRIDE) == 0)) && + // Get user locale, see if it matches localeName. + // Note that they should match exactly, including letter case + GetUserDefaultLocaleName() == localeName) + { + // They want user overrides, see if the user calendar matches the input calendar + CalendarId userCalendar = (CalendarId)CultureData.GetLocaleInfoExInt(localeName, LOCALE_ICALENDARTYPE); + + // If the calendars were the same, see if the locales were the same + if (userCalendar == calendar) + { + // They matched, get the user override since locale & calendar match + string res = CultureData.GetLocaleInfoEx(localeName, lcType); + + // if it succeeded remember the override for the later callers + if (res != "") + { + // Remember this was the override (so we can look for duplicates later in the enum function) + context.userOverride = res; + + // Add to the result strings. + context.strings.Add(res); + } + } + } + + GCHandle contextHandle = GCHandle.Alloc(context); + try + { +#if CORECLR + Interop.Kernel32.EnumCalendarInfoExEx(EnumCalendarInfoCallback, localeName, (uint)calendar, null, calType, (IntPtr)contextHandle); +#else + // Now call the enumeration API. Work is done by our callback function + IntPtr callback = AddrofIntrinsics.AddrOf<Func<IntPtr, uint, IntPtr, IntPtr, Interop.BOOL>>(EnumCalendarInfoCallback); + Interop.Kernel32.EnumCalendarInfoExEx(callback, localeName, (uint)calendar, null, calType, (IntPtr)contextHandle); +#endif // CORECLR + } + finally + { + contextHandle.Free(); + } + + // Now we have a list of data, fail if we didn't find anything. + if (context.strings.Count == 0) + { + data = null; + return false; + } + + string[] output = context.strings.ToArray(); + + if (calType == CAL_SABBREVERASTRING || calType == CAL_SERASTRING) + { + // Eras are enumerated backwards. (oldest era name first, but + // Japanese calendar has newest era first in array, and is only + // calendar with multiple eras) + Array.Reverse(output, 0, output.Length); + } + + data = output; + + return true; + } + + //////////////////////////////////////////////////////////////////////// + // + // Get the native day names + // + // NOTE: There's a disparity between .Net & windows day orders, the input day should + // start with Sunday + // + // Parameters: + // OUT pOutputStrings The output string[] value. + // + //////////////////////////////////////////////////////////////////////// + private static bool GetCalendarDayInfo(string localeName, CalendarId calendar, uint calType, out string[] outputStrings) + { + bool result = true; + + // + // We'll need a new array of 7 items + // + string[] results = new string[7]; + + // Get each one of them + for (int i = 0; i < 7; i++, calType++) + { + result &= CallGetCalendarInfoEx(localeName, calendar, calType, out results[i]); + + // On the first iteration we need to go from CAL_SDAYNAME7 to CAL_SDAYNAME1, so subtract 7 before the ++ happens + // This is because the framework starts on sunday and windows starts on monday when counting days + if (i == 0) + calType -= 7; + } + + outputStrings = results; + + return result; + } + + //////////////////////////////////////////////////////////////////////// + // + // Get the native month names + // + // Parameters: + // OUT pOutputStrings The output string[] value. + // + //////////////////////////////////////////////////////////////////////// + private static bool GetCalendarMonthInfo(string localeName, CalendarId calendar, uint calType, out string[] outputStrings) + { + // + // We'll need a new array of 13 items + // + string[] results = new string[13]; + + // Get each one of them + for (int i = 0; i < 13; i++, calType++) + { + if (!CallGetCalendarInfoEx(localeName, calendar, calType, out results[i])) + results[i] = ""; + } + + outputStrings = results; + + return true; + } + + // + // struct to help our calendar data enumaration callback + // + private class EnumCalendarsData + { + public int userOverride; // user override value (if found) + public IntList calendars; // list of calendars found so far + } + +#if !CORECLR + [NativeCallable(CallingConvention = CallingConvention.StdCall)] +#endif + private static Interop.BOOL EnumCalendarsCallback(IntPtr lpCalendarInfoString, uint calendar, IntPtr reserved, IntPtr lParam) + { + EnumCalendarsData context = (EnumCalendarsData)((GCHandle)lParam).Target; + try + { + // If we had a user override, check to make sure this differs + if (context.userOverride != calendar) + context.calendars.Add((int)calendar); + + return Interop.BOOL.TRUE; + } + catch (Exception) + { + return Interop.BOOL.FALSE; + } + } + + private static unsafe String GetUserDefaultLocaleName() + { + Debug.Assert(!GlobalizationMode.Invariant); + + const int LOCALE_NAME_MAX_LENGTH = 85; + const uint LOCALE_SNAME = 0x0000005c; + const string LOCALE_NAME_USER_DEFAULT = null; + + int result; + char* localeName = stackalloc char[LOCALE_NAME_MAX_LENGTH]; + result = CultureData.GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SNAME, localeName, LOCALE_NAME_MAX_LENGTH); + + return result <= 0 ? "" : new String(localeName, 0, result - 1); // exclude the null termination + } + } +} diff --git a/src/mscorlib/src/System/Globalization/CalendarData.cs b/src/mscorlib/src/System/Globalization/CalendarData.cs index d66331b31d..0991149e07 100644 --- a/src/mscorlib/src/System/Globalization/CalendarData.cs +++ b/src/mscorlib/src/System/Globalization/CalendarData.cs @@ -2,159 +2,136 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics; + namespace System.Globalization { - - using System; - using System.Runtime.InteropServices; - using System.Runtime.CompilerServices; - using System.Runtime.Versioning; - using System.Diagnostics; - using System.Diagnostics.Contracts; - // // List of calendar data // Note the we cache overrides. // Note that localized names (resource names) aren't available from here. // // NOTE: Calendars depend on the locale name that creates it. Only a few - // properties are available without locales using CalendarData.GetCalendar(int) + // properties are available without locales using CalendarData.GetCalendar(CalendarData) - // StructLayout is needed here otherwise compiler can re-arrange the fields. - // We have to keep this in-sync with the definition in calendardata.h - // - // WARNING WARNING WARNING - // - // WARNING: Anything changed here also needs to be updated on the native side (object.h see type CalendarDataBaseObject) - // WARNING: The type loader will rearrange class member offsets so the mscorwks!CalendarDataBaseObject - // WARNING: must be manually structured to match the true loaded class layout - // - internal class CalendarData + internal partial class CalendarData { // Max calendars internal const int MAX_CALENDARS = 23; // Identity - internal String sNativeName ; // Calendar Name for the locale + internal String sNativeName; // Calendar Name for the locale // Formats - internal String[] saShortDates ; // Short Data format, default first - internal String[] saYearMonths ; // Year/Month Data format, default first - internal String[] saLongDates ; // Long Data format, default first - internal String sMonthDay ; // Month/Day format + internal String[] saShortDates; // Short Data format, default first + internal String[] saYearMonths; // Year/Month Data format, default first + internal String[] saLongDates; // Long Data format, default first + internal String sMonthDay; // Month/Day format // Calendar Parts Names - internal String[] saEraNames ; // Names of Eras - internal String[] saAbbrevEraNames ; // Abbreviated Era Names - internal String[] saAbbrevEnglishEraNames ; // Abbreviated Era Names in English - internal String[] saDayNames ; // Day Names, null to use locale data, starts on Sunday - internal String[] saAbbrevDayNames ; // Abbrev Day Names, null to use locale data, starts on Sunday - internal String[] saSuperShortDayNames ; // Super short Day of week names - internal String[] saMonthNames ; // Month Names (13) - internal String[] saAbbrevMonthNames ; // Abbrev Month Names (13) - internal String[] saMonthGenitiveNames ; // Genitive Month Names (13) - internal String[] saAbbrevMonthGenitiveNames; // Genitive Abbrev Month Names (13) - internal String[] saLeapYearMonthNames ; // Multiple strings for the month names in a leap year. + internal String[] saEraNames; // Names of Eras + internal String[] saAbbrevEraNames; // Abbreviated Era Names + internal String[] saAbbrevEnglishEraNames; // Abbreviated Era Names in English + internal String[] saDayNames; // Day Names, null to use locale data, starts on Sunday + internal String[] saAbbrevDayNames; // Abbrev Day Names, null to use locale data, starts on Sunday + internal String[] saSuperShortDayNames; // Super short Day of week names + internal String[] saMonthNames; // Month Names (13) + internal String[] saAbbrevMonthNames; // Abbrev Month Names (13) + internal String[] saMonthGenitiveNames; // Genitive Month Names (13) + internal String[] saAbbrevMonthGenitiveNames; // Genitive Abbrev Month Names (13) + internal String[] saLeapYearMonthNames; // Multiple strings for the month names in a leap year. // Integers at end to make marshaller happier - internal int iTwoDigitYearMax=2029 ; // Max 2 digit year (for Y2K bug data entry) - internal int iCurrentEra=0 ; // current era # (usually 1) + internal int iTwoDigitYearMax = 2029; // Max 2 digit year (for Y2K bug data entry) + internal int iCurrentEra = 0; // current era # (usually 1) // Use overrides? - internal bool bUseUserOverrides ; // True if we want user overrides. + internal bool bUseUserOverrides; // True if we want user overrides. // Static invariant for the invariant locale - internal static CalendarData Invariant; + internal static readonly CalendarData Invariant = CreateInvariant(); // Private constructor - private CalendarData() {} + private CalendarData() { } - // Invariant constructor - static CalendarData() + // Invariant factory + private static CalendarData CreateInvariant() { - // Set our default/gregorian US calendar data // Calendar IDs are 1-based, arrays are 0 based. CalendarData invariant = new CalendarData(); // Set default data for calendar // Note that we don't load resources since this IS NOT supposed to change (by definition) - invariant.sNativeName = "Gregorian Calendar"; // Calendar Name + invariant.sNativeName = "Gregorian Calendar"; // Calendar Name // Year - invariant.iTwoDigitYearMax = 2029; // Max 2 digit year (for Y2K bug data entry) - invariant.iCurrentEra = 1; // Current era # + invariant.iTwoDigitYearMax = 2029; // Max 2 digit year (for Y2K bug data entry) + invariant.iCurrentEra = 1; // Current era # // Formats - invariant.saShortDates = new String[] { "MM/dd/yyyy", "yyyy-MM-dd" }; // short date format - invariant.saLongDates = new String[] { "dddd, dd MMMM yyyy"}; // long date format - invariant.saYearMonths = new String[] { "yyyy MMMM" }; // year month format - invariant.sMonthDay = "MMMM dd"; // Month day pattern + invariant.saShortDates = new String[] { "MM/dd/yyyy", "yyyy-MM-dd" }; // short date format + invariant.saLongDates = new String[] { "dddd, dd MMMM yyyy" }; // long date format + invariant.saYearMonths = new String[] { "yyyy MMMM" }; // year month format + invariant.sMonthDay = "MMMM dd"; // Month day pattern // Calendar Parts Names - invariant.saEraNames = new String[] { "A.D." }; // Era names - invariant.saAbbrevEraNames = new String[] { "AD" }; // Abbreviated Era names - invariant.saAbbrevEnglishEraNames=new String[] { "AD" }; // Abbreviated era names in English - invariant.saDayNames = new String[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };// day names - invariant.saAbbrevDayNames = new String[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; // abbreviated day names - invariant.saSuperShortDayNames = new String[] { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" }; // The super short day names - invariant.saMonthNames = new String[] { "January", "February", "March", "April", "May", "June", + invariant.saEraNames = new String[] { "A.D." }; // Era names + invariant.saAbbrevEraNames = new String[] { "AD" }; // Abbreviated Era names + invariant.saAbbrevEnglishEraNames = new String[] { "AD" }; // Abbreviated era names in English + invariant.saDayNames = new String[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };// day names + invariant.saAbbrevDayNames = new String[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; // abbreviated day names + invariant.saSuperShortDayNames = new String[] { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" }; // The super short day names + invariant.saMonthNames = new String[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", String.Empty}; // month names - invariant.saAbbrevMonthNames = new String[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + invariant.saAbbrevMonthNames = new String[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", String.Empty}; // abbreviated month names - invariant.saMonthGenitiveNames = invariant.saMonthNames; // Genitive month names (same as month names for invariant) - invariant.saAbbrevMonthGenitiveNames=invariant.saAbbrevMonthNames; // Abbreviated genitive month names (same as abbrev month names for invariant) - invariant.saLeapYearMonthNames = invariant.saMonthNames; // leap year month names are unused in Gregorian English (invariant) + invariant.saMonthGenitiveNames = invariant.saMonthNames; // Genitive month names (same as month names for invariant) + invariant.saAbbrevMonthGenitiveNames = invariant.saAbbrevMonthNames; // Abbreviated genitive month names (same as abbrev month names for invariant) + invariant.saLeapYearMonthNames = invariant.saMonthNames; // leap year month names are unused in Gregorian English (invariant) - invariant.bUseUserOverrides = false; + invariant.bUseUserOverrides = false; - // Calendar was built, go ahead and assign it... - Invariant = invariant; + return invariant; } - - // // Get a bunch of data for a calendar // - internal CalendarData(String localeName, int calendarId, bool bUseUserOverrides) + internal CalendarData(String localeName, CalendarId calendarId, bool bUseUserOverrides) { - // Call nativeGetCalendarData to populate the data this.bUseUserOverrides = bUseUserOverrides; - if (!nativeGetCalendarData(this, localeName, calendarId)) + + Debug.Assert(!GlobalizationMode.Invariant); + + if (!LoadCalendarDataFromSystem(localeName, calendarId)) { - Debug.Assert(false, "[CalendarData] nativeGetCalendarData call isn't expected to fail for calendar " + calendarId + " locale " +localeName); - + Debug.Assert(false, "[CalendarData] LoadCalendarDataFromSystem call isn't expected to fail for calendar " + calendarId + " locale " + localeName); + // Something failed, try invariant for missing parts // This is really not good, but we don't want the callers to crash. - if (this.sNativeName == null) this.sNativeName = String.Empty; // Calendar Name for the locale. - + if (this.sNativeName == null) this.sNativeName = String.Empty; // Calendar Name for the locale. + // Formats - if (this.saShortDates == null) this.saShortDates = Invariant.saShortDates; // Short Data format, default first - if (this.saYearMonths == null) this.saYearMonths = Invariant.saYearMonths; // Year/Month Data format, default first - if (this.saLongDates == null) this.saLongDates = Invariant.saLongDates; // Long Data format, default first - if (this.sMonthDay == null) this.sMonthDay = Invariant.sMonthDay; // Month/Day format - + if (this.saShortDates == null) this.saShortDates = Invariant.saShortDates; // Short Data format, default first + if (this.saYearMonths == null) this.saYearMonths = Invariant.saYearMonths; // Year/Month Data format, default first + if (this.saLongDates == null) this.saLongDates = Invariant.saLongDates; // Long Data format, default first + if (this.sMonthDay == null) this.sMonthDay = Invariant.sMonthDay; // Month/Day format + // Calendar Parts Names - if (this.saEraNames == null) this.saEraNames = Invariant.saEraNames; // Names of Eras - if (this.saAbbrevEraNames == null) this.saAbbrevEraNames = Invariant.saAbbrevEraNames; // Abbreviated Era Names + if (this.saEraNames == null) this.saEraNames = Invariant.saEraNames; // Names of Eras + if (this.saAbbrevEraNames == null) this.saAbbrevEraNames = Invariant.saAbbrevEraNames; // Abbreviated Era Names if (this.saAbbrevEnglishEraNames == null) this.saAbbrevEnglishEraNames = Invariant.saAbbrevEnglishEraNames; // Abbreviated Era Names in English - if (this.saDayNames == null) this.saDayNames = Invariant.saDayNames; // Day Names, null to use locale data, starts on Sunday - if (this.saAbbrevDayNames == null) this.saAbbrevDayNames = Invariant.saAbbrevDayNames; // Abbrev Day Names, null to use locale data, starts on Sunday - if (this.saSuperShortDayNames == null) this.saSuperShortDayNames = Invariant.saSuperShortDayNames; // Super short Day of week names - if (this.saMonthNames == null) this.saMonthNames = Invariant.saMonthNames; // Month Names (13) - if (this.saAbbrevMonthNames == null) this.saAbbrevMonthNames = Invariant.saAbbrevMonthNames; // Abbrev Month Names (13) + if (this.saDayNames == null) this.saDayNames = Invariant.saDayNames; // Day Names, null to use locale data, starts on Sunday + if (this.saAbbrevDayNames == null) this.saAbbrevDayNames = Invariant.saAbbrevDayNames; // Abbrev Day Names, null to use locale data, starts on Sunday + if (this.saSuperShortDayNames == null) this.saSuperShortDayNames = Invariant.saSuperShortDayNames; // Super short Day of week names + if (this.saMonthNames == null) this.saMonthNames = Invariant.saMonthNames; // Month Names (13) + if (this.saAbbrevMonthNames == null) this.saAbbrevMonthNames = Invariant.saAbbrevMonthNames; // Abbrev Month Names (13) // Genitive and Leap names can follow the fallback below } - // Clean up the escaping of the formats - this.saShortDates = CultureData.ReescapeWin32Strings(this.saShortDates); - this.saLongDates = CultureData.ReescapeWin32Strings(this.saLongDates); - this.saYearMonths = CultureData.ReescapeWin32Strings(this.saYearMonths); - this.sMonthDay = CultureData.ReescapeWin32String(this.sMonthDay); - - if ((CalendarId)calendarId == CalendarId.TAIWAN) + if (calendarId == CalendarId.TAIWAN) { - if (CultureInfo.IsTaiwanSku) + if (SystemSupportsTaiwaneseCalendar()) { // We got the month/day names from the OS (same as gregorian), but the native name is wrong this.sNativeName = "\x4e2d\x83ef\x6c11\x570b\x66c6"; @@ -166,11 +143,11 @@ namespace System.Globalization } // Check for null genitive names (in case unmanaged side skips it for non-gregorian calendars, etc) - if (this.saMonthGenitiveNames == null || String.IsNullOrEmpty(this.saMonthGenitiveNames[0])) + if (this.saMonthGenitiveNames == null || this.saMonthGenitiveNames.Length == 0 || String.IsNullOrEmpty(this.saMonthGenitiveNames[0])) this.saMonthGenitiveNames = this.saMonthNames; // Genitive month names (same as month names for invariant) - if (this.saAbbrevMonthGenitiveNames == null || String.IsNullOrEmpty(this.saAbbrevMonthGenitiveNames[0])) + if (this.saAbbrevMonthGenitiveNames == null || this.saAbbrevMonthGenitiveNames.Length == 0 || String.IsNullOrEmpty(this.saAbbrevMonthGenitiveNames[0])) this.saAbbrevMonthGenitiveNames = this.saAbbrevMonthNames; // Abbreviated genitive month names (same as abbrev month names for invariant) - if (this.saLeapYearMonthNames == null || String.IsNullOrEmpty(this.saLeapYearMonthNames[0])) + if (this.saLeapYearMonthNames == null || this.saLeapYearMonthNames.Length == 0 || String.IsNullOrEmpty(this.saLeapYearMonthNames[0])) this.saLeapYearMonthNames = this.saMonthNames; InitializeEraNames(localeName, calendarId); @@ -178,7 +155,7 @@ namespace System.Globalization InitializeAbbreviatedEraNames(localeName, calendarId); // Abbreviated English Era Names are only used for the Japanese calendar. - if (calendarId == (int)CalendarId.JAPAN) + if (calendarId == CalendarId.JAPAN) { this.saAbbrevEnglishEraNames = JapaneseCalendar.EnglishEraNames(); } @@ -193,12 +170,12 @@ namespace System.Globalization this.iCurrentEra = this.saEraNames.Length; } - private void InitializeEraNames(string localeName, int calendarId) + private void InitializeEraNames(string localeName, CalendarId calendarId) { // Note that the saEraNames only include "A.D." We don't have localized names for other calendars available from windows - switch ((CalendarId)calendarId) + switch (calendarId) { - // For Localized Gregorian we really expect the data from the OS. + // For Localized Gregorian we really expect the data from the OS. case CalendarId.GREGORIAN: // Fallback for CoreCLR < Win7 or culture.dll missing if (this.saEraNames == null || this.saEraNames.Length == 0 || String.IsNullOrEmpty(this.saEraNames[0])) @@ -207,7 +184,7 @@ namespace System.Globalization } break; - // The rest of the calendars have constant data, so we'll just use that + // The rest of the calendars have constant data, so we'll just use that case CalendarId.GREGORIAN_US: case CalendarId.JULIAN: this.saEraNames = new String[] { "A.D." }; @@ -237,9 +214,9 @@ namespace System.Globalization case CalendarId.GREGORIAN_ME_FRENCH: this.saEraNames = new String[] { "ap. J.-C." }; break; - + case CalendarId.TAIWAN: - if (CultureInfo.IsTaiwanSku) + if (SystemSupportsTaiwaneseCalendar()) { this.saEraNames = new String[] { "\x4e2d\x83ef\x6c11\x570b" }; } @@ -252,11 +229,11 @@ namespace System.Globalization case CalendarId.KOREA: this.saEraNames = new String[] { "\xb2e8\xae30" }; break; - + case CalendarId.THAI: this.saEraNames = new String[] { "\x0e1e\x002e\x0e28\x002e" }; break; - + case CalendarId.JAPAN: case CalendarId.JAPANESELUNISOLAR: this.saEraNames = JapaneseCalendar.EraNames(); @@ -276,25 +253,25 @@ namespace System.Globalization } } - private void InitializeAbbreviatedEraNames(string localeName, int calendarId) + private void InitializeAbbreviatedEraNames(string localeName, CalendarId calendarId) { // Note that the saAbbrevEraNames only include "AD" We don't have localized names for other calendars available from windows - switch ((CalendarId)calendarId) + switch (calendarId) { - // For Localized Gregorian we really expect the data from the OS. + // For Localized Gregorian we really expect the data from the OS. case CalendarId.GREGORIAN: - // Fallback for culture.dll missing + // Fallback for CoreCLR < Win7 or culture.dll missing if (this.saAbbrevEraNames == null || this.saAbbrevEraNames.Length == 0 || String.IsNullOrEmpty(this.saAbbrevEraNames[0])) { this.saAbbrevEraNames = new String[] { "AD" }; } break; - // The rest of the calendars have constant data, so we'll just use that + // The rest of the calendars have constant data, so we'll just use that case CalendarId.GREGORIAN_US: - case CalendarId.JULIAN: + case CalendarId.JULIAN: this.saAbbrevEraNames = new String[] { "AD" }; - break; + break; case CalendarId.JAPAN: case CalendarId.JAPANESELUNISOLAR: this.saAbbrevEraNames = JapaneseCalendar.AbbrevEraNames(); @@ -316,12 +293,12 @@ namespace System.Globalization this.saAbbrevEraNames = new String[1]; if (this.saEraNames[0].Length == 4) { - this.saAbbrevEraNames[0] = this.saEraNames[0].Substring(2,2); + this.saAbbrevEraNames[0] = this.saEraNames[0].Substring(2, 2); } else { this.saAbbrevEraNames[0] = this.saEraNames[0]; - } + } break; case CalendarId.PERSIAN: @@ -338,7 +315,7 @@ namespace System.Globalization } } - internal static CalendarData GetCalendarData(int calendarId) + internal static CalendarData GetCalendarData(CalendarId calendarId) { // // Get a calendar. @@ -348,50 +325,47 @@ namespace System.Globalization // // Get a culture name + // TODO: Note that this doesn't handle the new calendars (lunisolar, etc) String culture = CalendarIdToCultureName(calendarId); - + // Return our calendar - return CultureInfo.GetCultureInfo(culture).m_cultureData.GetCalendar(calendarId); + return CultureInfo.GetCultureInfo(culture)._cultureData.GetCalendar(calendarId); } - // - // Helper methods - // - private static String CalendarIdToCultureName(int calendarId) + private static String CalendarIdToCultureName(CalendarId calendarId) { - // note that this doesn't handle the new calendars (lunisolar, etc) switch (calendarId) { - case Calendar.CAL_GREGORIAN_US: + case CalendarId.GREGORIAN_US: return "fa-IR"; // "fa-IR" Iran - - case Calendar.CAL_JAPAN: + + case CalendarId.JAPAN: return "ja-JP"; // "ja-JP" Japan - case Calendar.CAL_TAIWAN: // zh-TW Taiwan - return "zh-TW"; - - case Calendar.CAL_KOREA: + case CalendarId.TAIWAN: + return "zh-TW"; // zh-TW Taiwan + + case CalendarId.KOREA: return "ko-KR"; // "ko-KR" Korea - - case Calendar.CAL_HIJRI: - case Calendar.CAL_GREGORIAN_ARABIC: - case Calendar.CAL_UMALQURA: + + case CalendarId.HIJRI: + case CalendarId.GREGORIAN_ARABIC: + case CalendarId.UMALQURA: return "ar-SA"; // "ar-SA" Saudi Arabia - case Calendar.CAL_THAI: + case CalendarId.THAI: return "th-TH"; // "th-TH" Thailand - - case Calendar.CAL_HEBREW: + + case CalendarId.HEBREW: return "he-IL"; // "he-IL" Israel - - case Calendar.CAL_GREGORIAN_ME_FRENCH: + + case CalendarId.GREGORIAN_ME_FRENCH: return "ar-DZ"; // "ar-DZ" Algeria - - case Calendar.CAL_GREGORIAN_XLIT_ENGLISH: - case Calendar.CAL_GREGORIAN_XLIT_FRENCH: + + case CalendarId.GREGORIAN_XLIT_ENGLISH: + case CalendarId.GREGORIAN_XLIT_FRENCH: return "ar-IQ"; // "ar-IQ"; Iraq - + default: // Default to gregorian en-US break; @@ -399,20 +373,6 @@ namespace System.Globalization return "en-US"; } - - - // Get native two digit year max - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern int nativeGetTwoDigitYearMax(int calID); - - // Call native side to load our calendar data - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern bool nativeGetCalendarData(CalendarData data, String localeName, int calendar); - - // Call native side to figure out which calendars are allowed - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern int nativeGetCalendars(String localeName, bool useUserOverride, [In, Out] int[] calendars); - } - } +} diff --git a/src/mscorlib/src/System/Globalization/CalendarWeekRule.cs b/src/mscorlib/src/System/Globalization/CalendarWeekRule.cs deleted file mode 100644 index fa2a6429f1..0000000000 --- a/src/mscorlib/src/System/Globalization/CalendarWeekRule.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Globalization { - using System; - - - [Serializable] - public enum CalendarWeekRule - { - - FirstDay = 0, // Week 1 begins on the first day of the year - - FirstFullWeek = 1, // Week 1 begins on first FirstDayOfWeek not before the first day of the year - - FirstFourDayWeek = 2 // Week 1 begins on first FirstDayOfWeek such that FirstDayOfWeek+3 is not before the first day of the year - }; -} diff --git a/src/mscorlib/src/System/Globalization/CalendricalCalculationsHelper.cs b/src/mscorlib/src/System/Globalization/CalendricalCalculationsHelper.cs deleted file mode 100644 index 1113cd58ba..0000000000 --- a/src/mscorlib/src/System/Globalization/CalendricalCalculationsHelper.cs +++ /dev/null @@ -1,414 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Globalization -{ - using System; - using System.Diagnostics; - using System.Diagnostics.Contracts; - - internal class CalendricalCalculationsHelper - { - const double FullCircleOfArc = 360.0; // 360.0; - const int HalfCircleOfArc = 180; - const double TwelveHours = 0.5; // half a day - const double Noon2000Jan01 = 730120.5; - internal const double MeanTropicalYearInDays = 365.242189; - const double MeanSpeedOfSun = MeanTropicalYearInDays / FullCircleOfArc; - const double LongitudeSpring = 0.0; - const double TwoDegreesAfterSpring = 2.0; - const int SecondsPerDay = 24 * 60 * 60; // 24 hours * 60 minutes * 60 seconds - - const int DaysInUniformLengthCentury = 36525; - const int SecondsPerMinute = 60; - const int MinutesPerDegree = 60; - - static long StartOf1810 = GetNumberOfDays(new DateTime(1810, 1, 1)); - static long StartOf1900Century = GetNumberOfDays(new DateTime(1900, 1, 1)); - - static double[] Coefficients1900to1987 = new double[] { -0.00002, 0.000297, 0.025184, -0.181133, 0.553040, -0.861938, 0.677066, -0.212591 }; - static double[] Coefficients1800to1899 = new double[] { -0.000009, 0.003844, 0.083563, 0.865736, 4.867575, 15.845535, 31.332267, 38.291999, 28.316289, 11.636204, 2.043794 }; - static double[] Coefficients1700to1799 = new double[] { 8.118780842, -0.005092142, 0.003336121, -0.0000266484 }; - static double[] Coefficients1620to1699 = new double[] { 196.58333, -4.0675, 0.0219167 }; - static double[] LambdaCoefficients = new double[] { 280.46645, 36000.76983, 0.0003032 }; - static double[] AnomalyCoefficients = new double[] { 357.52910, 35999.05030, -0.0001559, -0.00000048 }; - static double[] EccentricityCoefficients = new double[] { 0.016708617, -0.000042037, -0.0000001236 }; - static double[] Coefficients = new double[] { Angle(23, 26, 21.448), Angle(0, 0, -46.8150), Angle(0, 0, -0.00059), Angle(0, 0, 0.001813) }; - static double[] CoefficientsA = new double[] { 124.90, -1934.134, 0.002063 }; - static double[] CoefficientsB = new double[] { 201.11, 72001.5377, 0.00057 }; - - static double RadiansFromDegrees(double degree) - { - return degree * Math.PI / 180; - } - - static double SinOfDegree(double degree) - { - return Math.Sin(RadiansFromDegrees(degree)); - } - - static double CosOfDegree(double degree) - { - return Math.Cos(RadiansFromDegrees(degree)); - } - static double TanOfDegree(double degree) - { - return Math.Tan(RadiansFromDegrees(degree)); - } - - public static double Angle(int degrees, int minutes, double seconds) - { - return ((seconds / SecondsPerMinute + minutes) / MinutesPerDegree) + degrees; - } - - static double Obliquity(double julianCenturies) - { - return PolynomialSum(Coefficients, julianCenturies); - } - - internal static long GetNumberOfDays(DateTime date) - { - return date.Ticks / GregorianCalendar.TicksPerDay; - } - - static int GetGregorianYear(double numberOfDays) - { - return new DateTime(Math.Min((long)(Math.Floor(numberOfDays) * GregorianCalendar.TicksPerDay), DateTime.MaxValue.Ticks)).Year; - } - - enum CorrectionAlgorithm - { - Default, - Year1988to2019, - Year1900to1987, - Year1800to1899, - Year1700to1799, - Year1620to1699 - } - - struct EphemerisCorrectionAlgorithmMap - { - public EphemerisCorrectionAlgorithmMap(int year, CorrectionAlgorithm algorithm) - { - _lowestYear = year; - _algorithm = algorithm; - } - - internal int _lowestYear; - internal CorrectionAlgorithm _algorithm; - }; - - static EphemerisCorrectionAlgorithmMap[] EphemerisCorrectionTable = new EphemerisCorrectionAlgorithmMap[] - { - // lowest year that starts algorithm, algorithm to use - new EphemerisCorrectionAlgorithmMap(2020, CorrectionAlgorithm.Default), - new EphemerisCorrectionAlgorithmMap(1988, CorrectionAlgorithm.Year1988to2019), - new EphemerisCorrectionAlgorithmMap(1900, CorrectionAlgorithm.Year1900to1987), - new EphemerisCorrectionAlgorithmMap(1800, CorrectionAlgorithm.Year1800to1899), - new EphemerisCorrectionAlgorithmMap(1700, CorrectionAlgorithm.Year1700to1799), - new EphemerisCorrectionAlgorithmMap(1620, CorrectionAlgorithm.Year1620to1699), - new EphemerisCorrectionAlgorithmMap(int.MinValue, CorrectionAlgorithm.Default) // default must be last - }; - - static double Reminder(double divisor, double dividend) - { - double whole = Math.Floor(divisor / dividend); - return divisor - (dividend * whole); - } - - static double NormalizeLongitude(double longitude) - { - longitude = Reminder(longitude, FullCircleOfArc); - if (longitude < 0) - { - longitude += FullCircleOfArc; - } - return longitude; - } - - static public double AsDayFraction(double longitude) - { - return longitude / FullCircleOfArc; - } - - static double PolynomialSum(double[] coefficients, double indeterminate) - { - double sum = coefficients[0]; - double indeterminateRaised = 1; - for (int i=1; i<coefficients.Length; i++) - { - indeterminateRaised *= indeterminate; - sum += (coefficients[i] * indeterminateRaised); - } - - return sum; - } - - static double CenturiesFrom1900(int gregorianYear) - { - long july1stOfYear = GetNumberOfDays(new DateTime(gregorianYear, 7, 1)); - return (double) (july1stOfYear - StartOf1900Century) / DaysInUniformLengthCentury; - } - - // the following formulas defines a polynomial function which gives us the amount that the earth is slowing down for specific year ranges - static double DefaultEphemerisCorrection(int gregorianYear) - { - Debug.Assert(gregorianYear < 1620 || 2020 <= gregorianYear); - long january1stOfYear = GetNumberOfDays(new DateTime(gregorianYear, 1, 1)); - double daysSinceStartOf1810 = january1stOfYear - StartOf1810; - double x = TwelveHours + daysSinceStartOf1810; - return ((Math.Pow(x, 2) / 41048480) - 15) / SecondsPerDay; - } - - static double EphemerisCorrection1988to2019(int gregorianYear) - { - Debug.Assert(1988 <= gregorianYear && gregorianYear <= 2019); - return (double)(gregorianYear - 1933) / SecondsPerDay; - } - - static double EphemerisCorrection1900to1987(int gregorianYear) - { - Debug.Assert(1900 <= gregorianYear && gregorianYear <= 1987); - double centuriesFrom1900 = CenturiesFrom1900(gregorianYear); - return PolynomialSum(Coefficients1900to1987, centuriesFrom1900); - } - - static double EphemerisCorrection1800to1899(int gregorianYear) - { - Debug.Assert(1800 <= gregorianYear && gregorianYear <= 1899); - double centuriesFrom1900 = CenturiesFrom1900(gregorianYear); - return PolynomialSum(Coefficients1800to1899, centuriesFrom1900); - } - - static double EphemerisCorrection1700to1799(int gregorianYear) - { - Debug.Assert(1700 <= gregorianYear && gregorianYear <= 1799); - double yearsSince1700 = gregorianYear - 1700; - return PolynomialSum(Coefficients1700to1799, yearsSince1700) / SecondsPerDay; - } - - static double EphemerisCorrection1620to1699(int gregorianYear) - { - Debug.Assert(1620 <= gregorianYear && gregorianYear <= 1699); - double yearsSince1600 = gregorianYear - 1600; - return PolynomialSum(Coefficients1620to1699, yearsSince1600) / SecondsPerDay; - } - - // ephemeris-correction: correction to account for the slowing down of the rotation of the earth - static double EphemerisCorrection(double time) - { - int year = GetGregorianYear(time); - foreach (EphemerisCorrectionAlgorithmMap map in EphemerisCorrectionTable) - { - if (map._lowestYear <= year) - { - switch (map._algorithm) - { - case CorrectionAlgorithm.Default: return DefaultEphemerisCorrection(year); - case CorrectionAlgorithm.Year1988to2019: return EphemerisCorrection1988to2019(year); - case CorrectionAlgorithm.Year1900to1987: return EphemerisCorrection1900to1987(year); - case CorrectionAlgorithm.Year1800to1899: return EphemerisCorrection1800to1899(year); - case CorrectionAlgorithm.Year1700to1799: return EphemerisCorrection1700to1799(year); - case CorrectionAlgorithm.Year1620to1699: return EphemerisCorrection1620to1699(year); - } - - break; // break the loop and assert eventually - } - } - - Debug.Assert(false, "Not expected to come here"); - return DefaultEphemerisCorrection(year); - } - - static public double JulianCenturies(double moment) - { - double dynamicalMoment = moment + EphemerisCorrection(moment); - return (dynamicalMoment - Noon2000Jan01) / DaysInUniformLengthCentury; - } - - static bool IsNegative(double value) - { - return Math.Sign(value) == -1; - } - - static double CopySign(double value, double sign) - { - return (IsNegative(value) == IsNegative(sign)) ? value : -value; - } - - // equation-of-time; approximate the difference between apparent solar time and mean solar time - // formal definition is EOT = GHA - GMHA - // GHA is the Greenwich Hour Angle of the apparent (actual) Sun - // GMHA is the Greenwich Mean Hour Angle of the mean (fictitious) Sun - // http://www.esrl.noaa.gov/gmd/grad/solcalc/ - // http://en.wikipedia.org/wiki/Equation_of_time - static double EquationOfTime(double time) - { - double julianCenturies = JulianCenturies(time); - double lambda = PolynomialSum(LambdaCoefficients, julianCenturies); - double anomaly = PolynomialSum(AnomalyCoefficients, julianCenturies); - double eccentricity = PolynomialSum(EccentricityCoefficients, julianCenturies); - - double epsilon = Obliquity(julianCenturies); - double tanHalfEpsilon = TanOfDegree(epsilon / 2); - double y = tanHalfEpsilon * tanHalfEpsilon; - - double dividend = ((y * SinOfDegree(2 * lambda)) - - (2 * eccentricity * SinOfDegree(anomaly)) - + (4 * eccentricity * y * SinOfDegree(anomaly) * CosOfDegree(2 * lambda)) - - (0.5 * Math.Pow(y, 2) * SinOfDegree(4 * lambda)) - - (1.25 * Math.Pow(eccentricity, 2) * SinOfDegree(2 * anomaly))); - double divisor = 2 * Math.PI; - double equation = dividend / divisor; - - // approximation of equation of time is not valid for dates that are many millennia in the past or future - // thus limited to a half day - return CopySign(Math.Min(Math.Abs(equation), TwelveHours), equation); - } - - static double AsLocalTime(double apparentMidday, double longitude) - { - // slightly inaccurate since equation of time takes mean time not apparent time as its argument, but the difference is negligible - double universalTime = apparentMidday - AsDayFraction(longitude); - return apparentMidday - EquationOfTime(universalTime); - } - - // midday - static public double Midday(double date, double longitude) - { - return AsLocalTime(date+TwelveHours, longitude) - AsDayFraction(longitude); - } - - static double InitLongitude(double longitude) - { - return NormalizeLongitude(longitude + HalfCircleOfArc) - HalfCircleOfArc; - } - - // midday-in-tehran - static public double MiddayAtPersianObservationSite(double date) - { - return Midday(date, InitLongitude(52.5)); // 52.5 degrees east - longitude of UTC+3:30 which defines Iranian Standard Time - } - - static double PeriodicTerm(double julianCenturies, int x, double y, double z) - { - return x * SinOfDegree(y + z * julianCenturies); - } - - static double SumLongSequenceOfPeriodicTerms(double julianCenturies) - { - double sum = 0.0; - sum += PeriodicTerm(julianCenturies, 403406, 270.54861, 0.9287892); - sum += PeriodicTerm(julianCenturies, 195207, 340.19128, 35999.1376958); - sum += PeriodicTerm(julianCenturies, 119433, 63.91854, 35999.4089666); - sum += PeriodicTerm(julianCenturies, 112392, 331.2622, 35998.7287385); - sum += PeriodicTerm(julianCenturies, 3891, 317.843, 71998.20261); - sum += PeriodicTerm(julianCenturies, 2819, 86.631, 71998.4403); - sum += PeriodicTerm(julianCenturies, 1721, 240.052, 36000.35726); - sum += PeriodicTerm(julianCenturies, 660, 310.26, 71997.4812); - sum += PeriodicTerm(julianCenturies, 350, 247.23, 32964.4678); - sum += PeriodicTerm(julianCenturies, 334, 260.87, -19.441); - sum += PeriodicTerm(julianCenturies, 314, 297.82, 445267.1117); - sum += PeriodicTerm(julianCenturies, 268, 343.14, 45036.884); - sum += PeriodicTerm(julianCenturies, 242, 166.79, 3.1008); - sum += PeriodicTerm(julianCenturies, 234, 81.53, 22518.4434); - sum += PeriodicTerm(julianCenturies, 158, 3.5, -19.9739); - sum += PeriodicTerm(julianCenturies, 132, 132.75, 65928.9345); - sum += PeriodicTerm(julianCenturies, 129, 182.95, 9038.0293); - sum += PeriodicTerm(julianCenturies, 114, 162.03, 3034.7684); - sum += PeriodicTerm(julianCenturies, 99, 29.8, 33718.148); - sum += PeriodicTerm(julianCenturies, 93, 266.4, 3034.448); - sum += PeriodicTerm(julianCenturies, 86, 249.2, -2280.773); - sum += PeriodicTerm(julianCenturies, 78, 157.6, 29929.992); - sum += PeriodicTerm(julianCenturies, 72, 257.8, 31556.493); - sum += PeriodicTerm(julianCenturies, 68, 185.1, 149.588); - sum += PeriodicTerm(julianCenturies, 64, 69.9, 9037.75); - sum += PeriodicTerm(julianCenturies, 46, 8.0, 107997.405); - sum += PeriodicTerm(julianCenturies, 38, 197.1, -4444.176); - sum += PeriodicTerm(julianCenturies, 37, 250.4, 151.771); - sum += PeriodicTerm(julianCenturies, 32, 65.3, 67555.316); - sum += PeriodicTerm(julianCenturies, 29, 162.7, 31556.08); - sum += PeriodicTerm(julianCenturies, 28, 341.5, -4561.54); - sum += PeriodicTerm(julianCenturies, 27, 291.6, 107996.706); - sum += PeriodicTerm(julianCenturies, 27, 98.5, 1221.655); - sum += PeriodicTerm(julianCenturies, 25, 146.7, 62894.167); - sum += PeriodicTerm(julianCenturies, 24, 110.0, 31437.369); - sum += PeriodicTerm(julianCenturies, 21, 5.2, 14578.298); - sum += PeriodicTerm(julianCenturies, 21, 342.6, -31931.757); - sum += PeriodicTerm(julianCenturies, 20, 230.9, 34777.243); - sum += PeriodicTerm(julianCenturies, 18, 256.1, 1221.999); - sum += PeriodicTerm(julianCenturies, 17, 45.3, 62894.511); - sum += PeriodicTerm(julianCenturies, 14, 242.9, -4442.039); - sum += PeriodicTerm(julianCenturies, 13, 115.2, 107997.909); - sum += PeriodicTerm(julianCenturies, 13, 151.8, 119.066); - sum += PeriodicTerm(julianCenturies, 13, 285.3, 16859.071); - sum += PeriodicTerm(julianCenturies, 12, 53.3, -4.578); - sum += PeriodicTerm(julianCenturies, 10, 126.6, 26895.292); - sum += PeriodicTerm(julianCenturies, 10, 205.7, -39.127); - sum += PeriodicTerm(julianCenturies, 10, 85.9, 12297.536); - sum += PeriodicTerm(julianCenturies, 10, 146.1, 90073.778); - return sum; - } - - static double Aberration(double julianCenturies) - { - return (0.0000974 * CosOfDegree(177.63 + (35999.01848 * julianCenturies))) - 0.005575; - } - - static double Nutation(double julianCenturies) - { - double a = PolynomialSum(CoefficientsA, julianCenturies); - double b = PolynomialSum(CoefficientsB, julianCenturies); - return (-0.004778 * SinOfDegree(a)) - (0.0003667 * SinOfDegree(b)); - } - - static public double Compute(double time) - { - double julianCenturies = JulianCenturies(time); - double lambda = 282.7771834 - + (36000.76953744 * julianCenturies) - + (0.000005729577951308232 * SumLongSequenceOfPeriodicTerms(julianCenturies)); - - double longitude = lambda + Aberration(julianCenturies) + Nutation(julianCenturies); - return InitLongitude(longitude); - } - - static public double AsSeason(double longitude) - { - return (longitude < 0) ? (longitude + FullCircleOfArc) : longitude; - } - - static double EstimatePrior(double longitude, double time) - { - double timeSunLastAtLongitude = time - (MeanSpeedOfSun * AsSeason(InitLongitude(Compute(time) - longitude))); - double longitudeErrorDelta = InitLongitude(Compute(timeSunLastAtLongitude) - longitude); - return Math.Min(time, timeSunLastAtLongitude - (MeanSpeedOfSun * longitudeErrorDelta)); - } - - // persian-new-year-on-or-before - // number of days is the absolute date. The absolute date is the number of days from January 1st, 1 A.D. - // 1/1/0001 is absolute date 1. - internal static long PersianNewYearOnOrBefore(long numberOfDays) - { - double date = (double) numberOfDays; - - double approx = EstimatePrior(LongitudeSpring, MiddayAtPersianObservationSite(date)); - long lowerBoundNewYearDay = (long) Math.Floor(approx) - 1; - long upperBoundNewYearDay = lowerBoundNewYearDay + 3; // estimate is generally within a day of the actual occurrance (at the limits, the error expands, since the calculations rely on the mean tropical year which changes...) - long day = lowerBoundNewYearDay; - for (; day != upperBoundNewYearDay; ++day) - { - double midday = MiddayAtPersianObservationSite((double) day); - double l = Compute(midday); - if ((LongitudeSpring <= l) && (l <= TwoDegreesAfterSpring)) - { - break; - } - } - Debug.Assert(day != upperBoundNewYearDay); - - return day - 1; - } - } -} diff --git a/src/mscorlib/src/System/Globalization/CharUnicodeInfo.cs b/src/mscorlib/src/System/Globalization/CharUnicodeInfo.cs index 2822b418ef..8e3bb47424 100644 --- a/src/mscorlib/src/System/Globalization/CharUnicodeInfo.cs +++ b/src/mscorlib/src/System/Globalization/CharUnicodeInfo.cs @@ -12,22 +12,12 @@ // //////////////////////////////////////////////////////////////////////////// -namespace System.Globalization { +using System.Diagnostics; +using System.Diagnostics.Contracts; - //This class has only static members and therefore doesn't need to be serialized. - - using System; - using System.Threading; - using System.Runtime.InteropServices; - using System.Runtime.CompilerServices; - using System.Runtime.Versioning; - using System.Reflection; - using System.Security; - using System.Diagnostics; - using System.Diagnostics.Contracts; - - - public static class CharUnicodeInfo +namespace System.Globalization +{ + public static partial class CharUnicodeInfo { //--------------------------------------------------------------------// // Internal Information // @@ -36,95 +26,18 @@ namespace System.Globalization { // // Native methods to access the Unicode category data tables in charinfo.nlp. // - internal const char HIGH_SURROGATE_START = '\ud800'; - internal const char HIGH_SURROGATE_END = '\udbff'; - internal const char LOW_SURROGATE_START = '\udc00'; - internal const char LOW_SURROGATE_END = '\udfff'; + internal const char HIGH_SURROGATE_START = '\ud800'; + internal const char HIGH_SURROGATE_END = '\udbff'; + internal const char LOW_SURROGATE_START = '\udc00'; + internal const char LOW_SURROGATE_END = '\udfff'; internal const int UNICODE_CATEGORY_OFFSET = 0; internal const int BIDI_CATEGORY_OFFSET = 1; - static bool s_initialized = InitTable(); - - // The native pointer to the 12:4:4 index table of the Unicode cateogry data. - unsafe static ushort* s_pCategoryLevel1Index; - unsafe static byte* s_pCategoriesValue; - - // The native pointer to the 12:4:4 index table of the Unicode numeric data. - // The value of this index table is an index into the real value table stored in s_pNumericValues. - unsafe static ushort* s_pNumericLevel1Index; - - // The numeric value table, which is indexed by s_pNumericLevel1Index. - // Every item contains the value for numeric value. - // unsafe static double* s_pNumericValues; - // To get around the IA64 alignment issue. Our double data is aligned in 8-byte boundary, but loader loads the embeded table starting - // at 4-byte boundary. This cause a alignment issue since double is 8-byte. - unsafe static byte* s_pNumericValues; - - // The digit value table, which is indexed by s_pNumericLevel1Index. It shares the same indice as s_pNumericValues. - // Every item contains the value for decimal digit/digit value. - unsafe static DigitValues* s_pDigitValues; - - internal const String UNICODE_INFO_FILE_NAME = "charinfo.nlp"; // The starting codepoint for Unicode plane 1. Plane 1 contains 0x010000 ~ 0x01ffff. internal const int UNICODE_PLANE01_START = 0x10000; - // - // This is the header for the native data table that we load from UNICODE_INFO_FILE_NAME. - // - // Excplicit layout is used here since a syntax like char[16] can not be used in sequential layout. - [StructLayout(LayoutKind.Explicit)] - internal unsafe struct UnicodeDataHeader { - [FieldOffset(0)] - internal char TableName; // WCHAR[16] - [FieldOffset(0x20)] - internal ushort version; // WORD[4] - [FieldOffset(0x28)] - internal uint OffsetToCategoriesIndex; // DWORD - [FieldOffset(0x2c)] - internal uint OffsetToCategoriesValue; // DWORD - [FieldOffset(0x30)] - internal uint OffsetToNumbericIndex; // DWORD - [FieldOffset(0x34)] - internal uint OffsetToDigitValue; // DWORD - [FieldOffset(0x38)] - internal uint OffsetToNumbericValue; // DWORD - - } - - // NOTE: It's important to specify pack size here, since the size of the structure is 2 bytes. Otherwise, - // the default pack size will be 4. - - [StructLayout(LayoutKind.Sequential, Pack=2)] - internal struct DigitValues { - internal sbyte decimalDigit; - internal sbyte digit; - } - - - //We need to allocate the underlying table that provides us with the information that we - //use. We allocate this once in the class initializer and then we don't need to worry - //about it again. - // - unsafe static bool InitTable() { - - // Go to native side and get pointer to the native table - byte * pDataTable = GlobalizationAssembly.GetGlobalizationResourceBytePtr(typeof(CharUnicodeInfo).Assembly, UNICODE_INFO_FILE_NAME); - - UnicodeDataHeader* mainHeader = (UnicodeDataHeader*)pDataTable; - - // Set up the native pointer to different part of the tables. - s_pCategoryLevel1Index = (ushort*) (pDataTable + mainHeader->OffsetToCategoriesIndex); - s_pCategoriesValue = (byte*) (pDataTable + mainHeader->OffsetToCategoriesValue); - s_pNumericLevel1Index = (ushort*) (pDataTable + mainHeader->OffsetToNumbericIndex); - s_pNumericValues = (byte*) (pDataTable + mainHeader->OffsetToNumbericValue); - s_pDigitValues = (DigitValues*) (pDataTable + mainHeader->OffsetToDigitValue); - - return true; - } - - //////////////////////////////////////////////////////////////////////// // // Actions: @@ -137,14 +50,18 @@ namespace System.Globalization { // //////////////////////////////////////////////////////////////////////// - internal static int InternalConvertToUtf32(String s, int index) { + internal static int InternalConvertToUtf32(String s, int index) + { Debug.Assert(s != null, "s != null"); Debug.Assert(index >= 0 && index < s.Length, "index < s.Length"); - if (index < s.Length - 1) { + if (index < s.Length - 1) + { int temp1 = (int)s[index] - HIGH_SURROGATE_START; - if (temp1 >= 0 && temp1 <= 0x3ff) { - int temp2 = (int)s[index+1] - LOW_SURROGATE_START; - if (temp2 >= 0 && temp2 <= 0x3ff) { + if (temp1 >= 0 && temp1 <= 0x3ff) + { + int temp2 = (int)s[index + 1] - LOW_SURROGATE_START; + if (temp2 >= 0 && temp2 <= 0x3ff) + { // Convert the surrogate to UTF32 and get the result. return ((temp1 * 0x400) + temp2 + UNICODE_PLANE01_START); } @@ -152,7 +69,6 @@ namespace System.Globalization { } return ((int)s[index]); } - //////////////////////////////////////////////////////////////////////// // // Convert a character or a surrogate pair starting at index of string s @@ -175,16 +91,20 @@ namespace System.Globalization { // //////////////////////////////////////////////////////////////////////// - internal static int InternalConvertToUtf32(String s, int index, out int charLength) { + internal static int InternalConvertToUtf32(String s, int index, out int charLength) + { Debug.Assert(s != null, "s != null"); Debug.Assert(s.Length > 0, "s.Length > 0"); Debug.Assert(index >= 0 && index < s.Length, "index >= 0 && index < s.Length"); charLength = 1; - if (index < s.Length - 1) { + if (index < s.Length - 1) + { int temp1 = (int)s[index] - HIGH_SURROGATE_START; - if (temp1 >= 0 && temp1 <= 0x3ff) { - int temp2 = (int)s[index+1] - LOW_SURROGATE_START; - if (temp2 >= 0 && temp2 <= 0x3ff) { + if (temp1 >= 0 && temp1 <= 0x3ff) + { + int temp2 = (int)s[index + 1] - LOW_SURROGATE_START; + if (temp2 >= 0 && temp2 <= 0x3ff) + { // Convert the surrogate to UTF32 and get the result. charLength++; return ((temp1 * 0x400) + temp2 + UNICODE_PLANE01_START); @@ -210,7 +130,8 @@ namespace System.Globalization { UnicodeCategory uc = GetUnicodeCategory(s, index); // In Unicode 3.0, U+2028 is the only character which is under the category "LineSeparator". // And U+2029 is th eonly character which is under the category "ParagraphSeparator". - switch (uc) { + switch (uc) + { case (UnicodeCategory.SpaceSeparator): case (UnicodeCategory.LineSeparator): case (UnicodeCategory.ParagraphSeparator): @@ -225,7 +146,8 @@ namespace System.Globalization { UnicodeCategory uc = GetUnicodeCategory(c); // In Unicode 3.0, U+2028 is the only character which is under the category "LineSeparator". // And U+2029 is th eonly character which is under the category "ParagraphSeparator". - switch (uc) { + switch (uc) + { case (UnicodeCategory.SpaceSeparator): case (UnicodeCategory.LineSeparator): case (UnicodeCategory.ParagraphSeparator): @@ -235,12 +157,14 @@ namespace System.Globalization { return (false); } + // // This is called by the public char and string, index versions // // Note that for ch in the range D800-DFFF we just treat it as any other non-numeric character // - internal unsafe static double InternalGetNumericValue(int ch) { + internal static unsafe double InternalGetNumericValue(int ch) + { Debug.Assert(ch >= 0 && ch <= 0x10ffff, "ch is not in valid Unicode range."); // Get the level 2 item from the highest 12 bit (8 - 19) of ch. ushort index = s_pNumericLevel1Index[ch >> 8]; @@ -248,52 +172,34 @@ namespace System.Globalization { // The offset is referred to an float item in m_pNumericFloatData. // Note that & has the lower precedence than addition, so don't forget the parathesis. index = s_pNumericLevel1Index[index + ((ch >> 4) & 0x000f)]; - byte* pBytePtr = (byte*)&(s_pNumericLevel1Index[index]); - // Get the result from the 0 -3 bit of ch. -#if BIT64 - // To get around the IA64 alignment issue. Our double data is aligned in 8-byte boundary, but loader loads the embeded table starting - // at 4-byte boundary. This cause a alignment issue since double is 8-byte. - byte* pSourcePtr = &(s_pNumericValues[pBytePtr[(ch & 0x000f)] * sizeof(double)]); - if (((long)pSourcePtr % 8) != 0) { - // We are not aligned in 8-byte boundary. Do a copy. - double ret; - byte* retPtr = (byte*)&ret; - Buffer.Memcpy(retPtr, pSourcePtr, sizeof(double)); - return (ret); + + fixed (ushort* pUshortPtr = &(s_pNumericLevel1Index[index])) + { + byte* pBytePtr = (byte*)pUshortPtr; + fixed (byte* pByteNum = s_pNumericValues) + { + double* pDouble = (double*)pByteNum; + return pDouble[pBytePtr[(ch & 0x000f)]]; + } } - return (((double*)s_pNumericValues)[pBytePtr[(ch & 0x000f)]]); -#else - return (((double*)s_pNumericValues)[pBytePtr[(ch & 0x000f)]]); -#endif } - // - // This is called by the public char and string, index versions - // - // Note that for ch in the range D800-DFFF we just treat it as any other non-numeric character - // - internal unsafe static DigitValues* InternalGetDigitValues(int ch) { + internal static unsafe ushort InternalGetDigitValues(int ch) + { Debug.Assert(ch >= 0 && ch <= 0x10ffff, "ch is not in valid Unicode range."); // Get the level 2 item from the highest 12 bit (8 - 19) of ch. ushort index = s_pNumericLevel1Index[ch >> 8]; // Get the level 2 WORD offset from the 4 - 7 bit of ch. This provides the base offset of the level 3 table. - // The offset is referred to an float item in m_pNumericFloatData. // Note that & has the lower precedence than addition, so don't forget the parathesis. index = s_pNumericLevel1Index[index + ((ch >> 4) & 0x000f)]; - byte* pBytePtr = (byte*)&(s_pNumericLevel1Index[index]); - // Get the result from the 0 -3 bit of ch. - return &(s_pDigitValues[pBytePtr[(ch & 0x000f)]]); - } - internal unsafe static sbyte InternalGetDecimalDigitValue(int ch) { - return (InternalGetDigitValues(ch)->decimalDigit); - } - - internal unsafe static sbyte InternalGetDigitValue(int ch) { - return (InternalGetDigitValues(ch)->digit); + fixed (ushort* pUshortPtr = &(s_pNumericLevel1Index[index])) + { + byte* pBytePtr = (byte*)pUshortPtr; + return s_pDigitValues[pBytePtr[(ch & 0x000f)]]; + } } - //////////////////////////////////////////////////////////////////////// // //Returns the numeric value associated with the character c. If the character is a fraction, @@ -310,114 +216,91 @@ namespace System.Globalization { //////////////////////////////////////////////////////////////////////// - public static double GetNumericValue(char ch) { + public static double GetNumericValue(char ch) + { return (InternalGetNumericValue(ch)); } - public static double GetNumericValue(String s, int index) { - if (s == null) { + public static double GetNumericValue(String s, int index) + { + if (s == null) + { throw new ArgumentNullException(nameof(s)); } - if (index < 0 || index >= s.Length) { - throw new ArgumentOutOfRangeException(nameof(index), Environment.GetResourceString("ArgumentOutOfRange_Index")); + if (index < 0 || index >= s.Length) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); } Contract.EndContractBlock(); return (InternalGetNumericValue(InternalConvertToUtf32(s, index))); - } - //////////////////////////////////////////////////////////////////////// - // - //Returns the decimal digit value associated with the character c. - // - // The value should be from 0 ~ 9. - // If the character does not have a numeric value, the return value is -1. - // From Unicode.org: Decimal Digits. Digits that can be used to form decimal-radix numbers. - //Returns: - // the decimal digit value for the specified Unicode character. If the character does not have a decimal digit value, the return value is -1. - //Arguments: - // ch a Unicode character - //Exceptions: - // ArgumentNullException - // ArgumentOutOfRangeException - // - //////////////////////////////////////////////////////////////////////// - - - public static int GetDecimalDigitValue(char ch) { - return (InternalGetDecimalDigitValue(ch)); + public static int GetDecimalDigitValue(char ch) + { + return (sbyte)(InternalGetDigitValues(ch) >> 8); } - - public static int GetDecimalDigitValue(String s, int index) { - if (s == null) { + public static int GetDecimalDigitValue(String s, int index) + { + if (s == null) + { throw new ArgumentNullException(nameof(s)); } - if (index < 0 || index >= s.Length) { - throw new ArgumentOutOfRangeException(nameof(index), Environment.GetResourceString("ArgumentOutOfRange_Index")); + + if (index < 0 || index >= s.Length) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); } Contract.EndContractBlock(); - return (InternalGetDecimalDigitValue(InternalConvertToUtf32(s, index))); + return (sbyte)(InternalGetDigitValues(InternalConvertToUtf32(s, index)) >> 8); } - //////////////////////////////////////////////////////////////////////// - // - //Action: Returns the digit value associated with the character c. - // If the character does not have a numeric value, the return value is -1. - // From Unicode.org: If the character represents a digit, not necessarily a decimal digit, - // the value is here. This covers digits which do not form decimal radix forms, such as the compatibility superscript digits. - // - // An example is: U+2460 IRCLED DIGIT ONE. This character has digit value 1, but does not have associcated decimal digit value. - // - //Returns: - // the digit value for the specified Unicode character. If the character does not have a digit value, the return value is -1. - //Arguments: - // ch a Unicode character - //Exceptions: - // ArgumentNullException - // ArgumentOutOfRangeException - // - //////////////////////////////////////////////////////////////////////// - - - public static int GetDigitValue(char ch) { - return (InternalGetDigitValue(ch)); + public static int GetDigitValue(char ch) + { + return (sbyte)(InternalGetDigitValues(ch) & 0x00FF); } - - public static int GetDigitValue(String s, int index) { - if (s == null) { + public static int GetDigitValue(String s, int index) + { + if (s == null) + { throw new ArgumentNullException(nameof(s)); } - if (index < 0 || index >= s.Length) { - throw new ArgumentOutOfRangeException(nameof(index), Environment.GetResourceString("ArgumentOutOfRange_Index")); + + if (index < 0 || index >= s.Length) + { + throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); } + Contract.EndContractBlock(); - return (InternalGetDigitValue(InternalConvertToUtf32(s, index))); + return (sbyte)(InternalGetDigitValues(InternalConvertToUtf32(s, index)) & 0x00FF); } public static UnicodeCategory GetUnicodeCategory(char ch) { - return (InternalGetUnicodeCategory(ch)) ; + return (InternalGetUnicodeCategory(ch)); } public static UnicodeCategory GetUnicodeCategory(String s, int index) { - if (s==null) + if (s == null) throw new ArgumentNullException(nameof(s)); - if (((uint)index)>=((uint)s.Length)) { + if (((uint)index) >= ((uint)s.Length)) + { throw new ArgumentOutOfRangeException(nameof(index)); } Contract.EndContractBlock(); return InternalGetUnicodeCategory(s, index); } - internal unsafe static UnicodeCategory InternalGetUnicodeCategory(int ch) { + internal static unsafe UnicodeCategory InternalGetUnicodeCategory(int ch) + { return ((UnicodeCategory)InternalGetCategoryValue(ch, UNICODE_CATEGORY_OFFSET)); } + //////////////////////////////////////////////////////////////////////// // //Action: Returns the Unicode Category property for the character c. @@ -432,37 +315,28 @@ namespace System.Globalization { // //////////////////////////////////////////////////////////////////////// - internal unsafe static byte InternalGetCategoryValue(int ch, int offset) { + internal static unsafe byte InternalGetCategoryValue(int ch, int offset) + { Debug.Assert(ch >= 0 && ch <= 0x10ffff, "ch is not in valid Unicode range."); // Get the level 2 item from the highest 12 bit (8 - 19) of ch. ushort index = s_pCategoryLevel1Index[ch >> 8]; // Get the level 2 WORD offset from the 4 - 7 bit of ch. This provides the base offset of the level 3 table. // Note that & has the lower precedence than addition, so don't forget the parathesis. index = s_pCategoryLevel1Index[index + ((ch >> 4) & 0x000f)]; - byte* pBytePtr = (byte*)&(s_pCategoryLevel1Index[index]); - // Get the result from the 0 -3 bit of ch. - byte valueIndex = pBytePtr[(ch & 0x000f)]; - byte uc = s_pCategoriesValue[valueIndex * 2 + offset]; - // - // Make sure that OtherNotAssigned is the last category in UnicodeCategory. - // If that changes, change the following assertion as well. - // - //Debug.Assert(uc >= 0 && uc <= UnicodeCategory.OtherNotAssigned, "Table returns incorrect Unicode category"); - return (uc); - } -// internal static BidiCategory GetBidiCategory(char ch) { -// return ((BidiCategory)InternalGetCategoryValue(c, BIDI_CATEGORY_OFFSET)); -// } - - internal static BidiCategory GetBidiCategory(String s, int index) { - if (s==null) - throw new ArgumentNullException(nameof(s)); - if (((uint)index)>=((uint)s.Length)) { - throw new ArgumentOutOfRangeException(nameof(index)); + fixed (ushort* pUshortPtr = &(s_pCategoryLevel1Index[index])) + { + byte* pBytePtr = (byte*)pUshortPtr; + // Get the result from the 0 -3 bit of ch. + byte valueIndex = pBytePtr[(ch & 0x000f)]; + byte uc = s_pCategoriesValue[valueIndex * 2 + offset]; + // + // Make sure that OtherNotAssigned is the last category in UnicodeCategory. + // If that changes, change the following assertion as well. + // + //Debug.Assert(uc >= 0 && uc <= UnicodeCategory.OtherNotAssigned, "Table returns incorrect Unicode category"); + return (uc); } - Contract.EndContractBlock(); - return ((BidiCategory)InternalGetCategoryValue(InternalConvertToUtf32(s, index), BIDI_CATEGORY_OFFSET)); } //////////////////////////////////////////////////////////////////////// @@ -478,13 +352,27 @@ namespace System.Globalization { // //////////////////////////////////////////////////////////////////////// - internal static UnicodeCategory InternalGetUnicodeCategory(String value, int index) { + internal static UnicodeCategory InternalGetUnicodeCategory(String value, int index) + { Debug.Assert(value != null, "value can not be null"); Debug.Assert(index < value.Length, "index < value.Length"); return (InternalGetUnicodeCategory(InternalConvertToUtf32(value, index))); } + internal static BidiCategory GetBidiCategory(String s, int index) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); + + if (((uint)index) >= ((uint)s.Length)) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + return ((BidiCategory) InternalGetCategoryValue(InternalConvertToUtf32(s, index), BIDI_CATEGORY_OFFSET)); + } + //////////////////////////////////////////////////////////////////////// // // Get the Unicode category of the character starting at index. If the character is in BMP, charLength will return 1. @@ -492,15 +380,17 @@ namespace System.Globalization { // //////////////////////////////////////////////////////////////////////// - internal static UnicodeCategory InternalGetUnicodeCategory(String str, int index, out int charLength) { + internal static UnicodeCategory InternalGetUnicodeCategory(String str, int index, out int charLength) + { Debug.Assert(str != null, "str can not be null"); - Debug.Assert(str.Length > 0, "str.Length > 0");; + Debug.Assert(str.Length > 0, "str.Length > 0"); ; Debug.Assert(index >= 0 && index < str.Length, "index >= 0 && index < str.Length"); return (InternalGetUnicodeCategory(InternalConvertToUtf32(str, index, out charLength))); } - internal static bool IsCombiningCategory(UnicodeCategory uc) { + internal static bool IsCombiningCategory(UnicodeCategory uc) + { Debug.Assert(uc >= 0, "uc >= 0"); return ( uc == UnicodeCategory.NonSpacingMark || diff --git a/src/mscorlib/src/System/Globalization/CharUnicodeInfoData.cs b/src/mscorlib/src/System/Globalization/CharUnicodeInfoData.cs new file mode 100644 index 0000000000..b1bef8146e --- /dev/null +++ b/src/mscorlib/src/System/Globalization/CharUnicodeInfoData.cs @@ -0,0 +1,1247 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Globalization +{ + public static partial class CharUnicodeInfo + { + // THE FOLLOWING DATA IS AUTO GENERATED BY GenUnicodeProp.pl SCRIPT UNDER THE TOOLS FOLDER + // PLEASE DON'T MODIFY BY HAND + + + // 12:4:4 index table of the Unicode cateogry data. + private static ushort[] s_pCategoryLevel1Index = new ushort[] + { + 0x1100, 0x1110, 0x1120, 0x1130, 0x1140, 0x1150, 0x1160, 0x1170, 0x1180, 0x1190, 0x11a0, 0x11b0, 0x11c0, 0x11d0, 0x11e0, 0x11f0, + 0x1200, 0x1210, 0x1220, 0x1230, 0x1240, 0x1210, 0x1250, 0x1260, 0x1270, 0x1280, 0x1290, 0x12a0, 0x12b0, 0x12c0, 0x12d0, 0x12e0, + 0x12f0, 0x1300, 0x1310, 0x1320, 0x1330, 0x1340, 0x1350, 0x1360, 0x1370, 0x1380, 0x1390, 0x13a0, 0x13b0, 0x13c0, 0x13d0, 0x13e0, + 0x13f0, 0x1400, 0x1410, 0x1420, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1430, 0x1210, 0x1210, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1440, + 0x1450, 0x1210, 0x1210, 0x1210, 0x1460, 0x1210, 0x1470, 0x1480, 0x1490, 0x14a0, 0x14b0, 0x14c0, 0x1210, 0x1210, 0x1210, 0x1210, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x14d0, 0x14e0, 0x14e0, 0x14e0, 0x14e0, 0x14e0, 0x14e0, 0x14e0, 0x14e0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x1210, 0x1500, 0x1510, 0x1520, 0x1530, 0x1540, 0x1550, + 0x1560, 0x1570, 0x1580, 0x1590, 0x15a0, 0x15b0, 0x1210, 0x15c0, 0x15d0, 0x15e0, 0x15f0, 0x1600, 0x1610, 0x1620, 0x1630, 0x1620, + 0x1640, 0x1650, 0x1660, 0x1670, 0x1680, 0x1690, 0x16a0, 0x16b0, 0x16c0, 0x1620, 0x16d0, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1210, 0x1210, 0x1210, 0x16e0, 0x16f0, 0x1700, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1710, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1210, 0x1210, 0x1720, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1210, 0x1210, 0x1730, 0x1740, 0x1620, 0x1620, 0x1620, 0x1750, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1760, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1770, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1780, 0x1790, 0x17a0, 0x17b0, 0x17c0, 0x17d0, 0x17e0, 0x17f0, 0x1370, 0x1370, 0x1800, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1810, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1820, 0x1620, + 0x1830, 0x1840, 0x1850, 0x1860, 0x1870, 0x1880, 0x1890, 0x18a0, 0x18b0, 0x18c0, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x18d0, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x18e0, 0x18f0, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, + 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1210, 0x1900, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1210, 0x1210, 0x1910, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1920, 0x1930, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, 0x1620, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x1940, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, + 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x14f0, 0x1940, + 0x1950, 0x1958, 0x1960, 0x1968, 0x1970, 0x1978, 0x1980, 0x1988, 0x1990, 0x1998, 0x19a0, 0x19a8, 0x19b0, 0x19b8, 0x19c0, 0x19c8, + 0x19d0, 0x19d0, 0x19d0, 0x19d8, 0x19e0, 0x19d0, 0x19d0, 0x19e8, 0x19f0, 0x19f8, 0x1a00, 0x1a08, 0x1a10, 0x1a18, 0x19d0, 0x1a20, + 0x19d0, 0x19d0, 0x19d0, 0x1a28, 0x1a30, 0x19c0, 0x19c0, 0x19c0, 0x19c0, 0x1a38, 0x19c0, 0x1a40, 0x1a48, 0x1a50, 0x1a58, 0x1a60, + 0x1a68, 0x1a68, 0x1a68, 0x1a68, 0x1a68, 0x1a68, 0x1a68, 0x1a70, 0x1a78, 0x1a80, 0x1a88, 0x19c0, 0x1a90, 0x1a98, 0x19d0, 0x1aa0, + 0x19b0, 0x19b0, 0x19b0, 0x19c0, 0x19c0, 0x19c0, 0x19d0, 0x19d0, 0x1aa8, 0x19d0, 0x19d0, 0x19d0, 0x1ab0, 0x19d0, 0x19d0, 0x19d0, + 0x19d0, 0x19d0, 0x19d0, 0x1ab8, 0x19b0, 0x1ac0, 0x1ac8, 0x19c0, 0x1ad0, 0x1ad8, 0x1a68, 0x1ae0, 0x1ae8, 0x1af0, 0x1af8, 0x1b00, + 0x1b08, 0x1b10, 0x1b18, 0x1b18, 0x1b20, 0x1a68, 0x1b28, 0x1b30, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x1b38, 0x1b40, 0x1b48, + 0x1b50, 0x1b58, 0x1b18, 0x1a68, 0x1b60, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x1b68, 0x1b70, 0x1b78, 0x1af0, 0x1b80, 0x1b88, + 0x1af0, 0x1b90, 0x1b98, 0x1ba0, 0x1af0, 0x1ba8, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1b18, 0x1bb8, 0x1bb0, 0x1bb0, 0x1bc0, 0x1a68, + 0x1bc8, 0x1bd0, 0x1bd0, 0x1bd8, 0x1be0, 0x1be8, 0x1bf0, 0x1bf8, 0x1c00, 0x1c08, 0x1c10, 0x1c18, 0x1c20, 0x1c28, 0x1c30, 0x1c38, + 0x1c40, 0x1c08, 0x1c10, 0x1c48, 0x1c50, 0x1c58, 0x1c60, 0x1c68, 0x1c70, 0x1c78, 0x1c10, 0x1c80, 0x1c88, 0x1c90, 0x1c30, 0x1c98, + 0x1ca0, 0x1c08, 0x1c10, 0x1ca8, 0x1cb0, 0x1cb8, 0x1c30, 0x1cc0, 0x1cc8, 0x1cd0, 0x1cd8, 0x1ce0, 0x1ce8, 0x1cf0, 0x1c60, 0x1cf8, + 0x1d00, 0x1d08, 0x1c10, 0x1d10, 0x1d18, 0x1d20, 0x1c30, 0x1d28, 0x1d30, 0x1d08, 0x1c10, 0x1d38, 0x1d40, 0x1d48, 0x1c30, 0x1d50, + 0x1d30, 0x1d08, 0x1bd0, 0x1d58, 0x1d60, 0x1d68, 0x1c30, 0x1d70, 0x1d78, 0x1d80, 0x1bd0, 0x1d88, 0x1d90, 0x1d98, 0x1c60, 0x1da0, + 0x1da8, 0x1bd0, 0x1bd0, 0x1db0, 0x1db8, 0x1dc0, 0x1bb0, 0x1bb0, 0x1dc8, 0x1dd0, 0x1dd8, 0x1de0, 0x1de8, 0x1df0, 0x1bb0, 0x1bb0, + 0x1df8, 0x1e00, 0x1e08, 0x1e10, 0x1e18, 0x1bd0, 0x1e20, 0x1e28, 0x1e30, 0x1e38, 0x1a68, 0x1e40, 0x1e48, 0x1e50, 0x1bb0, 0x1bb0, + 0x1bd0, 0x1bd0, 0x1e58, 0x1e60, 0x1e68, 0x1e70, 0x1e78, 0x1e80, 0x1e88, 0x1e90, 0x19b0, 0x19b0, 0x1e98, 0x1bd0, 0x1bd0, 0x1ea0, + 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, + 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1ea8, 0x1eb0, 0x1bd0, 0x1bd0, 0x1ea8, 0x1bd0, 0x1bd0, 0x1eb8, 0x1ec0, 0x1ec8, 0x1bd0, 0x1bd0, + 0x1bd0, 0x1ec0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1ed0, 0x1ed8, 0x1ee0, 0x1bd0, 0x1ee8, 0x19b0, 0x19b0, 0x19b0, 0x19b0, 0x19b0, 0x1ef0, + 0x1ef8, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, + 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1f00, 0x1bd0, 0x1f08, 0x1f10, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1f18, 0x1f20, + 0x1f28, 0x1f30, 0x1bd0, 0x1f38, 0x1bd0, 0x1f40, 0x1f28, 0x1f48, 0x1bd0, 0x1bd0, 0x1bd0, 0x1f50, 0x1f58, 0x1f60, 0x1f68, 0x1f70, + 0x1f78, 0x1f68, 0x1bd0, 0x1bd0, 0x1f80, 0x1bd0, 0x1bd0, 0x1f88, 0x1bd0, 0x1bd0, 0x1f90, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1f98, + 0x1bd0, 0x1fa0, 0x1fa8, 0x1fb0, 0x1fb8, 0x1bd0, 0x1fc0, 0x1fc8, 0x1bd0, 0x1bd0, 0x1fd0, 0x1bd0, 0x1fd8, 0x1fe0, 0x1fe8, 0x1fe8, + 0x1bd0, 0x1ff0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1ff8, 0x2000, 0x2008, 0x1f68, 0x1f68, 0x2010, 0x2018, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x2020, 0x1bd0, 0x1bd0, 0x2028, 0x2030, 0x1e68, 0x2038, 0x2040, 0x2048, 0x1bd0, 0x2050, 0x2058, 0x1bd0, 0x1bd0, 0x2060, 0x2068, + 0x1bd0, 0x1bd0, 0x2070, 0x2078, 0x2080, 0x2058, 0x1bd0, 0x2088, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x2090, 0x2098, 0x20a0, 0x20a8, + 0x19c0, 0x19c0, 0x20b0, 0x20b8, 0x20b8, 0x20b8, 0x20c0, 0x20c8, 0x19c0, 0x20d0, 0x20b8, 0x20b8, 0x1a68, 0x1a68, 0x1a68, 0x20d8, + 0x19d0, 0x19d0, 0x19d0, 0x19d0, 0x19d0, 0x19d0, 0x19d0, 0x19d0, 0x19d0, 0x20e0, 0x19d0, 0x19d0, 0x19d0, 0x19d0, 0x19d0, 0x19d0, + 0x20e8, 0x20f0, 0x20e8, 0x20e8, 0x20f0, 0x20f8, 0x20e8, 0x2100, 0x2108, 0x2108, 0x2108, 0x2110, 0x2118, 0x2120, 0x2128, 0x2130, + 0x2138, 0x2140, 0x2148, 0x2150, 0x2158, 0x2160, 0x2168, 0x2170, 0x2178, 0x2180, 0x2188, 0x2190, 0x1bb0, 0x2198, 0x21a0, 0x21a8, + 0x21b0, 0x21b8, 0x21c0, 0x21c8, 0x21d0, 0x21d8, 0x21e0, 0x21e0, 0x21e8, 0x21f0, 0x21f8, 0x1fe8, 0x2200, 0x2208, 0x1fe8, 0x2210, + 0x2218, 0x2220, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, + 0x2228, 0x1fe8, 0x2230, 0x2238, 0x2240, 0x2240, 0x2240, 0x2248, 0x1fe8, 0x2250, 0x2218, 0x2258, 0x1fe8, 0x2260, 0x2268, 0x2270, + 0x1fe8, 0x1fe8, 0x2278, 0x1bb0, 0x2270, 0x1bb0, 0x21d8, 0x21d8, 0x2280, 0x2288, 0x2240, 0x2240, 0x2240, 0x2240, 0x2290, 0x21d8, + 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x2298, 0x22a0, 0x1fe8, 0x1fe8, 0x22a8, + 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x22b0, 0x1fe8, 0x1fe8, 0x1fe8, 0x22b8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, + 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x22c0, 0x22c8, 0x21d8, 0x22d0, 0x1fe8, 0x1fe8, 0x22d8, 0x2218, 0x22e0, 0x2218, + 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, + 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x22e8, 0x22f0, 0x2218, 0x2218, 0x2218, 0x22f8, 0x2218, 0x2300, + 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, 0x2218, + 0x1fe8, 0x1fe8, 0x1fe8, 0x2218, 0x2308, 0x1fe8, 0x1fe8, 0x2310, 0x1fe8, 0x2318, 0x1fe8, 0x2320, 0x2328, 0x2330, 0x2338, 0x1bb0, + 0x19b0, 0x19b0, 0x2340, 0x19c0, 0x19c0, 0x2348, 0x2350, 0x2358, 0x19d0, 0x19d0, 0x19d0, 0x19d0, 0x19d0, 0x19d0, 0x2360, 0x2368, + 0x19c0, 0x19c0, 0x2370, 0x1bd0, 0x1bd0, 0x1bd0, 0x2378, 0x2380, 0x1bd0, 0x2388, 0x2390, 0x2390, 0x2390, 0x2390, 0x1a68, 0x1a68, + 0x2398, 0x23a0, 0x23a8, 0x23b0, 0x23b8, 0x1bb0, 0x1bb0, 0x1bb0, 0x1fe8, 0x23c0, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x23c8, + 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x23d0, 0x1bb0, 0x23d8, + 0x23e0, 0x23e8, 0x23f0, 0x23f8, 0x1da8, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x2400, 0x1ef8, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x2408, + 0x2410, 0x1bd0, 0x1fc0, 0x1da8, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1fa0, 0x2418, 0x1bd0, 0x2420, 0x1fe8, 0x1fe8, 0x23c8, 0x1bd0, + 0x2240, 0x2428, 0x2430, 0x2240, 0x2438, 0x2440, 0x2240, 0x2448, 0x2430, 0x2240, 0x2240, 0x2450, 0x2458, 0x2240, 0x2240, 0x2460, + 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2468, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2470, 0x2240, 0x2478, + 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1f98, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, + 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1f98, 0x1bb0, 0x1bb0, + 0x1bd0, 0x2480, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, + 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1e20, 0x1fe8, 0x1fe8, 0x1fe8, 0x2278, 0x1bd0, 0x1bd0, 0x2088, + 0x2488, 0x1bd0, 0x2490, 0x1bb0, 0x19d0, 0x19d0, 0x2498, 0x24a0, 0x19d0, 0x24a8, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x24b0, 0x24b8, + 0x1a60, 0x24c0, 0x24c8, 0x24d0, 0x19d0, 0x19d0, 0x19d0, 0x24d8, 0x24e0, 0x24e8, 0x24f0, 0x24f8, 0x1bb0, 0x1bb0, 0x1bb0, 0x2500, + 0x2508, 0x1bd0, 0x2510, 0x2518, 0x1bd0, 0x1bd0, 0x1bd0, 0x2520, 0x2528, 0x1bd0, 0x1bd0, 0x2530, 0x2538, 0x1f68, 0x1a68, 0x2540, + 0x2058, 0x1bd0, 0x2548, 0x1bd0, 0x2550, 0x2558, 0x1bd0, 0x1e20, 0x1bc8, 0x1bd0, 0x1bd0, 0x2560, 0x2568, 0x2570, 0x2578, 0x2580, + 0x1bd0, 0x1bd0, 0x2588, 0x2590, 0x2598, 0x25a0, 0x1bd0, 0x25a8, 0x1bd0, 0x1bd0, 0x1bd0, 0x25b0, 0x25b8, 0x25c0, 0x25c8, 0x25d0, + 0x25d8, 0x25e0, 0x2390, 0x19c0, 0x19c0, 0x25e8, 0x25f0, 0x19c0, 0x19c0, 0x19c0, 0x19c0, 0x19c0, 0x1bd0, 0x1bd0, 0x25f8, 0x1f68, + 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x2600, 0x1bd0, 0x2608, 0x1bd0, 0x1bd0, 0x1fd0, + 0x2610, 0x2610, 0x2610, 0x2610, 0x2610, 0x2610, 0x2610, 0x2610, 0x2610, 0x2610, 0x2610, 0x2610, 0x2610, 0x2610, 0x2610, 0x2610, + 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, + 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1fc0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1fd8, 0x1bb0, 0x1bb0, + 0x2620, 0x2628, 0x2630, 0x2638, 0x2640, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x2648, 0x2650, 0x2658, 0x1b18, 0x1b18, + 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x1b18, + 0x1b18, 0x1b18, 0x1b18, 0x2660, 0x1bb0, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x2668, 0x1b18, 0x1b18, 0x2670, 0x1bb0, 0x1bb0, 0x2678, + 0x1a68, 0x2680, 0x1a68, 0x2688, 0x2690, 0x2698, 0x26a0, 0x26a8, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x1b18, 0x26b0, + 0x26b8, 0x1968, 0x1970, 0x1978, 0x1980, 0x26c0, 0x26c8, 0x26d0, 0x1bd0, 0x26d8, 0x1bd0, 0x1fa0, 0x26e0, 0x26e8, 0x26f0, 0x26f8, + 0x2700, 0x1bd0, 0x1ec8, 0x2708, 0x1fc0, 0x1fc0, 0x1bb0, 0x1bb0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x2420, + 0x2710, 0x2718, 0x2718, 0x2720, 0x2728, 0x2728, 0x2728, 0x2730, 0x2738, 0x23d8, 0x2740, 0x1bb0, 0x1bb0, 0x2240, 0x2240, 0x2748, + 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bd0, 0x1e20, 0x1bd0, 0x1bd0, 0x1bd0, 0x1c90, 0x2750, 0x2758, + 0x1bd0, 0x1bd0, 0x2760, 0x1bd0, 0x2768, 0x1bd0, 0x1bd0, 0x2770, 0x1bd0, 0x2778, 0x1bd0, 0x1bd0, 0x2780, 0x2788, 0x1bb0, 0x1bb0, + 0x19b0, 0x19b0, 0x2790, 0x19c0, 0x19c0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1fc0, 0x1f68, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x1bd0, 0x1bd0, 0x1f88, 0x1bd0, 0x1bd0, 0x1bd0, 0x2798, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x1bd0, 0x1bd0, 0x1bd0, 0x2388, 0x1bd0, 0x1f98, 0x1f88, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x27a0, 0x1af0, 0x1af0, 0x27a8, 0x1af0, 0x27b0, 0x1af0, 0x27b8, 0x1af0, 0x27c0, 0x27c8, 0x1bb0, 0x1bb0, 0x1bb0, 0x1af0, 0x27d0, + 0x1af0, 0x27d8, 0x1af0, 0x27e0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1af0, 0x1af0, 0x1af0, 0x27e8, 0x27f0, 0x27f8, 0x27f0, 0x27f0, + 0x2800, 0x2808, 0x1af0, 0x2810, 0x2818, 0x2820, 0x1af0, 0x2828, 0x1af0, 0x2830, 0x1bb0, 0x1bb0, 0x2838, 0x1af0, 0x2840, 0x2848, + 0x1af0, 0x1af0, 0x1af0, 0x2850, 0x1af0, 0x2858, 0x1af0, 0x2860, 0x1af0, 0x2868, 0x2870, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x1af0, 0x1af0, 0x1af0, 0x1af0, 0x2878, 0x1bb0, 0x1bb0, 0x1bb0, 0x2880, 0x2880, 0x2880, 0x2888, 0x2890, 0x2890, 0x2890, 0x2898, + 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x28a0, 0x28a8, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x28b0, 0x1bd0, 0x1bd0, 0x28b8, 0x28c0, 0x28c8, 0x28d0, 0x28d8, 0x2048, 0x1bd0, 0x1bd0, 0x28e0, 0x28e8, 0x1bd0, 0x28f0, 0x1f68, + 0x28f8, 0x1bd0, 0x2900, 0x2908, 0x2910, 0x1bd0, 0x1bd0, 0x2918, 0x2048, 0x1bd0, 0x1bd0, 0x2920, 0x2928, 0x2930, 0x2938, 0x2940, + 0x1bd0, 0x1c78, 0x2948, 0x2950, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x2958, 0x2960, 0x2968, 0x1bd0, 0x1bd0, 0x2970, 0x2978, 0x1f68, + 0x2980, 0x1c08, 0x1c10, 0x1c80, 0x2988, 0x2990, 0x2998, 0x29a0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bd0, 0x1bd0, 0x1bd0, 0x29a8, 0x29b0, 0x1f68, 0x1bb0, 0x1bb0, + 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bd0, 0x1bd0, 0x29b8, 0x29c0, 0x29c8, 0x29d0, 0x1bb0, 0x1bb0, + 0x1bd0, 0x1bd0, 0x1bd0, 0x29d8, 0x29e0, 0x1f68, 0x1bb0, 0x1bb0, 0x1bd0, 0x1bd0, 0x29e8, 0x29f0, 0x1f68, 0x1bb0, 0x1bb0, 0x1bb0, + 0x1bd0, 0x29f8, 0x2a00, 0x2a08, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x19b0, 0x19b0, 0x19c0, 0x19c0, 0x1e08, 0x2a10, + 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bd0, 0x1bd0, 0x1bd0, 0x28f0, + 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1fd8, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x21e0, 0x21e0, 0x21e0, 0x21e0, 0x21e0, 0x21e0, 0x2a18, 0x2a20, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, + 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x2600, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x1bd0, 0x1bd0, 0x1fa0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x2388, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x1bd0, 0x1bd0, 0x1bd0, 0x28f0, 0x1bd0, 0x1fa0, 0x2570, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bd0, 0x1fc0, 0x2a28, + 0x1bd0, 0x1bd0, 0x1bd0, 0x2a30, 0x2a38, 0x2a40, 0x2a48, 0x2a50, 0x1bd0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1fc8, 0x2a58, 0x2a60, 0x2a68, 0x28d8, 0x2a70, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x2a78, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x2420, 0x1e20, 0x28f0, 0x2a80, 0x2a88, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2240, 0x2a90, + 0x2240, 0x2240, 0x2a98, 0x2240, 0x2240, 0x2240, 0x2aa0, 0x2aa8, 0x2ab0, 0x2240, 0x2ab8, 0x2240, 0x2240, 0x2240, 0x2ac0, 0x1bb0, + 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x2ac8, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x2278, 0x2718, 0x2ad0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x19b0, 0x2ad8, 0x19c0, 0x2ae0, 0x2ae8, 0x2af0, 0x20e8, 0x19b0, 0x2af8, 0x2b00, 0x2b08, 0x2b10, 0x2b18, 0x19b0, 0x2ad8, 0x19c0, + 0x2b20, 0x2b28, 0x19c0, 0x2b30, 0x2b38, 0x2b40, 0x2b48, 0x19b0, 0x2b50, 0x19c0, 0x19b0, 0x2ad8, 0x19c0, 0x2ae0, 0x2ae8, 0x19c0, + 0x20e8, 0x19b0, 0x2af8, 0x2b48, 0x19b0, 0x2b50, 0x19c0, 0x19b0, 0x2ad8, 0x19c0, 0x2b58, 0x19b0, 0x2b60, 0x2b68, 0x2b70, 0x2b78, + 0x19c0, 0x2b80, 0x19b0, 0x2b88, 0x2b90, 0x2b98, 0x2ba0, 0x19c0, 0x2ba8, 0x19b0, 0x2bb0, 0x19c0, 0x2bb8, 0x2bc0, 0x2bc0, 0x2bc0, + 0x1a68, 0x1a68, 0x1a68, 0x2bc8, 0x1a68, 0x1a68, 0x2bd0, 0x2bd8, 0x2be0, 0x2be8, 0x1ad8, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x1af0, 0x1af0, 0x1af0, 0x1af0, 0x1af0, 0x1af0, 0x1af0, 0x1af0, 0x1af0, 0x1af0, 0x1af0, 0x1af0, 0x2bf0, 0x2bf8, 0x1bb0, 0x1bb0, + 0x2c00, 0x1b18, 0x2c08, 0x2c10, 0x2c18, 0x2c20, 0x2c28, 0x2c30, 0x2c38, 0x2c40, 0x2c48, 0x2c40, 0x1bb0, 0x1bb0, 0x1bb0, 0x2c50, + 0x1fe8, 0x1fe8, 0x23d8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x23c8, 0x2c58, 0x2c60, 0x2c60, 0x2c60, 0x1fe8, 0x23d0, + 0x2c68, 0x2240, 0x2460, 0x2240, 0x2240, 0x2240, 0x2c70, 0x2240, 0x2240, 0x2c78, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x2c80, 0x2240, + 0x2c88, 0x2240, 0x2240, 0x2c78, 0x2ac0, 0x2c90, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x2c98, + 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, + 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x23c0, 0x1fe8, 0x1fe8, 0x2ca0, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, + 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x2740, 0x2ca8, 0x23c8, + 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x23c8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x1fe8, 0x2cb0, 0x1bb0, 0x1bb0, + 0x23d8, 0x1fe8, 0x1fe8, 0x1fe8, 0x2cb8, 0x1ee8, 0x1fe8, 0x1fe8, 0x2cb8, 0x1fe8, 0x2cc0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x1bb0, 0x2cc8, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x2cb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x2740, 0x1bb0, 0x1bb0, 0x1bb0, + 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x2388, 0x1bb0, 0x1bb0, + 0x1bd0, 0x1bd0, 0x1bd0, 0x1fc8, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, + 0x1bd0, 0x1fc0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, + 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x1bd0, 0x2a78, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x1bd0, 0x1fc0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x2cd0, 0x1bb0, 0x2cd8, 0x2cd8, 0x2cd8, 0x2cd8, 0x2cd8, 0x2cd8, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, 0x1bb0, + 0x1a68, 0x1a68, 0x1a68, 0x1a68, 0x1a68, 0x1a68, 0x1a68, 0x1a68, 0x1a68, 0x1a68, 0x1a68, 0x1a68, 0x1a68, 0x1a68, 0x1a68, 0x1bb0, + 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2618, 0x2ce0, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0201, 0x0203, 0x0304, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0303, 0x0203, + 0x0605, 0x0706, 0x0708, 0x0606, 0x0a09, 0x0b06, 0x0d0c, 0x0c0c, 0x0e0e, 0x0e0e, 0x0e0e, 0x0e0e, 0x0e0e, 0x060c, 0x0f0f, 0x060f, + 0x1006, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x0910, 0x0a06, 0x1211, + 0x1311, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x0913, 0x0a0f, 0x010f, + 0x0101, 0x0101, 0x0301, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0614, 0x0808, 0x0808, 0x0615, 0x1511, 0x1716, 0x180f, 0x1115, 0x1a19, 0x1b1b, 0x1311, 0x0606, 0x1b11, 0x1c16, 0x1d1d, 0x061d, + 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x0f10, 0x1010, 0x1010, 0x1010, 0x1310, + 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x0f13, 0x1313, 0x1313, 0x1313, 0x1313, + 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1013, 0x1013, 0x1013, 0x1013, + 0x1013, 0x1013, 0x1013, 0x1013, 0x1313, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1010, 0x1013, 0x1013, 0x1313, + 0x1013, 0x1310, 0x1310, 0x1010, 0x1013, 0x1010, 0x1313, 0x1010, 0x1010, 0x1013, 0x1310, 0x1010, 0x1310, 0x1313, 0x1010, 0x1013, + 0x1310, 0x1310, 0x1310, 0x1010, 0x1013, 0x1313, 0x1310, 0x1010, 0x1013, 0x1010, 0x1013, 0x1013, 0x1310, 0x1613, 0x1310, 0x1313, + 0x1616, 0x1616, 0x1e10, 0x1013, 0x131e, 0x1e10, 0x1013, 0x1013, 0x1013, 0x1013, 0x1013, 0x1013, 0x1013, 0x1013, 0x1313, 0x1310, + 0x1013, 0x131e, 0x1310, 0x1010, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1313, 0x1313, 0x1313, 0x1010, 0x1013, 0x1310, + 0x1013, 0x1013, 0x1010, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1313, 0x1313, 0x1316, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, + 0x1f1f, 0x1f1f, 0x1f1f, 0x1f1f, 0x201f, 0x1f20, 0x1f1f, 0x1f1f, 0x1f1f, 0x1111, 0x1111, 0x2020, 0x2020, 0x2020, 0x2020, 0x2020, + 0x1f1f, 0x1111, 0x1111, 0x1111, 0x1111, 0x1111, 0x1111, 0x1111, 0x1f1f, 0x1f1f, 0x111f, 0x1111, 0x1111, 0x1111, 0x1120, 0x111f, + 0x1111, 0x1111, 0x1111, 0x1111, 0x1111, 0x1111, 0x1111, 0x1111, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, + 0x1310, 0x1310, 0x1120, 0x1310, 0x0000, 0x131f, 0x1313, 0x1006, 0x0000, 0x0000, 0x1111, 0x0610, 0x1010, 0x0010, 0x0010, 0x1010, + 0x1013, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1000, 0x1010, 0x1010, 0x1010, 0x1010, 0x1313, 0x1313, + 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1013, 0x1313, 0x1010, 0x1310, 0x1313, 0x1310, 0x1310, 0x1310, 0x1310, + 0x1313, 0x1313, 0x1310, 0x100f, 0x1013, 0x1310, 0x1013, 0x1010, 0x1310, 0x2122, 0x2121, 0x2121, 0x2323, 0x1310, 0x1310, 0x1310, + 0x1010, 0x1013, 0x1013, 0x1013, 0x1013, 0x1013, 0x1013, 0x1313, 0x1000, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, + 0x1010, 0x1010, 0x1010, 0x0010, 0x1f00, 0x2424, 0x2424, 0x2424, 0x1300, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, + 0x1313, 0x1313, 0x1313, 0x1313, 0x2400, 0x0025, 0x1500, 0x0815, 0x2100, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, + 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2126, 0x2127, 0x2721, 0x2121, 0x2127, 0x0000, 0x0000, 0x0000, 0x0000, + 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x0028, 0x0000, 0x0000, + 0x2828, 0x2728, 0x0027, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2929, 0x2929, 0x2929, 0x0f0f, 0x072a, 0x2b07, 0x2c0c, 0x1515, + 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2c21, 0x002d, 0x2c2c, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, + 0x2e2f, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x212e, 0x2121, 0x2121, 0x3030, 0x3030, 0x3030, 0x3030, 0x3030, 0x3107, 0x2c31, 0x2e2e, + 0x2e21, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2c, 0x2121, 0x2121, 0x2121, 0x2921, 0x2115, + 0x2121, 0x2121, 0x2f21, 0x212f, 0x1521, 0x2121, 0x2121, 0x2e2e, 0x0e0e, 0x0e0e, 0x0e0e, 0x0e0e, 0x0e0e, 0x2e2e, 0x322e, 0x2e32, + 0x2c2c, 0x2c2c, 0x2c2c, 0x2c2c, 0x2c2c, 0x2c2c, 0x2c2c, 0x2d00, 0x212e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, + 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x0021, 0x2e00, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, + 0x2e21, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3333, 0x3333, 0x3333, 0x3333, 0x3333, 0x2828, 0x2828, 0x2828, + 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2128, 0x2121, 0x2121, 0x2121, 0x2121, 0x3434, 0x0615, 0x0606, 0x0034, 0x0000, 0x0000, + 0x2828, 0x2828, 0x2828, 0x2121, 0x2121, 0x2134, 0x2121, 0x2121, 0x2121, 0x2121, 0x2134, 0x2121, 0x2134, 0x2121, 0x2121, 0x0000, + 0x2727, 0x2727, 0x2727, 0x2727, 0x2727, 0x2727, 0x2727, 0x0027, 0x2828, 0x2828, 0x2828, 0x2828, 0x2128, 0x2121, 0x0000, 0x0027, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2e2e, 0x2e2e, 0x002e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2100, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x3521, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, + 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x3521, 0x1621, 0x3535, + 0x2135, 0x2121, 0x2121, 0x2121, 0x3521, 0x3535, 0x2135, 0x3535, 0x2116, 0x2121, 0x2121, 0x2121, 0x1616, 0x1616, 0x1616, 0x1616, + 0x1616, 0x2121, 0x2424, 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, 0x1f24, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, + 0x2116, 0x3535, 0x1600, 0x1616, 0x1616, 0x1616, 0x0016, 0x1600, 0x0016, 0x1600, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, + 0x1616, 0x1616, 0x1616, 0x1616, 0x0016, 0x1616, 0x1616, 0x1616, 0x0016, 0x0016, 0x0000, 0x1616, 0x1616, 0x0000, 0x1621, 0x3535, + 0x2135, 0x2121, 0x0021, 0x3500, 0x0035, 0x3500, 0x2135, 0x0016, 0x0000, 0x0000, 0x0000, 0x3500, 0x0000, 0x0000, 0x1616, 0x1600, + 0x1616, 0x2121, 0x0000, 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, 0x1616, 0x0808, 0x3737, 0x3737, 0x3737, 0x0822, 0x0000, 0x0000, + 0x2100, 0x3521, 0x1600, 0x1616, 0x1616, 0x0016, 0x0000, 0x1600, 0x0016, 0x1616, 0x1600, 0x0016, 0x1616, 0x0000, 0x0021, 0x3535, + 0x2135, 0x0021, 0x0000, 0x2100, 0x0021, 0x2100, 0x2121, 0x0000, 0x2100, 0x0000, 0x0000, 0x0000, 0x1600, 0x1616, 0x0016, 0x0016, + 0x0000, 0x0000, 0x0000, 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, 0x2121, 0x1616, 0x2116, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x2100, 0x3521, 0x1600, 0x1616, 0x1616, 0x1616, 0x1616, 0x1600, 0x1616, 0x1600, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, + 0x0016, 0x1616, 0x1600, 0x1616, 0x1616, 0x0000, 0x1621, 0x3535, 0x2135, 0x2121, 0x2121, 0x2100, 0x3521, 0x3500, 0x2135, 0x0000, + 0x0016, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0824, 0x0000, 0x0000, 0x0000, 0x1600, 0x0000, 0x0000, 0x0000, + 0x2100, 0x3535, 0x1600, 0x1616, 0x1616, 0x1616, 0x0016, 0x1600, 0x0016, 0x1616, 0x1600, 0x1616, 0x1616, 0x0000, 0x1621, 0x2135, + 0x2135, 0x2121, 0x0021, 0x3500, 0x0035, 0x3500, 0x2135, 0x0000, 0x0000, 0x0000, 0x0000, 0x3521, 0x0000, 0x0000, 0x1616, 0x1600, + 0x1622, 0x3737, 0x3737, 0x3737, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1621, 0x1600, 0x1616, 0x1616, 0x0016, 0x0000, 0x1616, + 0x0016, 0x1616, 0x1616, 0x0000, 0x1600, 0x0016, 0x0016, 0x1616, 0x0000, 0x1600, 0x0016, 0x0000, 0x1616, 0x0016, 0x0000, 0x1616, + 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x0000, 0x0000, 0x3535, 0x3521, 0x0035, 0x0000, 0x3535, 0x0035, 0x3535, 0x2135, 0x0000, + 0x0016, 0x0000, 0x0000, 0x3500, 0x0000, 0x0000, 0x0000, 0x0000, 0x3737, 0x1537, 0x1515, 0x1515, 0x0815, 0x0015, 0x0000, 0x0000, + 0x3521, 0x3535, 0x1600, 0x1616, 0x1616, 0x1616, 0x0016, 0x1616, 0x0016, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, + 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x0000, 0x1600, 0x2121, 0x3521, 0x3535, 0x0035, 0x2121, 0x0021, 0x2121, 0x2121, 0x0000, + 0x0000, 0x0000, 0x2100, 0x0021, 0x1616, 0x0016, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1d1d, 0x1d1d, 0x1d1d, 0x221d, + 0x2100, 0x3535, 0x1600, 0x1616, 0x1616, 0x1616, 0x0016, 0x1616, 0x1616, 0x1616, 0x1600, 0x1616, 0x1616, 0x0000, 0x1621, 0x3835, + 0x3535, 0x3535, 0x0035, 0x3538, 0x0035, 0x3535, 0x2121, 0x0000, 0x0000, 0x0000, 0x3500, 0x0035, 0x0000, 0x0000, 0x0000, 0x0016, + 0x1600, 0x0016, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x0016, 0x1600, 0x3535, + 0x2135, 0x2121, 0x0021, 0x3535, 0x0035, 0x3535, 0x2135, 0x0016, 0x0000, 0x0000, 0x0000, 0x3500, 0x0000, 0x0000, 0x0000, 0x1600, + 0x3737, 0x3737, 0x3737, 0x0000, 0x2200, 0x1616, 0x1616, 0x1616, 0x0000, 0x3535, 0x1600, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, + 0x1616, 0x1616, 0x1616, 0x0016, 0x0000, 0x1616, 0x1616, 0x1616, 0x1616, 0x1600, 0x1616, 0x1616, 0x1616, 0x1616, 0x1600, 0x0000, + 0x1616, 0x1616, 0x1616, 0x0016, 0x0000, 0x0021, 0x0000, 0x3500, 0x3535, 0x2121, 0x0021, 0x0021, 0x3535, 0x3535, 0x3535, 0x3535, + 0x0000, 0x3535, 0x0024, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1600, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, + 0x2116, 0x1616, 0x2121, 0x2121, 0x2121, 0x0021, 0x0000, 0x0800, 0x1616, 0x1616, 0x1616, 0x211f, 0x2121, 0x2121, 0x2121, 0x2421, + 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, 0x2424, 0x0000, 0x0000, 0x1600, 0x0016, 0x0016, 0x1600, 0x0016, 0x0016, 0x1600, 0x0000, + 0x0000, 0x0000, 0x1616, 0x1616, 0x1600, 0x1616, 0x1616, 0x1616, 0x1600, 0x1616, 0x1600, 0x1600, 0x0000, 0x1616, 0x1600, 0x1616, + 0x2116, 0x1616, 0x2121, 0x2121, 0x2121, 0x2100, 0x1621, 0x0000, 0x1616, 0x1616, 0x0016, 0x001f, 0x2121, 0x2121, 0x2121, 0x0000, + 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, 0x0000, 0x1616, 0x1616, 0x2216, 0x2222, 0x2424, 0x2424, 0x2424, 0x2424, 0x2424, 0x2424, + 0x2424, 0x2224, 0x2224, 0x2222, 0x2121, 0x2222, 0x2222, 0x2222, 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, 0x3737, 0x3737, 0x3737, + 0x3737, 0x3737, 0x2122, 0x2122, 0x2122, 0x0a09, 0x0a09, 0x3535, 0x1616, 0x1616, 0x1616, 0x1616, 0x1600, 0x1616, 0x1616, 0x1616, + 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x0016, 0x0000, 0x2100, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x3521, + 0x2121, 0x2121, 0x2421, 0x2121, 0x1616, 0x1616, 0x2116, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2100, 0x2121, 0x2121, 0x2121, + 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x0021, 0x2222, 0x2222, 0x2222, 0x2222, 0x2221, 0x2222, 0x2222, 0x0022, 0x2222, + 0x2424, 0x2424, 0x2224, 0x2222, 0x2422, 0x0024, 0x0000, 0x0000, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x3516, 0x2135, 0x2121, + 0x3521, 0x2121, 0x2121, 0x2121, 0x2135, 0x3521, 0x2135, 0x1621, 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, 0x2424, 0x2424, 0x2424, + 0x1616, 0x1616, 0x1616, 0x3535, 0x2121, 0x1616, 0x1616, 0x2121, 0x1621, 0x3535, 0x1635, 0x3516, 0x3535, 0x3535, 0x3535, 0x1616, + 0x2116, 0x2121, 0x1621, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x3521, 0x2135, 0x3521, 0x3535, 0x3535, 0x2135, 0x3516, + 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, 0x3535, 0x2135, 0x2222, 0x1010, 0x1010, 0x1010, 0x1000, 0x0000, 0x0000, 0x1000, 0x0000, + 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x2416, 0x161f, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x0016, 0x1616, 0x1616, 0x0000, + 0x1616, 0x1616, 0x1616, 0x0016, 0x0016, 0x1616, 0x1616, 0x0000, 0x0016, 0x1616, 0x1616, 0x0000, 0x1616, 0x1616, 0x1616, 0x0016, + 0x0016, 0x1616, 0x1616, 0x0000, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x0016, 0x1616, 0x1616, 0x1616, 0x1616, + 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x0016, 0x2100, 0x2121, 0x2424, 0x2424, 0x2424, 0x2424, 0x3724, 0x3737, 0x3737, 0x3737, + 0x3737, 0x3737, 0x3737, 0x3737, 0x3737, 0x3737, 0x0037, 0x0000, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x0000, 0x0000, 0x0000, + 0x1010, 0x1010, 0x1010, 0x0000, 0x1313, 0x1313, 0x1313, 0x0000, 0x1625, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, + 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x2416, 0x1624, 0x1605, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, + 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x0916, 0x000a, 0x0000, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x2416, 0x2424, 0x3939, + 0x1639, 0x1616, 0x1616, 0x1616, 0x0016, 0x0000, 0x0000, 0x0000, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x0016, 0x1616, + 0x1616, 0x2121, 0x0021, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1616, 0x2121, 0x2421, 0x0024, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1616, 0x2121, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0016, 0x2121, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1616, 0x1616, 0x2121, 0x2135, 0x2121, 0x2121, 0x2121, 0x3535, 0x3535, 0x3535, 0x3535, 0x3521, 0x2135, 0x2121, 0x2121, 0x2121, + 0x2121, 0x2121, 0x2424, 0x1f24, 0x2424, 0x0824, 0x2116, 0x0000, 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, 0x0000, 0x0000, 0x0000, + 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, 0x0000, 0x0000, 0x0000, 0x0606, 0x0606, 0x0606, 0x0625, 0x0606, 0x2106, 0x2121, 0x0018, + 0x1616, 0x1f16, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1616, 0x1616, 0x1616, 0x1616, 0x2116, 0x0016, 0x0000, 0x0000, 0x1616, 0x1616, 0x1616, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x0016, 0x2121, 0x3521, 0x3535, 0x2135, 0x3521, 0x3535, 0x0000, 0x0000, + 0x3535, 0x3521, 0x3535, 0x3535, 0x2135, 0x2121, 0x0000, 0x0000, 0x0015, 0x0000, 0x0606, 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, + 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x0000, 0x1616, 0x1616, 0x0016, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x0000, 0x0000, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x0000, 0x0000, 0x0000, + 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, 0x0037, 0x0000, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, + 0x1616, 0x1616, 0x1616, 0x2116, 0x3521, 0x2135, 0x0000, 0x2424, 0x1616, 0x1616, 0x3516, 0x3521, 0x2121, 0x2121, 0x2121, 0x0021, + 0x3521, 0x3521, 0x2135, 0x2121, 0x2121, 0x2121, 0x3521, 0x3535, 0x3535, 0x2135, 0x2121, 0x2121, 0x2121, 0x2121, 0x0021, 0x2100, + 0x2424, 0x2424, 0x2424, 0x1f24, 0x2424, 0x2424, 0x2424, 0x0000, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x0023, + 0x2121, 0x2121, 0x1635, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x3521, 0x2121, 0x2121, 0x3521, 0x3521, 0x3535, + 0x3535, 0x3521, 0x1635, 0x1616, 0x1616, 0x1616, 0x0000, 0x0000, 0x2224, 0x2222, 0x2222, 0x2222, 0x2222, 0x2122, 0x2121, 0x2121, + 0x2121, 0x2121, 0x2222, 0x2222, 0x2222, 0x2222, 0x0022, 0x0000, 0x2121, 0x1635, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, + 0x3516, 0x2121, 0x2121, 0x3535, 0x2121, 0x2135, 0x2121, 0x1616, 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, 0x1616, 0x1616, 0x1616, + 0x1616, 0x1616, 0x1616, 0x3521, 0x2121, 0x3535, 0x2135, 0x2135, 0x2121, 0x3535, 0x0000, 0x0000, 0x0000, 0x0000, 0x2424, 0x2424, + 0x1616, 0x1616, 0x3535, 0x3535, 0x3535, 0x3535, 0x2121, 0x2121, 0x2121, 0x2121, 0x3535, 0x2121, 0x0000, 0x2400, 0x2424, 0x2424, + 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, 0x0000, 0x1600, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1f1f, 0x1f1f, 0x1f1f, 0x2424, + 0x2424, 0x2424, 0x2424, 0x2424, 0x0000, 0x0000, 0x0000, 0x0000, 0x2121, 0x2421, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, + 0x3521, 0x2121, 0x2121, 0x2121, 0x1621, 0x1616, 0x2116, 0x1616, 0x1616, 0x3535, 0x1621, 0x0016, 0x2121, 0x0000, 0x0000, 0x0000, + 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1f1f, 0x1f1f, 0x1f1f, 0x1f1f, 0x1f1f, 0x1f1f, 0x1f1f, 0x1f1f, 0x1f1f, 0x1f1f, + 0x1f1f, 0x1f1f, 0x1f1f, 0x1f1f, 0x1f1f, 0x131f, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x131f, 0x1313, 0x1313, 0x1313, + 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1f13, 0x1f1f, 0x1f1f, 0x2121, 0x2121, 0x2121, 0x0000, 0x0000, 0x0000, 0x2121, 0x2121, + 0x1310, 0x1310, 0x1310, 0x1313, 0x1313, 0x1313, 0x1313, 0x1310, 0x1313, 0x1313, 0x1313, 0x1313, 0x1010, 0x1010, 0x1010, 0x1010, + 0x1313, 0x1313, 0x1313, 0x0000, 0x1010, 0x1010, 0x1010, 0x0000, 0x1313, 0x1313, 0x1313, 0x1313, 0x1000, 0x1000, 0x1000, 0x1000, + 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x0000, 0x1313, 0x1313, 0x1313, 0x1313, 0x1e1e, 0x1e1e, 0x1e1e, 0x1e1e, + 0x1313, 0x1313, 0x0013, 0x1313, 0x1010, 0x1010, 0x111e, 0x1113, 0x1111, 0x1313, 0x0013, 0x1313, 0x1010, 0x1010, 0x111e, 0x1111, + 0x1313, 0x1313, 0x0000, 0x1313, 0x1010, 0x1010, 0x1100, 0x1111, 0x1313, 0x1313, 0x1313, 0x1313, 0x1010, 0x1010, 0x1110, 0x1111, + 0x0000, 0x1313, 0x0013, 0x1313, 0x1010, 0x1010, 0x111e, 0x0011, 0x0505, 0x0505, 0x0505, 0x0505, 0x0505, 0x1805, 0x1818, 0x3b3a, + 0x2525, 0x2525, 0x2525, 0x0606, 0x1c17, 0x1709, 0x1c17, 0x1709, 0x0606, 0x0606, 0x0606, 0x0606, 0x3d3c, 0x3f3e, 0x4140, 0x1442, + 0x0707, 0x0707, 0x0607, 0x0606, 0x1706, 0x061c, 0x0606, 0x1206, 0x0612, 0x0606, 0x0943, 0x060a, 0x0606, 0x0606, 0x0606, 0x0606, + 0x0606, 0x060f, 0x0612, 0x0606, 0x0606, 0x0606, 0x0606, 0x0506, 0x1818, 0x1818, 0x0018, 0x4544, 0x4746, 0x1818, 0x1818, 0x1818, + 0x1f1b, 0x0000, 0x1b1b, 0x1b1b, 0x1b1b, 0x0b0b, 0x090f, 0x1f0a, 0x1b1b, 0x1b1b, 0x1b1b, 0x1b1b, 0x1b1b, 0x0b0b, 0x090f, 0x000a, + 0x1f1f, 0x1f1f, 0x1f1f, 0x1f1f, 0x1f1f, 0x1f1f, 0x001f, 0x0000, 0x0808, 0x0808, 0x0808, 0x0808, 0x0808, 0x0808, 0x0808, 0x0808, + 0x0808, 0x0808, 0x0808, 0x0808, 0x0808, 0x0808, 0x0808, 0x0008, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2321, 0x2323, + 0x2123, 0x2323, 0x2123, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x0021, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1515, 0x1510, 0x1515, 0x1015, 0x1515, 0x1013, 0x1010, 0x1313, 0x1010, 0x1310, 0x1015, 0x1515, 0x100f, 0x1010, 0x1010, 0x1515, + 0x1515, 0x1515, 0x1510, 0x1510, 0x1510, 0x1010, 0x1010, 0x1319, 0x1010, 0x1010, 0x1613, 0x1616, 0x1316, 0x1515, 0x1313, 0x1010, + 0x0f0f, 0x0f0f, 0x100f, 0x1313, 0x1313, 0x0f15, 0x1515, 0x2213, 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, + 0x3939, 0x3939, 0x3939, 0x3939, 0x3939, 0x3939, 0x3939, 0x3939, 0x3939, 0x1039, 0x3913, 0x3939, 0x1d39, 0x1515, 0x0000, 0x0000, + 0x0f0f, 0x0f0f, 0x150f, 0x1515, 0x1515, 0x0f0f, 0x1515, 0x1515, 0x150f, 0x0f15, 0x1515, 0x150f, 0x1515, 0x1515, 0x1515, 0x150f, + 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x0f0f, 0x1515, 0x150f, 0x150f, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, + 0x1515, 0x1515, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, + 0x0f0f, 0x1a0b, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, 0x1515, 0x1515, 0x1515, 0x1515, 0x0a09, 0x0a09, 0x1515, 0x1515, + 0x0f0f, 0x1515, 0x1515, 0x1515, 0x0915, 0x150a, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, + 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x1522, 0x150f, 0x1515, + 0x1515, 0x1515, 0x2215, 0x1515, 0x1515, 0x0f15, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, + 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x0f0f, 0x0f0f, 0x0f0f, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, + 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x0015, 0x0000, 0x0000, 0x1515, 0x1515, 0x1515, 0x0015, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, 0x1b1b, 0x1b1b, 0x1b1b, 0x1b1b, 0x1b1b, 0x1b1b, 0x1b1b, 0x1b1b, 0x1b1b, 0x1b1b, 0x2222, 0x2222, + 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x1d1d, 0x1d1d, 0x1d1d, 0x1515, 0x1515, 0x1515, 0x0f15, 0x1515, 0x1515, 0x1515, 0x1515, + 0x0f15, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, + 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x0f15, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1522, 0x1515, + 0x1515, 0x1515, 0x1515, 0x1515, 0x0a09, 0x0a09, 0x0a09, 0x0a09, 0x0a09, 0x0a09, 0x0a09, 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, + 0x1d1d, 0x1d1d, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x0f0f, 0x0f0f, 0x090f, 0x0f0a, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, + 0x0f0f, 0x0f0f, 0x0f0f, 0x0a09, 0x0a09, 0x0a09, 0x0a09, 0x0a09, 0x0f0f, 0x090f, 0x090a, 0x090a, 0x090a, 0x090a, 0x090a, 0x090a, + 0x090a, 0x090a, 0x090a, 0x090a, 0x0f0a, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, 0x0a09, 0x0a09, 0x0f0f, 0x0f0f, + 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, 0x0f0f, 0x0a09, 0x0f0f, 0x0f0f, 0x0f0f, 0x150f, 0x0f15, 0x0f0f, 0x0f0f, 0x150f, 0x1515, + 0x1515, 0x1515, 0x0000, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x0000, 0x1515, 0x1515, 0x1515, 0x1515, + 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x0000, 0x1500, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x0015, 0x1515, 0x1515, 0x1515, + 0x1515, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1515, 0x1515, + 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x0010, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x0013, + 0x1310, 0x1010, 0x1310, 0x1013, 0x1013, 0x1013, 0x1013, 0x1010, 0x1310, 0x1310, 0x1013, 0x1313, 0x1313, 0x1313, 0x1f1f, 0x1010, + 0x1310, 0x1310, 0x1513, 0x1515, 0x1515, 0x1015, 0x1013, 0x2113, 0x2121, 0x1310, 0x0000, 0x0000, 0x0600, 0x0606, 0x1d06, 0x0606, + 0x1313, 0x1313, 0x1313, 0x1300, 0x0000, 0x0000, 0x1300, 0x0000, 0x1616, 0x1616, 0x1616, 0x1616, 0x0000, 0x0000, 0x0000, 0x1f00, + 0x0024, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2100, 0x1616, 0x1616, 0x1616, 0x0016, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1616, 0x1616, 0x1616, 0x0016, 0x1616, 0x1616, 0x1616, 0x0016, 0x0606, 0x1c17, 0x1c17, 0x0606, 0x1706, 0x061c, 0x1c17, 0x0606, + 0x0606, 0x0606, 0x0606, 0x2506, 0x0606, 0x0625, 0x1c17, 0x0606, 0x1c17, 0x0a09, 0x0a09, 0x0a09, 0x0a09, 0x0606, 0x0606, 0x2006, + 0x0606, 0x0606, 0x0606, 0x0606, 0x0606, 0x2525, 0x0606, 0x0606, 0x0625, 0x0009, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1500, 0x1515, 0x1515, 0x1515, 0x1515, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1515, 0x1515, 0x1515, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x0000, 0x0000, + 0x0605, 0x0606, 0x1f15, 0x3916, 0x0a09, 0x0a09, 0x0a09, 0x0a09, 0x0a09, 0x1515, 0x0a09, 0x0a09, 0x0a09, 0x0a09, 0x0925, 0x0a0a, + 0x3915, 0x3939, 0x3939, 0x3939, 0x3939, 0x2121, 0x2121, 0x3535, 0x1f25, 0x1f1f, 0x1f1f, 0x1515, 0x3939, 0x1f39, 0x0616, 0x1515, + 0x1616, 0x1616, 0x1616, 0x0016, 0x2100, 0x1121, 0x1f11, 0x161f, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x0616, 0x1f1f, 0x161f, + 0x0000, 0x0000, 0x1600, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x2222, 0x3737, 0x3737, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, + 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x0016, 0x0000, 0x0000, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x1522, 0x0015, + 0x3737, 0x3737, 0x3737, 0x3737, 0x3737, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x3737, 0x3737, 0x3737, 0x3737, + 0x1d15, 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x1515, 0x2215, + 0x1d22, 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x1515, 0x1515, + 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x0022, 0x2222, 0x2222, 0x2222, 0x1522, 0x1515, 0x2215, 0x2222, 0x2222, + 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x1515, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x1522, + 0x1616, 0x1616, 0x1f16, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x061f, 0x0606, + 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, 0x1616, 0x0000, 0x0000, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x2116, + 0x2323, 0x0623, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2006, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1f1f, 0x2121, + 0x1616, 0x1616, 0x1616, 0x3939, 0x3939, 0x3939, 0x3939, 0x3939, 0x2121, 0x2424, 0x2424, 0x2424, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1111, 0x1111, 0x1111, 0x2011, 0x2020, 0x2020, 0x2020, 0x2020, 0x1111, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, + 0x1313, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x131f, 0x1313, 0x1313, 0x1313, 0x1013, 0x1013, 0x1013, 0x1310, + 0x1310, 0x1310, 0x1310, 0x1310, 0x4820, 0x1048, 0x1013, 0x1613, 0x1310, 0x1310, 0x1313, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, + 0x1310, 0x1310, 0x1310, 0x1310, 0x1310, 0x1010, 0x1010, 0x0000, 0x1010, 0x1010, 0x1310, 0x1310, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x1600, 0x1f1f, 0x1613, 0x1616, 0x1616, 0x1616, 0x1621, 0x1616, 0x1621, 0x1616, 0x2116, 0x1616, 0x1616, + 0x1616, 0x3516, 0x2135, 0x3521, 0x1515, 0x1515, 0x0000, 0x0000, 0x3737, 0x3737, 0x3737, 0x2222, 0x1908, 0x0000, 0x0000, 0x0000, + 0x1616, 0x1616, 0x0606, 0x0606, 0x0000, 0x0000, 0x0000, 0x0000, 0x3535, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, + 0x1616, 0x1616, 0x3535, 0x3535, 0x3535, 0x3535, 0x3535, 0x3535, 0x3535, 0x3535, 0x0021, 0x0000, 0x0000, 0x0000, 0x0000, 0x2424, + 0x2121, 0x1616, 0x1616, 0x1616, 0x2424, 0x1624, 0x1624, 0x0000, 0x1616, 0x1616, 0x1616, 0x2121, 0x2121, 0x2121, 0x2121, 0x2424, + 0x1616, 0x1616, 0x1616, 0x2116, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x3535, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2400, + 0x1616, 0x2116, 0x3535, 0x2121, 0x2121, 0x3535, 0x3521, 0x3535, 0x2435, 0x2424, 0x2424, 0x2424, 0x2424, 0x2424, 0x2424, 0x1f00, + 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, 0x0000, 0x0000, 0x2424, 0x1616, 0x1616, 0x2116, 0x161f, 0x1616, 0x1616, 0x1616, 0x1616, + 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, 0x1616, 0x1616, 0x0016, 0x1616, 0x1616, 0x1616, 0x1616, 0x2116, 0x2121, 0x2121, 0x3521, + 0x2135, 0x3521, 0x2135, 0x0021, 0x0000, 0x0000, 0x0000, 0x0000, 0x1616, 0x2116, 0x1616, 0x1616, 0x1616, 0x1616, 0x3521, 0x0000, + 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, 0x0000, 0x2424, 0x2424, 0x161f, 0x1616, 0x1616, 0x2216, 0x2222, 0x3516, 0x3521, 0x1616, + 0x1621, 0x2121, 0x1621, 0x2116, 0x1621, 0x1616, 0x1616, 0x2121, 0x2116, 0x0016, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1600, 0x1f16, 0x2424, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x3516, 0x2121, 0x3535, + 0x2424, 0x1f16, 0x351f, 0x0021, 0x0000, 0x0000, 0x0000, 0x0000, 0x1600, 0x1616, 0x1616, 0x0016, 0x1600, 0x1616, 0x1616, 0x0016, + 0x1600, 0x1616, 0x1616, 0x0016, 0x0000, 0x0000, 0x0000, 0x0000, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x4813, 0x1f1f, 0x1f1f, + 0x1313, 0x1313, 0x1313, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1616, 0x3516, 0x2135, 0x3535, 0x3521, 0x2435, 0x2135, 0x0000, + 0x1616, 0x1616, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1616, 0x1616, 0x1616, 0x0016, 0x0000, 0x1600, 0x1616, 0x1616, + 0x4949, 0x4949, 0x4949, 0x4949, 0x4949, 0x4949, 0x4949, 0x4949, 0x4a4a, 0x4a4a, 0x4a4a, 0x4a4a, 0x4a4a, 0x4a4a, 0x4a4a, 0x4a4a, + 0x1313, 0x1313, 0x1313, 0x0013, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1300, 0x1313, 0x1313, 0x0000, 0x0000, 0x2800, 0x2821, + 0x2828, 0x2828, 0x2828, 0x2828, 0x0b28, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x0028, 0x2828, 0x2828, 0x0028, 0x0028, + 0x2828, 0x2800, 0x0028, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2e2e, 0x4b4b, 0x4b4b, 0x4b4b, 0x4b4b, 0x4b4b, 0x4b4b, 0x4b4b, + 0x4b4b, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2e00, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, + 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x090a, 0x0000, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, + 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x0000, 0x0000, 0x0000, 0x0000, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x152b, 0x0000, + 0x0606, 0x0606, 0x0606, 0x0906, 0x060a, 0x0000, 0x0000, 0x0000, 0x2506, 0x1225, 0x0912, 0x090a, 0x090a, 0x090a, 0x090a, 0x090a, + 0x090a, 0x090a, 0x060a, 0x0906, 0x060a, 0x0606, 0x1206, 0x1212, 0x060c, 0x000c, 0x0c06, 0x0606, 0x0925, 0x090a, 0x090a, 0x070a, + 0x0606, 0x0d0b, 0x0f0f, 0x000f, 0x0806, 0x0607, 0x0000, 0x0000, 0x2e2e, 0x2e2e, 0x002e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, + 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x002e, 0x1800, 0x0600, 0x0706, 0x0708, 0x0606, 0x0a09, 0x0b06, 0x0d0c, 0x0c0c, + 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x0913, 0x0a0f, 0x090f, 0x060a, 0x0a09, 0x0606, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, + 0x161f, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1f1f, + 0x0000, 0x1616, 0x1616, 0x1616, 0x0000, 0x1616, 0x1616, 0x1616, 0x0000, 0x1616, 0x1616, 0x1616, 0x0000, 0x1616, 0x0016, 0x0000, + 0x0808, 0x110f, 0x0815, 0x0008, 0x0f15, 0x0f0f, 0x150f, 0x0015, 0x0000, 0x0000, 0x0000, 0x0000, 0x4c00, 0x4c4c, 0x1515, 0x0000, + 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1600, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x0016, 0x1616, 0x1600, + 0x0624, 0x0024, 0x0000, 0x3700, 0x3737, 0x3737, 0x3737, 0x3737, 0x3737, 0x3737, 0x3737, 0x3737, 0x3737, 0x3737, 0x3737, 0x3737, + 0x3737, 0x3737, 0x0000, 0x2200, 0x2222, 0x2222, 0x2222, 0x2222, 0x4d4d, 0x4d4d, 0x4d4d, 0x4d4d, 0x4d4d, 0x4d4d, 0x4d4d, 0x4d4d, + 0x4d4d, 0x4d4d, 0x1d4d, 0x1d1d, 0x151d, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1d1d, 0x0015, 0x0000, + 0x0015, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2122, 0x0000, + 0x1b21, 0x1b1b, 0x1b1b, 0x1b1b, 0x1b1b, 0x1b1b, 0x1b1b, 0x1b1b, 0x1b1b, 0x1b1b, 0x1b1b, 0x1b1b, 0x1b1b, 0x1b1b, 0x0000, 0x0000, + 0x3737, 0x3737, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3916, 0x1616, 0x1616, 0x1616, 0x1616, 0x0039, 0x0000, 0x0000, + 0x1616, 0x1616, 0x1616, 0x2121, 0x2121, 0x0021, 0x0000, 0x0000, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x2400, + 0x1616, 0x1616, 0x0000, 0x0000, 0x1616, 0x1616, 0x1616, 0x1616, 0x3924, 0x3939, 0x3939, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1010, 0x1010, 0x1010, 0x1010, 0x1313, 0x1313, 0x1313, 0x1313, 0x1616, 0x1616, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2400, + 0x2828, 0x2828, 0x2828, 0x0000, 0x0028, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2800, 0x0028, 0x0000, 0x0028, 0x2800, + 0x2828, 0x2828, 0x2828, 0x2700, 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, 0x2828, 0x2828, 0x2828, 0x4f28, 0x4e4f, 0x4e4e, 0x4e4e, 0x4e4e, + 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x0028, 0x0000, 0x0000, 0x0000, 0x4e00, 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, + 0x2828, 0x0028, 0x2828, 0x0000, 0x0000, 0x4e00, 0x4e4e, 0x4e4e, 0x2828, 0x2828, 0x2828, 0x4e4e, 0x4e4e, 0x4e4e, 0x0000, 0x0600, + 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x0000, 0x0000, 0x2700, 0x2828, 0x2828, 0x2828, 0x2828, 0x0000, 0x0000, 0x4e4e, 0x2828, + 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, 0x0000, 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, + 0x2128, 0x2121, 0x2100, 0x0021, 0x0000, 0x0000, 0x2121, 0x2121, 0x2828, 0x2828, 0x2800, 0x2828, 0x2800, 0x2828, 0x2828, 0x2828, + 0x2828, 0x2828, 0x0000, 0x0000, 0x2121, 0x0021, 0x0000, 0x2100, 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, 0x0000, 0x0000, 0x0000, 0x0000, + 0x2727, 0x2727, 0x2727, 0x2727, 0x0027, 0x0000, 0x0000, 0x0000, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x4e28, 0x274e, + 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x4e28, 0x4e4e, 0x2828, 0x2828, 0x2828, 0x2828, 0x284f, 0x2828, 0x2828, 0x2828, + 0x2828, 0x2828, 0x2128, 0x0021, 0x0000, 0x4e00, 0x4e4e, 0x4e4e, 0x2727, 0x2727, 0x2727, 0x0027, 0x0000, 0x0000, 0x0000, 0x0000, + 0x2828, 0x2828, 0x2828, 0x0000, 0x0600, 0x0606, 0x0606, 0x0606, 0x2828, 0x2828, 0x2828, 0x0000, 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, + 0x2828, 0x0028, 0x0000, 0x0000, 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, 0x2828, 0x0000, 0x0000, 0x0000, 0x2700, 0x2727, 0x0027, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x4e00, 0x4e4e, 0x4e4e, 0x4e4e, 0x2828, 0x2828, 0x2828, 0x2828, 0x0028, 0x0000, 0x0000, 0x0000, + 0x5050, 0x5050, 0x5050, 0x5050, 0x5050, 0x5050, 0x5050, 0x5050, 0x5050, 0x0050, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x5151, 0x5151, 0x5151, 0x5151, 0x5151, 0x5151, 0x5151, 0x5151, 0x5151, 0x0051, 0x0000, 0x0000, 0x0000, 0x4e4e, 0x4e4e, 0x4e4e, + 0x5252, 0x5252, 0x5252, 0x5252, 0x5252, 0x5252, 0x5252, 0x5252, 0x5252, 0x5252, 0x5252, 0x5252, 0x5252, 0x5252, 0x5252, 0x0052, + 0x2135, 0x1635, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x2121, 0x2121, 0x2121, 0x2121, + 0x2121, 0x2121, 0x2121, 0x2421, 0x2424, 0x2424, 0x2424, 0x0000, 0x0000, 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, 0x1d1d, + 0x1d1d, 0x1d1d, 0x1d1d, 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2100, + 0x3535, 0x2135, 0x2121, 0x3521, 0x2135, 0x2421, 0x3a24, 0x2424, 0x2424, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1616, 0x1616, 0x1616, 0x1616, 0x0016, 0x0000, 0x0000, 0x0000, 0x2121, 0x1621, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, + 0x1616, 0x1616, 0x1616, 0x2116, 0x2121, 0x2121, 0x2135, 0x2121, 0x2121, 0x2121, 0x0021, 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, + 0x2424, 0x2424, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1616, 0x2116, 0x2424, 0x0016, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1616, 0x3516, 0x3535, 0x2121, 0x2121, 0x2121, 0x2121, 0x3521, 0x1635, 0x1616, 0x2416, 0x2424, 0x2424, 0x2121, 0x2421, 0x0000, + 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, 0x2416, 0x2416, 0x2424, 0x3700, 0x3737, 0x3737, 0x3737, 0x3737, 0x3737, 0x3737, 0x3737, + 0x3737, 0x3737, 0x0037, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x3535, 0x2135, + 0x2121, 0x3535, 0x3521, 0x2121, 0x2424, 0x2424, 0x2424, 0x0000, 0x1616, 0x1616, 0x1616, 0x0016, 0x0016, 0x1616, 0x1616, 0x1600, + 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1600, 0x1616, 0x1616, 0x1616, 0x1616, 0x2416, 0x0000, 0x0000, 0x0000, + 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x2116, 0x3535, 0x2135, 0x2121, 0x2121, 0x2121, 0x0021, 0x0000, 0x0000, + 0x2121, 0x3535, 0x1600, 0x1616, 0x1616, 0x1616, 0x0016, 0x1600, 0x3521, 0x3535, 0x0035, 0x3500, 0x0035, 0x3500, 0x3535, 0x0000, + 0x0016, 0x0000, 0x0000, 0x3500, 0x0000, 0x0000, 0x1600, 0x1616, 0x1616, 0x3535, 0x0000, 0x2121, 0x2121, 0x2121, 0x0021, 0x0000, + 0x2121, 0x2121, 0x0021, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3535, 0x2135, 0x2121, 0x2121, 0x3521, 0x3521, 0x3535, 0x2135, + 0x3521, 0x2121, 0x1616, 0x1624, 0x0000, 0x0000, 0x0000, 0x0000, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x3516, + 0x3535, 0x2121, 0x2121, 0x0000, 0x3535, 0x3535, 0x2121, 0x2135, 0x2421, 0x2424, 0x2424, 0x2424, 0x2424, 0x2424, 0x2424, 0x2424, + 0x2424, 0x2424, 0x2424, 0x2424, 0x1616, 0x1616, 0x2121, 0x0000, 0x3535, 0x2135, 0x2121, 0x2121, 0x2121, 0x3521, 0x2135, 0x2135, + 0x2421, 0x2424, 0x0016, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x2116, 0x2135, 0x3535, + 0x2121, 0x2121, 0x2121, 0x2135, 0x0000, 0x0000, 0x0000, 0x0000, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x0000, 0x2100, 0x2121, + 0x3535, 0x2121, 0x2121, 0x2135, 0x2121, 0x2121, 0x0000, 0x0000, 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, 0x3737, 0x2424, 0x2224, + 0x3737, 0x0037, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1600, 0x3939, 0x3939, 0x3939, 0x3939, 0x3939, 0x3939, 0x3939, 0x0039, + 0x2424, 0x2424, 0x0024, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2121, 0x2121, 0x2421, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x2121, 0x2121, 0x2121, 0x2421, 0x2424, 0x2424, 0x2222, 0x2222, 0x1f1f, 0x1f1f, 0x2224, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x3636, 0x3636, 0x3636, 0x3636, 0x3636, 0x3700, 0x3737, 0x3737, 0x3737, 0x1600, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, + 0x1616, 0x1616, 0x1616, 0x1616, 0x0000, 0x0000, 0x1600, 0x1616, 0x3516, 0x3535, 0x3535, 0x3535, 0x3535, 0x3535, 0x3535, 0x3535, + 0x3535, 0x3535, 0x3535, 0x3535, 0x3535, 0x3535, 0x3535, 0x3535, 0x3535, 0x3535, 0x3535, 0x3535, 0x3535, 0x3535, 0x3535, 0x0035, + 0x2121, 0x1f21, 0x1f1f, 0x1f1f, 0x1f1f, 0x1f1f, 0x1f1f, 0x1f1f, 0x1616, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1616, 0x1616, 0x1616, 0x1616, 0x1616, 0x0000, 0x2122, 0x2421, 0x1818, 0x1818, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x2222, 0x2222, 0x2222, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2222, 0x2222, 0x2222, 0x0022, 0x2200, 0x2222, 0x2222, 0x2222, + 0x2222, 0x2222, 0x3522, 0x2135, 0x2121, 0x2222, 0x3522, 0x3535, 0x3535, 0x1835, 0x1818, 0x1818, 0x1818, 0x2118, 0x2121, 0x2121, + 0x2121, 0x2221, 0x2122, 0x2121, 0x2121, 0x2121, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2121, 0x2121, 0x2222, + 0x2222, 0x2222, 0x2222, 0x2222, 0x0022, 0x0000, 0x0000, 0x0000, 0x1515, 0x2121, 0x1521, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x3737, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1313, 0x1313, 0x1313, + 0x1313, 0x1313, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1313, + 0x1313, 0x1313, 0x0013, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1010, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, + 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x0010, 0x1010, 0x0000, 0x0010, 0x1000, 0x0010, 0x1000, 0x1010, 0x0010, 0x1010, + 0x1010, 0x1010, 0x1010, 0x1313, 0x1313, 0x1300, 0x1300, 0x1313, 0x1313, 0x1313, 0x1300, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, + 0x1313, 0x1313, 0x1010, 0x1000, 0x1010, 0x0010, 0x1000, 0x1010, 0x1010, 0x1010, 0x0010, 0x1010, 0x1010, 0x1010, 0x0010, 0x1313, + 0x1313, 0x1313, 0x1313, 0x1313, 0x1010, 0x1000, 0x1010, 0x0010, 0x1010, 0x1010, 0x0010, 0x0010, 0x0000, 0x1010, 0x1010, 0x1010, + 0x0010, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1010, 0x1010, + 0x1010, 0x1010, 0x1010, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x0000, 0x1010, 0x1010, 0x1010, 0x1010, + 0x5310, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x0f13, 0x1313, 0x1313, + 0x1313, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x5310, 0x1313, 0x1313, + 0x1313, 0x1313, 0x0f13, 0x1313, 0x1313, 0x1313, 0x1010, 0x1010, 0x1010, 0x1010, 0x5310, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, + 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x1313, 0x0f13, 0x1313, 0x1313, 0x1313, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, + 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x1010, 0x5310, 0x1313, 0x1313, 0x1313, 0x1313, 0x0f13, 0x1313, 0x1313, 0x1313, + 0x1010, 0x1010, 0x1010, 0x1010, 0x5310, 0x1313, 0x1313, 0x1313, 0x1313, 0x0f13, 0x1313, 0x1313, 0x1313, 0x1310, 0x0000, 0x0e0e, + 0x0e0e, 0x0e0e, 0x0e0e, 0x0e0e, 0x0e0e, 0x0e0e, 0x0e0e, 0x0e0e, 0x2121, 0x2121, 0x2121, 0x2221, 0x2222, 0x2122, 0x2121, 0x2121, + 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2121, 0x2221, 0x2222, 0x2222, 0x2222, 0x2122, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, + 0x2222, 0x2222, 0x2221, 0x2422, 0x2424, 0x2424, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2100, 0x2121, 0x2121, + 0x2828, 0x2828, 0x0028, 0x4e00, 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, 0x2121, 0x2121, 0x2121, 0x0021, 0x0000, 0x0000, 0x0000, 0x0000, + 0x2e2e, 0x2e2e, 0x2e00, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e00, 0x002e, 0x002e, 0x2e00, 0x2e00, 0x2e2e, 0x2e2e, 0x2e2e, + 0x2e2e, 0x002e, 0x2e2e, 0x2e2e, 0x2e00, 0x2e00, 0x0000, 0x0000, 0x0000, 0x002e, 0x0000, 0x2e00, 0x2e00, 0x2e00, 0x2e00, 0x2e2e, + 0x2e00, 0x002e, 0x002e, 0x2e00, 0x2e00, 0x2e00, 0x2e00, 0x2e00, 0x2e00, 0x002e, 0x002e, 0x2e00, 0x2e2e, 0x002e, 0x2e2e, 0x2e2e, + 0x2e2e, 0x002e, 0x2e2e, 0x2e2e, 0x2e00, 0x2e2e, 0x002e, 0x002e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e00, 0x2e2e, 0x2e2e, + 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x2e2e, 0x0000, 0x0000, 0x2e00, 0x2e2e, 0x2e00, 0x2e2e, 0x2e2e, 0x2e00, 0x2e2e, 0x2e2e, + 0x0f0f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x0015, + 0x1500, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1b1b, 0x1b1b, 0x1b1b, 0x1b1b, 0x1b1b, 0x1d1b, 0x001d, 0x0000, + 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x1515, 0x0000, 0x0000, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x0022, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x0022, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x2222, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1115, 0x1111, 0x1111, + 0x1515, 0x1515, 0x1500, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x0015, 0x0000, + 0x1515, 0x1515, 0x0015, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1515, 0x1515, 0x1515, 0x1515, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x1515, 0x0000, 0x1515, 0x1515, 0x1515, 0x1515, 0x0015, 0x0000, 0x0000, 0x0000, + 0x1800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1818, 0x1818, 0x1818, 0x1818, 0x1818, 0x1818, 0x1818, 0x1818, + 0x4a4a, 0x4a4a, 0x4a4a, 0x4a4a, 0x4a4a, 0x4a4a, 0x4a4a, 0x0000, 0xeeee, 0xeeee + }; + + private static byte[] s_pCategoriesValue = new byte[] + { + 0x1d, 0x00, 0x0e, 0x0e, 0x0e, 0x10, 0x0e, 0x0f, 0x0e, 0x11, 0x0b, 0x11, 0x18, 0x12, 0x18, 0x0a, + 0x1a, 0x0a, 0x14, 0x12, 0x15, 0x12, 0x19, 0x09, 0x18, 0x0c, 0x13, 0x09, 0x08, 0x08, 0x19, 0x12, + 0x00, 0x00, 0x1b, 0x12, 0x12, 0x12, 0x01, 0x00, 0x0b, 0x0c, 0x1c, 0x12, 0x04, 0x00, 0x16, 0x12, + 0x0f, 0x0e, 0x1c, 0x0a, 0x19, 0x0a, 0x0a, 0x08, 0x17, 0x12, 0x0a, 0x12, 0x02, 0x00, 0x03, 0x00, + 0x03, 0x12, 0x05, 0x0d, 0x1c, 0x00, 0x07, 0x0d, 0x18, 0x00, 0x13, 0x12, 0x13, 0x03, 0x18, 0x03, + 0x04, 0x03, 0x0f, 0x0b, 0x19, 0x04, 0x1a, 0x04, 0x18, 0x04, 0x0f, 0x04, 0x04, 0x04, 0x03, 0x04, + 0x08, 0x0b, 0x18, 0x0b, 0x1c, 0x04, 0x08, 0x03, 0x03, 0x03, 0x06, 0x00, 0x08, 0x00, 0x0a, 0x00, + 0x05, 0x00, 0x09, 0x00, 0x0f, 0x00, 0x0f, 0x03, 0x0c, 0x11, 0x0d, 0x0f, 0x0f, 0x01, 0x0f, 0x05, + 0x0f, 0x07, 0x0f, 0x02, 0x0f, 0x06, 0x19, 0x0c, 0x0f, 0x13, 0x0f, 0x14, 0x0f, 0x15, 0x0f, 0x16, + 0x1b, 0x00, 0x10, 0x00, 0x11, 0x00, 0x1b, 0x04, 0x0f, 0x12, 0x09, 0x12, 0x0a, 0x03, 0x1c, 0x03, + 0x00, 0x03, 0x01, 0x03, 0x0a, 0x0b, 0x19, 0x00 + }; + // 12:4:4 index table of the Unicode numeric data. + private static ushort[] s_pNumericLevel1Index = new ushort[] + { + 0x1100, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1120, 0x1130, 0x1110, 0x1140, 0x1150, 0x1160, 0x1170, 0x1180, 0x1190, 0x11a0, + 0x11b0, 0x1110, 0x1110, 0x11c0, 0x1110, 0x1110, 0x11d0, 0x11e0, 0x11f0, 0x1200, 0x1210, 0x1220, 0x1230, 0x1110, 0x1110, 0x1110, + 0x1240, 0x1250, 0x1110, 0x1110, 0x1260, 0x1110, 0x1110, 0x1270, 0x1110, 0x1110, 0x1110, 0x1110, 0x1280, 0x1110, 0x1110, 0x1110, + 0x1290, 0x12a0, 0x12b0, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x12c0, 0x1110, 0x12d0, 0x12e0, 0x12f0, 0x1300, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1310, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x11f0, + 0x1110, 0x1320, 0x1330, 0x1340, 0x1350, 0x1110, 0x1110, 0x1110, 0x1360, 0x1370, 0x1380, 0x1390, 0x13a0, 0x1110, 0x13b0, 0x1110, + 0x13c0, 0x13d0, 0x1300, 0x1110, 0x13e0, 0x1110, 0x13f0, 0x1400, 0x1410, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1420, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1430, 0x1440, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1450, 0x1110, 0x1110, 0x1110, 0x1460, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1470, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1480, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1490, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, 0x1110, + 0x14a0, 0x14a0, 0x14a0, 0x14a8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14b0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14b8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14b8, 0x14c0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14b8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14b8, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14b8, 0x14c8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14b8, 0x14d0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14b8, 0x14d8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14b8, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14b8, 0x14e0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14b8, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14e8, 0x14f0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14f8, 0x1500, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x1508, 0x1510, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, 0x1518, + 0x14a0, 0x14a8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14b8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x1520, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, 0x14a8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, 0x14a8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x1528, 0x1530, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x1538, 0x1540, 0x1540, 0x1548, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x1550, 0x1558, 0x1560, 0x1568, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x1570, 0x1578, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x1580, 0x1588, 0x1590, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x1598, + 0x15a0, 0x14a0, 0x15a8, 0x15b0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x15b8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x15c0, 0x14a0, 0x15c8, 0x15d0, 0x14a0, 0x14a0, 0x15c0, 0x14a0, 0x14a0, 0x15d8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x15e0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x15e8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, 0x14a0, 0x14a0, + 0x14a8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, 0x14a0, 0x14a8, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x15f0, 0x15f8, 0x14a0, 0x14a0, 0x14a0, 0x1600, 0x14a0, 0x1608, 0x14a0, 0x1610, + 0x1618, 0x1620, 0x1628, 0x1630, 0x1638, 0x1640, 0x1648, 0x1650, 0x1658, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x1660, 0x1668, + 0x14a0, 0x14a0, 0x1670, 0x14a0, 0x1678, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x1680, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x1688, 0x14a0, 0x1690, 0x14a0, 0x14a0, 0x1698, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x16a0, + 0x14a0, 0x16a8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x16b0, 0x16b8, 0x16c0, 0x16c8, 0x16d0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x16d8, 0x14a0, 0x14a0, 0x16e0, 0x14a0, 0x16e8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x16a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x16f0, 0x14a0, 0x16f0, 0x14a0, 0x14a0, 0x16f8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x1700, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x1708, 0x1710, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x1718, 0x1720, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, + 0x14a0, 0x14a0, 0x14a0, 0x14b8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, 0x1660, 0x1728, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x1730, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x1738, 0x1740, + 0x1748, 0x1750, 0x1758, 0x1760, 0x1768, 0x1770, 0x1778, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x1780, 0x1788, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x16b8, 0x1790, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x1798, 0x17a0, 0x17a8, 0x17b0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x1618, 0x14a0, 0x14a0, 0x14a0, + 0x17b8, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x17c0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, 0x14a0, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0a09, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0c0b, 0x0000, 0x0000, 0x0d00, 0x0000, 0x0f0e, 0x0010, 0x0000, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0a09, + 0x0000, 0x0000, 0x1211, 0x0e13, 0x1410, 0x0000, 0x0000, 0x0000, 0x0000, 0x0f0e, 0x1110, 0x1312, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1615, 0x0017, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1918, 0x1b1a, 0x1a19, 0x001b, + 0x1615, 0x0e17, 0x100f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0a09, 0x1c0f, 0x1e1d, 0x201f, + 0x2221, 0x2423, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0d00, 0x0c0b, 0x2625, 0x2827, + 0x2a29, 0x2b15, 0x2d2c, 0x2f2e, 0x3130, 0x1632, 0x0033, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3534, + 0x0036, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1918, 0x1b1a, 0x3837, 0x3a39, 0x3c3b, 0x0000, 0x0000, 0x0000, + 0x0201, 0x0403, 0x0605, 0x0807, 0x0a09, 0x000d, 0x0000, 0x0000, 0x003d, 0x0000, 0x2625, 0x2827, 0x2a29, 0x0000, 0x0000, 0x0000, + 0x0d3d, 0x0c0b, 0x2625, 0x2827, 0x2a29, 0x0000, 0x0000, 0x0000, 0x3f3e, 0x4140, 0x4342, 0x4544, 0x4746, 0x1248, 0x4a49, 0x194b, + 0x1a19, 0x371b, 0x3938, 0x3b3a, 0x153c, 0x4d4c, 0x162e, 0x174e, 0x4f17, 0x0033, 0x3900, 0x502e, 0x1851, 0x0000, 0x0000, 0x0000, + 0x0b0d, 0x250c, 0x2726, 0x2928, 0x152a, 0x4d4c, 0x5352, 0x1454, 0x3534, 0x2b36, 0x0b0d, 0x250c, 0x2726, 0x2928, 0x152a, 0x4d4c, + 0x5352, 0x1454, 0x3534, 0x2b36, 0x0b0d, 0x250c, 0x2726, 0x2928, 0x152a, 0x4d4c, 0x5352, 0x1454, 0x3534, 0x2b36, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4c3d, 0x524d, 0x5453, 0x3414, 0x3635, 0x0d2b, 0x0c0b, 0x2625, 0x2827, 0x2a29, 0x3d15, + 0x0000, 0x0000, 0x0000, 0x0b0d, 0x250c, 0x2726, 0x2928, 0x152a, 0x0b0d, 0x250c, 0x2726, 0x2928, 0x152a, 0x0b0d, 0x250c, 0x2726, + 0x2928, 0x152a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0f00, 0x0000, + 0x0000, 0x0000, 0x0000, 0x1800, 0x0000, 0x0000, 0x0000, 0x0000, 0x1900, 0x1b1a, 0x3837, 0x3a39, 0x3c3b, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x2b15, 0x002c, 0x0000, 0x0000, 0x0000, 0x1a19, 0x371b, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x1a19, 0x371b, 0x3938, 0x3b3a, 0x153c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2b15, 0x2d2c, 0x2f2e, 0x3130, + 0x5500, 0x5756, 0x5958, 0x5b5a, 0x5d5c, 0x5e2c, 0x605f, 0x6261, 0x6300, 0x6564, 0x2d66, 0x6867, 0x6a69, 0x6c6b, 0x6e6d, 0x2e6f, + 0x0000, 0x0000, 0x0000, 0x1a19, 0x371b, 0x3938, 0x3b3a, 0x183c, 0x0f0e, 0x1110, 0x1312, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1b00, 0x0000, 0x0000, 0x0000, 0x1500, 0x0000, 0x0000, 0x001a, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0018, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3900, 0x3900, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1500, 0x0000, 0x0000, 0x0000, 0x0000, 0x1900, 0x1b1a, 0x3837, 0x3a39, 0x3c3b, + 0x2b15, 0x2d2c, 0x2f2e, 0x3130, 0x1632, 0x7170, 0x4e72, 0x7473, 0x7675, 0x7717, 0x7978, 0x7a4f, 0x7c7b, 0x337d, 0x7f7e, 0x5080, + 0x8281, 0x8483, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0f0e, 0x3819, 0x4e2e, 0x504f, 0x1538, 0x162e, 0x174e, 0x384f, + 0x2e15, 0x4e16, 0x3317, 0x1550, 0x1919, 0x1a19, 0x1a1a, 0x381a, 0x1515, 0x1515, 0x2c15, 0x2e2e, 0x2e2e, 0x7116, 0x4e4e, 0x4e4e, + 0x174e, 0x384f, 0x0f2e, 0x420f, 0x0010, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0e18, 0x0000, 0x0000, + 0x1900, 0x1b1a, 0x3837, 0x3a39, 0x3c3b, 0x2b15, 0x2d2c, 0x2f2e, 0x3130, 0x1632, 0x7170, 0x4e72, 0x7473, 0x7675, 0x0000, 0x0000, + 0x3819, 0x2e15, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3200, 0x0000, 0x0000, 0x0000, 0x0000, 0x0076, 0x0000, 0x0000, + 0x1900, 0x151a, 0x162b, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1a19, 0x151b, 0x162b, 0x3317, + 0x0000, 0x0000, 0x0000, 0x0000, 0x1900, 0x1b1a, 0x3837, 0x2b15, 0x0000, 0x0000, 0x0000, 0x1900, 0x1b1a, 0x3737, 0x1538, 0x162b, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1900, 0x1538, 0x162b, 0x0000, 0x0000, 0x0000, 0x1519, 0x162b, 0x1b1a, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0f85, 0x0000, 0x1a19, 0x371b, 0x3938, 0x3b3a, 0x153c, 0x2c2b, 0x2e2d, 0x302f, + 0x0000, 0x7016, 0x7271, 0x734e, 0x7574, 0x1776, 0x7877, 0x4f79, 0x7b7a, 0x7d7c, 0x7e33, 0x807f, 0x8150, 0x8382, 0x5184, 0x8786, + 0x8988, 0x8b8a, 0x8d8c, 0x8f8e, 0x9190, 0x9392, 0x9594, 0x9796, 0x0b0d, 0x250c, 0x2b15, 0x1716, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1900, 0x002e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1900, 0x2b15, + 0x0000, 0x0000, 0x0000, 0x0000, 0x1a19, 0x371b, 0x2b15, 0x1716, 0x0000, 0x0000, 0x0000, 0x0000, 0x1900, 0x1b1a, 0x1537, 0x162b, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3819, 0x2e15, 0x1716, 0x0b0d, 0x250c, 0x2726, 0x2928, 0x152a, 0x2c2b, 0x2e2d, 0x302f, + 0x3231, 0x7016, 0x7271, 0x734e, 0x7574, 0x0f76, 0x410e, 0x0042, 0x0000, 0x0b0d, 0x250c, 0x2726, 0x2928, 0x152a, 0x2c2b, 0x2e2d, + 0x302f, 0x3231, 0x1716, 0x0201, 0x0403, 0x0605, 0x0807, 0x0a09, 0x3130, 0x1632, 0x0017, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0201, 0x0403, 0x0605, 0x0807, 0x0a09, 0x2b15, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0a09, 0x2b15, 0x2d2c, 0x2f2e, + 0x3130, 0x0032, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1b1a, 0x3837, 0x3a39, 0x3c3b, 0x371b, 0x3938, 0x3b3a, 0x373c, + 0x3938, 0x3b3a, 0x193c, 0x1b1a, 0x3837, 0x3a39, 0x3c3b, 0x1a19, 0x371b, 0x1a38, 0x1b1b, 0x3837, 0x3a39, 0x3c3b, 0x1a19, 0x1b1b, + 0x3837, 0x9998, 0x1a19, 0x1b1b, 0x3837, 0x1b1b, 0x3737, 0x3737, 0x3a39, 0x3a3a, 0x3b3b, 0x3c3c, 0x3c3c, 0x1b1a, 0x3837, 0x1939, + 0x1b1a, 0x3737, 0x3838, 0x1b1a, 0x1a19, 0x4241, 0x4148, 0x1242, 0x470e, 0x0e0e, 0x410f, 0x2d42, 0x372e, 0x3938, 0x3b3a, 0x003c, + 0x0201, 0x0403, 0x0605, 0x0807, 0x0a09, 0x1500, 0x3316, 0x9b9a, 0x9d9c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x3231, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0201, + 0x0403, 0x0605, 0x0807, 0x0a09, 0x0201, 0x0403, 0x0605, 0x0807, 0x0a09, 0x0201, 0x0403, 0x0605, 0x0807, 0x0a09, 0x0201, 0x0403, + 0x0605, 0x0807, 0x0a09, 0x0201, 0x0403, 0x0605, 0x0807, 0x0a09, 0x3d3d, 0x0b0d, 0x250c, 0x2726, 0x2928, 0x182a, 0x0018, 0x0000, + 0x003c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xeeee, 0xeeee, 0xeeee, 0xeeee + }; + // Every item contains the value for numeric value. + private static byte[] s_pNumericValues = new byte[] + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x8f, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x51, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x56, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0xc3, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x92, 0x24, 0x49, 0x92, 0x24, 0x49, 0xc2, 0x3f, 0x1c, 0xc7, 0x71, 0x1c, 0xc7, 0x71, 0xbc, 0x3f, + 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99, 0xb9, 0x3f, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xd5, 0x3f, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xe5, 0x3f, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99, 0xc9, 0x3f, + 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99, 0xd9, 0x3f, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0xe3, 0x3f, + 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99, 0xe9, 0x3f, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xc5, 0x3f, + 0xab, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xea, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x7f, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0xb3, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0xe8, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0xf8, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x41, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x43, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x44, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x45, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x46, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x47, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x48, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x72, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x82, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x8c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x9f, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xa7, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xaf, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xb7, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0xbb, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xbf, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0xc1, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0xd3, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xdd, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0xe3, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xed, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xf1, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0xf3, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf5, 0x40, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xed, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x08, 0x41, 0x00, 0x00, 0x00, 0x00, 0x80, 0x4f, 0x12, 0x41, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x18, 0x41, 0x00, 0x00, 0x00, 0x00, 0x80, 0x84, 0x1e, 0x41, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x4f, 0x22, 0x41, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x5c, 0x25, 0x41, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x28, 0x41, 0x00, 0x00, 0x00, 0x00, 0x40, 0x77, 0x2b, 0x41, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xb5, 0x3f, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xc5, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x3f, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xd5, 0x3f, + 0xab, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xda, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x3f, + 0xab, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xe2, 0x3f, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xe5, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x3f, 0xab, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xea, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x0a, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x1a, 0x41, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x84, 0x2e, 0x41, 0x00, 0x00, 0x00, 0x00, 0x84, 0xd7, 0x97, 0x41, + 0x00, 0x00, 0x00, 0x20, 0x5f, 0xa0, 0x02, 0x42, 0x00, 0x00, 0x00, 0xa2, 0x94, 0x1a, 0x6d, 0x42 + }; + + private static ushort[] s_pDigitValues = new ushort [] + { + 0xffff, 0x0000, 0x0101, 0x0202, 0x0303, 0x0404, 0x0505, 0x0606, + 0x0707, 0x0808, 0x0909, 0xff02, 0xff03, 0xff01, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xff04, 0xff05, 0xff06, + 0xff07, 0xff08, 0xff09, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xff00, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0x0000 + }; + + } +} diff --git a/src/mscorlib/src/System/Globalization/ChineseLunisolarCalendar.cs b/src/mscorlib/src/System/Globalization/ChineseLunisolarCalendar.cs deleted file mode 100644 index 374ed0dcfa..0000000000 --- a/src/mscorlib/src/System/Globalization/ChineseLunisolarCalendar.cs +++ /dev/null @@ -1,397 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Globalization { - using System; - using System.Diagnostics.Contracts; - - //////////////////////////////////////////////////////////////////////////// - // - // Notes about ChineseLunisolarCalendar - // - //////////////////////////////////////////////////////////////////////////// - /* - ** Calendar support range: - ** Calendar Minimum Maximum - ** ========== ========== ========== - ** Gregorian 1901/02/19 2101/01/28 - ** ChineseLunisolar 1901/01/01 2100/12/29 - */ - - [Serializable] - public class ChineseLunisolarCalendar : EastAsianLunisolarCalendar { - - - // - // The era value for the current era. - // - - public const int ChineseEra = 1; - //internal static Calendar m_defaultInstance; - - internal const int MIN_LUNISOLAR_YEAR = 1901; - internal const int MAX_LUNISOLAR_YEAR = 2100; - - internal const int MIN_GREGORIAN_YEAR = 1901; - internal const int MIN_GREGORIAN_MONTH = 2; - internal const int MIN_GREGORIAN_DAY = 19; - - internal const int MAX_GREGORIAN_YEAR = 2101; - internal const int MAX_GREGORIAN_MONTH = 1; - internal const int MAX_GREGORIAN_DAY = 28; - - internal static DateTime minDate = new DateTime(MIN_GREGORIAN_YEAR, MIN_GREGORIAN_MONTH, MIN_GREGORIAN_DAY); - internal static DateTime maxDate = new DateTime((new DateTime(MAX_GREGORIAN_YEAR, MAX_GREGORIAN_MONTH, MAX_GREGORIAN_DAY, 23, 59, 59, 999)).Ticks + 9999); - - public override DateTime MinSupportedDateTime { - get - { - return (minDate); - } - } - - - public override DateTime MaxSupportedDateTime { - get - { - return (maxDate); - } - } - - protected override int DaysInYearBeforeMinSupportedYear - { - get - { - // 1900: 1-29 2-30 3-29 4-29 5-30 6-29 7-30 8-30 Leap8-29 9-30 10-30 11-29 12-30 from Calendrical Tabulations - return 384; - } - } - - - static readonly int [,] yinfo = - { - /*Y LM Lmon Lday DaysPerMonth D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 D13 #Days -1901 */{ 0 , 2 , 19 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 -1902 */{ 0 , 2 , 8 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 -1903 */{ 5 , 1 , 29 , 21096 },/* 29 30 29 30 29 29 30 29 29 30 30 29 30 383 -1904 */{ 0 , 2 , 16 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354 -1905 */{ 0 , 2 , 4 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355 -1906 */{ 4 , 1 , 25 , 27304 },/* 29 30 30 29 30 29 30 29 30 29 30 29 30 384 -1907 */{ 0 , 2 , 13 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 -1908 */{ 0 , 2 , 2 , 39632 },/* 30 29 29 30 30 29 30 29 30 30 29 30 0 355 -1909 */{ 2 , 1 , 22 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 -1910 */{ 0 , 2 , 10 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 -1911 */{ 6 , 1 , 30 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384 -1912 */{ 0 , 2 , 18 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 -1913 */{ 0 , 2 , 6 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 -1914 */{ 5 , 1 , 26 , 54568 },/* 30 30 29 30 29 30 29 30 29 29 30 29 30 384 -1915 */{ 0 , 2 , 14 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354 -1916 */{ 0 , 2 , 3 , 54944 },/* 30 30 29 30 29 30 30 29 30 29 30 29 0 355 -1917 */{ 2 , 1 , 23 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384 -1918 */{ 0 , 2 , 11 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 -1919 */{ 7 , 2 , 1 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384 -1920 */{ 0 , 2 , 20 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 -1921 */{ 0 , 2 , 8 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 -1922 */{ 5 , 1 , 28 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384 -1923 */{ 0 , 2 , 16 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354 -1924 */{ 0 , 2 , 5 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354 -1925 */{ 4 , 1 , 24 , 44456 },/* 30 29 30 29 30 30 29 30 30 29 30 29 30 385 -1926 */{ 0 , 2 , 13 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 -1927 */{ 0 , 2 , 2 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 0 355 -1928 */{ 2 , 1 , 23 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384 -1929 */{ 0 , 2 , 10 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 -1930 */{ 6 , 1 , 30 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 29 383 -1931 */{ 0 , 2 , 17 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 -1932 */{ 0 , 2 , 6 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355 -1933 */{ 5 , 1 , 26 , 27976 },/* 29 30 30 29 30 30 29 30 29 30 29 29 30 384 -1934 */{ 0 , 2 , 14 , 23248 },/* 29 30 29 30 30 29 30 29 30 30 29 30 0 355 -1935 */{ 0 , 2 , 4 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354 -1936 */{ 3 , 1 , 24 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 29 384 -1937 */{ 0 , 2 , 11 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 -1938 */{ 7 , 1 , 31 , 51560 },/* 30 30 29 29 30 29 29 30 29 30 30 29 30 384 -1939 */{ 0 , 2 , 19 , 51536 },/* 30 30 29 29 30 29 29 30 29 30 29 30 0 354 -1940 */{ 0 , 2 , 8 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 -1941 */{ 6 , 1 , 27 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 29 384 -1942 */{ 0 , 2 , 15 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 -1943 */{ 0 , 2 , 5 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 -1944 */{ 4 , 1 , 25 , 43736 },/* 30 29 30 29 30 29 30 29 30 30 29 30 30 385 -1945 */{ 0 , 2 , 13 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 -1946 */{ 0 , 2 , 2 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354 -1947 */{ 2 , 1 , 22 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384 -1948 */{ 0 , 2 , 10 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 -1949 */{ 7 , 1 , 29 , 46248 },/* 30 29 30 30 29 30 29 29 30 29 30 29 30 384 -1950 */{ 0 , 2 , 17 , 27808 },/* 29 30 30 29 30 30 29 29 30 29 30 29 0 354 -1951 */{ 0 , 2 , 6 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355 -1952 */{ 5 , 1 , 27 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384 -1953 */{ 0 , 2 , 14 , 19872 },/* 29 30 29 29 30 30 29 30 30 29 30 29 0 354 -1954 */{ 0 , 2 , 3 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355 -1955 */{ 3 , 1 , 24 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384 -1956 */{ 0 , 2 , 12 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 -1957 */{ 8 , 1 , 31 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 29 383 -1958 */{ 0 , 2 , 18 , 59728 },/* 30 30 30 29 30 29 29 30 29 30 29 30 0 355 -1959 */{ 0 , 2 , 8 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 -1960 */{ 6 , 1 , 28 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384 -1961 */{ 0 , 2 , 15 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355 -1962 */{ 0 , 2 , 5 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 -1963 */{ 4 , 1 , 25 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 -1964 */{ 0 , 2 , 13 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 -1965 */{ 0 , 2 , 2 , 21088 },/* 29 30 29 30 29 29 30 29 29 30 30 29 0 353 -1966 */{ 3 , 1 , 21 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384 -1967 */{ 0 , 2 , 9 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355 -1968 */{ 7 , 1 , 30 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 -1969 */{ 0 , 2 , 17 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 -1970 */{ 0 , 2 , 6 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355 -1971 */{ 5 , 1 , 27 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 -1972 */{ 0 , 2 , 15 , 19152 },/* 29 30 29 29 30 29 30 29 30 30 29 30 0 354 -1973 */{ 0 , 2 , 3 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 -1974 */{ 4 , 1 , 23 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384 -1975 */{ 0 , 2 , 11 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 -1976 */{ 8 , 1 , 31 , 54568 },/* 30 30 29 30 29 30 29 30 29 29 30 29 30 384 -1977 */{ 0 , 2 , 18 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354 -1978 */{ 0 , 2 , 7 , 46752 },/* 30 29 30 30 29 30 30 29 30 29 30 29 0 355 -1979 */{ 6 , 1 , 28 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384 -1980 */{ 0 , 2 , 16 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355 -1981 */{ 0 , 2 , 5 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 -1982 */{ 4 , 1 , 25 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384 -1983 */{ 0 , 2 , 13 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 -1984 */{ 10 , 2 , 2 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384 -1985 */{ 0 , 2 , 20 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354 -1986 */{ 0 , 2 , 9 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354 -1987 */{ 6 , 1 , 29 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 29 384 -1988 */{ 0 , 2 , 17 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355 -1989 */{ 0 , 2 , 6 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 0 355 -1990 */{ 5 , 1 , 27 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384 -1991 */{ 0 , 2 , 15 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 -1992 */{ 0 , 2 , 4 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354 -1993 */{ 3 , 1 , 23 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383 -1994 */{ 0 , 2 , 10 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355 -1995 */{ 8 , 1 , 31 , 27432 },/* 29 30 30 29 30 29 30 30 29 29 30 29 30 384 -1996 */{ 0 , 2 , 19 , 23232 },/* 29 30 29 30 30 29 30 29 30 30 29 29 0 354 -1997 */{ 0 , 2 , 7 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355 -1998 */{ 5 , 1 , 28 , 37736 },/* 30 29 29 30 29 29 30 30 29 30 30 29 30 384 -1999 */{ 0 , 2 , 16 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 -2000 */{ 0 , 2 , 5 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354 -2001 */{ 4 , 1 , 24 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384 -2002 */{ 0 , 2 , 12 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 -2003 */{ 0 , 2 , 1 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355 -2004 */{ 2 , 1 , 22 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 -2005 */{ 0 , 2 , 9 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 -2006 */{ 7 , 1 , 29 , 43736 },/* 30 29 30 29 30 29 30 29 30 30 29 30 30 385 -2007 */{ 0 , 2 , 18 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354 -2008 */{ 0 , 2 , 7 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354 -2009 */{ 5 , 1 , 26 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384 -2010 */{ 0 , 2 , 14 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 -2011 */{ 0 , 2 , 3 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354 -2012 */{ 4 , 1 , 23 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384 -2013 */{ 0 , 2 , 10 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 -2014 */{ 9 , 1 , 31 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384 -2015 */{ 0 , 2 , 19 , 19360 },/* 29 30 29 29 30 29 30 30 30 29 30 29 0 354 -2016 */{ 0 , 2 , 8 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355 -2017 */{ 6 , 1 , 28 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384 -2018 */{ 0 , 2 , 16 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 -2019 */{ 0 , 2 , 5 , 43312 },/* 30 29 30 29 30 29 29 30 29 29 30 30 0 354 -2020 */{ 4 , 1 , 25 , 29864 },/* 29 30 30 30 29 30 29 29 30 29 30 29 30 384 -2021 */{ 0 , 2 , 12 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 -2022 */{ 0 , 2 , 1 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 -2023 */{ 2 , 1 , 22 , 19880 },/* 29 30 29 29 30 30 29 30 30 29 30 29 30 384 -2024 */{ 0 , 2 , 10 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 -2025 */{ 6 , 1 , 29 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384 -2026 */{ 0 , 2 , 17 , 42208 },/* 30 29 30 29 29 30 29 29 30 30 30 29 0 354 -2027 */{ 0 , 2 , 6 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354 -2028 */{ 5 , 1 , 26 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384 -2029 */{ 0 , 2 , 13 , 54576 },/* 30 30 29 30 29 30 29 30 29 29 30 30 0 355 -2030 */{ 0 , 2 , 3 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354 -2031 */{ 3 , 1 , 23 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 29 384 -2032 */{ 0 , 2 , 11 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355 -2033 */{ 11 , 1 , 31 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384 -2034 */{ 0 , 2 , 19 , 19152 },/* 29 30 29 29 30 29 30 29 30 30 29 30 0 354 -2035 */{ 0 , 2 , 8 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 -2036 */{ 6 , 1 , 28 , 53848 },/* 30 30 29 30 29 29 30 29 29 30 29 30 30 384 -2037 */{ 0 , 2 , 15 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354 -2038 */{ 0 , 2 , 4 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354 -2039 */{ 5 , 1 , 24 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 29 384 -2040 */{ 0 , 2 , 12 , 46496 },/* 30 29 30 30 29 30 29 30 30 29 30 29 0 355 -2041 */{ 0 , 2 , 1 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355 -2042 */{ 2 , 1 , 22 , 19160 },/* 29 30 29 29 30 29 30 29 30 30 29 30 30 384 -2043 */{ 0 , 2 , 10 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354 -2044 */{ 7 , 1 , 30 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384 -2045 */{ 0 , 2 , 17 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354 -2046 */{ 0 , 2 , 6 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354 -2047 */{ 5 , 1 , 26 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384 -2048 */{ 0 , 2 , 14 , 27936 },/* 29 30 30 29 30 30 29 30 29 29 30 29 0 354 -2049 */{ 0 , 2 , 2 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 0 355 -2050 */{ 3 , 1 , 23 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 29 384 -2051 */{ 0 , 2 , 11 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 0 355 -2052 */{ 8 , 2 , 1 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384 -2053 */{ 0 , 2 , 19 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354 -2054 */{ 0 , 2 , 8 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354 -2055 */{ 6 , 1 , 28 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383 -2056 */{ 0 , 2 , 15 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355 -2057 */{ 0 , 2 , 4 , 27424 },/* 29 30 30 29 30 29 30 30 29 29 30 29 0 354 -2058 */{ 4 , 1 , 24 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 29 384 -2059 */{ 0 , 2 , 12 , 43744 },/* 30 29 30 29 30 29 30 29 30 30 30 29 0 355 -2060 */{ 0 , 2 , 2 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354 -2061 */{ 3 , 1 , 21 , 51568 },/* 30 30 29 29 30 29 29 30 29 30 30 30 29 384 -2062 */{ 0 , 2 , 9 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354 -2063 */{ 7 , 1 , 29 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384 -2064 */{ 0 , 2 , 17 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354 -2065 */{ 0 , 2 , 5 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355 -2066 */{ 5 , 1 , 26 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384 -2067 */{ 0 , 2 , 14 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354 -2068 */{ 0 , 2 , 3 , 42704 },/* 30 29 30 29 29 30 30 29 30 30 29 30 0 355 -2069 */{ 4 , 1 , 23 , 21224 },/* 29 30 29 30 29 29 30 29 30 30 30 29 30 384 -2070 */{ 0 , 2 , 11 , 21200 },/* 29 30 29 30 29 29 30 29 30 30 29 30 0 354 -2071 */{ 8 , 1 , 31 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384 -2072 */{ 0 , 2 , 19 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354 -2073 */{ 0 , 2 , 7 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354 -2074 */{ 6 , 1 , 27 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384 -2075 */{ 0 , 2 , 15 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 -2076 */{ 0 , 2 , 5 , 21920 },/* 29 30 29 30 29 30 29 30 30 29 30 29 0 354 -2077 */{ 4 , 1 , 24 , 42448 },/* 30 29 30 29 29 30 29 30 30 30 29 30 29 384 -2078 */{ 0 , 2 , 12 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355 -2079 */{ 0 , 2 , 2 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354 -2080 */{ 3 , 1 , 22 , 43320 },/* 30 29 30 29 30 29 29 30 29 29 30 30 30 384 -2081 */{ 0 , 2 , 9 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 0 354 -2082 */{ 7 , 1 , 29 , 29336 },/* 29 30 30 30 29 29 30 29 30 29 29 30 30 384 -2083 */{ 0 , 2 , 17 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354 -2084 */{ 0 , 2 , 6 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355 -2085 */{ 5 , 1 , 26 , 19880 },/* 29 30 29 29 30 30 29 30 30 29 30 29 30 384 -2086 */{ 0 , 2 , 14 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354 -2087 */{ 0 , 2 , 3 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355 -2088 */{ 4 , 1 , 24 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 29 383 -2089 */{ 0 , 2 , 10 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354 -2090 */{ 8 , 1 , 30 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384 -2091 */{ 0 , 2 , 18 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354 -2092 */{ 0 , 2 , 7 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 0 355 -2093 */{ 6 , 1 , 27 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 29 384 -2094 */{ 0 , 2 , 15 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355 -2095 */{ 0 , 2 , 5 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354 -2096 */{ 4 , 1 , 25 , 42216 },/* 30 29 30 29 29 30 29 29 30 30 30 29 30 384 -2097 */{ 0 , 2 , 12 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354 -2098 */{ 0 , 2 , 1 , 53584 },/* 30 30 29 30 29 29 29 30 29 30 29 30 0 354 -2099 */{ 2 , 1 , 21 , 55592 },/* 30 30 29 30 30 29 29 30 29 29 30 29 30 384 -2100 */{ 0 , 2 , 9 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354 - */}; - - - internal override int MinCalendarYear { - get - { - return (MIN_LUNISOLAR_YEAR); - } - } - - internal override int MaxCalendarYear { - get - { - return (MAX_LUNISOLAR_YEAR); - } - } - - internal override DateTime MinDate { - get - { - return (minDate); - } - } - - internal override DateTime MaxDate { - get - { - return (maxDate); - } - } - - internal override EraInfo[] CalEraInfo { - get - { - return (null); - } - } - - internal override int GetYearInfo(int LunarYear, int Index) { - if ((LunarYear < MIN_LUNISOLAR_YEAR) || (LunarYear > MAX_LUNISOLAR_YEAR)) { - throw new ArgumentOutOfRangeException( - "year", - String.Format( - CultureInfo.CurrentCulture, - Environment.GetResourceString("ArgumentOutOfRange_Range"), MIN_LUNISOLAR_YEAR, MAX_LUNISOLAR_YEAR )); - } - Contract.EndContractBlock(); - - return yinfo[LunarYear - MIN_LUNISOLAR_YEAR, Index]; - } - - internal override int GetYear(int year, DateTime time) { - return year; - } - - internal override int GetGregorianYear(int year, int era) { - if (era != CurrentEra && era != ChineseEra) { - throw new ArgumentOutOfRangeException(nameof(era), Environment.GetResourceString("ArgumentOutOfRange_InvalidEraValue")); - } - - if (year < MIN_LUNISOLAR_YEAR || year > MAX_LUNISOLAR_YEAR) { - throw new ArgumentOutOfRangeException( - nameof(year), - String.Format( - CultureInfo.CurrentCulture, - Environment.GetResourceString("ArgumentOutOfRange_Range"), MIN_LUNISOLAR_YEAR, MAX_LUNISOLAR_YEAR)); - } - Contract.EndContractBlock(); - - return year; - } - - - /*=================================GetDefaultInstance========================== - **Action: Internal method to provide a default intance of ChineseLunisolarCalendar. Used by NLS+ implementation - ** and other calendars. - **Returns: - **Arguments: - **Exceptions: - ============================================================================*/ - - /* - internal static Calendar GetDefaultInstance() - { - if (m_defaultInstance == null) { - m_defaultInstance = new ChineseLunisolarCalendar(); - } - return (m_defaultInstance); - } - */ - - // Construct an instance of ChineseLunisolar calendar. - - public ChineseLunisolarCalendar() { - } - - - public override int GetEra(DateTime time) { - CheckTicksRange(time.Ticks); - return (ChineseEra); - } - - internal override int ID { - get { - return (CAL_CHINESELUNISOLAR); - } - } - - internal override int BaseCalendarID { - get { - //Use CAL_GREGORIAN just to get CurrentEraValue as 1 since we do not have data under the ID CAL_ChineseLunisolar yet - return (CAL_GREGORIAN); - } - } - - - public override int[] Eras { - get { - return (new int[] {ChineseEra}); - } - } - } -} diff --git a/src/mscorlib/src/System/Globalization/CompareInfo.Invariant.cs b/src/mscorlib/src/System/Globalization/CompareInfo.Invariant.cs new file mode 100644 index 0000000000..2a20de76bb --- /dev/null +++ b/src/mscorlib/src/System/Globalization/CompareInfo.Invariant.cs @@ -0,0 +1,238 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Diagnostics.Contracts; + +namespace System.Globalization +{ + public partial class CompareInfo + { + internal static unsafe int InvariantIndexOf(string source, string value, int startIndex, int count, bool ignoreCase) + { + Debug.Assert(source != null); + Debug.Assert(value != null); + Debug.Assert(startIndex >= 0 && startIndex < source.Length); + + fixed (char* pSource = source) fixed (char* pValue = value) + { + char* pSrc = &pSource[startIndex]; + int index = InvariantFindString(pSrc, count, pValue, value.Length, ignoreCase, start : true); + if (index >= 0) + { + return index + startIndex; + } + return -1; + } + } + + internal static unsafe int InvariantLastIndexOf(string source, string value, int startIndex, int count, bool ignoreCase) + { + Debug.Assert(source != null); + Debug.Assert(value != null); + Debug.Assert(startIndex >= 0 && startIndex < source.Length); + + fixed (char* pSource = source) fixed (char* pValue = value) + { + char* pSrc = &pSource[startIndex - count + 1]; + int index = InvariantFindString(pSrc, count, pValue, value.Length, ignoreCase, start : false); + if (index >= 0) + { + return index + startIndex - count + 1; + } + return -1; + } + } + + private static unsafe int InvariantFindString(char* source, int sourceCount, char* value, int valueCount, bool ignoreCase, bool start) + { + int ctrSource = 0; // index value into source + int ctrValue = 0; // index value into value + char sourceChar; // Character for case lookup in source + char valueChar; // Character for case lookup in value + int lastSourceStart; + + Debug.Assert(source != null); + Debug.Assert(value != null); + Debug.Assert(sourceCount >= 0); + Debug.Assert(valueCount >= 0); + + if (valueCount == 0) + { + return start ? 0 : sourceCount - 1; + } + + if (sourceCount < valueCount) + { + return -1; + } + + if (start) + { + lastSourceStart = sourceCount - valueCount; + if (ignoreCase) + { + char firstValueChar = InvariantToUpper(value[0]); + for (ctrSource = 0; ctrSource <= lastSourceStart; ctrSource++) + { + sourceChar = InvariantToUpper(source[ctrSource]); + if (sourceChar != firstValueChar) + { + continue; + } + + for (ctrValue = 1; ctrValue < valueCount; ctrValue++) + { + sourceChar = InvariantToUpper(source[ctrSource + ctrValue]); + valueChar = InvariantToUpper(value[ctrValue]); + + if (sourceChar != valueChar) + { + break; + } + } + + if (ctrValue == valueCount) + { + return ctrSource; + } + } + } + else + { + char firstValueChar = value[0]; + for (ctrSource = 0; ctrSource <= lastSourceStart; ctrSource++) + { + sourceChar = source[ctrSource]; + if (sourceChar != firstValueChar) + { + continue; + } + + for (ctrValue = 1; ctrValue < valueCount; ctrValue++) + { + sourceChar = source[ctrSource + ctrValue]; + valueChar = value[ctrValue]; + + if (sourceChar != valueChar) + { + break; + } + } + + if (ctrValue == valueCount) + { + return ctrSource; + } + } + } + } + else + { + lastSourceStart = sourceCount - valueCount; + if (ignoreCase) + { + char firstValueChar = InvariantToUpper(value[0]); + for (ctrSource = lastSourceStart; ctrSource >= 0; ctrSource--) + { + sourceChar = InvariantToUpper(source[ctrSource]); + if (sourceChar != firstValueChar) + { + continue; + } + for (ctrValue = 1; ctrValue < valueCount; ctrValue++) + { + sourceChar = InvariantToUpper(source[ctrSource + ctrValue]); + valueChar = InvariantToUpper(value[ctrValue]); + + if (sourceChar != valueChar) + { + break; + } + } + + if (ctrValue == valueCount) + { + return ctrSource; + } + } + } + else + { + char firstValueChar = value[0]; + for (ctrSource = lastSourceStart; ctrSource >= 0; ctrSource--) + { + sourceChar = source[ctrSource]; + if (sourceChar != firstValueChar) + { + continue; + } + + for (ctrValue = 1; ctrValue < valueCount; ctrValue++) + { + sourceChar = source[ctrSource + ctrValue]; + valueChar = value[ctrValue]; + + if (sourceChar != valueChar) + { + break; + } + } + + if (ctrValue == valueCount) + { + return ctrSource; + } + } + } + } + + return -1; + } + + private static char InvariantToUpper(char c) + { + return (uint)(c - 'a') <= (uint)('z' - 'a') ? (char)(c - 0x20) : c; + } + + private unsafe SortKey InvariantCreateSortKey(string source, CompareOptions options) + { + if (source == null) { throw new ArgumentNullException(nameof(source)); } + Contract.EndContractBlock(); + + if ((options & ValidSortkeyCtorMaskOffFlags) != 0) + { + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + } + + byte [] keyData; + if (source.Length == 0) + { + keyData = Array.Empty<byte>(); + } + else + { + // In the invariant mode, all string comparisons are done as ordinal so when generating the sort keys we generate it according to this fact + keyData = new byte[source.Length * sizeof(char)]; + + fixed (char* pChar = source) fixed (byte* pByte = keyData) + { + if ((options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0) + { + short *pShort = (short *) pByte; + for (int i=0; i<source.Length; i++) + { + pShort[i] = (short) InvariantToUpper(source[i]); + } + } + else + { + Buffer.MemoryCopy(pChar, pByte, keyData.Length, keyData.Length); + } + } + } + return new SortKey(Name, source, options, keyData); + } + } +} diff --git a/src/mscorlib/src/System/Globalization/CompareInfo.Unix.cs b/src/mscorlib/src/System/Globalization/CompareInfo.Unix.cs new file mode 100644 index 0000000000..edc9a7f575 --- /dev/null +++ b/src/mscorlib/src/System/Globalization/CompareInfo.Unix.cs @@ -0,0 +1,439 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; + +namespace System.Globalization +{ + public partial class CompareInfo + { + [NonSerialized] + private Interop.GlobalizationInterop.SafeSortHandle _sortHandle; + + [NonSerialized] + private bool _isAsciiEqualityOrdinal; + + private void InitSort(CultureInfo culture) + { + _sortName = culture.SortName; + + if (_invariantMode) + { + _isAsciiEqualityOrdinal = true; + } + else + { + Interop.GlobalizationInterop.ResultCode resultCode = Interop.GlobalizationInterop.GetSortHandle(GetNullTerminatedUtf8String(_sortName), out _sortHandle); + if (resultCode != Interop.GlobalizationInterop.ResultCode.Success) + { + _sortHandle.Dispose(); + + if (resultCode == Interop.GlobalizationInterop.ResultCode.OutOfMemory) + throw new OutOfMemoryException(); + + throw new ExternalException(SR.Arg_ExternalException); + } + _isAsciiEqualityOrdinal = (_sortName == "en-US" || _sortName == ""); + } + } + + internal static unsafe int IndexOfOrdinalCore(string source, string value, int startIndex, int count, bool ignoreCase) + { + Debug.Assert(!GlobalizationMode.Invariant); + + Debug.Assert(source != null); + Debug.Assert(value != null); + + if (value.Length == 0) + { + return startIndex; + } + + if (count < value.Length) + { + return -1; + } + + if (ignoreCase) + { + fixed (char* pSource = source) + { + int index = Interop.GlobalizationInterop.IndexOfOrdinalIgnoreCase(value, value.Length, pSource + startIndex, count, findLast: false); + return index != -1 ? + startIndex + index : + -1; + } + } + + int endIndex = startIndex + (count - value.Length); + for (int i = startIndex; i <= endIndex; i++) + { + int valueIndex, sourceIndex; + + for (valueIndex = 0, sourceIndex = i; + valueIndex < value.Length && source[sourceIndex] == value[valueIndex]; + valueIndex++, sourceIndex++) ; + + if (valueIndex == value.Length) + { + return i; + } + } + + return -1; + } + + internal static unsafe int LastIndexOfOrdinalCore(string source, string value, int startIndex, int count, bool ignoreCase) + { + Debug.Assert(!GlobalizationMode.Invariant); + + Debug.Assert(source != null); + Debug.Assert(value != null); + + if (value.Length == 0) + { + return startIndex; + } + + if (count < value.Length) + { + return -1; + } + + // startIndex is the index into source where we start search backwards from. + // leftStartIndex is the index into source of the start of the string that is + // count characters away from startIndex. + int leftStartIndex = startIndex - count + 1; + + if (ignoreCase) + { + fixed (char* pSource = source) + { + int lastIndex = Interop.GlobalizationInterop.IndexOfOrdinalIgnoreCase(value, value.Length, pSource + leftStartIndex, count, findLast: true); + return lastIndex != -1 ? + leftStartIndex + lastIndex : + -1; + } + } + + for (int i = startIndex - value.Length + 1; i >= leftStartIndex; i--) + { + int valueIndex, sourceIndex; + + for (valueIndex = 0, sourceIndex = i; + valueIndex < value.Length && source[sourceIndex] == value[valueIndex]; + valueIndex++, sourceIndex++) ; + + if (valueIndex == value.Length) { + return i; + } + } + + return -1; + } + + private int GetHashCodeOfStringCore(string source, CompareOptions options) + { + Debug.Assert(source != null); + Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); + + return GetHashCodeOfStringCore(source, options, forceRandomizedHashing: false, additionalEntropy: 0); + } + + private static unsafe int CompareStringOrdinalIgnoreCase(char* string1, int count1, char* string2, int count2) + { + Debug.Assert(!GlobalizationMode.Invariant); + + return Interop.GlobalizationInterop.CompareStringOrdinalIgnoreCase(string1, count1, string2, count2); + } + + private unsafe int CompareString(string string1, int offset1, int length1, string string2, int offset2, int length2, CompareOptions options) + { + Debug.Assert(!_invariantMode); + + Debug.Assert(string1 != null); + Debug.Assert(string2 != null); + Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); + + fixed (char* pString1 = string1) + { + fixed (char* pString2 = string2) + { + return Interop.GlobalizationInterop.CompareString(_sortHandle, pString1 + offset1, length1, pString2 + offset2, length2, options); + } + } + } + + internal unsafe int IndexOfCore(string source, string target, int startIndex, int count, CompareOptions options, int* matchLengthPtr) + { + Debug.Assert(!_invariantMode); + + Debug.Assert(!string.IsNullOrEmpty(source)); + Debug.Assert(target != null); + Debug.Assert((options & CompareOptions.OrdinalIgnoreCase) == 0); + + int index; + + if (target.Length == 0) + { + if(matchLengthPtr != null) + *matchLengthPtr = 0; + return startIndex; + } + + if (options == CompareOptions.Ordinal) + { + index = IndexOfOrdinal(source, target, startIndex, count, ignoreCase: false); + if(index != -1) + { + if(matchLengthPtr != null) + *matchLengthPtr = target.Length; + } + return index; + } + + if (_isAsciiEqualityOrdinal && CanUseAsciiOrdinalForOptions(options) && source.IsFastSort() && target.IsFastSort()) + { + index = IndexOf(source, target, startIndex, count, GetOrdinalCompareOptions(options)); + if(index != -1) + { + if(matchLengthPtr != null) + *matchLengthPtr = target.Length; + } + return index; + } + + fixed (char* pSource = source) + { + index = Interop.GlobalizationInterop.IndexOf(_sortHandle, target, target.Length, pSource + startIndex, count, options, matchLengthPtr); + + return index != -1 ? index + startIndex : -1; + } + } + + private unsafe int LastIndexOfCore(string source, string target, int startIndex, int count, CompareOptions options) + { + Debug.Assert(!_invariantMode); + + Debug.Assert(!string.IsNullOrEmpty(source)); + Debug.Assert(target != null); + Debug.Assert((options & CompareOptions.OrdinalIgnoreCase) == 0); + + if (target.Length == 0) + { + return startIndex; + } + + if (options == CompareOptions.Ordinal) + { + return LastIndexOfOrdinalCore(source, target, startIndex, count, ignoreCase: false); + } + + if (_isAsciiEqualityOrdinal && CanUseAsciiOrdinalForOptions(options) && source.IsFastSort() && target.IsFastSort()) + { + return LastIndexOf(source, target, startIndex, count, GetOrdinalCompareOptions(options)); + } + + // startIndex is the index into source where we start search backwards from. leftStartIndex is the index into source + // of the start of the string that is count characters away from startIndex. + int leftStartIndex = (startIndex - count + 1); + + fixed (char* pSource = source) + { + int lastIndex = Interop.GlobalizationInterop.LastIndexOf(_sortHandle, target, target.Length, pSource + (startIndex - count + 1), count, options); + + return lastIndex != -1 ? lastIndex + leftStartIndex : -1; + } + } + + private bool StartsWith(string source, string prefix, CompareOptions options) + { + Debug.Assert(!_invariantMode); + + Debug.Assert(!string.IsNullOrEmpty(source)); + Debug.Assert(!string.IsNullOrEmpty(prefix)); + Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); + + if (_isAsciiEqualityOrdinal && CanUseAsciiOrdinalForOptions(options) && source.IsFastSort() && prefix.IsFastSort()) + { + return IsPrefix(source, prefix, GetOrdinalCompareOptions(options)); + } + + return Interop.GlobalizationInterop.StartsWith(_sortHandle, prefix, prefix.Length, source, source.Length, options); + } + + private bool EndsWith(string source, string suffix, CompareOptions options) + { + Debug.Assert(!_invariantMode); + + Debug.Assert(!string.IsNullOrEmpty(source)); + Debug.Assert(!string.IsNullOrEmpty(suffix)); + Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); + + if (_isAsciiEqualityOrdinal && CanUseAsciiOrdinalForOptions(options) && source.IsFastSort() && suffix.IsFastSort()) + { + return IsSuffix(source, suffix, GetOrdinalCompareOptions(options)); + } + + return Interop.GlobalizationInterop.EndsWith(_sortHandle, suffix, suffix.Length, source, source.Length, options); + } + + private unsafe SortKey CreateSortKey(String source, CompareOptions options) + { + Debug.Assert(!_invariantMode); + + if (source==null) { throw new ArgumentNullException(nameof(source)); } + Contract.EndContractBlock(); + + if ((options & ValidSortkeyCtorMaskOffFlags) != 0) + { + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + } + + byte [] keyData; + if (source.Length == 0) + { + keyData = Array.Empty<Byte>(); + } + else + { + int sortKeyLength = Interop.GlobalizationInterop.GetSortKey(_sortHandle, source, source.Length, null, 0, options); + keyData = new byte[sortKeyLength]; + + fixed (byte* pSortKey = keyData) + { + Interop.GlobalizationInterop.GetSortKey(_sortHandle, source, source.Length, pSortKey, sortKeyLength, options); + } + } + + return new SortKey(Name, source, options, keyData); + } + + private unsafe static bool IsSortable(char *text, int length) + { + Debug.Assert(!GlobalizationMode.Invariant); + + int index = 0; + UnicodeCategory uc; + + while (index < length) + { + if (Char.IsHighSurrogate(text[index])) + { + if (index == length - 1 || !Char.IsLowSurrogate(text[index+1])) + return false; // unpaired surrogate + + uc = CharUnicodeInfo.InternalGetUnicodeCategory(Char.ConvertToUtf32(text[index], text[index+1])); + if (uc == UnicodeCategory.PrivateUse || uc == UnicodeCategory.OtherNotAssigned) + return false; + + index += 2; + continue; + } + + if (Char.IsLowSurrogate(text[index])) + { + return false; // unpaired surrogate + } + + uc = CharUnicodeInfo.GetUnicodeCategory(text[index]); + if (uc == UnicodeCategory.PrivateUse || uc == UnicodeCategory.OtherNotAssigned) + { + return false; + } + + index++; + } + + return true; + } + + // ----------------------------- + // ---- PAL layer ends here ---- + // ----------------------------- + + internal unsafe int GetHashCodeOfStringCore(string source, CompareOptions options, bool forceRandomizedHashing, long additionalEntropy) + { + Debug.Assert(!_invariantMode); + + Debug.Assert(source != null); + Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); + + if (source.Length == 0) + { + return 0; + } + + int sortKeyLength = Interop.GlobalizationInterop.GetSortKey(_sortHandle, source, source.Length, null, 0, options); + + // As an optimization, for small sort keys we allocate the buffer on the stack. + if (sortKeyLength <= 256) + { + byte* pSortKey = stackalloc byte[sortKeyLength]; + Interop.GlobalizationInterop.GetSortKey(_sortHandle, source, source.Length, pSortKey, sortKeyLength, options); + return InternalHashSortKey(pSortKey, sortKeyLength, false, additionalEntropy); + } + + byte[] sortKey = new byte[sortKeyLength]; + + fixed (byte* pSortKey = sortKey) + { + Interop.GlobalizationInterop.GetSortKey(_sortHandle, source, source.Length, pSortKey, sortKeyLength, options); + return InternalHashSortKey(pSortKey, sortKeyLength, false, additionalEntropy); + } + } + + [DllImport(JitHelpers.QCall)] + [SuppressUnmanagedCodeSecurity] + private static unsafe extern int InternalHashSortKey(byte* sortKey, int sortKeyLength, [MarshalAs(UnmanagedType.Bool)] bool forceRandomizedHashing, long additionalEntropy); + + private static CompareOptions GetOrdinalCompareOptions(CompareOptions options) + { + if ((options & CompareOptions.IgnoreCase) == CompareOptions.IgnoreCase) + { + return CompareOptions.OrdinalIgnoreCase; + } + else + { + return CompareOptions.Ordinal; + } + } + + private static bool CanUseAsciiOrdinalForOptions(CompareOptions options) + { + // Unlike the other Ignore options, IgnoreSymbols impacts ASCII characters (e.g. '). + return (options & CompareOptions.IgnoreSymbols) == 0; + } + + private static byte[] GetNullTerminatedUtf8String(string s) + { + int byteLen = System.Text.Encoding.UTF8.GetByteCount(s); + + // Allocate an extra byte (which defaults to 0) as the null terminator. + byte[] buffer = new byte[byteLen + 1]; + + int bytesWritten = System.Text.Encoding.UTF8.GetBytes(s, 0, s.Length, buffer, 0); + + Debug.Assert(bytesWritten == byteLen); + + return buffer; + } + + private SortVersion GetSortVersion() + { + Debug.Assert(!_invariantMode); + + int sortVersion = Interop.GlobalizationInterop.GetSortVersion(); + return new SortVersion(sortVersion, LCID, new Guid(sortVersion, 0, 0, 0, 0, 0, 0, + (byte) (LCID >> 24), + (byte) ((LCID & 0x00FF0000) >> 16), + (byte) ((LCID & 0x0000FF00) >> 8), + (byte) (LCID & 0xFF))); + } + } +} diff --git a/src/mscorlib/src/System/Globalization/CompareInfo.Windows.cs b/src/mscorlib/src/System/Globalization/CompareInfo.Windows.cs new file mode 100644 index 0000000000..d20bb9f9f3 --- /dev/null +++ b/src/mscorlib/src/System/Globalization/CompareInfo.Windows.cs @@ -0,0 +1,486 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Security; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Globalization +{ + public partial class CompareInfo + { + private unsafe void InitSort(CultureInfo culture) + { + _sortName = culture.SortName; + + _name = culture._name; + _sortName = culture.SortName; + + if (_invariantMode) + { + _sortHandle = IntPtr.Zero; + } + else + { + const uint LCMAP_SORTHANDLE = 0x20000000; + IntPtr handle; + int ret = Interop.Kernel32.LCMapStringEx(_sortName, LCMAP_SORTHANDLE, null, 0, &handle, IntPtr.Size, null, null, IntPtr.Zero); + _sortHandle = ret > 0 ? handle : IntPtr.Zero; + } + } + + private static unsafe int FindStringOrdinal( + uint dwFindStringOrdinalFlags, + string stringSource, + int offset, + int cchSource, + string value, + int cchValue, + bool bIgnoreCase) + { + Debug.Assert(!GlobalizationMode.Invariant); + + fixed (char* pSource = stringSource) + fixed (char* pValue = value) + { + int ret = Interop.Kernel32.FindStringOrdinal( + dwFindStringOrdinalFlags, + pSource + offset, + cchSource, + pValue, + cchValue, + bIgnoreCase ? 1 : 0); + return ret < 0 ? ret : ret + offset; + } + } + + internal static int IndexOfOrdinalCore(string source, string value, int startIndex, int count, bool ignoreCase) + { + Debug.Assert(!GlobalizationMode.Invariant); + + Debug.Assert(source != null); + Debug.Assert(value != null); + + return FindStringOrdinal(FIND_FROMSTART, source, startIndex, count, value, value.Length, ignoreCase); + } + + internal static int LastIndexOfOrdinalCore(string source, string value, int startIndex, int count, bool ignoreCase) + { + Debug.Assert(!GlobalizationMode.Invariant); + + Debug.Assert(source != null); + Debug.Assert(value != null); + + return FindStringOrdinal(FIND_FROMEND, source, startIndex - count + 1, count, value, value.Length, ignoreCase); + } + + private unsafe int GetHashCodeOfStringCore(string source, CompareOptions options) + { + Debug.Assert(!_invariantMode); + + Debug.Assert(source != null); + Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); + + if (source.Length == 0) + { + return 0; + } + + int flags = GetNativeCompareFlags(options); + int tmpHash = 0; +#if CORECLR + tmpHash = InternalGetGlobalizedHashCode(_sortHandle, _sortName, source, source.Length, flags, 0); +#else + fixed (char* pSource = source) + { + if (Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _sortName, + LCMAP_HASH | (uint)flags, + pSource, source.Length, + &tmpHash, sizeof(int), + null, null, _sortHandle) == 0) + { + Environment.FailFast("LCMapStringEx failed!"); + } + } +#endif + return tmpHash; + } + + private static unsafe int CompareStringOrdinalIgnoreCase(char* string1, int count1, char* string2, int count2) + { + Debug.Assert(!GlobalizationMode.Invariant); + + // Use the OS to compare and then convert the result to expected value by subtracting 2 + return Interop.Kernel32.CompareStringOrdinal(string1, count1, string2, count2, true) - 2; + } + + private unsafe int CompareString(string string1, int offset1, int length1, string string2, int offset2, int length2, CompareOptions options) + { + Debug.Assert(!_invariantMode); + + Debug.Assert(string1 != null); + Debug.Assert(string2 != null); + Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); + + string localeName = _sortHandle != IntPtr.Zero ? null : _sortName; + + fixed (char* pLocaleName = localeName) + fixed (char* pString1 = string1) + fixed (char* pString2 = string2) + { + int result = Interop.Kernel32.CompareStringEx( + pLocaleName, + (uint)GetNativeCompareFlags(options), + pString1 + offset1, + length1, + pString2 + offset2, + length2, + null, + null, + _sortHandle); + + if (result == 0) + { + Environment.FailFast("CompareStringEx failed"); + } + + // Map CompareStringEx return value to -1, 0, 1. + return result - 2; + } + } + + private unsafe int FindString( + uint dwFindNLSStringFlags, + string lpStringSource, + int startSource, + int cchSource, + string lpStringValue, + int startValue, + int cchValue, + int *pcchFound) + { + Debug.Assert(!_invariantMode); + + string localeName = _sortHandle != IntPtr.Zero ? null : _sortName; + + fixed (char* pLocaleName = localeName) + fixed (char* pSource = lpStringSource) + fixed (char* pValue = lpStringValue) + { + char* pS = pSource + startSource; + char* pV = pValue + startValue; + + return Interop.Kernel32.FindNLSStringEx( + pLocaleName, + dwFindNLSStringFlags, + pS, + cchSource, + pV, + cchValue, + pcchFound, + null, + null, + _sortHandle); + } + } + + internal unsafe int IndexOfCore(String source, String target, int startIndex, int count, CompareOptions options, int* matchLengthPtr) + { + Debug.Assert(!_invariantMode); + + Debug.Assert(source != null); + Debug.Assert(target != null); + Debug.Assert((options & CompareOptions.OrdinalIgnoreCase) == 0); + + if (target.Length == 0) + { + if (matchLengthPtr != null) + *matchLengthPtr = 0; + return 0; + } + + if (source.Length == 0) + { + return -1; + } + + if ((options & CompareOptions.Ordinal) != 0) + { + int retValue = FastIndexOfString(source, target, startIndex, count, target.Length, findLastIndex: false); + if (retValue >= 0) + { + if (matchLengthPtr != null) + *matchLengthPtr = target.Length; + } + return retValue; + } + else + { + int retValue = FindString(FIND_FROMSTART | (uint)GetNativeCompareFlags(options), source, startIndex, count, + target, 0, target.Length, matchLengthPtr); + if (retValue >= 0) + { + return retValue + startIndex; + } + } + + return -1; + } + + private unsafe int LastIndexOfCore(string source, string target, int startIndex, int count, CompareOptions options) + { + Debug.Assert(!_invariantMode); + + Debug.Assert(!string.IsNullOrEmpty(source)); + Debug.Assert(target != null); + Debug.Assert((options & CompareOptions.OrdinalIgnoreCase) == 0); + + // TODO: Consider moving this up to the relevent APIs we need to ensure this behavior for + // and add a precondition that target is not empty. + if (target.Length == 0) + return startIndex; // keep Whidbey compatibility + + if ((options & CompareOptions.Ordinal) != 0) + { + return FastIndexOfString(source, target, startIndex, count, target.Length, findLastIndex: true); + } + else + { + int retValue = FindString(FIND_FROMEND | (uint) GetNativeCompareFlags(options), source, startIndex - count + 1, + count, target, 0, target.Length, null); + + if (retValue >= 0) + { + return retValue + startIndex - (count - 1); + } + } + + return -1; + } + + private unsafe bool StartsWith(string source, string prefix, CompareOptions options) + { + Debug.Assert(!_invariantMode); + + Debug.Assert(!string.IsNullOrEmpty(source)); + Debug.Assert(!string.IsNullOrEmpty(prefix)); + Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); + + return FindString(FIND_STARTSWITH | (uint)GetNativeCompareFlags(options), source, 0, source.Length, + prefix, 0, prefix.Length, null) >= 0; + } + + private unsafe bool EndsWith(string source, string suffix, CompareOptions options) + { + Debug.Assert(!_invariantMode); + + Debug.Assert(!string.IsNullOrEmpty(source)); + Debug.Assert(!string.IsNullOrEmpty(suffix)); + Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0); + + return FindString(FIND_ENDSWITH | (uint)GetNativeCompareFlags(options), source, 0, source.Length, + suffix, 0, suffix.Length, null) >= 0; + } + + // PAL ends here + [NonSerialized] + private IntPtr _sortHandle; + + private const uint LCMAP_SORTKEY = 0x00000400; + private const uint LCMAP_HASH = 0x00040000; + + private const int FIND_STARTSWITH = 0x00100000; + private const int FIND_ENDSWITH = 0x00200000; + private const int FIND_FROMSTART = 0x00400000; + private const int FIND_FROMEND = 0x00800000; + + // TODO: Instead of this method could we just have upstack code call IndexOfOrdinal with ignoreCase = false? + private static unsafe int FastIndexOfString(string source, string target, int startIndex, int sourceCount, int targetCount, bool findLastIndex) + { + int retValue = -1; + + int sourceStartIndex = findLastIndex ? startIndex - sourceCount + 1 : startIndex; + + fixed (char* pSource = source, spTarget = target) + { + char* spSubSource = pSource + sourceStartIndex; + + if (findLastIndex) + { + int startPattern = (sourceCount - 1) - targetCount + 1; + if (startPattern < 0) + return -1; + + char patternChar0 = spTarget[0]; + for (int ctrSrc = startPattern; ctrSrc >= 0; ctrSrc--) + { + if (spSubSource[ctrSrc] != patternChar0) + continue; + + int ctrPat; + for (ctrPat = 1; ctrPat < targetCount; ctrPat++) + { + if (spSubSource[ctrSrc + ctrPat] != spTarget[ctrPat]) + break; + } + if (ctrPat == targetCount) + { + retValue = ctrSrc; + break; + } + } + + if (retValue >= 0) + { + retValue += startIndex - sourceCount + 1; + } + } + else + { + int endPattern = (sourceCount - 1) - targetCount + 1; + if (endPattern < 0) + return -1; + + char patternChar0 = spTarget[0]; + for (int ctrSrc = 0; ctrSrc <= endPattern; ctrSrc++) + { + if (spSubSource[ctrSrc] != patternChar0) + continue; + int ctrPat; + for (ctrPat = 1; ctrPat < targetCount; ctrPat++) + { + if (spSubSource[ctrSrc + ctrPat] != spTarget[ctrPat]) + break; + } + if (ctrPat == targetCount) + { + retValue = ctrSrc; + break; + } + } + + if (retValue >= 0) + { + retValue += startIndex; + } + } + } + + return retValue; + } + + private unsafe SortKey CreateSortKey(String source, CompareOptions options) + { + Debug.Assert(!_invariantMode); + + if (source == null) { throw new ArgumentNullException(nameof(source)); } + Contract.EndContractBlock(); + + if ((options & ValidSortkeyCtorMaskOffFlags) != 0) + { + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + } + + byte [] keyData = null; + if (source.Length == 0) + { + keyData = Array.Empty<byte>(); + } + else + { + fixed (char *pSource = source) + { + int result = Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _sortName, + LCMAP_SORTKEY | (uint) GetNativeCompareFlags(options), + pSource, source.Length, + null, 0, + null, null, _sortHandle); + if (result == 0) + { + throw new ArgumentException(SR.Argument_InvalidFlag, "source"); + } + + keyData = new byte[result]; + + fixed (byte* pBytes = keyData) + { + result = Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _sortName, + LCMAP_SORTKEY | (uint) GetNativeCompareFlags(options), + pSource, source.Length, + pBytes, keyData.Length, + null, null, _sortHandle); + } + } + } + + return new SortKey(Name, source, options, keyData); + } + + private static unsafe bool IsSortable(char* text, int length) + { + Debug.Assert(!GlobalizationMode.Invariant); + + return Interop.Kernel32.IsNLSDefinedString(Interop.Kernel32.COMPARE_STRING, 0, IntPtr.Zero, text, length); + } + + private const int COMPARE_OPTIONS_ORDINAL = 0x40000000; // Ordinal + private const int NORM_IGNORECASE = 0x00000001; // Ignores case. (use LINGUISTIC_IGNORECASE instead) + private const int NORM_IGNOREKANATYPE = 0x00010000; // Does not differentiate between Hiragana and Katakana characters. Corresponding Hiragana and Katakana will compare as equal. + private const int NORM_IGNORENONSPACE = 0x00000002; // Ignores nonspacing. This flag also removes Japanese accent characters. (use LINGUISTIC_IGNOREDIACRITIC instead) + private const int NORM_IGNORESYMBOLS = 0x00000004; // Ignores symbols. + private const int NORM_IGNOREWIDTH = 0x00020000; // Does not differentiate between a single-byte character and the same character as a double-byte character. + private const int NORM_LINGUISTIC_CASING = 0x08000000; // use linguistic rules for casing + private const int SORT_STRINGSORT = 0x00001000; // Treats punctuation the same as symbols. + + private static int GetNativeCompareFlags(CompareOptions options) + { + // Use "linguistic casing" by default (load the culture's casing exception tables) + int nativeCompareFlags = NORM_LINGUISTIC_CASING; + + if ((options & CompareOptions.IgnoreCase) != 0) { nativeCompareFlags |= NORM_IGNORECASE; } + if ((options & CompareOptions.IgnoreKanaType) != 0) { nativeCompareFlags |= NORM_IGNOREKANATYPE; } + if ((options & CompareOptions.IgnoreNonSpace) != 0) { nativeCompareFlags |= NORM_IGNORENONSPACE; } + if ((options & CompareOptions.IgnoreSymbols) != 0) { nativeCompareFlags |= NORM_IGNORESYMBOLS; } + if ((options & CompareOptions.IgnoreWidth) != 0) { nativeCompareFlags |= NORM_IGNOREWIDTH; } + if ((options & CompareOptions.StringSort) != 0) { nativeCompareFlags |= SORT_STRINGSORT; } + + // TODO: Can we try for GetNativeCompareFlags to never + // take Ordinal or OrdinalIgnoreCase. This value is not part of Win32, we just handle it special + // in some places. + // Suffix & Prefix shouldn't use this, make sure to turn off the NORM_LINGUISTIC_CASING flag + if (options == CompareOptions.Ordinal) { nativeCompareFlags = COMPARE_OPTIONS_ORDINAL; } + + Debug.Assert(((options & ~(CompareOptions.IgnoreCase | + CompareOptions.IgnoreKanaType | + CompareOptions.IgnoreNonSpace | + CompareOptions.IgnoreSymbols | + CompareOptions.IgnoreWidth | + CompareOptions.StringSort)) == 0) || + (options == CompareOptions.Ordinal), "[CompareInfo.GetNativeCompareFlags]Expected all flags to be handled"); + + return nativeCompareFlags; + } + + private unsafe SortVersion GetSortVersion() + { + Debug.Assert(!_invariantMode); + + Interop.Kernel32.NlsVersionInfoEx nlsVersion = new Interop.Kernel32.NlsVersionInfoEx(); + Interop.Kernel32.GetNLSVersionEx(Interop.Kernel32.COMPARE_STRING, _sortName, &nlsVersion); + return new SortVersion( + nlsVersion.dwNLSVersion, + nlsVersion.dwEffectiveId == 0 ? LCID : nlsVersion.dwEffectiveId, + nlsVersion.guidCustomVersion); + } + +#if CORECLR + // Get a locale sensitive sort hash code from native code -- COMNlsInfo::InternalGetGlobalizedHashCode + [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + private static extern int InternalGetGlobalizedHashCode(IntPtr handle, string localeName, string source, int length, int dwFlags, long additionalEntropy); +#endif + } +} diff --git a/src/mscorlib/src/System/Globalization/CompareInfo.cs b/src/mscorlib/src/System/Globalization/CompareInfo.cs index 6c2230b66b..285a81d906 100644 --- a/src/mscorlib/src/System/Globalization/CompareInfo.cs +++ b/src/mscorlib/src/System/Globalization/CompareInfo.cs @@ -12,57 +12,28 @@ // //////////////////////////////////////////////////////////////////////////// -namespace System.Globalization { - - // - // We pass all of the sorting calls to the native side, preferrably to the OS to do - // the actual work. - // - - using System; - using System.Collections; - using System.Collections.Generic; - using System.Reflection; - using System.Runtime.Serialization; - using System.Runtime.CompilerServices; - using System.Runtime.ConstrainedExecution; - using System.Runtime.InteropServices; - using System.Runtime.Versioning; - using System.Threading; - using Microsoft.Win32; - using System.Security; - using System.Diagnostics; - using System.Diagnostics.Contracts; - - // - // Options can be used during string comparison. - // - // Native implementation (COMNlsInfo.cpp & SortingTable.cpp) relies on the values of these, - // If you change the values below, be sure to change the values in native part as well. - // - - -[Serializable] +using System.Reflection; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Runtime.Serialization; + +namespace System.Globalization +{ [Flags] + [Serializable] public enum CompareOptions { - None = 0x00000000, - IgnoreCase = 0x00000001, - IgnoreNonSpace = 0x00000002, - IgnoreSymbols = 0x00000004, - IgnoreKanaType = 0x00000008, // ignore kanatype - IgnoreWidth = 0x00000010, // ignore width - OrdinalIgnoreCase = 0x10000000, // This flag can not be used with other flags. - StringSort = 0x20000000, // use string sort method - Ordinal = 0x40000000, // This flag can not be used with other flags. - - // StopOnNull = 0x10000000, - - // StopOnNull is defined in SortingTable.h, but we didn't enable this option here. - // Do not use this value for other flags accidentally. + None = 0x00000000, + IgnoreCase = 0x00000001, + IgnoreNonSpace = 0x00000002, + IgnoreSymbols = 0x00000004, + IgnoreKanaType = 0x00000008, // ignore kanatype + IgnoreWidth = 0x00000010, // ignore width + OrdinalIgnoreCase = 0x10000000, // This flag can not be used with other flags. + StringSort = 0x20000000, // use string sort method + Ordinal = 0x40000000, // This flag can not be used with other flags. } - [Serializable] public partial class CompareInfo : IDeserializationCallback { @@ -81,6 +52,11 @@ namespace System.Globalization { ~(CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType); + // Mask used to check if we have the right flags. + private const CompareOptions ValidSortkeyCtorMaskOffFlags = + ~(CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace | + CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType | CompareOptions.StringSort); + // // CompareInfos have an interesting identity. They are attached to the locale that created them, // ie: en-US would have an en-US sort. For haw-US (custom), then we serialize it as haw-US. @@ -88,33 +64,21 @@ namespace System.Globalization { // locale, which is what SCOMPAREINFO does. [OptionalField(VersionAdded = 2)] - private String m_name; // The name used to construct this CompareInfo - + private string _name; // The name used to construct this CompareInfo [NonSerialized] - private String m_sortName; // The name that defines our behavior + private string _sortName; // The name that defines our behavior - [NonSerialized] - private IntPtr m_dataHandle; + [OptionalField(VersionAdded = 3)] + private SortVersion _sortVersion; - [NonSerialized] - private IntPtr m_handleOrigin; + // _invariantMode is defined for the perf reason as accessing the instance field is faster than access the static property GlobalizationMode.Invariant + [NonSerialized] + private readonly bool _invariantMode = GlobalizationMode.Invariant; - //////////////////////////////////////////////////////////////////////// - // - // CompareInfo Constructor - // - // - //////////////////////////////////////////////////////////////////////// - // Constructs an instance that most closely corresponds to the NLS locale - // identifier. internal CompareInfo(CultureInfo culture) { - this.m_name = culture.m_name; - this.m_sortName = culture.SortName; - - IntPtr handleOrigin; - this.m_dataHandle = InternalInitSortHandle(m_sortName, out handleOrigin); - this.m_handleOrigin = handleOrigin; + _name = culture._name; + InitSort(culture); } /*=================================GetCompareInfo========================== @@ -122,49 +86,52 @@ namespace System.Globalization { ** Warning: The assembly versioning mechanism is dead! **Returns: The CompareInfo for the specified culture. **Arguments: - ** culture the ID of the culture + ** culture the ID of the culture ** assembly the assembly which contains the sorting table. **Exceptions: ** ArugmentNullException when the assembly is null ** ArgumentException if culture is invalid. ============================================================================*/ -#if FEATURE_USE_LCID // Assembly constructor should be deprecated, we don't act on the assembly information any more - public static CompareInfo GetCompareInfo(int culture, Assembly assembly){ + public static CompareInfo GetCompareInfo(int culture, Assembly assembly) + { // Parameter checking. - if (assembly == null) { + if (assembly == null) + { throw new ArgumentNullException(nameof(assembly)); } - if (assembly!=typeof(Object).Module.Assembly) { - throw new ArgumentException(Environment.GetResourceString("Argument_OnlyMscorlib")); + if (assembly != typeof(Object).Module.Assembly) + { + throw new ArgumentException(SR.Argument_OnlyMscorlib); } Contract.EndContractBlock(); return GetCompareInfo(culture); } -#endif - /*=================================GetCompareInfo========================== **Action: Get the CompareInfo constructed from the data table in the specified assembly for the specified culture. ** The purpose of this method is to provide version for CompareInfo tables. **Returns: The CompareInfo for the specified culture. **Arguments: - ** name the name of the culture - ** assembly the assembly which contains the sorting table. + ** name the name of the culture + ** assembly the assembly which contains the sorting table. **Exceptions: ** ArugmentNullException when the assembly is null ** ArgumentException if name is invalid. ============================================================================*/ // Assembly constructor should be deprecated, we don't act on the assembly information any more - public static CompareInfo GetCompareInfo(String name, Assembly assembly){ - if (name == null || assembly == null) { + public static CompareInfo GetCompareInfo(string name, Assembly assembly) + { + if (name == null || assembly == null) + { throw new ArgumentNullException(name == null ? nameof(name) : nameof(assembly)); } Contract.EndContractBlock(); - if (assembly!=typeof(Object).Module.Assembly) { - throw new ArgumentException(Environment.GetResourceString("Argument_OnlyMscorlib")); + if (assembly != typeof(Object).Module.Assembly) + { + throw new ArgumentException(SR.Argument_OnlyMscorlib); } return GetCompareInfo(name); @@ -179,20 +146,17 @@ namespace System.Globalization { **Exceptions: ** ArgumentException if culture is invalid. ============================================================================*/ - -#if FEATURE_USE_LCID // People really shouldn't be calling LCID versions, no custom support public static CompareInfo GetCompareInfo(int culture) { if (CultureData.IsCustomCultureId(culture)) { // Customized culture cannot be created by the LCID. - throw new ArgumentException(Environment.GetResourceString("Argument_CustomCultureCannotBePassedByNumber", nameof(culture))); + throw new ArgumentException(SR.Argument_CustomCultureCannotBePassedByNumber, nameof(culture)); } return CultureInfo.GetCultureInfo(culture).CompareInfo; } -#endif /*=================================GetCompareInfo========================== **Action: Get the CompareInfo for the specified culture. @@ -203,7 +167,7 @@ namespace System.Globalization { ** ArgumentException if name is invalid. ============================================================================*/ - public static CompareInfo GetCompareInfo(String name) + public static CompareInfo GetCompareInfo(string name) { if (name == null) { @@ -214,64 +178,51 @@ namespace System.Globalization { return CultureInfo.GetCultureInfo(name).CompareInfo; } - public static bool IsSortable(char ch) { - return(IsSortable(ch.ToString())); + public static unsafe bool IsSortable(char ch) + { + if (GlobalizationMode.Invariant) + { + return true; + } + char *pChar = &ch; + return IsSortable(pChar, 1); } - public static bool IsSortable(String text) { - if (text == null) { + public static unsafe bool IsSortable(string text) + { + if (text == null) + { // A null param is invalid here. throw new ArgumentNullException(nameof(text)); } - if (0 == text.Length) { + if (text.Length == 0) + { // A zero length string is not invalid, but it is also not sortable. - return(false); + return (false); } - CompareInfo c = CultureInfo.InvariantCulture.CompareInfo; - - return (InternalIsSortable(c.m_dataHandle, c.m_handleOrigin, c.m_sortName, text, text.Length)); + if (GlobalizationMode.Invariant) + { + return true; + } + + fixed (char *pChar = text) + { + return IsSortable(pChar, text.Length); + } } -#region Serialization - // the following fields are defined to keep the compatibility with Whidbey. - // don't change/remove the names/types of these fields. -#if FEATURE_USE_LCID - [OptionalField(VersionAdded = 1)] - private int win32LCID; // mapped sort culture id of this instance - private int culture; // the culture ID used to create this instance. -#endif [OnDeserializing] private void OnDeserializing(StreamingContext ctx) { - this.m_name = null; + _name = null; } - private void OnDeserialized() + void IDeserializationCallback.OnDeserialization(Object sender) { - CultureInfo ci; - // If we didn't have a name, use the LCID - if (this.m_name == null) - { -#if FEATURE_USE_LCID - // From whidbey, didn't have a name - ci = CultureInfo.GetCultureInfo(this.culture); - this.m_name = ci.m_name; - this.m_sortName = ci.SortName; -#endif - } - else - { - ci = CultureInfo.GetCultureInfo(m_name); - this.m_sortName = ci.SortName; - } - - IntPtr handleOrigin; - this.m_dataHandle = InternalInitSortHandle(m_sortName, out handleOrigin); - this.m_handleOrigin = handleOrigin; - + OnDeserialized(); } [OnDeserialized] @@ -280,23 +231,16 @@ namespace System.Globalization { OnDeserialized(); } - [OnSerializing] - private void OnSerializing(StreamingContext ctx) - { -#if FEATURE_USE_LCID - // This is merely for serialization compatibility with Whidbey/Orcas, it can go away when we don't want that compat any more. - culture = CultureInfo.GetCultureInfo(this.Name).LCID; // This is the lcid of the constructing culture (still have to dereference to get target sort) - Debug.Assert(m_name != null, "CompareInfo.OnSerializing - expected m_name to be set already"); -#endif - } - - void IDeserializationCallback.OnDeserialization(Object sender) + private void OnDeserialized() { - OnDeserialized(); + if (_name != null) + { + InitSort(CultureInfo.GetCultureInfo(_name)); + } } -#endregion Serialization - + [OnSerializing] + private void OnSerializing(StreamingContext ctx) { } ///////////////////////////----- Name -----///////////////////////////////// // @@ -311,65 +255,20 @@ namespace System.Globalization { // //////////////////////////////////////////////////////////////////////// - public virtual String Name + public virtual string Name { get { - Debug.Assert(m_name != null, "CompareInfo.Name Expected m_name to be set"); - return (m_sortName); - } - } + Debug.Assert(_name != null, "CompareInfo.Name Expected _name to be set"); + if (_name == "zh-CHT" || _name == "zh-CHS") + { + return _name; + } - // These flags are used in the native Win32. so we need to map the managed options to those flags - private const int LINGUISTIC_IGNORECASE = 0x00000010; // linguistically appropriate 'ignore case' - private const int NORM_IGNORECASE = 0x00000001; // Ignores case. (use LINGUISTIC_IGNORECASE instead) - private const int NORM_IGNOREKANATYPE = 0x00010000; // Does not differentiate between Hiragana and Katakana characters. Corresponding Hiragana and Katakana will compare as equal. - private const int LINGUISTIC_IGNOREDIACRITIC = 0x00000020; // linguistically appropriate 'ignore nonspace' - private const int NORM_IGNORENONSPACE = 0x00000002; // Ignores nonspacing. This flag also removes Japanese accent characters. (use LINGUISTIC_IGNOREDIACRITIC instead) - private const int NORM_IGNORESYMBOLS = 0x00000004; // Ignores symbols. - private const int NORM_IGNOREWIDTH = 0x00020000; // Does not differentiate between a single-byte character and the same character as a double-byte character. - private const int SORT_STRINGSORT = 0x00001000; // Treats punctuation the same as symbols. - private const int COMPARE_OPTIONS_ORDINAL = 0x40000000; // Ordinal (handled by Comnlsinfo) - internal const int NORM_LINGUISTIC_CASING = 0x08000000; // use linguistic rules for casing - - - private const int RESERVED_FIND_ASCII_STRING = 0x20000000; // This flag used only to tell the sorting DLL can assume the string characters are in ASCII. - - [Pure] - internal static int GetNativeCompareFlags(CompareOptions options) - { - // some NLS VM functions can handle COMPARE_OPTIONS_ORDINAL - // in which case options should be simply cast to int instead of using this function - // Does not look like the best approach to me but for now I am going to leave it as it is - Debug.Assert(options != CompareOptions.OrdinalIgnoreCase, "[CompareInfo.GetNativeCompareFlags]CompareOptions.OrdinalIgnoreCase should be handled separately"); - - // Use "linguistic casing" by default (load the culture's casing exception tables) - int nativeCompareFlags = NORM_LINGUISTIC_CASING; - - if ((options & CompareOptions.IgnoreCase) != 0) { nativeCompareFlags |= NORM_IGNORECASE; } - if ((options & CompareOptions.IgnoreKanaType) != 0) { nativeCompareFlags |= NORM_IGNOREKANATYPE; } - if ((options & CompareOptions.IgnoreNonSpace) != 0) { nativeCompareFlags |= NORM_IGNORENONSPACE; } - if ((options & CompareOptions.IgnoreSymbols) != 0) { nativeCompareFlags |= NORM_IGNORESYMBOLS; } - if ((options & CompareOptions.IgnoreWidth) != 0) { nativeCompareFlags |= NORM_IGNOREWIDTH; } - if ((options & CompareOptions.StringSort) != 0) { nativeCompareFlags |= SORT_STRINGSORT; } - - // Suffix & Prefix shouldn't use this, make sure to turn off the NORM_LINGUISTIC_CASING flag - if (options == CompareOptions.Ordinal) { nativeCompareFlags = COMPARE_OPTIONS_ORDINAL; } - - Debug.Assert(((options & ~(CompareOptions.IgnoreCase | - CompareOptions.IgnoreKanaType | - CompareOptions.IgnoreNonSpace | - CompareOptions.IgnoreSymbols | - CompareOptions.IgnoreWidth | - CompareOptions.StringSort)) == 0) || - (options == CompareOptions.Ordinal), "[CompareInfo.GetNativeCompareFlags]Expected all flags to be handled"); - - Debug.Assert((nativeCompareFlags & RESERVED_FIND_ASCII_STRING) == 0, "[CompareInfo.GetNativeCompareFlags] RESERVED_FIND_ASCII_STRING shouldn't be set here"); - - return nativeCompareFlags; + return _sortName; + } } - //////////////////////////////////////////////////////////////////////// // // Compare @@ -381,14 +280,13 @@ namespace System.Globalization { // //////////////////////////////////////////////////////////////////////// - - public virtual int Compare(String string1, String string2) + public virtual int Compare(string string1, string string2) { return (Compare(string1, string2, CompareOptions.None)); } - public unsafe virtual int Compare(String string1, String string2, CompareOptions options){ - + public unsafe virtual int Compare(string string1, string string2, CompareOptions options) + { if (options == CompareOptions.OrdinalIgnoreCase) { return String.Compare(string1, string2, StringComparison.OrdinalIgnoreCase); @@ -399,29 +297,41 @@ namespace System.Globalization { { if (options != CompareOptions.Ordinal) { - throw new ArgumentException(Environment.GetResourceString("Argument_CompareOptionOrdinal"), nameof(options)); - } + throw new ArgumentException(SR.Argument_CompareOptionOrdinal, nameof(options)); + } + return String.CompareOrdinal(string1, string2); - } + } if ((options & ValidCompareMaskOffFlags) != 0) { - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(options)); + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); } //Our paradigm is that null sorts less than any other string and //that two nulls sort as equal. - if (string1 == null) { - if (string2 == null) { + if (string1 == null) + { + if (string2 == null) + { return (0); // Equal } return (-1); // null < non-null } - if (string2 == null) { + if (string2 == null) + { return (1); // non-null > null } - return InternalCompareString(m_dataHandle, m_handleOrigin, m_sortName, string1, 0, string1.Length, string2, 0, string2.Length, GetNativeCompareFlags(options)); + if (_invariantMode) + { + if ((options & CompareOptions.IgnoreCase) != 0) + return CompareOrdinalIgnoreCase(string1, 0, string1.Length, string2, 0, string2.Length); + + return String.CompareOrdinal(string1, string2); + } + + return CompareString(string1, 0, string1.Length, string2, 0, string2.Length, options); } @@ -438,63 +348,63 @@ namespace System.Globalization { //////////////////////////////////////////////////////////////////////// - public unsafe virtual int Compare(String string1, int offset1, int length1, String string2, int offset2, int length2) + public unsafe virtual int Compare(string string1, int offset1, int length1, string string2, int offset2, int length2) { return Compare(string1, offset1, length1, string2, offset2, length2, 0); } - public unsafe virtual int Compare(String string1, int offset1, String string2, int offset2, CompareOptions options) + public virtual int Compare(string string1, int offset1, string string2, int offset2, CompareOptions options) { - return Compare(string1, offset1, string1 == null ? 0 : string1.Length-offset1, - string2, offset2, string2 == null ? 0 : string2.Length-offset2, options); + return Compare(string1, offset1, string1 == null ? 0 : string1.Length - offset1, + string2, offset2, string2 == null ? 0 : string2.Length - offset2, options); } - public unsafe virtual int Compare(String string1, int offset1, String string2, int offset2) + public virtual int Compare(string string1, int offset1, string string2, int offset2) { return Compare(string1, offset1, string2, offset2, 0); } - public unsafe virtual int Compare(String string1, int offset1, int length1, String string2, int offset2, int length2, CompareOptions options) + public virtual int Compare(string string1, int offset1, int length1, string string2, int offset2, int length2, CompareOptions options) { if (options == CompareOptions.OrdinalIgnoreCase) { - int result = String.Compare(string1, offset1, string2, offset2, length1<length2 ? length1 : length2, StringComparison.OrdinalIgnoreCase); + int result = String.Compare(string1, offset1, string2, offset2, length1 < length2 ? length1 : length2, StringComparison.OrdinalIgnoreCase); if ((length1 != length2) && result == 0) - return (length1 > length2? 1: -1); + return (length1 > length2 ? 1 : -1); return (result); } // Verify inputs if (length1 < 0 || length2 < 0) { - throw new ArgumentOutOfRangeException((length1 < 0) ? nameof(length1) : nameof(length2), Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); + throw new ArgumentOutOfRangeException((length1 < 0) ? nameof(length1) : nameof(length2), SR.ArgumentOutOfRange_NeedPosNum); } if (offset1 < 0 || offset2 < 0) { - throw new ArgumentOutOfRangeException((offset1 < 0) ? nameof(offset1) : nameof(offset2), Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); + throw new ArgumentOutOfRangeException((offset1 < 0) ? nameof(offset1) : nameof(offset2), SR.ArgumentOutOfRange_NeedPosNum); } if (offset1 > (string1 == null ? 0 : string1.Length) - length1) { - throw new ArgumentOutOfRangeException(nameof(string1), Environment.GetResourceString("ArgumentOutOfRange_OffsetLength")); + throw new ArgumentOutOfRangeException(nameof(string1), SR.ArgumentOutOfRange_OffsetLength); } if (offset2 > (string2 == null ? 0 : string2.Length) - length2) { - throw new ArgumentOutOfRangeException(nameof(string2), Environment.GetResourceString("ArgumentOutOfRange_OffsetLength")); + throw new ArgumentOutOfRangeException(nameof(string2), SR.ArgumentOutOfRange_OffsetLength); } if ((options & CompareOptions.Ordinal) != 0) { if (options != CompareOptions.Ordinal) { - throw new ArgumentException(Environment.GetResourceString("Argument_CompareOptionOrdinal"), + throw new ArgumentException(SR.Argument_CompareOptionOrdinal, nameof(options)); } } else if ((options & ValidCompareMaskOffFlags) != 0) { - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(options)); + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); } // @@ -515,12 +425,89 @@ namespace System.Globalization { if (options == CompareOptions.Ordinal) { - return string.CompareOrdinalHelper(string1, offset1, length1, string2, offset2, length2); + return CompareOrdinal(string1, offset1, length1, + string2, offset2, length2); + } + + if (_invariantMode) + { + if ((options & CompareOptions.IgnoreCase) != 0) + return CompareOrdinalIgnoreCase(string1, offset1, length1, string2, offset2, length2); + + return CompareOrdinal(string1, offset1, length1, string2, offset2, length2); + } + + return CompareString(string1, offset1, length1, + string2, offset2, length2, + options); + } + + private static int CompareOrdinal(string string1, int offset1, int length1, string string2, int offset2, int length2) + { + int result = String.CompareOrdinal(string1, offset1, string2, offset2, + (length1 < length2 ? length1 : length2)); + if ((length1 != length2) && result == 0) + { + return (length1 > length2 ? 1 : -1); + } + return (result); + } + + // + // CompareOrdinalIgnoreCase compare two string ordinally with ignoring the case. + // it assumes the strings are Ascii string till we hit non Ascii character in strA or strB and then we continue the comparison by + // calling the OS. + // + internal static unsafe int CompareOrdinalIgnoreCase(string strA, int indexA, int lengthA, string strB, int indexB, int lengthB) + { + Debug.Assert(indexA + lengthA <= strA.Length); + Debug.Assert(indexB + lengthB <= strB.Length); + + int length = Math.Min(lengthA, lengthB); + int range = length; + + fixed (char* ap = strA) fixed (char* bp = strB) + { + char* a = ap + indexA; + char* b = bp + indexB; + + // in InvariantMode we support all range and not only the ascii characters. + char maxChar = (char) (GlobalizationMode.Invariant ? 0xFFFF : 0x80); + + while (length != 0 && (*a <= maxChar) && (*b <= maxChar)) + { + int charA = *a; + int charB = *b; + + if (charA == charB) + { + a++; b++; + length--; + continue; + } + + // uppercase both chars - notice that we need just one compare per char + if ((uint)(charA - 'a') <= (uint)('z' - 'a')) charA -= 0x20; + if ((uint)(charB - 'a') <= (uint)('z' - 'a')) charB -= 0x20; + + // Return the (case-insensitive) difference between them. + if (charA != charB) + return charA - charB; + + // Next char + a++; b++; + length--; + } + + if (length == 0) + return lengthA - lengthB; + + Debug.Assert(!GlobalizationMode.Invariant); + + range -= length; + + return CompareStringOrdinalIgnoreCase(a, lengthA - range, b, lengthB - range); } - return InternalCompareString(this.m_dataHandle, this.m_handleOrigin, this.m_sortName, - string1, offset1, length1, - string2, offset2, length2, - GetNativeCompareFlags(options)); } //////////////////////////////////////////////////////////////////////// @@ -531,22 +518,25 @@ namespace System.Globalization { // String.Empty, true is returned. // //////////////////////////////////////////////////////////////////////// - - - public unsafe virtual bool IsPrefix(String source, String prefix, CompareOptions options) + public virtual bool IsPrefix(string source, string prefix, CompareOptions options) { - if (source == null || prefix == null) { + if (source == null || prefix == null) + { throw new ArgumentNullException((source == null ? nameof(source) : nameof(prefix)), - Environment.GetResourceString("ArgumentNull_String")); + SR.ArgumentNull_String); } Contract.EndContractBlock(); - int prefixLen = prefix.Length; - if (prefixLen == 0) + if (prefix.Length == 0) { return (true); } + if (source.Length == 0) + { + return false; + } + if (options == CompareOptions.OrdinalIgnoreCase) { return source.StartsWith(prefix, StringComparison.OrdinalIgnoreCase); @@ -557,26 +547,24 @@ namespace System.Globalization { return source.StartsWith(prefix, StringComparison.Ordinal); } - if ((options & ValidIndexMaskOffFlags) != 0) { - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(options)); + if ((options & ValidIndexMaskOffFlags) != 0) + { + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); } + if (_invariantMode) + { + return source.StartsWith(prefix, (options & CompareOptions.IgnoreCase) != 0 ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); + } - // to let the sorting DLL do the call optimization in case of Ascii strings, we check if the strings are in Ascii and then send the flag RESERVED_FIND_ASCII_STRING to - // the sorting DLL API SortFindString so sorting DLL don't have to check if the string is Ascii with every call to SortFindString. - - return (InternalFindNLSStringEx( - m_dataHandle, m_handleOrigin, m_sortName, - GetNativeCompareFlags(options) | Win32Native.FIND_STARTSWITH | ((source.IsAscii() && prefix.IsAscii()) ? RESERVED_FIND_ASCII_STRING : 0), - source, source.Length, 0, prefix, prefix.Length) > -1); + return StartsWith(source, prefix, options); } - public virtual bool IsPrefix(String source, String prefix) + public virtual bool IsPrefix(string source, string prefix) { return (IsPrefix(source, prefix, 0)); } - //////////////////////////////////////////////////////////////////////// // // IsSuffix @@ -585,44 +573,50 @@ namespace System.Globalization { // String.Empty, true is returned. // //////////////////////////////////////////////////////////////////////// - - - public unsafe virtual bool IsSuffix(String source, String suffix, CompareOptions options) + public virtual bool IsSuffix(string source, string suffix, CompareOptions options) { - if (source == null || suffix == null) { + if (source == null || suffix == null) + { throw new ArgumentNullException((source == null ? nameof(source) : nameof(suffix)), - Environment.GetResourceString("ArgumentNull_String")); + SR.ArgumentNull_String); } Contract.EndContractBlock(); - int suffixLen = suffix.Length; - if (suffixLen == 0) + if (suffix.Length == 0) { return (true); } - if (options == CompareOptions.OrdinalIgnoreCase) { + if (source.Length == 0) + { + return false; + } + + if (options == CompareOptions.OrdinalIgnoreCase) + { return source.EndsWith(suffix, StringComparison.OrdinalIgnoreCase); } - if (options == CompareOptions.Ordinal) { + if (options == CompareOptions.Ordinal) + { return source.EndsWith(suffix, StringComparison.Ordinal); } - if ((options & ValidIndexMaskOffFlags) != 0) { - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(options)); + if ((options & ValidIndexMaskOffFlags) != 0) + { + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + } + + if (_invariantMode) + { + return source.EndsWith(suffix, (options & CompareOptions.IgnoreCase) != 0 ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); } - // to let the sorting DLL do the call optimization in case of Ascii strings, we check if the strings are in Ascii and then send the flag RESERVED_FIND_ASCII_STRING to - // the sorting DLL API SortFindString so sorting DLL don't have to check if the string is Ascii with every call to SortFindString. - return InternalFindNLSStringEx( - m_dataHandle, m_handleOrigin, m_sortName, - GetNativeCompareFlags(options) | Win32Native.FIND_ENDSWITH | ((source.IsAscii() && suffix.IsAscii()) ? RESERVED_FIND_ASCII_STRING : 0), - source, source.Length, source.Length - 1, suffix, suffix.Length) >= 0; + return EndsWith(source, suffix, options); } - public virtual bool IsSuffix(String source, String suffix) + public virtual bool IsSuffix(string source, string suffix) { return (IsSuffix(source, suffix, 0)); } @@ -641,9 +635,9 @@ namespace System.Globalization { //////////////////////////////////////////////////////////////////////// - public unsafe virtual int IndexOf(String source, char value) + public virtual int IndexOf(string source, char value) { - if (source==null) + if (source == null) throw new ArgumentNullException(nameof(source)); Contract.EndContractBlock(); @@ -651,9 +645,9 @@ namespace System.Globalization { } - public unsafe virtual int IndexOf(String source, String value) + public virtual int IndexOf(string source, string value) { - if (source==null) + if (source == null) throw new ArgumentNullException(nameof(source)); Contract.EndContractBlock(); @@ -661,9 +655,9 @@ namespace System.Globalization { } - public unsafe virtual int IndexOf(String source, char value, CompareOptions options) + public virtual int IndexOf(string source, char value, CompareOptions options) { - if (source==null) + if (source == null) throw new ArgumentNullException(nameof(source)); Contract.EndContractBlock(); @@ -671,17 +665,16 @@ namespace System.Globalization { } - public unsafe virtual int IndexOf(String source, String value, CompareOptions options) + public virtual int IndexOf(string source, string value, CompareOptions options) { - if (source==null) + if (source == null) throw new ArgumentNullException(nameof(source)); Contract.EndContractBlock(); return IndexOf(source, value, 0, source.Length, options); } - - public unsafe virtual int IndexOf(String source, char value, int startIndex) + public virtual int IndexOf(string source, char value, int startIndex) { if (source == null) throw new ArgumentNullException(nameof(source)); @@ -690,8 +683,7 @@ namespace System.Globalization { return IndexOf(source, value, startIndex, source.Length - startIndex, CompareOptions.None); } - - public unsafe virtual int IndexOf(String source, String value, int startIndex) + public virtual int IndexOf(string source, string value, int startIndex) { if (source == null) throw new ArgumentNullException(nameof(source)); @@ -700,8 +692,7 @@ namespace System.Globalization { return IndexOf(source, value, startIndex, source.Length - startIndex, CompareOptions.None); } - - public unsafe virtual int IndexOf(String source, char value, int startIndex, CompareOptions options) + public virtual int IndexOf(string source, char value, int startIndex, CompareOptions options) { if (source == null) throw new ArgumentNullException(nameof(source)); @@ -711,7 +702,7 @@ namespace System.Globalization { } - public unsafe virtual int IndexOf(String source, String value, int startIndex, CompareOptions options) + public virtual int IndexOf(string source, string value, int startIndex, CompareOptions options) { if (source == null) throw new ArgumentNullException(nameof(source)); @@ -721,28 +712,28 @@ namespace System.Globalization { } - public unsafe virtual int IndexOf(String source, char value, int startIndex, int count) + public virtual int IndexOf(string source, char value, int startIndex, int count) { return IndexOf(source, value, startIndex, count, CompareOptions.None); } - public unsafe virtual int IndexOf(String source, String value, int startIndex, int count) + public virtual int IndexOf(string source, string value, int startIndex, int count) { return IndexOf(source, value, startIndex, count, CompareOptions.None); } - public unsafe virtual int IndexOf(String source, char value, int startIndex, int count, CompareOptions options) + public unsafe virtual int IndexOf(string source, char value, int startIndex, int count, CompareOptions options) { // Validate inputs if (source == null) throw new ArgumentNullException(nameof(source)); if (startIndex < 0 || startIndex > source.Length) - throw new ArgumentOutOfRangeException(nameof(startIndex), Environment.GetResourceString("ArgumentOutOfRange_Index")); + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); if (count < 0 || startIndex > source.Length - count) - throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_Count")); + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count); Contract.EndContractBlock(); if (options == CompareOptions.OrdinalIgnoreCase) @@ -753,18 +744,16 @@ namespace System.Globalization { // Validate CompareOptions // Ordinal can't be selected with other flags if ((options & ValidIndexMaskOffFlags) != 0 && (options != CompareOptions.Ordinal)) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(options)); - - // to let the sorting DLL do the call optimization in case of Ascii strings, we check if the strings are in Ascii and then send the flag RESERVED_FIND_ASCII_STRING to - // the sorting DLL API SortFindString so sorting DLL don't have to check if the string is Ascii with every call to SortFindString. - return InternalFindNLSStringEx( - m_dataHandle, m_handleOrigin, m_sortName, - GetNativeCompareFlags(options) | Win32Native.FIND_FROMSTART | ((source.IsAscii() && (value <= '\x007f')) ? RESERVED_FIND_ASCII_STRING : 0), - source, count, startIndex, new String(value, 1), 1); + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + + if (_invariantMode) + return IndexOfOrdinal(source, new string(value, 1), startIndex, count, ignoreCase: (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0); + + return IndexOfCore(source, new string(value, 1), startIndex, count, options, null); } - public unsafe virtual int IndexOf(String source, String value, int startIndex, int count, CompareOptions options) + public unsafe virtual int IndexOf(string source, string value, int startIndex, int count, CompareOptions options) { // Validate inputs if (source == null) @@ -774,7 +763,7 @@ namespace System.Globalization { if (startIndex > source.Length) { - throw new ArgumentOutOfRangeException(nameof(startIndex), Environment.GetResourceString("ArgumentOutOfRange_Index")); + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); } Contract.EndContractBlock(); @@ -791,28 +780,36 @@ namespace System.Globalization { if (startIndex < 0) { - throw new ArgumentOutOfRangeException(nameof(startIndex), Environment.GetResourceString("ArgumentOutOfRange_Index")); + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); } if (count < 0 || startIndex > source.Length - count) - throw new ArgumentOutOfRangeException(nameof(count),Environment.GetResourceString("ArgumentOutOfRange_Count")); + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count); if (options == CompareOptions.OrdinalIgnoreCase) { - return source.IndexOf(value, startIndex, count, StringComparison.OrdinalIgnoreCase); + return IndexOfOrdinal(source, value, startIndex, count, ignoreCase: true); } // Validate CompareOptions // Ordinal can't be selected with other flags if ((options & ValidIndexMaskOffFlags) != 0 && (options != CompareOptions.Ordinal)) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(options)); - - // to let the sorting DLL do the call optimization in case of Ascii strings, we check if the strings are in Ascii and then send the flag RESERVED_FIND_ASCII_STRING to - // the sorting DLL API SortFindString so sorting DLL don't have to check if the string is Ascii with every call to SortFindString. - return InternalFindNLSStringEx( - m_dataHandle, m_handleOrigin, m_sortName, - GetNativeCompareFlags(options) | Win32Native.FIND_FROMSTART | ((source.IsAscii() && value.IsAscii()) ? RESERVED_FIND_ASCII_STRING : 0), - source, count, startIndex, value, value.Length); + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); + + if (_invariantMode) + return IndexOfOrdinal(source, value, startIndex, count, ignoreCase: (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0); + + return IndexOfCore(source, value, startIndex, count, options, null); + } + + internal int IndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase) + { + if (_invariantMode) + { + return InvariantIndexOf(source, value, startIndex, count, ignoreCase); + } + + return IndexOfOrdinalCore(source, value, startIndex, count, ignoreCase); } //////////////////////////////////////////////////////////////////////// @@ -829,21 +826,20 @@ namespace System.Globalization { //////////////////////////////////////////////////////////////////////// - public unsafe virtual int LastIndexOf(String source, char value) + public virtual int LastIndexOf(String source, char value) { - if (source==null) + if (source == null) throw new ArgumentNullException(nameof(source)); Contract.EndContractBlock(); // Can't start at negative index, so make sure we check for the length == 0 case. - return LastIndexOf(source, value, source.Length - 1, - source.Length, CompareOptions.None); + return LastIndexOf(source, value, source.Length - 1, source.Length, CompareOptions.None); } - public virtual int LastIndexOf(String source, String value) + public virtual int LastIndexOf(string source, string value) { - if (source==null) + if (source == null) throw new ArgumentNullException(nameof(source)); Contract.EndContractBlock(); @@ -853,9 +849,9 @@ namespace System.Globalization { } - public virtual int LastIndexOf(String source, char value, CompareOptions options) + public virtual int LastIndexOf(string source, char value, CompareOptions options) { - if (source==null) + if (source == null) throw new ArgumentNullException(nameof(source)); Contract.EndContractBlock(); @@ -864,58 +860,55 @@ namespace System.Globalization { source.Length, options); } - public unsafe virtual int LastIndexOf(String source, String value, CompareOptions options) + public virtual int LastIndexOf(string source, string value, CompareOptions options) { - if (source==null) + if (source == null) throw new ArgumentNullException(nameof(source)); Contract.EndContractBlock(); // Can't start at negative index, so make sure we check for the length == 0 case. - return LastIndexOf(source, value, source.Length - 1, - source.Length, options); + return LastIndexOf(source, value, source.Length - 1, source.Length, options); } - - public unsafe virtual int LastIndexOf(String source, char value, int startIndex) + public virtual int LastIndexOf(string source, char value, int startIndex) { return LastIndexOf(source, value, startIndex, startIndex + 1, CompareOptions.None); } - public unsafe virtual int LastIndexOf(String source, String value, int startIndex) + public virtual int LastIndexOf(string source, string value, int startIndex) { return LastIndexOf(source, value, startIndex, startIndex + 1, CompareOptions.None); } - - public unsafe virtual int LastIndexOf(String source, char value, int startIndex, CompareOptions options) + public virtual int LastIndexOf(string source, char value, int startIndex, CompareOptions options) { return LastIndexOf(source, value, startIndex, startIndex + 1, options); } - public unsafe virtual int LastIndexOf(String source, String value, int startIndex, CompareOptions options) + public virtual int LastIndexOf(string source, string value, int startIndex, CompareOptions options) { return LastIndexOf(source, value, startIndex, startIndex + 1, options); } - public unsafe virtual int LastIndexOf(String source, char value, int startIndex, int count) + public virtual int LastIndexOf(string source, char value, int startIndex, int count) { return LastIndexOf(source, value, startIndex, count, CompareOptions.None); } - public unsafe virtual int LastIndexOf(String source, String value, int startIndex, int count) + public virtual int LastIndexOf(string source, string value, int startIndex, int count) { return LastIndexOf(source, value, startIndex, count, CompareOptions.None); } - public unsafe virtual int LastIndexOf(String source, char value, int startIndex, int count, CompareOptions options) + public virtual int LastIndexOf(string source, char value, int startIndex, int count, CompareOptions options) { // Verify Arguments - if (source==null) + if (source == null) throw new ArgumentNullException(nameof(source)); Contract.EndContractBlock(); @@ -924,7 +917,7 @@ namespace System.Globalization { if ((options & ValidIndexMaskOffFlags) != 0 && (options != CompareOptions.Ordinal) && (options != CompareOptions.OrdinalIgnoreCase)) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(options)); + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); // Special case for 0 length input strings if (source.Length == 0 && (startIndex == -1 || startIndex == 0)) @@ -932,7 +925,7 @@ namespace System.Globalization { // Make sure we're not out of range if (startIndex < 0 || startIndex > source.Length) - throw new ArgumentOutOfRangeException(nameof(startIndex), Environment.GetResourceString("ArgumentOutOfRange_Index")); + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); // Make sure that we allow startIndex == source.Length if (startIndex == source.Length) @@ -944,23 +937,21 @@ namespace System.Globalization { // 2nd have of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0. if (count < 0 || startIndex - count + 1 < 0) - throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_Count")); + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count); if (options == CompareOptions.OrdinalIgnoreCase) { return source.LastIndexOf(value.ToString(), startIndex, count, StringComparison.OrdinalIgnoreCase); } - // to let the sorting DLL do the call optimization in case of Ascii strings, we check if the strings are in Ascii and then send the flag RESERVED_FIND_ASCII_STRING to - // the sorting DLL API SortFindString so sorting DLL don't have to check if the string is Ascii with every call to SortFindString. - return InternalFindNLSStringEx( - m_dataHandle, m_handleOrigin, m_sortName, - GetNativeCompareFlags(options) | Win32Native.FIND_FROMEND | ((source.IsAscii() && (value <= '\x007f')) ? RESERVED_FIND_ASCII_STRING : 0), - source, count, startIndex, new String(value, 1), 1); + if (_invariantMode) + return InvariantLastIndexOf(source, new string(value, 1), startIndex, count, (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0); + + return LastIndexOfCore(source, value.ToString(), startIndex, count, options); } - public unsafe virtual int LastIndexOf(String source, String value, int startIndex, int count, CompareOptions options) + public virtual int LastIndexOf(string source, string value, int startIndex, int count, CompareOptions options) { // Verify Arguments if (source == null) @@ -974,7 +965,7 @@ namespace System.Globalization { if ((options & ValidIndexMaskOffFlags) != 0 && (options != CompareOptions.Ordinal) && (options != CompareOptions.OrdinalIgnoreCase)) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(options)); + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); // Special case for 0 length input strings if (source.Length == 0 && (startIndex == -1 || startIndex == 0)) @@ -982,7 +973,7 @@ namespace System.Globalization { // Make sure we're not out of range if (startIndex < 0 || startIndex > source.Length) - throw new ArgumentOutOfRangeException(nameof(startIndex), Environment.GetResourceString("ArgumentOutOfRange_Index")); + throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index); // Make sure that we allow startIndex == source.Length if (startIndex == source.Length) @@ -998,21 +989,28 @@ namespace System.Globalization { // 2nd half of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0. if (count < 0 || startIndex - count + 1 < 0) - throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_Count")); + throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Count); if (options == CompareOptions.OrdinalIgnoreCase) { - return source.LastIndexOf(value, startIndex, count, StringComparison.OrdinalIgnoreCase); + return LastIndexOfOrdinal(source, value, startIndex, count, ignoreCase: true); } - // to let the sorting DLL do the call optimization in case of Ascii strings, we check if the strings are in Ascii and then send the flag RESERVED_FIND_ASCII_STRING to - // the sorting DLL API SortFindString so sorting DLL don't have to check if the string is Ascii with every call to SortFindString. - return InternalFindNLSStringEx( - m_dataHandle, m_handleOrigin, m_sortName, - GetNativeCompareFlags(options) | Win32Native.FIND_FROMEND | ((source.IsAscii() && value.IsAscii()) ? RESERVED_FIND_ASCII_STRING : 0), - source, count, startIndex, value, value.Length); + if (_invariantMode) + return InvariantLastIndexOf(source, value, startIndex, count, (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0); + + return LastIndexOfCore(source, value, startIndex, count, options); } + internal int LastIndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase) + { + if (_invariantMode) + { + return InvariantLastIndexOf(source, value, startIndex, count, ignoreCase); + } + + return LastIndexOfOrdinalCore(source, value, startIndex, count, ignoreCase); + } //////////////////////////////////////////////////////////////////////// // @@ -1021,74 +1019,23 @@ namespace System.Globalization { // Gets the SortKey for the given string with the given options. // //////////////////////////////////////////////////////////////////////// - public unsafe virtual SortKey GetSortKey(String source, CompareOptions options) + public virtual SortKey GetSortKey(string source, CompareOptions options) { + if (_invariantMode) + return InvariantCreateSortKey(source, options); + return CreateSortKey(source, options); } - public unsafe virtual SortKey GetSortKey(String source) + public virtual SortKey GetSortKey(string source) { - return CreateSortKey(source, CompareOptions.None); - } + if (_invariantMode) + return InvariantCreateSortKey(source, CompareOptions.None); - private SortKey CreateSortKey(String source, CompareOptions options) - { - if (source==null) { throw new ArgumentNullException(nameof(source)); } - Contract.EndContractBlock(); - - // Mask used to check if we have the right flags. - const CompareOptions ValidSortkeyCtorMaskOffFlags = ~(CompareOptions.IgnoreCase | - CompareOptions.IgnoreSymbols | - CompareOptions.IgnoreNonSpace | - CompareOptions.IgnoreWidth | - CompareOptions.IgnoreKanaType | - CompareOptions.StringSort); - - if ((options & ValidSortkeyCtorMaskOffFlags) != 0) - { - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(options)); - } - byte[] keyData = null; - // The OS doesn't have quite the same behavior so we have to test for empty inputs - if (String.IsNullOrEmpty(source)) - { - // Empty strings get an empty sort key - keyData = EmptyArray<Byte>.Value; - // Fake value to test though so we can verify our flags - source = "\x0000"; - } - - int flags = GetNativeCompareFlags(options); - - // Go ahead and call the OS - // First get the count - int length = InternalGetSortKey(m_dataHandle, m_handleOrigin, m_sortName, flags, source, source.Length, null, 0); - - // If there was an error, return an error - if (length == 0) - { - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(source)); - } - - // If input was empty, return the empty byte[] we made earlier and skip this - if (keyData == null) - { - // Make an appropriate byte array - keyData = new byte[length]; - - // Fill up the array - length = InternalGetSortKey(m_dataHandle, m_handleOrigin, m_sortName, flags, source, source.Length, keyData, keyData.Length); - } - else - { - source = String.Empty; // back to original - } - - return new SortKey(Name, source, options, keyData); + return CreateSortKey(source, CompareOptions.None); } - //////////////////////////////////////////////////////////////////////// // // Equals @@ -1129,34 +1076,6 @@ namespace System.Globalization { return (this.Name.GetHashCode()); } - // - // return hash value for the string according to the input CompareOptions - // - - public virtual int GetHashCode(string source, CompareOptions options) - { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } - - if (options == CompareOptions.Ordinal) - { - return source.GetHashCode(); - } - - if (options == CompareOptions.OrdinalIgnoreCase) - { - return TextInfo.GetHashCodeOrdinalIgnoreCase(source); - } - - // - // GetHashCodeOfString does more parameters validation. basically will throw when - // having Ordinal, OrdinalIgnoreCase and StringSort - // - - return GetHashCodeOfString(source, options, false, 0); - } //////////////////////////////////////////////////////////////////////// // @@ -1184,33 +1103,46 @@ namespace System.Globalization { //////////////////////////////////////////////////////////////////////// internal int GetHashCodeOfString(string source, CompareOptions options) { - return GetHashCodeOfString(source, options, false, 0); - } - - internal int GetHashCodeOfString(string source, CompareOptions options, bool forceRandomizedHashing, long additionalEntropy) - { // // Parameter validation // - if(null == source) + if (null == source) { throw new ArgumentNullException(nameof(source)); } if ((options & ValidHashCodeOfStringMaskOffFlags) != 0) { - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), nameof(options)); + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); } Contract.EndContractBlock(); - if(0 == source.Length) + return GetHashCodeOfStringCore(source, options); + } + + public virtual int GetHashCode(string source, CompareOptions options) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (options == CompareOptions.Ordinal) { - return(0); + return source.GetHashCode(); } + if (options == CompareOptions.OrdinalIgnoreCase) + { + return TextInfo.GetHashCodeOrdinalIgnoreCase(source); + } + + // + // GetHashCodeOfString does more parameters validation. basically will throw when + // having Ordinal, OrdinalIgnoreCase and StringSort // - //////////////////////////////////////////////////////////////////////// - return (InternalGetGlobalizedHashCode(m_dataHandle, m_handleOrigin, this.m_sortName, source, source.Length, GetNativeCompareFlags(options), forceRandomizedHashing, additionalEntropy)); + + return GetHashCodeOfString(source, options); } //////////////////////////////////////////////////////////////////////// @@ -1221,82 +1153,41 @@ namespace System.Globalization { // CompareInfo. // //////////////////////////////////////////////////////////////////////// - - - public override String ToString() + public override string ToString() { return ("CompareInfo - " + this.Name); } -#if FEATURE_USE_LCID - public int LCID + public SortVersion Version { get { - return CultureInfo.GetCultureInfo(this.Name).LCID; - } - } -#endif + if (_sortVersion == null) + { + if (_invariantMode) + { + _sortVersion = new SortVersion(0, CultureInfo.LOCALE_INVARIANT, new Guid(0, 0, 0, 0, 0, 0, 0, + (byte) (CultureInfo.LOCALE_INVARIANT >> 24), + (byte) ((CultureInfo.LOCALE_INVARIANT & 0x00FF0000) >> 16), + (byte) ((CultureInfo.LOCALE_INVARIANT & 0x0000FF00) >> 8), + (byte) (CultureInfo.LOCALE_INVARIANT & 0xFF))); + } + else + { + _sortVersion = GetSortVersion(); + } + } - internal static IntPtr InternalInitSortHandle(String localeName, out IntPtr handleOrigin) - { - return NativeInternalInitSortHandle(localeName, out handleOrigin); + return _sortVersion; + } } - [OptionalField(VersionAdded = 3)] - private SortVersion m_SortVersion; - - public SortVersion Version + public int LCID { get { - if(m_SortVersion == null) - { - Win32Native.NlsVersionInfoEx v = new Win32Native.NlsVersionInfoEx(); - v.dwNLSVersionInfoSize = Marshal.SizeOf(typeof(Win32Native.NlsVersionInfoEx)); - InternalGetNlsVersionEx(m_dataHandle, m_handleOrigin, m_sortName, ref v); - m_SortVersion = new SortVersion(v.dwNLSVersion, (v.dwEffectiveId != 0) ? v.dwEffectiveId : LCID, v.guidCustomVersion); - } - - return m_SortVersion; + return CultureInfo.GetCultureInfo(Name).LCID; } } - - [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - [SuppressUnmanagedCodeSecurity] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool InternalGetNlsVersionEx(IntPtr handle, IntPtr handleOrigin, String localeName, ref Win32Native.NlsVersionInfoEx lpNlsVersionInformation); - - [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - [SuppressUnmanagedCodeSecurity] - private static extern IntPtr NativeInternalInitSortHandle(String localeName, out IntPtr handleOrigin); - - // Get a locale sensitive sort hash code from native code -- COMNlsInfo::InternalGetGlobalizedHashCode - [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - [SuppressUnmanagedCodeSecurity] - private static extern int InternalGetGlobalizedHashCode(IntPtr handle, IntPtr handleOrigin, string localeName, string source, int length, int dwFlags, bool forceRandomizedHashing, long additionalEntropy); - - // Use native API calls to see if this string is entirely defined -- COMNlsInfo::InternalIsSortable - [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - [SuppressUnmanagedCodeSecurity] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool InternalIsSortable(IntPtr handle, IntPtr handleOrigin, String localeName, String source, int length); - - // Compare a string using the native API calls -- COMNlsInfo::InternalCompareString - [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - [SuppressUnmanagedCodeSecurity] - private static extern int InternalCompareString(IntPtr handle, IntPtr handleOrigin, String localeName, String string1, int offset1, int length1, - String string2, int offset2, int length2, int flags); - - // InternalFindNLSStringEx parameters is not exactly matching kernel32::FindNLSStringEx parameters. - // Call through to NewApis::FindNLSStringEx so we can get the right behavior - [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - [SuppressUnmanagedCodeSecurity] - private static extern int InternalFindNLSStringEx(IntPtr handle, IntPtr handleOrigin, String localeName, int flags, String source, int sourceCount, int startIndex, string target, int targetCount); - - // Call through to NewAPis::LCMapStringEx so we can get appropriate behavior for all platforms - [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - [SuppressUnmanagedCodeSecurity] - private static extern int InternalGetSortKey(IntPtr handle, IntPtr handleOrigin, String localeName, int flags, String source, int sourceCount, byte[] target, int targetCount); } } diff --git a/src/mscorlib/src/System/Globalization/CultureData.Unix.cs b/src/mscorlib/src/System/Globalization/CultureData.Unix.cs new file mode 100644 index 0000000000..4f685de580 --- /dev/null +++ b/src/mscorlib/src/System/Globalization/CultureData.Unix.cs @@ -0,0 +1,431 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Security; +using System.Text; + +namespace System.Globalization +{ + internal partial class CultureData + { + // ICU constants + const int ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY = 100; // max size of keyword or value + const int ICU_ULOC_FULLNAME_CAPACITY = 157; // max size of locale name + const string ICU_COLLATION_KEYWORD = "@collation="; + + + /// <summary> + /// This method uses the sRealName field (which is initialized by the constructor before this is called) to + /// initialize the rest of the state of CultureData based on the underlying OS globalization library. + /// </summary> + private unsafe bool InitCultureData() + { + Debug.Assert(_sRealName != null); + + Debug.Assert(!GlobalizationMode.Invariant); + + string alternateSortName = string.Empty; + string realNameBuffer = _sRealName; + + // Basic validation + if (realNameBuffer.Contains("@")) + { + return false; // don't allow ICU variants to come in directly + } + + // Replace _ (alternate sort) with @collation= for ICU + int index = realNameBuffer.IndexOf('_'); + if (index > 0) + { + if (index >= (realNameBuffer.Length - 1) // must have characters after _ + || realNameBuffer.Substring(index + 1).Contains("_")) // only one _ allowed + { + return false; // fail + } + alternateSortName = realNameBuffer.Substring(index + 1); + realNameBuffer = realNameBuffer.Substring(0, index) + ICU_COLLATION_KEYWORD + alternateSortName; + } + + // Get the locale name from ICU + if (!GetLocaleName(realNameBuffer, out _sWindowsName)) + { + return false; // fail + } + + // Replace the ICU collation keyword with an _ + index = _sWindowsName.IndexOf(ICU_COLLATION_KEYWORD, StringComparison.Ordinal); + if (index >= 0) + { + _sName = _sWindowsName.Substring(0, index) + "_" + alternateSortName; + } + else + { + _sName = _sWindowsName; + } + _sRealName = _sName; + + _iLanguage = this.ILANGUAGE; + if (_iLanguage == 0) + { + _iLanguage = CultureInfo.LOCALE_CUSTOM_UNSPECIFIED; + } + + _bNeutral = (this.SISO3166CTRYNAME.Length == 0); + + _sSpecificCulture = _bNeutral ? LocaleData.GetSpecificCultureName(_sRealName) : _sRealName; + + // Remove the sort from sName unless custom culture + if (index>0 && !_bNeutral && !IsCustomCultureId(_iLanguage)) + { + _sName = _sWindowsName.Substring(0, index); + } + return true; + } + + internal static bool GetLocaleName(string localeName, out string windowsName) + { + // Get the locale name from ICU + StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_FULLNAME_CAPACITY); + if (!Interop.GlobalizationInterop.GetLocaleName(localeName, sb, sb.Capacity)) + { + StringBuilderCache.Release(sb); + windowsName = null; + return false; // fail + } + + // Success - use the locale name returned which may be different than realNameBuffer (casing) + windowsName = StringBuilderCache.GetStringAndRelease(sb); // the name passed to subsequent ICU calls + return true; + } + + internal static bool GetDefaultLocaleName(out string windowsName) + { + // Get the default (system) locale name from ICU + StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_FULLNAME_CAPACITY); + if (!Interop.GlobalizationInterop.GetDefaultLocaleName(sb, sb.Capacity)) + { + StringBuilderCache.Release(sb); + windowsName = null; + return false; // fail + } + + // Success - use the locale name returned which may be different than realNameBuffer (casing) + windowsName = StringBuilderCache.GetStringAndRelease(sb); // the name passed to subsequent ICU calls + return true; + } + + private string GetLocaleInfo(LocaleStringData type) + { + Debug.Assert(!GlobalizationMode.Invariant); + + Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfo] Expected _sWindowsName to be populated already"); + return GetLocaleInfo(_sWindowsName, type); + } + + // For LOCALE_SPARENT we need the option of using the "real" name (forcing neutral names) instead of the + // "windows" name, which can be specific for downlevel (< windows 7) os's. + private string GetLocaleInfo(string localeName, LocaleStringData type) + { + Debug.Assert(localeName != null, "[CultureData.GetLocaleInfo] Expected localeName to be not be null"); + + switch (type) + { + case LocaleStringData.NegativeInfinitySymbol: + // not an equivalent in ICU; prefix the PositiveInfinitySymbol with NegativeSign + return GetLocaleInfo(localeName, LocaleStringData.NegativeSign) + + GetLocaleInfo(localeName, LocaleStringData.PositiveInfinitySymbol); + } + + StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY); + + bool result = Interop.GlobalizationInterop.GetLocaleInfoString(localeName, (uint)type, sb, sb.Capacity); + if (!result) + { + // Failed, just use empty string + StringBuilderCache.Release(sb); + Debug.Assert(false, "[CultureData.GetLocaleInfo(LocaleStringData)] Failed"); + return String.Empty; + } + return StringBuilderCache.GetStringAndRelease(sb); + } + + private int GetLocaleInfo(LocaleNumberData type) + { + Debug.Assert(!GlobalizationMode.Invariant); + + Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfo(LocaleNumberData)] Expected _sWindowsName to be populated already"); + + switch (type) + { + case LocaleNumberData.CalendarType: + // returning 0 will cause the first supported calendar to be returned, which is the preferred calendar + return 0; + } + + + int value = 0; + bool result = Interop.GlobalizationInterop.GetLocaleInfoInt(_sWindowsName, (uint)type, ref value); + if (!result) + { + // Failed, just use 0 + Debug.Assert(false, "[CultureData.GetLocaleInfo(LocaleNumberData)] failed"); + } + + return value; + } + + private int[] GetLocaleInfo(LocaleGroupingData type) + { + Debug.Assert(_sWindowsName != null, "[CultureData.GetLocaleInfo(LocaleGroupingData)] Expected _sWindowsName to be populated already"); + + int primaryGroupingSize = 0; + int secondaryGroupingSize = 0; + bool result = Interop.GlobalizationInterop.GetLocaleInfoGroupingSizes(_sWindowsName, (uint)type, ref primaryGroupingSize, ref secondaryGroupingSize); + if (!result) + { + Debug.Assert(false, "[CultureData.GetLocaleInfo(LocaleGroupingData type)] failed"); + } + + if (secondaryGroupingSize == 0) + { + return new int[] { primaryGroupingSize }; + } + + return new int[] { primaryGroupingSize, secondaryGroupingSize }; + } + + private string GetTimeFormatString() + { + return GetTimeFormatString(false); + } + + private string GetTimeFormatString(bool shortFormat) + { + Debug.Assert(_sWindowsName != null, "[CultureData.GetTimeFormatString(bool shortFormat)] Expected _sWindowsName to be populated already"); + + StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY); + + bool result = Interop.GlobalizationInterop.GetLocaleTimeFormat(_sWindowsName, shortFormat, sb, sb.Capacity); + if (!result) + { + // Failed, just use empty string + StringBuilderCache.Release(sb); + Debug.Assert(false, "[CultureData.GetTimeFormatString(bool shortFormat)] Failed"); + return String.Empty; + } + + return ConvertIcuTimeFormatString(StringBuilderCache.GetStringAndRelease(sb)); + } + + private int GetFirstDayOfWeek() + { + return this.GetLocaleInfo(LocaleNumberData.FirstDayOfWeek); + } + + private String[] GetTimeFormats() + { + string format = GetTimeFormatString(false); + return new string[] { format }; + } + + private String[] GetShortTimeFormats() + { + string format = GetTimeFormatString(true); + return new string[] { format }; + } + + private static CultureData GetCultureDataFromRegionName(String regionName) + { + // no support to lookup by region name, other than the hard-coded list in CultureData + return null; + } + + private static string GetLanguageDisplayName(string cultureName) + { + return new CultureInfo(cultureName)._cultureData.GetLocaleInfo(cultureName, LocaleStringData.LocalizedDisplayName); + } + + private static string GetRegionDisplayName(string isoCountryCode) + { + // use the fallback which is to return NativeName + return null; + } + + private static CultureInfo GetUserDefaultCulture() + { + return CultureInfo.GetUserDefaultCulture(); + } + + private static string ConvertIcuTimeFormatString(string icuFormatString) + { + StringBuilder sb = StringBuilderCache.Acquire(ICU_ULOC_FULLNAME_CAPACITY); + bool amPmAdded = false; + + for (int i = 0; i < icuFormatString.Length; i++) + { + switch(icuFormatString[i]) + { + case ':': + case '.': + case 'H': + case 'h': + case 'm': + case 's': + sb.Append(icuFormatString[i]); + break; + + case ' ': + case '\u00A0': + // Convert nonbreaking spaces into regular spaces + sb.Append(' '); + break; + + case 'a': // AM/PM + if (!amPmAdded) + { + amPmAdded = true; + sb.Append("tt"); + } + break; + + } + } + + return StringBuilderCache.GetStringAndRelease(sb); + } + + private static string LCIDToLocaleName(int culture) + { + Debug.Assert(!GlobalizationMode.Invariant); + + return LocaleData.LCIDToLocaleName(culture); + } + + private static int LocaleNameToLCID(string cultureName) + { + Debug.Assert(!GlobalizationMode.Invariant); + + int lcid = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.Lcid); + return lcid == -1 ? CultureInfo.LOCALE_CUSTOM_UNSPECIFIED : lcid; + } + + private static int GetAnsiCodePage(string cultureName) + { + int ansiCodePage = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.AnsiCodePage); + return ansiCodePage == -1 ? CultureData.Invariant.IDEFAULTANSICODEPAGE : ansiCodePage; + } + + private static int GetOemCodePage(string cultureName) + { + int oemCodePage = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.OemCodePage); + return oemCodePage == -1 ? CultureData.Invariant.IDEFAULTOEMCODEPAGE : oemCodePage; + } + + private static int GetMacCodePage(string cultureName) + { + int macCodePage = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.MacCodePage); + return macCodePage == -1 ? CultureData.Invariant.IDEFAULTMACCODEPAGE : macCodePage; + } + + private static int GetEbcdicCodePage(string cultureName) + { + int ebcdicCodePage = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.EbcdicCodePage); + return ebcdicCodePage == -1 ? CultureData.Invariant.IDEFAULTEBCDICCODEPAGE : ebcdicCodePage; + } + + private static int GetGeoId(string cultureName) + { + int geoId = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.GeoId); + return geoId == -1 ? CultureData.Invariant.IGEOID : geoId; + } + + private static int GetDigitSubstitution(string cultureName) + { + int digitSubstitution = LocaleData.GetLocaleDataNumericPart(cultureName, LocaleDataParts.DigitSubstitution); + return digitSubstitution == -1 ? (int) DigitShapes.None : digitSubstitution; + } + + private static string GetThreeLetterWindowsLanguageName(string cultureName) + { + string langName = LocaleData.GetThreeLetterWindowsLangageName(cultureName); + return langName == null ? "ZZZ" /* default lang name */ : langName; + } + + private static CultureInfo[] EnumCultures(CultureTypes types) + { + Debug.Assert(!GlobalizationMode.Invariant); + + if ((types & (CultureTypes.NeutralCultures | CultureTypes.SpecificCultures)) == 0) + { + return Array.Empty<CultureInfo>(); + } + + int bufferLength = Interop.GlobalizationInterop.GetLocales(null, 0); + if (bufferLength <= 0) + { + return Array.Empty<CultureInfo>(); + } + + Char [] chars = new Char[bufferLength]; + + bufferLength = Interop.GlobalizationInterop.GetLocales(chars, bufferLength); + if (bufferLength <= 0) + { + return Array.Empty<CultureInfo>(); + } + + bool enumNeutrals = (types & CultureTypes.NeutralCultures) != 0; + bool enumSpecificss = (types & CultureTypes.SpecificCultures) != 0; + + List<CultureInfo> list = new List<CultureInfo>(); + if (enumNeutrals) + { + list.Add(CultureInfo.InvariantCulture); + } + + int index = 0; + while (index < bufferLength) + { + int length = (int) chars[index++]; + if (index + length <= bufferLength) + { + CultureInfo ci = CultureInfo.GetCultureInfo(new String(chars, index, length)); + if ((enumNeutrals && ci.IsNeutralCulture) || (enumSpecificss && !ci.IsNeutralCulture)) + { + list.Add(ci); + } + } + + index += length; + } + + return list.ToArray(); + } + + private static string GetConsoleFallbackName(string cultureName) + { + return LocaleData.GetConsoleUICulture(cultureName); + } + + internal bool IsFramework // not applicable on Linux based systems + { + get { return false; } + } + + internal bool IsWin32Installed // not applicable on Linux based systems + { + get { return false; } + } + + internal bool IsReplacementCulture // not applicable on Linux based systems + { + get { return false; } + } + } +} diff --git a/src/mscorlib/src/System/Globalization/CultureData.Windows.cs b/src/mscorlib/src/System/Globalization/CultureData.Windows.cs new file mode 100644 index 0000000000..6d2678b550 --- /dev/null +++ b/src/mscorlib/src/System/Globalization/CultureData.Windows.cs @@ -0,0 +1,830 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; + +#if ENABLE_WINRT +using Internal.Runtime.Augments; +#endif + +namespace System.Globalization +{ +#if CORECLR + using StringList = List<string>; +#else + using StringList = LowLevelList<string>; +#endif + + internal partial class CultureData + { + private const uint LOCALE_NOUSEROVERRIDE = 0x80000000; + private const uint LOCALE_RETURN_NUMBER = 0x20000000; + private const uint LOCALE_SISO3166CTRYNAME = 0x0000005A; + + private const uint TIME_NOSECONDS = 0x00000002; + + /// <summary> + /// Check with the OS to see if this is a valid culture. + /// If so we populate a limited number of fields. If its not valid we return false. + /// + /// The fields we populate: + /// + /// sWindowsName -- The name that windows thinks this culture is, ie: + /// en-US if you pass in en-US + /// de-DE_phoneb if you pass in de-DE_phoneb + /// fj-FJ if you pass in fj (neutral, on a pre-Windows 7 machine) + /// fj if you pass in fj (neutral, post-Windows 7 machine) + /// + /// sRealName -- The name you used to construct the culture, in pretty form + /// en-US if you pass in EN-us + /// en if you pass in en + /// de-DE_phoneb if you pass in de-DE_phoneb + /// + /// sSpecificCulture -- The specific culture for this culture + /// en-US for en-US + /// en-US for en + /// de-DE_phoneb for alt sort + /// fj-FJ for fj (neutral) + /// + /// sName -- The IETF name of this culture (ie: no sort info, could be neutral) + /// en-US if you pass in en-US + /// en if you pass in en + /// de-DE if you pass in de-DE_phoneb + /// + /// bNeutral -- TRUE if it is a neutral locale + /// + /// For a neutral we just populate the neutral name, but we leave the windows name pointing to the + /// windows locale that's going to provide data for us. + /// </summary> + private unsafe bool InitCultureData() + { + Debug.Assert(!GlobalizationMode.Invariant); + + const uint LOCALE_ILANGUAGE = 0x00000001; + const uint LOCALE_INEUTRAL = 0x00000071; + const uint LOCALE_SNAME = 0x0000005c; + + int result; + string realNameBuffer = _sRealName; + char* pBuffer = stackalloc char[LOCALE_NAME_MAX_LENGTH]; + + result = GetLocaleInfoEx(realNameBuffer, LOCALE_SNAME, pBuffer, LOCALE_NAME_MAX_LENGTH); + + // Did it fail? + if (result == 0) + { + return false; + } + + // It worked, note that the name is the locale name, so use that (even for neutrals) + // We need to clean up our "real" name, which should look like the windows name right now + // so overwrite the input with the cleaned up name + _sRealName = new String(pBuffer, 0, result - 1); + realNameBuffer = _sRealName; + + // Check for neutrality, don't expect to fail + // (buffer has our name in it, so we don't have to do the gc. stuff) + + result = GetLocaleInfoEx(realNameBuffer, LOCALE_INEUTRAL | LOCALE_RETURN_NUMBER, pBuffer, sizeof(int) / sizeof(char)); + if (result == 0) + { + return false; + } + + // Remember our neutrality + _bNeutral = *((uint*)pBuffer) != 0; + + // Note: Parents will be set dynamically + + // Start by assuming the windows name will be the same as the specific name since windows knows + // about specifics on all versions. Only for downlevel Neutral locales does this have to change. + _sWindowsName = realNameBuffer; + + // Neutrals and non-neutrals are slightly different + if (_bNeutral) + { + // Neutral Locale + + // IETF name looks like neutral name + _sName = realNameBuffer; + + // Specific locale name is whatever ResolveLocaleName (win7+) returns. + // (Buffer has our name in it, and we can recycle that because windows resolves it before writing to the buffer) + result = Interop.Kernel32.ResolveLocaleName(realNameBuffer, pBuffer, LOCALE_NAME_MAX_LENGTH); + + // 0 is failure, 1 is invariant (""), which we expect + if (result < 1) + { + return false; + } + // We found a locale name, so use it. + // In vista this should look like a sort name (de-DE_phoneb) or a specific culture (en-US) and be in the "pretty" form + _sSpecificCulture = new String(pBuffer, 0, result - 1); + } + else + { + // Specific Locale + + // Specific culture's the same as the locale name since we know its not neutral + // On mac we'll use this as well, even for neutrals. There's no obvious specific + // culture to use and this isn't exposed, but behaviorally this is correct on mac. + // Note that specifics include the sort name (de-DE_phoneb) + _sSpecificCulture = realNameBuffer; + + _sName = realNameBuffer; + + // We need the IETF name (sname) + // If we aren't an alt sort locale then this is the same as the windows name. + // If we are an alt sort locale then this is the same as the part before the _ in the windows name + // This is for like de-DE_phoneb and es-ES_tradnl that hsouldn't have the _ part + + result = GetLocaleInfoEx(realNameBuffer, LOCALE_ILANGUAGE | LOCALE_RETURN_NUMBER, pBuffer, sizeof(int) / sizeof(char)); + if (result == 0) + { + return false; + } + + _iLanguage = *((int*)pBuffer); + + if (!IsCustomCultureId(_iLanguage)) + { + // not custom locale + int index = realNameBuffer.IndexOf('_'); + if (index > 0 && index < realNameBuffer.Length) + { + _sName = realNameBuffer.Substring(0, index); + } + } + } + + // It succeeded. + return true; + } + + // Wrappers around the GetLocaleInfoEx APIs which handle marshalling the returned + // data as either and Int or String. + internal static unsafe String GetLocaleInfoEx(String localeName, uint field) + { + // REVIEW: Determine the maximum size for the buffer + const int BUFFER_SIZE = 530; + + char* pBuffer = stackalloc char[BUFFER_SIZE]; + int resultCode = GetLocaleInfoEx(localeName, field, pBuffer, BUFFER_SIZE); + if (resultCode > 0) + { + return new String(pBuffer); + } + + return ""; + } + + internal static unsafe int GetLocaleInfoExInt(String localeName, uint field) + { + const uint LOCALE_RETURN_NUMBER = 0x20000000; + field |= LOCALE_RETURN_NUMBER; + int value = 0; + GetLocaleInfoEx(localeName, field, (char*) &value, sizeof(int)); + return value; + } + + internal static unsafe int GetLocaleInfoEx(string lpLocaleName, uint lcType, char* lpLCData, int cchData) + { + Debug.Assert(!GlobalizationMode.Invariant); + + return Interop.Kernel32.GetLocaleInfoEx(lpLocaleName, lcType, (IntPtr)lpLCData, cchData); + } + + private string GetLocaleInfo(LocaleStringData type) + { + Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfo] Expected _sWindowsName to be populated by already"); + return GetLocaleInfo(_sWindowsName, type); + } + + // For LOCALE_SPARENT we need the option of using the "real" name (forcing neutral names) instead of the + // "windows" name, which can be specific for downlevel (< windows 7) os's. + private string GetLocaleInfo(string localeName, LocaleStringData type) + { + uint lctype = (uint)type; + + return GetLocaleInfoFromLCType(localeName, lctype, UseUserOverride); + } + + private int GetLocaleInfo(LocaleNumberData type) + { + uint lctype = (uint)type; + + // Fix lctype if we don't want overrides + if (!UseUserOverride) + { + lctype |= LOCALE_NOUSEROVERRIDE; + } + + // Ask OS for data, note that we presume it returns success, so we have to know that + // sWindowsName is valid before calling. + Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected _sWindowsName to be populated by already"); + return GetLocaleInfoExInt(_sWindowsName, lctype); + } + + private int[] GetLocaleInfo(LocaleGroupingData type) + { + return ConvertWin32GroupString(GetLocaleInfoFromLCType(_sWindowsName, (uint)type, UseUserOverride)); + } + + private string GetTimeFormatString() + { + const uint LOCALE_STIMEFORMAT = 0x00001003; + + return ReescapeWin32String(GetLocaleInfoFromLCType(_sWindowsName, LOCALE_STIMEFORMAT, UseUserOverride)); + } + + private int GetFirstDayOfWeek() + { + Debug.Assert(_sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected _sWindowsName to be populated by already"); + + const uint LOCALE_IFIRSTDAYOFWEEK = 0x0000100C; + + int result = GetLocaleInfoExInt(_sWindowsName, LOCALE_IFIRSTDAYOFWEEK | (!UseUserOverride ? LOCALE_NOUSEROVERRIDE : 0)); + + // Win32 and .NET disagree on the numbering for days of the week, so we have to convert. + return ConvertFirstDayOfWeekMonToSun(result); + } + + private String[] GetTimeFormats() + { + // Note that this gets overrides for us all the time + Debug.Assert(_sWindowsName != null, "[CultureData.DoEnumTimeFormats] Expected _sWindowsName to be populated by already"); + String[] result = ReescapeWin32Strings(nativeEnumTimeFormats(_sWindowsName, 0, UseUserOverride)); + + return result; + } + + private String[] GetShortTimeFormats() + { + // Note that this gets overrides for us all the time + Debug.Assert(_sWindowsName != null, "[CultureData.DoEnumShortTimeFormats] Expected _sWindowsName to be populated by already"); + String[] result = ReescapeWin32Strings(nativeEnumTimeFormats(_sWindowsName, TIME_NOSECONDS, UseUserOverride)); + + return result; + } + + // Enumerate all system cultures and then try to find out which culture has + // region name match the requested region name + private static CultureData GetCultureDataFromRegionName(String regionName) + { + Debug.Assert(regionName != null); + + const uint LOCALE_SUPPLEMENTAL = 0x00000002; + const uint LOCALE_SPECIFICDATA = 0x00000020; + + EnumLocaleData context = new EnumLocaleData(); + context.cultureName = null; + context.regionName = regionName; + + GCHandle contextHandle = GCHandle.Alloc(context); + try + { +#if CORECLR + Interop.Kernel32.EnumSystemLocalesEx(EnumSystemLocalesProc, LOCALE_SPECIFICDATA | LOCALE_SUPPLEMENTAL, (IntPtr)contextHandle, IntPtr.Zero); +#else + IntPtr callback = AddrofIntrinsics.AddrOf<Func<IntPtr, uint, IntPtr, Interop.BOOL>>(EnumSystemLocalesProc); + Interop.Kernel32.EnumSystemLocalesEx(callback, LOCALE_SPECIFICDATA | LOCALE_SUPPLEMENTAL, (IntPtr)contextHandle, IntPtr.Zero); +#endif + } + finally + { + contextHandle.Free(); + } + + if (context.cultureName != null) + { + // we got a matched culture + return GetCultureData(context.cultureName, true); + } + + return null; + } + + private string GetLanguageDisplayName(string cultureName) + { +#if ENABLE_WINRT + return WinRTInterop.Callbacks.GetLanguageDisplayName(cultureName); +#else + // Usually the UI culture shouldn't be different than what we got from WinRT except + // if DefaultThreadCurrentUICulture was set + CultureInfo ci; + + if (CultureInfo.DefaultThreadCurrentUICulture != null && + ((ci = GetUserDefaultCulture()) != null) && + !CultureInfo.DefaultThreadCurrentUICulture.Name.Equals(ci.Name)) + { + return SNATIVEDISPLAYNAME; + } + else + { + return GetLocaleInfo(cultureName, LocaleStringData.LocalizedDisplayName); + } +#endif // ENABLE_WINRT + } + + private string GetRegionDisplayName(string isoCountryCode) + { +#if ENABLE_WINRT + return WinRTInterop.Callbacks.GetRegionDisplayName(isoCountryCode); +#else + // Usually the UI culture shouldn't be different than what we got from WinRT except + // if DefaultThreadCurrentUICulture was set + CultureInfo ci; + + if (CultureInfo.DefaultThreadCurrentUICulture != null && + ((ci = GetUserDefaultCulture()) != null) && + !CultureInfo.DefaultThreadCurrentUICulture.Name.Equals(ci.Name)) + { + return SNATIVECOUNTRY; + } + else + { + return GetLocaleInfo(LocaleStringData.LocalizedCountryName); + } +#endif // ENABLE_WINRT + } + + private static CultureInfo GetUserDefaultCulture() + { +#if ENABLE_WINRT + return (CultureInfo)WinRTInterop.Callbacks.GetUserDefaultCulture(); +#else + return CultureInfo.GetUserDefaultCulture(); +#endif // ENABLE_WINRT + } + + // PAL methods end here. + + private static string GetLocaleInfoFromLCType(string localeName, uint lctype, bool useUserOveride) + { + Debug.Assert(localeName != null, "[CultureData.GetLocaleInfoFromLCType] Expected localeName to be not be null"); + + // Fix lctype if we don't want overrides + if (!useUserOveride) + { + lctype |= LOCALE_NOUSEROVERRIDE; + } + + // Ask OS for data + string result = GetLocaleInfoEx(localeName, lctype); + if (result == null) + { + // Failed, just use empty string + result = String.Empty; + } + + return result; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Reescape a Win32 style quote string as a NLS+ style quoted string + // + // This is also the escaping style used by custom culture data files + // + // NLS+ uses \ to escape the next character, whether in a quoted string or + // not, so we always have to change \ to \\. + // + // NLS+ uses \' to escape a quote inside a quoted string so we have to change + // '' to \' (if inside a quoted string) + // + // We don't build the stringbuilder unless we find something to change + //////////////////////////////////////////////////////////////////////////// + internal static String ReescapeWin32String(String str) + { + // If we don't have data, then don't try anything + if (str == null) + return null; + + StringBuilder result = null; + + bool inQuote = false; + for (int i = 0; i < str.Length; i++) + { + // Look for quote + if (str[i] == '\'') + { + // Already in quote? + if (inQuote) + { + // See another single quote. Is this '' of 'fred''s' or '''', or is it an ending quote? + if (i + 1 < str.Length && str[i + 1] == '\'') + { + // Found another ', so we have ''. Need to add \' instead. + // 1st make sure we have our stringbuilder + if (result == null) + result = new StringBuilder(str, 0, i, str.Length * 2); + + // Append a \' and keep going (so we don't turn off quote mode) + result.Append("\\'"); + i++; + continue; + } + + // Turning off quote mode, fall through to add it + inQuote = false; + } + else + { + // Found beginning quote, fall through to add it + inQuote = true; + } + } + // Is there a single \ character? + else if (str[i] == '\\') + { + // Found a \, need to change it to \\ + // 1st make sure we have our stringbuilder + if (result == null) + result = new StringBuilder(str, 0, i, str.Length * 2); + + // Append our \\ to the string & continue + result.Append("\\\\"); + continue; + } + + // If we have a builder we need to add our character + if (result != null) + result.Append(str[i]); + } + + // Unchanged string? , just return input string + if (result == null) + return str; + + // String changed, need to use the builder + return result.ToString(); + } + + internal static String[] ReescapeWin32Strings(String[] array) + { + if (array != null) + { + for (int i = 0; i < array.Length; i++) + { + array[i] = ReescapeWin32String(array[i]); + } + } + + return array; + } + + // If we get a group from windows, then its in 3;0 format with the 0 backwards + // of how NLS+ uses it (ie: if the string has a 0, then the int[] shouldn't and vice versa) + // EXCEPT in the case where the list only contains 0 in which NLS and NLS+ have the same meaning. + private static int[] ConvertWin32GroupString(String win32Str) + { + // None of these cases make any sense + if (win32Str == null || win32Str.Length == 0) + { + return (new int[] { 3 }); + } + + if (win32Str[0] == '0') + { + return (new int[] { 0 }); + } + + // Since its in n;n;n;n;n format, we can always get the length quickly + int[] values; + if (win32Str[win32Str.Length - 1] == '0') + { + // Trailing 0 gets dropped. 1;0 -> 1 + values = new int[(win32Str.Length / 2)]; + } + else + { + // Need extra space for trailing zero 1 -> 1;0 + values = new int[(win32Str.Length / 2) + 2]; + values[values.Length - 1] = 0; + } + + int i; + int j; + for (i = 0, j = 0; i < win32Str.Length && j < values.Length; i += 2, j++) + { + // Note that this # shouldn't ever be zero, 'cause 0 is only at end + // But we'll test because its registry that could be anything + if (win32Str[i] < '1' || win32Str[i] > '9') + return new int[] { 3 }; + + values[j] = (int)(win32Str[i] - '0'); + } + + return (values); + } + + private static int ConvertFirstDayOfWeekMonToSun(int iTemp) + { + // Convert Mon-Sun to Sun-Sat format + iTemp++; + if (iTemp > 6) + { + // Wrap Sunday and convert invalid data to Sunday + iTemp = 0; + } + return iTemp; + } + + + // Context for EnumCalendarInfoExEx callback. + private class EnumLocaleData + { + public string regionName; + public string cultureName; + } + + // EnumSystemLocaleEx callback. +#if !CORECLR + [NativeCallable(CallingConvention = CallingConvention.StdCall)] +#endif + private static unsafe Interop.BOOL EnumSystemLocalesProc(IntPtr lpLocaleString, uint flags, IntPtr contextHandle) + { + EnumLocaleData context = (EnumLocaleData)((GCHandle)contextHandle).Target; + try + { + string cultureName = new string((char*)lpLocaleString); + string regionName = GetLocaleInfoEx(cultureName, LOCALE_SISO3166CTRYNAME); + if (regionName != null && regionName.Equals(context.regionName, StringComparison.OrdinalIgnoreCase)) + { + context.cultureName = cultureName; + return Interop.BOOL.FALSE; // we found a match, then stop the enumeration + } + + return Interop.BOOL.TRUE; + } + catch (Exception) + { + return Interop.BOOL.FALSE; + } + } + + // EnumSystemLocaleEx callback. +#if !CORECLR + [NativeCallable(CallingConvention = CallingConvention.StdCall)] +#endif + private static unsafe Interop.BOOL EnumAllSystemLocalesProc(IntPtr lpLocaleString, uint flags, IntPtr contextHandle) + { + EnumData context = (EnumData)((GCHandle)contextHandle).Target; + try + { + context.strings.Add(new string((char*)lpLocaleString)); + return Interop.BOOL.TRUE; + } + catch (Exception) + { + return Interop.BOOL.FALSE; + } + } + + // Context for EnumTimeFormatsEx callback. + private class EnumData + { + public StringList strings; + } + + // EnumTimeFormatsEx callback itself. +#if !CORECLR + [NativeCallable(CallingConvention = CallingConvention.StdCall)] +#endif + private static unsafe Interop.BOOL EnumTimeCallback(IntPtr lpTimeFormatString, IntPtr lParam) + { + EnumData context = (EnumData)((GCHandle)lParam).Target; + + try + { + context.strings.Add(new string((char*)lpTimeFormatString)); + return Interop.BOOL.TRUE; + } + catch (Exception) + { + return Interop.BOOL.FALSE; + } + } + + private static unsafe String[] nativeEnumTimeFormats(String localeName, uint dwFlags, bool useUserOverride) + { + const uint LOCALE_SSHORTTIME = 0x00000079; + const uint LOCALE_STIMEFORMAT = 0x00001003; + + EnumData data = new EnumData(); + data.strings = new StringList(); + GCHandle dataHandle = GCHandle.Alloc(data); + try + { +#if CORECLR + Interop.Kernel32.EnumTimeFormatsEx(EnumTimeCallback, localeName, (uint)dwFlags, (IntPtr)dataHandle); +#else + // Now call the enumeration API. Work is done by our callback function + IntPtr callback = AddrofIntrinsics.AddrOf<Func<IntPtr, IntPtr, Interop.BOOL>>(EnumTimeCallback); + Interop.Kernel32.EnumTimeFormatsEx(callback, localeName, (uint)dwFlags, (IntPtr)dataHandle); +#endif + } + finally + { + dataHandle.Free(); + } + + if (data.strings.Count > 0) + { + // Now we need to allocate our stringarray and populate it + string[] results = data.strings.ToArray(); + + if (!useUserOverride && data.strings.Count > 1) + { + // Since there is no "NoUserOverride" aware EnumTimeFormatsEx, we always get an override + // The override is the first entry if it is overriden. + // We can check if we have overrides by checking the GetLocaleInfo with no override + // If we do have an override, we don't know if it is a user defined override or if the + // user has just selected one of the predefined formats so we can't just remove it + // but we can move it down. + uint lcType = (dwFlags == TIME_NOSECONDS) ? LOCALE_SSHORTTIME : LOCALE_STIMEFORMAT; + string timeFormatNoUserOverride = GetLocaleInfoFromLCType(localeName, lcType, useUserOverride); + if (timeFormatNoUserOverride != "") + { + string firstTimeFormat = results[0]; + if (timeFormatNoUserOverride != firstTimeFormat) + { + results[0] = results[1]; + results[1] = firstTimeFormat; + } + } + } + + return results; + } + + return null; + } + + private static int LocaleNameToLCID(string cultureName) + { + Debug.Assert(!GlobalizationMode.Invariant); + + return Interop.Kernel32.LocaleNameToLCID(cultureName, Interop.Kernel32.LOCALE_ALLOW_NEUTRAL_NAMES); + } + + private static unsafe string LCIDToLocaleName(int culture) + { + Debug.Assert(!GlobalizationMode.Invariant); + + char *pBuffer = stackalloc char[Interop.Kernel32.LOCALE_NAME_MAX_LENGTH + 1]; // +1 for the null termination + int length = Interop.Kernel32.LCIDToLocaleName(culture, pBuffer, Interop.Kernel32.LOCALE_NAME_MAX_LENGTH + 1, Interop.Kernel32.LOCALE_ALLOW_NEUTRAL_NAMES); + + if (length > 0) + { + return new String(pBuffer); + } + + return null; + } + + private int GetAnsiCodePage(string cultureName) + { + return GetLocaleInfo(LocaleNumberData.AnsiCodePage); + } + + private int GetOemCodePage(string cultureName) + { + return GetLocaleInfo(LocaleNumberData.OemCodePage); + } + + private int GetMacCodePage(string cultureName) + { + return GetLocaleInfo(LocaleNumberData.MacCodePage); + } + + private int GetEbcdicCodePage(string cultureName) + { + return GetLocaleInfo(LocaleNumberData.EbcdicCodePage); + } + + private int GetGeoId(string cultureName) + { + return GetLocaleInfo(LocaleNumberData.GeoId); + } + + private int GetDigitSubstitution(string cultureName) + { + return GetLocaleInfo(LocaleNumberData.DigitSubstitution); + } + + private string GetThreeLetterWindowsLanguageName(string cultureName) + { + return GetLocaleInfo(cultureName, LocaleStringData.AbbreviatedWindowsLanguageName); + } + + private static CultureInfo[] EnumCultures(CultureTypes types) + { + Debug.Assert(!GlobalizationMode.Invariant); + + uint flags = 0; + +#pragma warning disable 618 + if ((types & (CultureTypes.FrameworkCultures | CultureTypes.InstalledWin32Cultures | CultureTypes.ReplacementCultures)) != 0) + { + flags |= Interop.Kernel32.LOCALE_NEUTRALDATA | Interop.Kernel32.LOCALE_SPECIFICDATA; + } +#pragma warning restore 618 + + if ((types & CultureTypes.NeutralCultures) != 0) + { + flags |= Interop.Kernel32.LOCALE_NEUTRALDATA; + } + + if ((types & CultureTypes.SpecificCultures) != 0) + { + flags |= Interop.Kernel32.LOCALE_SPECIFICDATA; + } + + if ((types & CultureTypes.UserCustomCulture) != 0) + { + flags |= Interop.Kernel32.LOCALE_SUPPLEMENTAL; + } + + if ((types & CultureTypes.ReplacementCultures) != 0) + { + flags |= Interop.Kernel32.LOCALE_SUPPLEMENTAL; + } + + EnumData context = new EnumData(); + context.strings = new StringList(); + GCHandle contextHandle = GCHandle.Alloc(context); + try + { +#if CORECLR + Interop.Kernel32.EnumSystemLocalesEx(EnumAllSystemLocalesProc, flags, (IntPtr)contextHandle, IntPtr.Zero); +#else + IntPtr callback = AddrofIntrinsics.AddrOf<Func<IntPtr, uint, IntPtr, Interop.BOOL>>(EnumAllSystemLocalesProc); + Interop.Kernel32.EnumSystemLocalesEx(callback, flags, (IntPtr)contextHandle, IntPtr.Zero); +#endif + } + finally + { + contextHandle.Free(); + } + + CultureInfo [] cultures = new CultureInfo[context.strings.Count]; + for (int i = 0; i < cultures.Length; i++) + { + cultures[i] = new CultureInfo(context.strings[i]); + } + + return cultures; + } + + private string GetConsoleFallbackName(string cultureName) + { + return GetLocaleInfo(cultureName, LocaleStringData.ConsoleFallbackName); + } + + internal bool IsFramework + { + get { return false; } + } + + internal bool IsWin32Installed + { + get { return true; } + } + + internal bool IsReplacementCulture + { + get + { + EnumData context = new EnumData(); + context.strings = new StringList(); + GCHandle contextHandle = GCHandle.Alloc(context); + try + { +#if CORECLR + Interop.Kernel32.EnumSystemLocalesEx(EnumAllSystemLocalesProc, Interop.Kernel32.LOCALE_REPLACEMENT, (IntPtr)contextHandle, IntPtr.Zero); +#else + IntPtr callback = AddrofIntrinsics.AddrOf<Func<IntPtr, uint, IntPtr, Interop.BOOL>>(EnumAllSystemLocalesProc); + Interop.Kernel32.EnumSystemLocalesEx(callback, Interop.Kernel32.LOCALE_REPLACEMENT, (IntPtr)contextHandle, IntPtr.Zero); +#endif + } + finally + { + contextHandle.Free(); + } + + for (int i=0; i<context.strings.Count; i++) + { + if (String.Compare(context.strings[i], _sWindowsName, StringComparison.OrdinalIgnoreCase) == 0) + return true; + } + + return false; + } + } + } +} diff --git a/src/mscorlib/src/System/Globalization/CultureData.cs b/src/mscorlib/src/System/Globalization/CultureData.cs index a93b7d43bb..0dcebf484b 100644 --- a/src/mscorlib/src/System/Globalization/CultureData.cs +++ b/src/mscorlib/src/System/Globalization/CultureData.cs @@ -2,20 +2,23 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Threading; + namespace System.Globalization { - - using System; - using System.Collections; - using System.Collections.Generic; - using System.Text; - using System.Threading; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Runtime.Versioning; - using System.Diagnostics; - using System.Diagnostics.Contracts; - using System.Security; +#if CORECLR + using StringStringDictionary = Dictionary<string, string>; + using StringCultureDataDictionary = Dictionary<string, CultureData>; + using LcidToCultureNameDictionary = Dictionary<int, string>; + using Lock = Object; +#else + using StringStringDictionary = LowLevelDictionary<string, string>; + using StringCultureDataDictionary = LowLevelDictionary<string, CultureData>; + using LcidToCultureNameDictionary = LowLevelDictionary<int, string>; +#endif // // List of culture data @@ -48,504 +51,263 @@ namespace System.Globalization // en if you pass in en // de-DE if you pass in de-DE_phoneb // - - // StructLayout is needed here otherwise compiler can re-arrange the fields. - // We have to keep this in-sync with the definition in comnlsinfo.h - // - // WARNING WARNING WARNING - // - // WARNING: Anything changed here also needs to be updated on the native side (object.h see type CultureDataBaseObject) - // WARNING: The type loader will rearrange class member offsets so the mscorwks!CultureDataBaseObject - // WARNING: must be manually structured to match the true loaded class layout - // - [FriendAccessAllowed] - internal class CultureData + internal partial class CultureData { - const int undef = -1; + private const int LOCALE_NAME_MAX_LENGTH = 85; + private const int undef = -1; // Override flag - private String sRealName; // Name you passed in (ie: en-US, en, or de-DE_phoneb) - private String sWindowsName; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in)) + private String _sRealName; // Name you passed in (ie: en-US, en, or de-DE_phoneb) + private String _sWindowsName; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in)) // Identity - private String sName; // locale name (ie: en-us, NO sort info, but could be neutral) - private String sParent; // Parent name (which may be a custom locale/culture) - private String sLocalizedDisplayName; // Localized pretty name for this locale - private String sEnglishDisplayName; // English pretty name for this locale - private String sNativeDisplayName; // Native pretty name for this locale - private String sSpecificCulture; // The culture name to be used in CultureInfo.CreateSpecificCulture(), en-US form if neutral, sort name if sort + private String _sName; // locale name (ie: en-us, NO sort info, but could be neutral) + private String _sParent; // Parent name (which may be a custom locale/culture) + private String _sLocalizedDisplayName; // Localized pretty name for this locale + private String _sEnglishDisplayName; // English pretty name for this locale + private String _sNativeDisplayName; // Native pretty name for this locale + private String _sSpecificCulture; // The culture name to be used in CultureInfo.CreateSpecificCulture(), en-US form if neutral, sort name if sort // Language - private String sISO639Language; // ISO 639 Language Name - private String sLocalizedLanguage; // Localized name for this language - private String sEnglishLanguage; // English name for this language - private String sNativeLanguage; // Native name of this language + private String _sISO639Language; // ISO 639 Language Name + private String _sISO639Language2; // ISO 639 Language Name + private String _sLocalizedLanguage; // Localized name for this language + private String _sEnglishLanguage; // English name for this language + private String _sNativeLanguage; // Native name of this language + private String _sAbbrevLang; // abbreviated language name (Windows Language Name) ex: ENU + private string _sConsoleFallbackName; // The culture name for the console fallback UI culture + private int _iInputLanguageHandle=undef;// input language handle // Region - private String sRegionName; // (RegionInfo) - private int iGeoId = undef; // GeoId - private String sLocalizedCountry; // localized country name - private String sEnglishCountry; // english country name (RegionInfo) - private String sNativeCountry; // native country name - private String sISO3166CountryName; // ISO 3166 (RegionInfo), ie: US + private String _sRegionName; // (RegionInfo) + private String _sLocalizedCountry; // localized country name + private String _sEnglishCountry; // english country name (RegionInfo) + private String _sNativeCountry; // native country name + private String _sISO3166CountryName; // ISO 3166 (RegionInfo), ie: US + private String _sISO3166CountryName2; // 3 char ISO 3166 country name 2 2(RegionInfo) ex: USA (ISO) + private int _iGeoId = undef; // GeoId // Numbers - private String sPositiveSign; // (user can override) positive sign - private String sNegativeSign; // (user can override) negative sign - private String[] saNativeDigits; // (user can override) native characters for digits 0-9 + private String _sPositiveSign; // (user can override) positive sign + private String _sNegativeSign; // (user can override) negative sign // (nfi populates these 5, don't have to be = undef) - private int iDigitSubstitution; // (user can override) Digit substitution 0=context, 1=none/arabic, 2=Native/national (2 seems to be unused) - private int iLeadingZeros; // (user can override) leading zeros 0 = no leading zeros, 1 = leading zeros - private int iDigits; // (user can override) number of fractional digits - private int iNegativeNumber; // (user can override) negative number format - private int[] waGrouping; // (user can override) grouping of digits - private String sDecimalSeparator; // (user can override) decimal separator - private String sThousandSeparator; // (user can override) thousands separator - private String sNaN; // Not a Number - private String sPositiveInfinity; // + Infinity - private String sNegativeInfinity; // - Infinity + private int _iDigits; // (user can override) number of fractional digits + private int _iNegativeNumber; // (user can override) negative number format + private int[] _waGrouping; // (user can override) grouping of digits + private String _sDecimalSeparator; // (user can override) decimal separator + private String _sThousandSeparator; // (user can override) thousands separator + private String _sNaN; // Not a Number + private String _sPositiveInfinity; // + Infinity + private String _sNegativeInfinity; // - Infinity // Percent - private int iNegativePercent = undef; // Negative Percent (0-3) - private int iPositivePercent = undef; // Positive Percent (0-11) - private String sPercent; // Percent (%) symbol - private String sPerMille; // PerMille (‰) symbol + private int _iNegativePercent = undef; // Negative Percent (0-3) + private int _iPositivePercent = undef; // Positive Percent (0-11) + private String _sPercent; // Percent (%) symbol + private String _sPerMille; // PerMille symbol // Currency - private String sCurrency; // (user can override) local monetary symbol - private String sIntlMonetarySymbol; // international monetary symbol (RegionInfo) - private String sEnglishCurrency; // English name for this currency - private String sNativeCurrency; // Native name for this currency + private String _sCurrency; // (user can override) local monetary symbol + private String _sIntlMonetarySymbol; // international monetary symbol (RegionInfo) + private String _sEnglishCurrency; // English name for this currency + private String _sNativeCurrency; // Native name for this currency // (nfi populates these 4, don't have to be = undef) - private int iCurrencyDigits; // (user can override) # local monetary fractional digits - private int iCurrency; // (user can override) positive currency format - private int iNegativeCurrency; // (user can override) negative currency format - private int[] waMonetaryGrouping; // (user can override) monetary grouping of digits - private String sMonetaryDecimal; // (user can override) monetary decimal separator - private String sMonetaryThousand; // (user can override) monetary thousands separator + private int _iCurrencyDigits; // (user can override) # local monetary fractional digits + private int _iCurrency; // (user can override) positive currency format + private int _iNegativeCurrency; // (user can override) negative currency format + private int[] _waMonetaryGrouping; // (user can override) monetary grouping of digits + private String _sMonetaryDecimal; // (user can override) monetary decimal separator + private String _sMonetaryThousand; // (user can override) monetary thousands separator // Misc - private int iMeasure = undef; // (user can override) system of measurement 0=metric, 1=US (RegionInfo) - private String sListSeparator; // (user can override) list separator - // private int iPaperSize ; // default paper size (RegionInfo) + private int _iMeasure = undef; // (user can override) system of measurement 0=metric, 1=US (RegionInfo) + private String _sListSeparator; // (user can override) list separator // Time - private String sAM1159; // (user can override) AM designator - private String sPM2359; // (user can override) PM designator - private String sTimeSeparator; - private volatile String[] saLongTimes; // (user can override) time format - private volatile String[] saShortTimes; // short time format - private volatile String[] saDurationFormats; // time duration format + private String _sAM1159; // (user can override) AM designator + private String _sPM2359; // (user can override) PM designator + private String _sTimeSeparator; + private volatile String[] _saLongTimes; // (user can override) time format + private volatile String[] _saShortTimes; // short time format + private volatile String[] _saDurationFormats; // time duration format // Calendar specific data - private int iFirstDayOfWeek = undef; // (user can override) first day of week (gregorian really) - private int iFirstWeekOfYear = undef; // (user can override) first week of year (gregorian really) - private volatile int[] waCalendars; // all available calendar type(s). The first one is the default calendar + private int _iFirstDayOfWeek = undef; // (user can override) first day of week (gregorian really) + private int _iFirstWeekOfYear = undef; // (user can override) first week of year (gregorian really) + private volatile CalendarId[] _waCalendars; // all available calendar type(s). The first one is the default calendar // Store for specific data about each calendar - private CalendarData[] calendars; // Store for specific calendar data + private CalendarData[] _calendars; // Store for specific calendar data // Text information - private int iReadingLayout = undef; // Reading layout data + private int _iReadingLayout = undef; // Reading layout data // 0 - Left to right (eg en-US) // 1 - Right to left (eg arabic locales) // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales) // 3 - Vertical top to bottom with columns proceeding to the right - private String sTextInfo; // Text info name to use for custom - private String sCompareInfo; // Compare info name (including sorting key) to use if custom - private String sScripts; // Typical Scripts for this locale (latn;cyrl; etc) - - private int iDefaultAnsiCodePage = undef; // default ansi code page ID (ACP) - private int iDefaultOemCodePage = undef; // default oem code page ID (OCP or OEM) - private int iDefaultMacCodePage = undef; // default macintosh code page - private int iDefaultEbcdicCodePage = undef; // default EBCDIC code page - - // These are desktop only, not coreclr - private int iLanguage; // locale ID (0409) - NO sort information - private String sAbbrevLang; // abbreviated language name (Windows Language Name) ex: ENU - private String sAbbrevCountry; // abbreviated country name (RegionInfo) (Windows Region Name) ex: USA - private String sISO639Language2; // 3 char ISO 639 lang name 2 ex: eng - private String sISO3166CountryName2; // 3 char ISO 3166 country name 2 2(RegionInfo) ex: USA (ISO) - private int iInputLanguageHandle=undef;// input language handle - private String sConsoleFallbackName; // The culture name for the console fallback UI culture - private String sKeyboardsToInstall; // Keyboard installation string. - private String fontSignature; // Font signature (16 WORDS) - - // The bools all need to be in one spot - private bool bUseOverrides; // use user overrides? - private bool bNeutral; // Flags for the culture (ie: neutral or not right now) - private bool bWin32Installed; // Flags indicate if the culture is Win32 installed - private bool bFramework; // Flags for indicate if the culture is one of Whidbey cultures + // CoreCLR depends on this even though its not exposed publicly. + + private int _iDefaultAnsiCodePage = undef; // default ansi code page ID (ACP) + private int _iDefaultOemCodePage = undef; // default oem code page ID (OCP or OEM) + private int _iDefaultMacCodePage = undef; // default macintosh code page + private int _iDefaultEbcdicCodePage = undef; // default EBCDIC code page + + private int _iLanguage; // locale ID (0409) - NO sort information + private bool _bUseOverrides; // use user overrides? + private bool _bNeutral; // Flags for the culture (ie: neutral or not right now) // Region Name to Culture Name mapping table // (In future would be nice to be in registry or something) - //Using a property so we avoid creating the dictionary untill we need it - private static Dictionary<string, string> RegionNames + //Using a property so we avoid creating the dictionary until we need it + private static StringStringDictionary RegionNames { - get + get { if (s_RegionNames == null) { - var regionNames = new Dictionary<string, string> { - { "029", "en-029" }, - { "AE", "ar-AE" }, - { "AF", "prs-AF" }, - { "AL", "sq-AL" }, - { "AM", "hy-AM" }, - { "AR", "es-AR" }, - { "AT", "de-AT" }, - { "AU", "en-AU" }, - { "AZ", "az-Cyrl-AZ" }, - { "BA", "bs-Latn-BA" }, - { "BD", "bn-BD" }, - { "BE", "nl-BE" }, - { "BG", "bg-BG" }, - { "BH", "ar-BH" }, - { "BN", "ms-BN" }, - { "BO", "es-BO" }, - { "BR", "pt-BR" }, - { "BY", "be-BY" }, - { "BZ", "en-BZ" }, - { "CA", "en-CA" }, - { "CH", "it-CH" }, - { "CL", "es-CL" }, - { "CN", "zh-CN" }, - { "CO", "es-CO" }, - { "CR", "es-CR" }, - { "CS", "sr-Cyrl-CS" }, - { "CZ", "cs-CZ" }, - { "DE", "de-DE" }, - { "DK", "da-DK" }, - { "DO", "es-DO" }, - { "DZ", "ar-DZ" }, - { "EC", "es-EC" }, - { "EE", "et-EE" }, - { "EG", "ar-EG" }, - { "ES", "es-ES" }, - { "ET", "am-ET" }, - { "FI", "fi-FI" }, - { "FO", "fo-FO" }, - { "FR", "fr-FR" }, - { "GB", "en-GB" }, - { "GE", "ka-GE" }, - { "GL", "kl-GL" }, - { "GR", "el-GR" }, - { "GT", "es-GT" }, - { "HK", "zh-HK" }, - { "HN", "es-HN" }, - { "HR", "hr-HR" }, - { "HU", "hu-HU" }, - { "ID", "id-ID" }, - { "IE", "en-IE" }, - { "IL", "he-IL" }, - { "IN", "hi-IN" }, - { "IQ", "ar-IQ" }, - { "IR", "fa-IR" }, - { "IS", "is-IS" }, - { "IT", "it-IT" }, - { "IV", "" }, - { "JM", "en-JM" }, - { "JO", "ar-JO" }, - { "JP", "ja-JP" }, - { "KE", "sw-KE" }, - { "KG", "ky-KG" }, - { "KH", "km-KH" }, - { "KR", "ko-KR" }, - { "KW", "ar-KW" }, - { "KZ", "kk-KZ" }, - { "LA", "lo-LA" }, - { "LB", "ar-LB" }, - { "LI", "de-LI" }, - { "LK", "si-LK" }, - { "LT", "lt-LT" }, - { "LU", "lb-LU" }, - { "LV", "lv-LV" }, - { "LY", "ar-LY" }, - { "MA", "ar-MA" }, - { "MC", "fr-MC" }, - { "ME", "sr-Latn-ME" }, - { "MK", "mk-MK" }, - { "MN", "mn-MN" }, - { "MO", "zh-MO" }, - { "MT", "mt-MT" }, - { "MV", "dv-MV" }, - { "MX", "es-MX" }, - { "MY", "ms-MY" }, - { "NG", "ig-NG" }, - { "NI", "es-NI" }, - { "NL", "nl-NL" }, - { "NO", "nn-NO" }, - { "NP", "ne-NP" }, - { "NZ", "en-NZ" }, - { "OM", "ar-OM" }, - { "PA", "es-PA" }, - { "PE", "es-PE" }, - { "PH", "en-PH" }, - { "PK", "ur-PK" }, - { "PL", "pl-PL" }, - { "PR", "es-PR" }, - { "PT", "pt-PT" }, - { "PY", "es-PY" }, - { "QA", "ar-QA" }, - { "RO", "ro-RO" }, - { "RS", "sr-Latn-RS" }, - { "RU", "ru-RU" }, - { "RW", "rw-RW" }, - { "SA", "ar-SA" }, - { "SE", "sv-SE" }, - { "SG", "zh-SG" }, - { "SI", "sl-SI" }, - { "SK", "sk-SK" }, - { "SN", "wo-SN" }, - { "SV", "es-SV" }, - { "SY", "ar-SY" }, - { "TH", "th-TH" }, - { "TJ", "tg-Cyrl-TJ" }, - { "TM", "tk-TM" }, - { "TN", "ar-TN" }, - { "TR", "tr-TR" }, - { "TT", "en-TT" }, - { "TW", "zh-TW" }, - { "UA", "uk-UA" }, - { "US", "en-US" }, - { "UY", "es-UY" }, - { "UZ", "uz-Cyrl-UZ" }, - { "VE", "es-VE" }, - { "VN", "vi-VN" }, - { "YE", "ar-YE" }, - { "ZA", "af-ZA" }, - { "ZW", "en-ZW" } - }; - s_RegionNames = regionNames; - } - return s_RegionNames; - } - } - private volatile static Dictionary<string, string> s_RegionNames; - - ///////////////////////////////////////////////////////////////////////// - // Build our invariant information - // - // We need an invariant instance, which we build hard-coded - ///////////////////////////////////////////////////////////////////////// - internal static CultureData Invariant - { - get - { - if (s_Invariant == null) - { - // Make a new culturedata - CultureData invariant = new CultureData(); - - // Call the native code to get the value of bWin32Installed. - // For versions <= Vista, we set this to false for compatibility with v2. - // For Windows 7, the flag is true. - invariant.bUseOverrides = false; - invariant.sRealName = ""; - - // Ask the native code to fill it out for us, we only need the field IsWin32Installed - nativeInitCultureData(invariant); - - // Basics - // Note that we override the resources since this IS NOT supposed to change (by definition) - invariant.bUseOverrides = false; - invariant.sRealName = ""; // Name you passed in (ie: en-US, en, or de-DE_phoneb) - invariant.sWindowsName = ""; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in)) - - // Identity - invariant.sName = ""; // locale name (ie: en-us) - invariant.sParent = ""; // Parent name (which may be a custom locale/culture) - invariant.bNeutral = false; // Flags for the culture (ie: neutral or not right now) - // Don't set invariant.bWin32Installed, we used nativeInitCultureData for that. - invariant.bFramework = true; - - invariant.sEnglishDisplayName = "Invariant Language (Invariant Country)"; // English pretty name for this locale - invariant.sNativeDisplayName = "Invariant Language (Invariant Country)"; // Native pretty name for this locale - invariant.sSpecificCulture = ""; // The culture name to be used in CultureInfo.CreateSpecificCulture() - - // Language - invariant.sISO639Language = "iv"; // ISO 639 Language Name - invariant.sLocalizedLanguage = "Invariant Language"; // Display name for this Language - invariant.sEnglishLanguage = "Invariant Language"; // English name for this language - invariant.sNativeLanguage = "Invariant Language"; // Native name of this language - - // Region - invariant.sRegionName = "IV"; // (RegionInfo) - // Unused for now: - // invariant.iCountry =1; // country code (RegionInfo) - invariant.iGeoId = 244; // GeoId (Windows Only) - invariant.sEnglishCountry = "Invariant Country"; // english country name (RegionInfo) - invariant.sNativeCountry = "Invariant Country"; // native country name (Windows Only) - invariant.sISO3166CountryName = "IV"; // (RegionInfo), ie: US - - // Numbers - invariant.sPositiveSign = "+"; // positive sign - invariant.sNegativeSign = "-"; // negative sign - invariant.saNativeDigits = new String[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; // native characters for digits 0-9 - invariant.iDigitSubstitution = 1; // Digit substitution 0=context, 1=none/arabic, 2=Native/national (2 seems to be unused) (Windows Only) - invariant.iLeadingZeros = 1; // leading zeros 0=no leading zeros, 1=leading zeros - invariant.iDigits = 2; // number of fractional digits - invariant.iNegativeNumber = 1; // negative number format - invariant.waGrouping = new int[] { 3 }; // grouping of digits - invariant.sDecimalSeparator = "."; // decimal separator - invariant.sThousandSeparator = ","; // thousands separator - invariant.sNaN = "NaN"; // Not a Number - invariant.sPositiveInfinity = "Infinity"; // + Infinity - invariant.sNegativeInfinity = "-Infinity"; // - Infinity - - // Percent - invariant.iNegativePercent = 0; // Negative Percent (0-3) - invariant.iPositivePercent = 0; // Positive Percent (0-11) - invariant.sPercent = "%"; // Percent (%) symbol - invariant.sPerMille = "\x2030"; // PerMille(‰) symbol - - // Currency - invariant.sCurrency = "\x00a4"; // local monetary symbol "¤: for international monetary symbol - invariant.sIntlMonetarySymbol = "XDR"; // international monetary symbol (RegionInfo) - invariant.sEnglishCurrency = "International Monetary Fund"; // English name for this currency (Windows Only) - invariant.sNativeCurrency = "International Monetary Fund"; // Native name for this currency (Windows Only) - invariant.iCurrencyDigits = 2; // # local monetary fractional digits - invariant.iCurrency = 0; // positive currency format - invariant.iNegativeCurrency = 0; // negative currency format - invariant.waMonetaryGrouping = new int[] { 3 }; // monetary grouping of digits - invariant.sMonetaryDecimal = "."; // monetary decimal separator - invariant.sMonetaryThousand = ","; // monetary thousands separator - - // Misc - invariant.iMeasure = 0; // system of measurement 0=metric, 1=US (RegionInfo) - invariant.sListSeparator = ","; // list separator - // Unused for now: - // invariant.iPaperSize =9; // default paper size (RegionInfo) - // invariant.waFontSignature ="\x0002\x0000\x0000\x0000\x0000\x0000\x0000\x8000\x0001\x0000\x0000\x8000\x0001\x0000\x0000\x8000"; // Font signature (16 WORDS) (Windows Only) - - // Time - invariant.sAM1159 = "AM"; // AM designator - invariant.sPM2359 = "PM"; // PM designator - invariant.saLongTimes = new String[] { "HH:mm:ss" }; // time format - invariant.saShortTimes = new String[] { "HH:mm", "hh:mm tt", "H:mm", "h:mm tt" }; // short time format - invariant.saDurationFormats = new String[] { "HH:mm:ss" }; // time duration format - - // Calendar specific data - invariant.iFirstDayOfWeek = 0; // first day of week - invariant.iFirstWeekOfYear = 0; // first week of year - invariant.waCalendars = new int[] { (int)CalendarId.GREGORIAN }; // all available calendar type(s). The first one is the default calendar - - // Store for specific data about each calendar - invariant.calendars = new CalendarData[CalendarData.MAX_CALENDARS]; - invariant.calendars[0] = CalendarData.Invariant; - - // Text information - invariant.iReadingLayout = 0; // Reading Layout = RTL - - invariant.sTextInfo = ""; // Text info name to use for custom - invariant.sCompareInfo = ""; // Compare info name (including sorting key) to use if custom - invariant.sScripts = "Latn;"; // Typical Scripts for this locale (latn,cyrl, etc) - - invariant.iLanguage = 0x007f; // locale ID (0409) - NO sort information - invariant.iDefaultAnsiCodePage = 1252; // default ansi code page ID (ACP) - invariant.iDefaultOemCodePage = 437; // default oem code page ID (OCP or OEM) - invariant.iDefaultMacCodePage = 10000; // default macintosh code page - invariant.iDefaultEbcdicCodePage = 037; // default EBCDIC code page - invariant.sAbbrevLang = "IVL"; // abbreviated language name (Windows Language Name) - invariant.sAbbrevCountry = "IVC"; // abbreviated country name (RegionInfo) (Windows Region Name) - invariant.sISO639Language2 = "ivl"; // 3 char ISO 639 lang name 2 - invariant.sISO3166CountryName2 = "ivc"; // 3 char ISO 3166 country name 2 2(RegionInfo) - invariant.iInputLanguageHandle = 0x007f; // input language handle - invariant.sConsoleFallbackName = ""; // The culture name for the console fallback UI culture - invariant.sKeyboardsToInstall = "0409:00000409"; // Keyboard installation string. - // Remember it - s_Invariant = invariant; + StringStringDictionary regionNames = new StringStringDictionary(211 /* prime */); + + regionNames.Add("029", "en-029"); + regionNames.Add("AE", "ar-AE"); + regionNames.Add("AF", "prs-AF"); + regionNames.Add("AL", "sq-AL"); + regionNames.Add("AM", "hy-AM"); + regionNames.Add("AR", "es-AR"); + regionNames.Add("AT", "de-AT"); + regionNames.Add("AU", "en-AU"); + regionNames.Add("AZ", "az-Cyrl-AZ"); + regionNames.Add("BA", "bs-Latn-BA"); + regionNames.Add("BD", "bn-BD"); + regionNames.Add("BE", "nl-BE"); + regionNames.Add("BG", "bg-BG"); + regionNames.Add("BH", "ar-BH"); + regionNames.Add("BN", "ms-BN"); + regionNames.Add("BO", "es-BO"); + regionNames.Add("BR", "pt-BR"); + regionNames.Add("BY", "be-BY"); + regionNames.Add("BZ", "en-BZ"); + regionNames.Add("CA", "en-CA"); + regionNames.Add("CH", "it-CH"); + regionNames.Add("CL", "es-CL"); + regionNames.Add("CN", "zh-CN"); + regionNames.Add("CO", "es-CO"); + regionNames.Add("CR", "es-CR"); + regionNames.Add("CS", "sr-Cyrl-CS"); + regionNames.Add("CZ", "cs-CZ"); + regionNames.Add("DE", "de-DE"); + regionNames.Add("DK", "da-DK"); + regionNames.Add("DO", "es-DO"); + regionNames.Add("DZ", "ar-DZ"); + regionNames.Add("EC", "es-EC"); + regionNames.Add("EE", "et-EE"); + regionNames.Add("EG", "ar-EG"); + regionNames.Add("ES", "es-ES"); + regionNames.Add("ET", "am-ET"); + regionNames.Add("FI", "fi-FI"); + regionNames.Add("FO", "fo-FO"); + regionNames.Add("FR", "fr-FR"); + regionNames.Add("GB", "en-GB"); + regionNames.Add("GE", "ka-GE"); + regionNames.Add("GL", "kl-GL"); + regionNames.Add("GR", "el-GR"); + regionNames.Add("GT", "es-GT"); + regionNames.Add("HK", "zh-HK"); + regionNames.Add("HN", "es-HN"); + regionNames.Add("HR", "hr-HR"); + regionNames.Add("HU", "hu-HU"); + regionNames.Add("ID", "id-ID"); + regionNames.Add("IE", "en-IE"); + regionNames.Add("IL", "he-IL"); + regionNames.Add("IN", "hi-IN"); + regionNames.Add("IQ", "ar-IQ"); + regionNames.Add("IR", "fa-IR"); + regionNames.Add("IS", "is-IS"); + regionNames.Add("IT", "it-IT"); + regionNames.Add("IV", ""); + regionNames.Add("JM", "en-JM"); + regionNames.Add("JO", "ar-JO"); + regionNames.Add("JP", "ja-JP"); + regionNames.Add("KE", "sw-KE"); + regionNames.Add("KG", "ky-KG"); + regionNames.Add("KH", "km-KH"); + regionNames.Add("KR", "ko-KR"); + regionNames.Add("KW", "ar-KW"); + regionNames.Add("KZ", "kk-KZ"); + regionNames.Add("LA", "lo-LA"); + regionNames.Add("LB", "ar-LB"); + regionNames.Add("LI", "de-LI"); + regionNames.Add("LK", "si-LK"); + regionNames.Add("LT", "lt-LT"); + regionNames.Add("LU", "lb-LU"); + regionNames.Add("LV", "lv-LV"); + regionNames.Add("LY", "ar-LY"); + regionNames.Add("MA", "ar-MA"); + regionNames.Add("MC", "fr-MC"); + regionNames.Add("ME", "sr-Latn-ME"); + regionNames.Add("MK", "mk-MK"); + regionNames.Add("MN", "mn-MN"); + regionNames.Add("MO", "zh-MO"); + regionNames.Add("MT", "mt-MT"); + regionNames.Add("MV", "dv-MV"); + regionNames.Add("MX", "es-MX"); + regionNames.Add("MY", "ms-MY"); + regionNames.Add("NG", "ig-NG"); + regionNames.Add("NI", "es-NI"); + regionNames.Add("NL", "nl-NL"); + regionNames.Add("NO", "nn-NO"); + regionNames.Add("NP", "ne-NP"); + regionNames.Add("NZ", "en-NZ"); + regionNames.Add("OM", "ar-OM"); + regionNames.Add("PA", "es-PA"); + regionNames.Add("PE", "es-PE"); + regionNames.Add("PH", "en-PH"); + regionNames.Add("PK", "ur-PK"); + regionNames.Add("PL", "pl-PL"); + regionNames.Add("PR", "es-PR"); + regionNames.Add("PT", "pt-PT"); + regionNames.Add("PY", "es-PY"); + regionNames.Add("QA", "ar-QA"); + regionNames.Add("RO", "ro-RO"); + regionNames.Add("RS", "sr-Latn-RS"); + regionNames.Add("RU", "ru-RU"); + regionNames.Add("RW", "rw-RW"); + regionNames.Add("SA", "ar-SA"); + regionNames.Add("SE", "sv-SE"); + regionNames.Add("SG", "zh-SG"); + regionNames.Add("SI", "sl-SI"); + regionNames.Add("SK", "sk-SK"); + regionNames.Add("SN", "wo-SN"); + regionNames.Add("SV", "es-SV"); + regionNames.Add("SY", "ar-SY"); + regionNames.Add("TH", "th-TH"); + regionNames.Add("TJ", "tg-Cyrl-TJ"); + regionNames.Add("TM", "tk-TM"); + regionNames.Add("TN", "ar-TN"); + regionNames.Add("TR", "tr-TR"); + regionNames.Add("TT", "en-TT"); + regionNames.Add("TW", "zh-TW"); + regionNames.Add("UA", "uk-UA"); + regionNames.Add("US", "en-US"); + regionNames.Add("UY", "es-UY"); + regionNames.Add("UZ", "uz-Cyrl-UZ"); + regionNames.Add("VE", "es-VE"); + regionNames.Add("VN", "vi-VN"); + regionNames.Add("YE", "ar-YE"); + regionNames.Add("ZA", "af-ZA"); + regionNames.Add("ZW", "en-ZW"); + + s_RegionNames = regionNames; } - return s_Invariant; - } - } - private volatile static CultureData s_Invariant; - /////////////// - // Constructors // - /////////////// - // Cache of cultures we've already looked up - private static volatile Dictionary<String, CultureData> s_cachedCultures; - - [FriendAccessAllowed] - internal static CultureData GetCultureData(String cultureName, bool useUserOverride) - { - // First do a shortcut for Invariant - if (String.IsNullOrEmpty(cultureName)) - { - return CultureData.Invariant; - } - - // Try the hash table first - String hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*'); - Dictionary<String, CultureData> tempHashTable = s_cachedCultures; - if (tempHashTable == null) - { - // No table yet, make a new one - tempHashTable = new Dictionary<String, CultureData>(); - } - else - { - // Check the hash table - CultureData retVal; - lock (((ICollection)tempHashTable).SyncRoot) - { - tempHashTable.TryGetValue(hashName, out retVal); - } - if (retVal != null) - { - return retVal; - } - } - - // Not found in the hash table, need to see if we can build one that works for us - CultureData culture = CreateCultureData(cultureName, useUserOverride); - if (culture == null) - { - return null; - } - - // Found one, add it to the cache - lock (((ICollection)tempHashTable).SyncRoot) - { - tempHashTable[hashName] = culture; - } - - // Copy the hashtable to the corresponding member variables. This will potentially overwrite - // new tables simultaneously created by a new thread, but maximizes thread safety. - s_cachedCultures = tempHashTable; - - return culture; - } - - private static CultureData CreateCultureData(string cultureName, bool useUserOverride) - { - CultureData culture = new CultureData(); - culture.bUseOverrides = useUserOverride; - culture.sRealName = cultureName; - - // Ask native code if that one's real - if (culture.InitCultureData() == false) - { - return null; - } - - return culture; - } - - private bool InitCultureData() - { - if (nativeInitCultureData(this) == false) - { - return false; + return s_RegionNames; } - return true; } // Cache of regions we've already looked up - private static volatile Dictionary<String, CultureData> s_cachedRegions; + private static volatile StringCultureDataDictionary s_cachedRegions; + private static volatile StringStringDictionary s_RegionNames; internal static CultureData GetCultureDataForRegion(String cultureName, bool useUserOverride) { @@ -571,16 +333,16 @@ namespace System.Globalization // Try the hash table next String hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*'); - Dictionary<String, CultureData> tempHashTable = s_cachedRegions; + StringCultureDataDictionary tempHashTable = s_cachedRegions; if (tempHashTable == null) { // No table yet, make a new one - tempHashTable = new Dictionary<String, CultureData>(); + tempHashTable = new StringCultureDataDictionary(); } else { // Check the hash table - lock (((ICollection)tempHashTable).SyncRoot) + lock (s_lock) { tempHashTable.TryGetValue(hashName, out retVal); } @@ -598,36 +360,25 @@ namespace System.Globalization if (retVal == null || (retVal.IsNeutralCulture == true)) { // Not a valid mapping, try the hard coded table - if (RegionNames.ContainsKey(cultureName)) + string name; + if (RegionNames.TryGetValue(cultureName, out name)) { // Make sure we can get culture data for it - retVal = GetCultureData(RegionNames[cultureName], useUserOverride); + retVal = GetCultureData(name, useUserOverride); } } // If not found in the hard coded table we'll have to find a culture that works for us if (retVal == null || (retVal.IsNeutralCulture == true)) { - // Not found in the hard coded table, need to see if we can find a culture that works for us - // Not a real culture name, see if it matches a region name - // (we just return the first culture we match) - CultureInfo[] specifics = SpecificCultures; - for (int i = 0; i < specifics.Length; i++) - { - if (String.Compare(specifics[i].m_cultureData.SREGIONNAME, cultureName, StringComparison.OrdinalIgnoreCase) == 0) - { - // Matched, use this culture - retVal = specifics[i].m_cultureData; - break; - } - } + retVal = GetCultureDataFromRegionName(cultureName); } - // If we found one we can use, then cash it for next time + // If we found one we can use, then cache it for next time if (retVal != null && (retVal.IsNeutralCulture == false)) { // first add it to the cache - lock (((ICollection)tempHashTable).SyncRoot) + lock (s_lock) { tempHashTable[hashName] = retVal; } @@ -647,54 +398,11 @@ namespace System.Globalization return retVal; } -#if FEATURE_USE_LCID - // Obtain locale name from LCID - // NOTE: This will get neutral names, unlike the OS API - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern String LCIDToLocaleName(int lcid); - - // We'd rather people use the named version since this doesn't allow custom locales - internal static CultureData GetCultureData(int culture, bool bUseUserOverride) - { - String localeName = null; - CultureData retVal = null; - - if (localeName == null) - { - // Convert the lcid to a name, then use that - // Note that this'll return neutral names (unlike Vista native API) - localeName = LCIDToLocaleName(culture); - } - - // If its not valid, then throw - if (String.IsNullOrEmpty(localeName)) - { - // Could be valid for Invariant - if (culture == 0x007f) - return Invariant; - } - else - { - // Valid name, use it - retVal = GetCultureData(localeName, bUseUserOverride); - } - - // If not successful, throw - if (retVal == null) - throw new CultureNotFoundException( - nameof(culture), culture, Environment.GetResourceString("Argument_CultureNotSupported")); - - // Return the one we found - return retVal; - } -#endif - // Clear our internal caches internal static void ClearCachedData() { s_cachedCultures = null; s_cachedRegions = null; - s_replacementCultureNames = null; } internal static CultureInfo[] GetCultures(CultureTypes types) @@ -703,23 +411,16 @@ namespace System.Globalization #pragma warning disable 618 // Validate flags if ((int)types <= 0 || ((int)types & (int)~(CultureTypes.NeutralCultures | CultureTypes.SpecificCultures | - CultureTypes.InstalledWin32Cultures | CultureTypes.UserCustomCulture | - CultureTypes.ReplacementCultures | CultureTypes.WindowsOnlyCultures | - CultureTypes.FrameworkCultures)) != 0) + CultureTypes.InstalledWin32Cultures | CultureTypes.UserCustomCulture | + CultureTypes.ReplacementCultures | CultureTypes.WindowsOnlyCultures | + CultureTypes.FrameworkCultures)) != 0) { - throw new ArgumentOutOfRangeException( - nameof(types), - String.Format( - CultureInfo.CurrentCulture, - Environment.GetResourceString("ArgumentOutOfRange_Range"), CultureTypes.NeutralCultures, CultureTypes.FrameworkCultures)); + throw new ArgumentOutOfRangeException(nameof(types), + SR.Format(SR.ArgumentOutOfRange_Range, CultureTypes.NeutralCultures, CultureTypes.FrameworkCultures)); } - // - // CHANGE FROM Whidbey - // // We have deprecated CultureTypes.FrameworkCultures. // When this enum is used, we will enumerate Whidbey framework cultures (for compatibility). - // // We have deprecated CultureTypes.WindowsOnlyCultures. // When this enum is used, we will return an empty array for this enum. @@ -728,85 +429,349 @@ namespace System.Globalization // Remove the enum as it is an no-op. types &= (~CultureTypes.WindowsOnlyCultures); } + + if (GlobalizationMode.Invariant) + { + // in invariant mode we always return invariant culture only from the enumeration + return new CultureInfo[1] { new CultureInfo("") }; + } - String[] cultureNames = null; +#pragma warning restore 618 + return EnumCultures(types); + } + + private static CultureData CreateCultureWithInvariantData() + { + // Make a new culturedata + CultureData invariant = new CultureData(); + + // Basics + // Note that we override the resources since this IS NOT supposed to change (by definition) + invariant._bUseOverrides = false; + invariant._sRealName = ""; // Name you passed in (ie: en-US, en, or de-DE_phoneb) + invariant._sWindowsName = ""; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in)) + + // Identity + invariant._sName = ""; // locale name (ie: en-us) + invariant._sParent = ""; // Parent name (which may be a custom locale/culture) + invariant._bNeutral = false; // Flags for the culture (ie: neutral or not right now) + invariant._sEnglishDisplayName = "Invariant Language (Invariant Country)"; // English pretty name for this locale + invariant._sNativeDisplayName = "Invariant Language (Invariant Country)"; // Native pretty name for this locale + invariant._sSpecificCulture = ""; // The culture name to be used in CultureInfo.CreateSpecificCulture() + + // Language + invariant._sISO639Language = "iv"; // ISO 639 Language Name + invariant._sISO639Language2 = "ivl"; // 3 char ISO 639 lang name 2 + invariant._sLocalizedLanguage = "Invariant Language"; // Display name for this Language + invariant._sEnglishLanguage = "Invariant Language"; // English name for this language + invariant._sNativeLanguage = "Invariant Language"; // Native name of this language + invariant._sAbbrevLang = "IVL"; // abbreviated language name (Windows Language Name) + invariant._sConsoleFallbackName = ""; // The culture name for the console fallback UI culture + invariant._iInputLanguageHandle = 0x07F; // input language handle + + // Region + invariant._sRegionName = "IV"; // (RegionInfo) + invariant._sEnglishCountry = "Invariant Country"; // english country name (RegionInfo) + invariant._sNativeCountry = "Invariant Country"; // native country name (Windows Only) + invariant._sISO3166CountryName = "IV"; // (RegionInfo), ie: US + invariant._sISO3166CountryName2 = "ivc"; // 3 char ISO 3166 country name 2 2(RegionInfo) + invariant._iGeoId = 244; // GeoId (Windows Only) + + // Numbers + invariant._sPositiveSign = "+"; // positive sign + invariant._sNegativeSign = "-"; // negative sign + invariant._iDigits = 2; // number of fractional digits + invariant._iNegativeNumber = 1; // negative number format + invariant._waGrouping = new int[] { 3 }; // grouping of digits + invariant._sDecimalSeparator = "."; // decimal separator + invariant._sThousandSeparator = ","; // thousands separator + invariant._sNaN = "NaN"; // Not a Number + invariant._sPositiveInfinity = "Infinity"; // + Infinity + invariant._sNegativeInfinity = "-Infinity"; // - Infinity + + // Percent + invariant._iNegativePercent = 0; // Negative Percent (0-3) + invariant._iPositivePercent = 0; // Positive Percent (0-11) + invariant._sPercent = "%"; // Percent (%) symbol + invariant._sPerMille = "\x2030"; // PerMille symbol + + // Currency + invariant._sCurrency = "\x00a4"; // local monetary symbol: for international monetary symbol + invariant._sIntlMonetarySymbol = "XDR"; // international monetary symbol (RegionInfo) + invariant._sEnglishCurrency = "International Monetary Fund"; // English name for this currency (Windows Only) + invariant._sNativeCurrency = "International Monetary Fund"; // Native name for this currency (Windows Only) + invariant._iCurrencyDigits = 2; // # local monetary fractional digits + invariant._iCurrency = 0; // positive currency format + invariant._iNegativeCurrency = 0; // negative currency format + invariant._waMonetaryGrouping = new int[] { 3 }; // monetary grouping of digits + invariant._sMonetaryDecimal = "."; // monetary decimal separator + invariant._sMonetaryThousand = ","; // monetary thousands separator + + // Misc + invariant._iMeasure = 0; // system of measurement 0=metric, 1=US (RegionInfo) + invariant._sListSeparator = ","; // list separator + + // Time + invariant._sTimeSeparator = ":"; + invariant._sAM1159 = "AM"; // AM designator + invariant._sPM2359 = "PM"; // PM designator + invariant._saLongTimes = new String[] { "HH:mm:ss" }; // time format + invariant._saShortTimes = new String[] { "HH:mm", "hh:mm tt", "H:mm", "h:mm tt" }; // short time format + invariant._saDurationFormats = new String[] { "HH:mm:ss" }; // time duration format + + + // Calendar specific data + invariant._iFirstDayOfWeek = 0; // first day of week + invariant._iFirstWeekOfYear = 0; // first week of year + invariant._waCalendars = new CalendarId[] { CalendarId.GREGORIAN }; // all available calendar type(s). The first one is the default calendar + + // Store for specific data about each calendar + invariant._calendars = new CalendarData[CalendarData.MAX_CALENDARS]; + invariant._calendars[0] = CalendarData.Invariant; + + // Text information + invariant._iReadingLayout = 0; + + // These are desktop only, not coreclr + + invariant._iLanguage = CultureInfo.LOCALE_INVARIANT; // locale ID (0409) - NO sort information + invariant._iDefaultAnsiCodePage = 1252; // default ansi code page ID (ACP) + invariant._iDefaultOemCodePage = 437; // default oem code page ID (OCP or OEM) + invariant._iDefaultMacCodePage = 10000; // default macintosh code page + invariant._iDefaultEbcdicCodePage = 037; // default EBCDIC code page + + if (GlobalizationMode.Invariant) + { + invariant._sLocalizedDisplayName = invariant._sNativeDisplayName; + invariant._sLocalizedCountry = invariant._sNativeCountry; + } + + return invariant; + } - // - // Call nativeEnumCultureNames() to get a string array of culture names based on the specified - // enumeration type. - // - // nativeEnumCultureNames is a QCall. We need to use a reference to return the string array - // allocated from the QCall. That ref has to be wrapped as object handle. - // See vm\qcall.h for details in QCall. - // + ///////////////////////////////////////////////////////////////////////// + // Build our invariant information + // + // We need an invariant instance, which we build hard-coded + ///////////////////////////////////////////////////////////////////////// + internal static CultureData Invariant + { + get + { + if (s_Invariant == null) + { + // Remember it + s_Invariant = CreateCultureWithInvariantData(); + } + return s_Invariant; + } + } + private volatile static CultureData s_Invariant; - if (nativeEnumCultureNames((int)types, JitHelpers.GetObjectHandleOnStack(ref cultureNames)) == 0) + /////////////// + // Constructors // + /////////////// + // Cache of cultures we've already looked up + private static volatile StringCultureDataDictionary s_cachedCultures; + private static readonly Lock s_lock = new Lock(); + + internal static CultureData GetCultureData(String cultureName, bool useUserOverride) + { + // First do a shortcut for Invariant + if (String.IsNullOrEmpty(cultureName)) { - return new CultureInfo[0]; + return CultureData.Invariant; } - int arrayLength = cultureNames.Length; + // Try the hash table first + String hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*'); + StringCultureDataDictionary tempHashTable = s_cachedCultures; + if (tempHashTable == null) + { + // No table yet, make a new one + tempHashTable = new StringCultureDataDictionary(); + } + else + { + // Check the hash table + bool ret; + CultureData retVal; + lock (s_lock) + { + ret = tempHashTable.TryGetValue(hashName, out retVal); + } + if (ret && retVal != null) + { + return retVal; + } + } - CultureInfo[] cultures = new CultureInfo[arrayLength]; + // Not found in the hash table, need to see if we can build one that works for us + CultureData culture = CreateCultureData(cultureName, useUserOverride); + if (culture == null) + { + return null; + } - for (int i = 0; i < cultureNames.Length; i++) + // Found one, add it to the cache + lock (s_lock) { - cultures[i] = new CultureInfo(cultureNames[i]); + tempHashTable[hashName] = culture; } -#pragma warning restore 618 - return cultures; - } + // Copy the hashtable to the corresponding member variables. This will potentially overwrite + // new tables simultaneously created by a new thread, but maximizes thread safety. + s_cachedCultures = tempHashTable; - internal static volatile CultureInfo[] specificCultures; + return culture; + } - private static CultureInfo[] SpecificCultures + private static unsafe string NormalizeCultureName(string name, out bool isNeutralName) { - get + isNeutralName = true; + int i = 0; + + Debug.Assert(name.Length <= LOCALE_NAME_MAX_LENGTH); + + char *pName = stackalloc char[LOCALE_NAME_MAX_LENGTH]; + bool changed = false; + + while (i < name.Length && name[i] != '-' && name[i] != '_') { - if (specificCultures == null) - specificCultures = GetCultures(CultureTypes.SpecificCultures); + if (name[i] >= 'A' && name[i] <= 'Z') + { + // lowercase characters before '-' + pName[i] = (char) (((int)name[i]) + 0x20); + changed = true; + } + else + { + pName[i] = name[i]; + } + i++; + } - return specificCultures; + if (i < name.Length) + { + // this is not perfect to detect the non neutral cultures but it is good enough when we are running in invariant mode + isNeutralName = false; } + + while (i < name.Length) + { + if (name[i] >= 'a' && name[i] <= 'z') + { + pName[i] = (char) (((int)name[i]) - 0x20); + changed = true; + } + else + { + pName[i] = name[i]; + } + i++; + } + + if (changed) + return new string(pName, 0, name.Length); + + return name; } - internal bool IsReplacementCulture + private static CultureData CreateCultureData(string cultureName, bool useUserOverride) { - get + if (GlobalizationMode.Invariant) { - return IsReplacementCultureName(this.SNAME); + CultureInfo.VerifyCultureName(cultureName, true); + CultureData cd = CreateCultureWithInvariantData(); + cd._bUseOverrides = useUserOverride; + cd._sName = NormalizeCultureName(cultureName, out cd._bNeutral); + cd._sRealName = cd._sName; + cd._sWindowsName = cd._sName; + cd._iLanguage = CultureInfo.LOCALE_CUSTOM_UNSPECIFIED; + + return cd; } - } - internal static volatile String[] s_replacementCultureNames; + CultureData culture = new CultureData(); + culture._bUseOverrides = useUserOverride; + culture._sRealName = cultureName; - //////////////////////////////////////////////////////////////////////// - // - // Cache for the known replacement cultures. - // This is used by CultureInfo.CultureType to check if a culture is a - // replacement culture. - // - //////////////////////////////////////////////////////////////////////// + // Ask native code if that one's real + if (culture.InitCultureData() == false) + { + if (culture.InitCompatibilityCultureData() == false) + { + return null; + } + } + return culture; + } - private static bool IsReplacementCultureName(String name) + private bool InitCompatibilityCultureData() { - Debug.Assert(name != null, "IsReplacementCultureName(): name should not be null"); - String[] replacementCultureNames = s_replacementCultureNames; - if (replacementCultureNames == null) + // for compatibility handle the deprecated ids: zh-chs, zh-cht + string cultureName = _sRealName; + + string fallbackCultureName; + string realCultureName; + switch (AnsiToLower(cultureName)) { - if (nativeEnumCultureNames((int)CultureTypes.ReplacementCultures, JitHelpers.GetObjectHandleOnStack(ref replacementCultureNames)) == 0) - { + case "zh-chs": + fallbackCultureName = "zh-Hans"; + realCultureName = "zh-CHS"; + break; + case "zh-cht": + fallbackCultureName = "zh-Hant"; + realCultureName = "zh-CHT"; + break; + default: return false; - } + } + + _sRealName = fallbackCultureName; + if (InitCultureData() == false) + { + return false; + } + // fixup our data + _sName = realCultureName; // the name that goes back to the user + _sParent = fallbackCultureName; + + return true; + } + + // We'd rather people use the named version since this doesn't allow custom locales + internal static CultureData GetCultureData(int culture, bool bUseUserOverride) + { + string localeName = null; + CultureData retVal = null; + + if (culture == CultureInfo.LOCALE_INVARIANT) + return Invariant; + + if (GlobalizationMode.Invariant) + { + // LCID is not supported in the InvariantMode + throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupported); + } + + // Convert the lcid to a name, then use that + // Note that this will return neutral names (unlike Vista native API) + localeName = LCIDToLocaleName(culture); - // Even if we don't have any replacement cultures, the returned replacementCultureNames will still an empty string array, not null. - Debug.Assert(name != null, "IsReplacementCultureName(): replacementCultureNames should not be null"); - Array.Sort(replacementCultureNames); - s_replacementCultureNames = replacementCultureNames; + if (!String.IsNullOrEmpty(localeName)) + { + // Valid name, use it + retVal = GetCultureData(localeName, bUseUserOverride); } - return Array.BinarySearch(replacementCultureNames, name) >= 0; + + // If not successful, throw + if (retVal == null) + throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupported); + + // Return the one we found + return retVal; } //////////////////////////////////////////////////////////////////////// @@ -826,17 +791,17 @@ namespace System.Globalization { get { - Debug.Assert(this.sRealName != null, "[CultureData.CultureName] Expected this.sRealName to be populated by COMNlsInfo::nativeInitCultureData already"); + Debug.Assert(_sRealName != null, "[CultureData.CultureName] Expected _sRealName to be populated by already"); // since windows doesn't know about zh-CHS and zh-CHT, // we leave sRealName == zh-Hanx but we still need to // pretend that it was zh-CHX. - switch (this.sName) + switch (_sName) { case "zh-CHS": case "zh-CHT": - return this.sName; + return _sName; } - return this.sRealName; + return _sRealName; } } @@ -845,7 +810,7 @@ namespace System.Globalization { get { - return this.bUseOverrides; + return _bUseOverrides; } } @@ -854,13 +819,11 @@ namespace System.Globalization { get { - // Debug.Assert(this.sName != null, - // "[CultureData.SNAME] Expected this.sName to be populated by COMNlsInfo::nativeInitCultureData already"); - if (this.sName == null) + if (_sName == null) { - this.sName = String.Empty; + _sName = String.Empty; } - return this.sName; + return _sName; } } @@ -869,12 +832,12 @@ namespace System.Globalization { get { - if (this.sParent == null) + if (_sParent == null) { // Ask using the real name, so that we get parents of neutrals - this.sParent = DoGetLocaleInfo(this.sRealName, LOCALE_SPARENT); + _sParent = GetLocaleInfo(_sRealName, LocaleStringData.ParentName); } - return this.sParent; + return _sParent; } } @@ -883,31 +846,73 @@ namespace System.Globalization { get { - if (this.sLocalizedDisplayName == null) + if (_sLocalizedDisplayName == null) { + if (this.IsSupplementalCustomCulture) + { + if (this.IsNeutralCulture) + { + _sLocalizedDisplayName = this.SNATIVELANGUAGE; + } + else + { + _sLocalizedDisplayName = this.SNATIVEDISPLAYNAME; + } + } + else + { + try + { + const string ZH_CHT = "zh-CHT"; + const string ZH_CHS = "zh-CHS"; + + if (SNAME.Equals(ZH_CHT, StringComparison.OrdinalIgnoreCase)) + { + _sLocalizedDisplayName = GetLanguageDisplayName("zh-Hant"); + } + else if (SNAME.Equals(ZH_CHS, StringComparison.OrdinalIgnoreCase)) + { + _sLocalizedDisplayName = GetLanguageDisplayName("zh-Hans"); + } + else + { + _sLocalizedDisplayName = GetLanguageDisplayName(SNAME); + } + } + catch (Exception) + { + // do nothing + } + } // If it hasn't been found (Windows 8 and up), fallback to the system - if (String.IsNullOrEmpty(this.sLocalizedDisplayName)) + if (String.IsNullOrEmpty(_sLocalizedDisplayName)) { // If its neutral use the language name if (this.IsNeutralCulture) { - this.sLocalizedDisplayName = this.SLOCALIZEDLANGUAGE; + _sLocalizedDisplayName = this.SLOCALIZEDLANGUAGE; } else { - // We have to make the neutral distinction in case the OS returns a specific name - if (CultureInfo.UserDefaultUICulture.Name.Equals(Thread.CurrentThread.CurrentUICulture.Name)) + // Usually the UI culture shouldn't be different than what we got from WinRT except + // if DefaultThreadCurrentUICulture was set + CultureInfo ci; + + if (CultureInfo.DefaultThreadCurrentUICulture != null && + ((ci = GetUserDefaultCulture()) != null) && + !CultureInfo.DefaultThreadCurrentUICulture.Name.Equals(ci.Name)) { - this.sLocalizedDisplayName = DoGetLocaleInfo(LOCALE_SLOCALIZEDDISPLAYNAME); + _sLocalizedDisplayName = this.SNATIVEDISPLAYNAME; } - if (String.IsNullOrEmpty(this.sLocalizedDisplayName)) + else { - this.sLocalizedDisplayName = this.SNATIVEDISPLAYNAME; + _sLocalizedDisplayName = GetLocaleInfo(LocaleStringData.LocalizedDisplayName); } } } } - return this.sLocalizedDisplayName; + + return _sLocalizedDisplayName; } } @@ -916,39 +921,47 @@ namespace System.Globalization { get { - if (this.sEnglishDisplayName == null) + if (_sEnglishDisplayName == null) { // If its neutral use the language name if (this.IsNeutralCulture) { - this.sEnglishDisplayName = this.SENGLISHLANGUAGE; + _sEnglishDisplayName = this.SENGLISHLANGUAGE; + // differentiate the legacy display names + switch (_sName) + { + case "zh-CHS": + case "zh-CHT": + _sEnglishDisplayName += " Legacy"; + break; + } } else { - this.sEnglishDisplayName = DoGetLocaleInfo(LOCALE_SENGLISHDISPLAYNAME); + _sEnglishDisplayName = GetLocaleInfo(LocaleStringData.EnglishDisplayName); // if it isn't found build one: - if (String.IsNullOrEmpty(this.sEnglishDisplayName)) + if (String.IsNullOrEmpty(_sEnglishDisplayName)) { // Our existing names mostly look like: // "English" + "United States" -> "English (United States)" // "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)" - if (this.SENGLISHLANGUAGE.EndsWith(')')) + if (this.SENGLISHLANGUAGE[this.SENGLISHLANGUAGE.Length - 1] == ')') { // "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)" - this.sEnglishDisplayName = - this.SENGLISHLANGUAGE.Substring(0, this.sEnglishLanguage.Length - 1) + + _sEnglishDisplayName = + this.SENGLISHLANGUAGE.Substring(0, _sEnglishLanguage.Length - 1) + ", " + this.SENGCOUNTRY + ")"; } else { // "English" + "United States" -> "English (United States)" - this.sEnglishDisplayName = this.SENGLISHLANGUAGE + " (" + this.SENGCOUNTRY + ")"; + _sEnglishDisplayName = this.SENGLISHLANGUAGE + " (" + this.SENGCOUNTRY + ")"; } } } } - return this.sEnglishDisplayName; + return _sEnglishDisplayName; } } @@ -957,37 +970,47 @@ namespace System.Globalization { get { - if (this.sNativeDisplayName == null) + if (_sNativeDisplayName == null) { // If its neutral use the language name if (this.IsNeutralCulture) { - this.sNativeDisplayName = this.SNATIVELANGUAGE; + _sNativeDisplayName = this.SNATIVELANGUAGE; + // differentiate the legacy display names + switch (_sName) + { + case "zh-CHS": + _sNativeDisplayName += " \u65E7\u7248"; + break; + case "zh-CHT": + _sNativeDisplayName += " \u820A\u7248"; + break; + } } else { - this.sNativeDisplayName = DoGetLocaleInfo(LOCALE_SNATIVEDISPLAYNAME); + _sNativeDisplayName = GetLocaleInfo(LocaleStringData.NativeDisplayName); // if it isn't found build one: - if (String.IsNullOrEmpty(this.sNativeDisplayName)) + if (String.IsNullOrEmpty(_sNativeDisplayName)) { // These should primarily be "Deutsch (Deutschland)" type names - this.sNativeDisplayName = this.SNATIVELANGUAGE + " (" + this.SNATIVECOUNTRY + ")"; + _sNativeDisplayName = this.SNATIVELANGUAGE + " (" + this.SNATIVECOUNTRY + ")"; } } } - return this.sNativeDisplayName; + return _sNativeDisplayName; } } // The culture name to be used in CultureInfo.CreateSpecificCulture() - internal String SSPECIFICCULTURE + internal string SSPECIFICCULTURE { get { - // This got populated when ComNlsInfo::nativeInitCultureData told us we had a culture - Debug.Assert(this.sSpecificCulture != null, "[CultureData.SSPECIFICCULTURE] Expected this.sSpecificCulture to be populated by COMNlsInfo::nativeInitCultureData already"); - return this.sSpecificCulture; + // This got populated during the culture initialization + Debug.Assert(_sSpecificCulture != null, "[CultureData.SSPECIFICCULTURE] Expected this.sSpecificCulture to be populated by culture data initialization already"); + return _sSpecificCulture; } } @@ -1000,37 +1023,37 @@ namespace System.Globalization { get { - if (this.sISO639Language == null) + if (_sISO639Language == null) { - this.sISO639Language = DoGetLocaleInfo(LOCALE_SISO639LANGNAME); + _sISO639Language = GetLocaleInfo(LocaleStringData.Iso639LanguageTwoLetterName); } - return this.sISO639Language; + return _sISO639Language; } } // iso 639 language name, ie: eng - internal String SISO639LANGNAME2 + internal string SISO639LANGNAME2 { get { - if (this.sISO639Language2 == null) + if (_sISO639Language2 == null) { - this.sISO639Language2 = DoGetLocaleInfo(LOCALE_SISO639LANGNAME2); + _sISO639Language2 = GetLocaleInfo(LocaleStringData.Iso639LanguageThreeLetterName); } - return this.sISO639Language2; + return _sISO639Language2; } } // abbreviated windows language name (ie: enu) (non-standard, avoid this) - internal String SABBREVLANGNAME + internal string SABBREVLANGNAME { get { - if (this.sAbbrevLang == null) + if (_sAbbrevLang == null) { - this.sAbbrevLang = DoGetLocaleInfo(LOCALE_SABBREVLANGNAME); + _sAbbrevLang = GetThreeLetterWindowsLanguageName(_sRealName); } - return this.sAbbrevLang; + return _sAbbrevLang; } } @@ -1040,20 +1063,25 @@ namespace System.Globalization { get { - if (this.sLocalizedLanguage == null) + if (_sLocalizedLanguage == null) { - if (CultureInfo.UserDefaultUICulture.Name.Equals(Thread.CurrentThread.CurrentUICulture.Name)) + // Usually the UI culture shouldn't be different than what we got from WinRT except + // if DefaultThreadCurrentUICulture was set + CultureInfo ci; + + if (CultureInfo.DefaultThreadCurrentUICulture != null && + ((ci = GetUserDefaultCulture()) != null) && + !CultureInfo.DefaultThreadCurrentUICulture.Name.Equals(ci.Name)) { - this.sLocalizedLanguage = DoGetLocaleInfo(LOCALE_SLOCALIZEDLANGUAGENAME); + _sLocalizedLanguage = SNATIVELANGUAGE; } - // Some OS's might not have this resource or LCTYPE - if (String.IsNullOrEmpty(this.sLocalizedLanguage)) + else { - this.sLocalizedLanguage = SNATIVELANGUAGE; + _sLocalizedLanguage = GetLocaleInfo(LocaleStringData.LocalizedLanguageName); } } - return this.sLocalizedLanguage; + return _sLocalizedLanguage; } } @@ -1062,11 +1090,11 @@ namespace System.Globalization { get { - if (this.sEnglishLanguage == null) + if (_sEnglishLanguage == null) { - this.sEnglishLanguage = DoGetLocaleInfo(LOCALE_SENGLISHLANGUAGENAME); + _sEnglishLanguage = GetLocaleInfo(LocaleStringData.EnglishLanguageName); } - return this.sEnglishLanguage; + return _sEnglishLanguage; } } @@ -1075,13 +1103,11 @@ namespace System.Globalization { get { - if (this.sNativeLanguage == null) + if (_sNativeLanguage == null) { - { - this.sNativeLanguage = DoGetLocaleInfo(LOCALE_SNATIVELANGUAGENAME); - } + _sNativeLanguage = GetLocaleInfo(LocaleStringData.NativeLanguageName); } - return this.sNativeLanguage; + return _sNativeLanguage; } } @@ -1094,24 +1120,23 @@ namespace System.Globalization { get { - if (this.sRegionName == null) + if (_sRegionName == null) { - this.sRegionName = DoGetLocaleInfo(LOCALE_SISO3166CTRYNAME); + _sRegionName = GetLocaleInfo(LocaleStringData.Iso3166CountryName); } - return this.sRegionName; + return _sRegionName; } } - // GeoId internal int IGEOID { get { - if (this.iGeoId == undef) + if (_iGeoId == undef) { - this.iGeoId = DoGetLocaleInfoInt(LOCALE_IGEOID); + _iGeoId = GetGeoId(_sRealName); } - return this.iGeoId; + return _iGeoId; } } @@ -1120,23 +1145,23 @@ namespace System.Globalization { get { - if (this.sLocalizedCountry == null) + if (_sLocalizedCountry == null) { - // If it hasn't been found (Windows 8 and up), fallback to the system - if (String.IsNullOrEmpty(this.sLocalizedCountry)) + try { - // We have to make the neutral distinction in case the OS returns a specific name - if (CultureInfo.UserDefaultUICulture.Name.Equals(Thread.CurrentThread.CurrentUICulture.Name)) - { - this.sLocalizedCountry = DoGetLocaleInfo(LOCALE_SLOCALIZEDCOUNTRYNAME); - } - if (String.IsNullOrEmpty(this.sLocalizedDisplayName)) - { - this.sLocalizedCountry = SNATIVECOUNTRY; - } + _sLocalizedCountry = GetRegionDisplayName(SISO3166CTRYNAME); + } + catch (Exception) + { + // do nothing. we'll fallback + } + + if (_sLocalizedCountry == null) + { + _sLocalizedCountry = SNATIVECOUNTRY; } } - return this.sLocalizedCountry; + return _sLocalizedCountry; } } @@ -1145,11 +1170,11 @@ namespace System.Globalization { get { - if (this.sEnglishCountry == null) + if (_sEnglishCountry == null) { - this.sEnglishCountry = DoGetLocaleInfo(LOCALE_SENGLISHCOUNTRYNAME); + _sEnglishCountry = GetLocaleInfo(LocaleStringData.EnglishCountryName); } - return this.sEnglishCountry; + return _sEnglishCountry; } } @@ -1158,11 +1183,11 @@ namespace System.Globalization { get { - if (this.sNativeCountry == null) + if (_sNativeCountry == null) { - this.sNativeCountry = DoGetLocaleInfo(LOCALE_SNATIVECOUNTRYNAME); + _sNativeCountry = GetLocaleInfo(LocaleStringData.NativeCountryName); } - return this.sNativeCountry; + return _sNativeCountry; } } @@ -1171,90 +1196,70 @@ namespace System.Globalization { get { - if (this.sISO3166CountryName == null) + if (_sISO3166CountryName == null) { - this.sISO3166CountryName = DoGetLocaleInfo(LOCALE_SISO3166CTRYNAME); + _sISO3166CountryName = GetLocaleInfo(LocaleStringData.Iso3166CountryName); } - return this.sISO3166CountryName; + return _sISO3166CountryName; } } - // ISO 3166 Country Name + // 3 letter ISO 3166 country code internal String SISO3166CTRYNAME2 { get { - if (this.sISO3166CountryName2 == null) + if (_sISO3166CountryName2 == null) { - this.sISO3166CountryName2 = DoGetLocaleInfo(LOCALE_SISO3166CTRYNAME2); + _sISO3166CountryName2 = GetLocaleInfo(LocaleStringData.Iso3166CountryName2); } - return this.sISO3166CountryName2; + return _sISO3166CountryName2; } } - // abbreviated Country Name (windows version, non-standard, avoid) - internal String SABBREVCTRYNAME - { - get - { - if (this.sAbbrevCountry == null) - { - this.sAbbrevCountry = DoGetLocaleInfo(LOCALE_SABBREVCTRYNAME); - } - return this.sAbbrevCountry; - } - } - - // Console fallback name (ie: locale to use for console apps for unicode-only locales) internal int IINPUTLANGUAGEHANDLE { get { - if (this.iInputLanguageHandle == undef) + if (_iInputLanguageHandle == undef) { if (IsSupplementalCustomCulture) { - this.iInputLanguageHandle = 0x0409; + _iInputLanguageHandle = 0x0409; } else { // Input Language is same as LCID for built-in cultures - this.iInputLanguageHandle = this.ILANGUAGE; + _iInputLanguageHandle = this.ILANGUAGE; } } - return this.iInputLanguageHandle; + return _iInputLanguageHandle; } } // Console fallback name (ie: locale to use for console apps for unicode-only locales) - internal String SCONSOLEFALLBACKNAME + internal string SCONSOLEFALLBACKNAME { get { - if (this.sConsoleFallbackName == null) + if (_sConsoleFallbackName == null) { - string consoleFallbackName = DoGetLocaleInfo(LOCALE_SCONSOLEFALLBACKNAME); - if (consoleFallbackName == "es-ES_tradnl") - { - consoleFallbackName = "es-ES"; - } - this.sConsoleFallbackName = consoleFallbackName; + _sConsoleFallbackName = GetConsoleFallbackName(_sRealName); } - return this.sConsoleFallbackName; + return _sConsoleFallbackName; } } - // (user can override) grouping of digits internal int[] WAGROUPING { get { - if (this.waGrouping == null || UseUserOverride) + if (_waGrouping == null) { - this.waGrouping = ConvertWin32GroupString(DoGetLocaleInfo(LOCALE_SGROUPING)); + _waGrouping = GetLocaleInfo(LocaleGroupingData.Digit); } - return this.waGrouping; + return _waGrouping; } } @@ -1267,11 +1272,11 @@ namespace System.Globalization { get { - if (this.sNaN == null) + if (_sNaN == null) { - this.sNaN = DoGetLocaleInfo(LOCALE_SNAN); + _sNaN = GetLocaleInfo(LocaleStringData.NaNSymbol); } - return this.sNaN; + return _sNaN; } } @@ -1280,11 +1285,11 @@ namespace System.Globalization { get { - if (this.sPositiveInfinity == null) + if (_sPositiveInfinity == null) { - this.sPositiveInfinity = DoGetLocaleInfo(LOCALE_SPOSINFINITY); + _sPositiveInfinity = GetLocaleInfo(LocaleStringData.PositiveInfinitySymbol); } - return this.sPositiveInfinity; + return _sPositiveInfinity; } } @@ -1293,11 +1298,11 @@ namespace System.Globalization { get { - if (this.sNegativeInfinity == null) + if (_sNegativeInfinity == null) { - this.sNegativeInfinity = DoGetLocaleInfo(LOCALE_SNEGINFINITY); + _sNegativeInfinity = GetLocaleInfo(LocaleStringData.NegativeInfinitySymbol); } - return this.sNegativeInfinity; + return _sNegativeInfinity; } } @@ -1311,12 +1316,12 @@ namespace System.Globalization { get { - if (this.iNegativePercent == undef) + if (_iNegativePercent == undef) { // Note that <= Windows Vista this is synthesized by native code - this.iNegativePercent = DoGetLocaleInfoInt(LOCALE_INEGATIVEPERCENT); + _iNegativePercent = GetLocaleInfo(LocaleNumberData.NegativePercentFormat); } - return this.iNegativePercent; + return _iNegativePercent; } } @@ -1325,12 +1330,12 @@ namespace System.Globalization { get { - if (this.iPositivePercent == undef) + if (_iPositivePercent == undef) { // Note that <= Windows Vista this is synthesized by native code - this.iPositivePercent = DoGetLocaleInfoInt(LOCALE_IPOSITIVEPERCENT); + _iPositivePercent = GetLocaleInfo(LocaleNumberData.PositivePercentFormat); } - return this.iPositivePercent; + return _iPositivePercent; } } @@ -1339,26 +1344,24 @@ namespace System.Globalization { get { - if (this.sPercent == null) + if (_sPercent == null) { - // Note that <= Windows Vista this is synthesized by native code - this.sPercent = DoGetLocaleInfo(LOCALE_SPERCENT); + _sPercent = GetLocaleInfo(LocaleStringData.PercentSymbol); } - return this.sPercent; + return _sPercent; } } - // PerMille (‰) symbol + // PerMille symbol internal String SPERMILLE { get { - if (this.sPerMille == null) + if (_sPerMille == null) { - // Note that <= Windows Vista this is synthesized by native code - this.sPerMille = DoGetLocaleInfo(LOCALE_SPERMILLE); + _sPerMille = GetLocaleInfo(LocaleStringData.PerMilleSymbol); } - return this.sPerMille; + return _sPerMille; } } @@ -1371,11 +1374,11 @@ namespace System.Globalization { get { - if (this.sCurrency == null || UseUserOverride) + if (_sCurrency == null) { - this.sCurrency = DoGetLocaleInfo(LOCALE_SCURRENCY); + _sCurrency = GetLocaleInfo(LocaleStringData.MonetarySymbol); } - return this.sCurrency; + return _sCurrency; } } @@ -1384,11 +1387,11 @@ namespace System.Globalization { get { - if (this.sIntlMonetarySymbol == null) + if (_sIntlMonetarySymbol == null) { - this.sIntlMonetarySymbol = DoGetLocaleInfo(LOCALE_SINTLSYMBOL); + _sIntlMonetarySymbol = GetLocaleInfo(LocaleStringData.Iso4217MonetarySymbol); } - return this.sIntlMonetarySymbol; + return _sIntlMonetarySymbol; } } @@ -1397,11 +1400,11 @@ namespace System.Globalization { get { - if (this.sEnglishCurrency == null) + if (_sEnglishCurrency == null) { - this.sEnglishCurrency = DoGetLocaleInfo(LOCALE_SENGCURRNAME); + _sEnglishCurrency = GetLocaleInfo(LocaleStringData.CurrencyEnglishName); } - return this.sEnglishCurrency; + return _sEnglishCurrency; } } @@ -1410,11 +1413,11 @@ namespace System.Globalization { get { - if (this.sNativeCurrency == null) + if (_sNativeCurrency == null) { - this.sNativeCurrency = DoGetLocaleInfo(LOCALE_SNATIVECURRNAME); + _sNativeCurrency = GetLocaleInfo(LocaleStringData.CurrencyNativeName); } - return this.sNativeCurrency; + return _sNativeCurrency; } } @@ -1427,31 +1430,24 @@ namespace System.Globalization { get { - if (this.waMonetaryGrouping == null || UseUserOverride) + if (_waMonetaryGrouping == null) { - this.waMonetaryGrouping = ConvertWin32GroupString(DoGetLocaleInfo(LOCALE_SMONGROUPING)); + _waMonetaryGrouping = GetLocaleInfo(LocaleGroupingData.Monetary); } - return this.waMonetaryGrouping; + return _waMonetaryGrouping; } } - // internal String sMonetaryDecimal ; // (user can override) monetary decimal separator - // internal String sMonetaryThousand ; // (user can override) monetary thousands separator - - ///////// - // Misc // - ///////// - // (user can override) system of measurement 0=metric, 1=US (RegionInfo) internal int IMEASURE { get { - if (this.iMeasure == undef || UseUserOverride) + if (_iMeasure == undef) { - this.iMeasure = DoGetLocaleInfoInt(LOCALE_IMEASURE); + _iMeasure = GetLocaleInfo(LocaleNumberData.MeasurementSystem); } - return this.iMeasure; + return _iMeasure; } } @@ -1460,14 +1456,15 @@ namespace System.Globalization { get { - if (this.sListSeparator == null || UseUserOverride) + if (_sListSeparator == null) { - this.sListSeparator = DoGetLocaleInfo(LOCALE_SLIST); + _sListSeparator = GetLocaleInfo(LocaleStringData.ListSeparator); } - return this.sListSeparator; + return _sListSeparator; } } + //////////////////////////// // Calendar/Time (Gregorian) // //////////////////////////// @@ -1477,11 +1474,11 @@ namespace System.Globalization { get { - if (this.sAM1159 == null || UseUserOverride) + if (_sAM1159 == null) { - this.sAM1159 = DoGetLocaleInfo(LOCALE_S1159); + _sAM1159 = GetLocaleInfo(LocaleStringData.AMDesignator); } - return this.sAM1159; + return _sAM1159; } } @@ -1490,11 +1487,11 @@ namespace System.Globalization { get { - if (this.sPM2359 == null || UseUserOverride) + if (_sPM2359 == null) { - this.sPM2359 = DoGetLocaleInfo(LOCALE_S2359); + _sPM2359 = GetLocaleInfo(LocaleStringData.PMDesignator); } - return this.sPM2359; + return _sPM2359; } } @@ -1503,32 +1500,38 @@ namespace System.Globalization { get { - if (this.saLongTimes == null || UseUserOverride) + if (_saLongTimes == null) { - String[] longTimes = DoEnumTimeFormats(); + Debug.Assert(!GlobalizationMode.Invariant); + + String[] longTimes = GetTimeFormats(); if (longTimes == null || longTimes.Length == 0) { - this.saLongTimes = Invariant.saLongTimes; + _saLongTimes = Invariant._saLongTimes; } else { - this.saLongTimes = longTimes; + _saLongTimes = longTimes; } } - return this.saLongTimes; + return _saLongTimes; } } // short time format // Short times (derived from long times format) + // TODO: NLS Arrowhead - On Windows 7 we should have short times so this isn't necessary internal String[] ShortTimes { get { - if (this.saShortTimes == null || UseUserOverride) + if (_saShortTimes == null) { + Debug.Assert(!GlobalizationMode.Invariant); + // Try to get the short times from the OS/culture.dll - String[] shortTimes = DoEnumShortTimeFormats(); + String[] shortTimes = null; + shortTimes = GetShortTimeFormats(); if (shortTimes == null || shortTimes.Length == 0) { @@ -1539,13 +1542,24 @@ namespace System.Globalization shortTimes = DeriveShortTimesFromLong(); } + /* The above logic doesn't make sense on Mac, since the OS can provide us a "short time pattern". + * currently this is the 4th element in the array returned by LongTimes. We'll add this to our array + * if it doesn't exist. + */ + shortTimes = AdjustShortTimesForMac(shortTimes); + // Found short times, use them - this.saShortTimes = shortTimes; + _saShortTimes = shortTimes; } - return this.saShortTimes; + return _saShortTimes; } } + private string[] AdjustShortTimesForMac(string[] shortTimes) + { + return shortTimes; + } + private string[] DeriveShortTimesFromLong() { // Our logic is to look for h,H,m,s,t. If we find an s, then we check the string @@ -1611,13 +1625,19 @@ namespace System.Globalization bool containsSpace; int endIndex = GetIndexOfNextTokenAfterSeconds(time, j, out containsSpace); - StringBuilder sb = new StringBuilder(time.Substring(0, j)); + + string sep; + if (containsSpace) { - sb.Append(' '); + sep = " "; } - sb.Append(time.Substring(endIndex)); - time = sb.ToString(); + else + { + sep = ""; + } + + time = time.Substring(0, j) + sep + time.Substring(endIndex); break; case 'm': case 'H': @@ -1670,12 +1690,11 @@ namespace System.Globalization { get { - if (this.iFirstDayOfWeek == undef || UseUserOverride) + if (_iFirstDayOfWeek == undef) { - // Have to convert it from windows to .Net formats - this.iFirstDayOfWeek = ConvertFirstDayOfWeekMonToSun(DoGetLocaleInfoInt(LOCALE_IFIRSTDAYOFWEEK)); + _iFirstDayOfWeek = GetFirstDayOfWeek(); } - return this.iFirstDayOfWeek; + return _iFirstDayOfWeek; } } @@ -1684,71 +1703,71 @@ namespace System.Globalization { get { - if (this.iFirstWeekOfYear == undef || UseUserOverride) + if (_iFirstWeekOfYear == undef) { - this.iFirstWeekOfYear = DoGetLocaleInfoInt(LOCALE_IFIRSTWEEKOFYEAR); + _iFirstWeekOfYear = GetLocaleInfo(LocaleNumberData.FirstWeekOfYear); } - return this.iFirstWeekOfYear; + return _iFirstWeekOfYear; } } // (user can override default only) short date format - internal String[] ShortDates(int calendarId) + internal String[] ShortDates(CalendarId calendarId) { return GetCalendar(calendarId).saShortDates; } // (user can override default only) long date format - internal String[] LongDates(int calendarId) + internal String[] LongDates(CalendarId calendarId) { return GetCalendar(calendarId).saLongDates; } // (user can override) date year/month format. - internal String[] YearMonths(int calendarId) + internal String[] YearMonths(CalendarId calendarId) { return GetCalendar(calendarId).saYearMonths; } // day names - internal string[] DayNames(int calendarId) + internal string[] DayNames(CalendarId calendarId) { return GetCalendar(calendarId).saDayNames; } // abbreviated day names - internal string[] AbbreviatedDayNames(int calendarId) + internal string[] AbbreviatedDayNames(CalendarId calendarId) { // Get abbreviated day names for this calendar from the OS if necessary return GetCalendar(calendarId).saAbbrevDayNames; } // The super short day names - internal string[] SuperShortDayNames(int calendarId) + internal string[] SuperShortDayNames(CalendarId calendarId) { return GetCalendar(calendarId).saSuperShortDayNames; } // month names - internal string[] MonthNames(int calendarId) + internal string[] MonthNames(CalendarId calendarId) { return GetCalendar(calendarId).saMonthNames; } // Genitive month names - internal string[] GenitiveMonthNames(int calendarId) + internal string[] GenitiveMonthNames(CalendarId calendarId) { return GetCalendar(calendarId).saMonthGenitiveNames; } // month names - internal string[] AbbreviatedMonthNames(int calendarId) + internal string[] AbbreviatedMonthNames(CalendarId calendarId) { return GetCalendar(calendarId).saAbbrevMonthNames; } // Genitive month names - internal string[] AbbreviatedGenitiveMonthNames(int calendarId) + internal string[] AbbreviatedGenitiveMonthNames(CalendarId calendarId) { return GetCalendar(calendarId).saAbbrevMonthGenitiveNames; } @@ -1756,13 +1775,13 @@ namespace System.Globalization // Leap year month names // Note: This only applies to Hebrew, and it basically adds a "1" to the 6th month name // the non-leap names skip the 7th name in the normal month name array - internal string[] LeapYearMonthNames(int calendarId) + internal string[] LeapYearMonthNames(CalendarId calendarId) { return GetCalendar(calendarId).saLeapYearMonthNames; } // month/day format (single string, no override) - internal String MonthDay(int calendarId) + internal String MonthDay(CalendarId calendarId) { return GetCalendar(calendarId).sMonthDay; } @@ -1774,29 +1793,30 @@ namespace System.Globalization ///////////// // all available calendar type(s), The first one is the default calendar. - internal int[] CalendarIds + internal CalendarId[] CalendarIds { get { - if (this.waCalendars == null) + if (_waCalendars == null) { // We pass in an array of ints, and native side fills it up with count calendars. // We then have to copy that list to a new array of the right size. // Default calendar should be first - int[] calendarInts = new int[23]; - Debug.Assert(this.sWindowsName != null, "[CultureData.CalendarIds] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already"); - int count = CalendarData.nativeGetCalendars(this.sWindowsName, this.bUseOverrides, calendarInts); + CalendarId[] calendars = new CalendarId[23]; + Debug.Assert(_sWindowsName != null, "[CultureData.CalendarIds] Expected _sWindowsName to be populated by already"); + int count = CalendarData.GetCalendars(_sWindowsName, _bUseOverrides, calendars); // See if we had a calendar to add. if (count == 0) { // Failed for some reason, just grab Gregorian from Invariant - this.waCalendars = Invariant.waCalendars; + _waCalendars = Invariant._waCalendars; } else { // The OS may not return calendar 4 for zh-TW, but we've always allowed it. - if (this.sWindowsName == "zh-TW") + // TODO: Is this hack necessary long-term? + if (_sWindowsName == "zh-TW") { bool found = false; @@ -1804,7 +1824,7 @@ namespace System.Globalization for (int i = 0; i < count; i++) { // Stop if we found calendar four - if (calendarInts[i] == Calendar.CAL_TAIWAN) + if (calendars[i] == CalendarId.TAIWAN) { found = true; break; @@ -1817,21 +1837,20 @@ namespace System.Globalization // Insert it as the 2nd calendar count++; // Copy them from the 2nd position to the end, -1 for skipping 1st, -1 for one being added. - Array.Copy(calendarInts, 1, calendarInts, 2, 23 - 1 - 1); - calendarInts[1] = Calendar.CAL_TAIWAN; + Array.Copy(calendars, 1, calendars, 2, 23 - 1 - 1); + calendars[1] = CalendarId.TAIWAN; } } // It worked, remember the list - int[] temp = new int[count]; - Array.Copy(calendarInts, temp, count); + CalendarId[] temp = new CalendarId[count]; + Array.Copy(calendars, temp, count); // Want 1st calendar to be default // Prior to Vista the enumeration didn't have default calendar first - // Only a coreclr concern, culture.dll does the right thing. if (temp.Length > 1) { - int i = DoGetLocaleInfoInt(LOCALE_ICALENDARTYPE); + CalendarId i = (CalendarId)GetLocaleInfo(LocaleNumberData.CalendarType); if (temp[1] == i) { temp[1] = temp[0]; @@ -1839,45 +1858,45 @@ namespace System.Globalization } } - this.waCalendars = temp; + _waCalendars = temp; } } - return this.waCalendars; + return _waCalendars; } } // Native calendar names. index of optional calendar - 1, empty if no optional calendar at that number - internal String CalendarName(int calendarId) + internal string CalendarName(CalendarId calendarId) { // Get the calendar return GetCalendar(calendarId).sNativeName; } - internal CalendarData GetCalendar(int calendarId) + internal CalendarData GetCalendar(CalendarId calendarId) { - Debug.Assert(calendarId > 0 && calendarId <= CalendarData.MAX_CALENDARS, + Debug.Assert(calendarId > 0 && calendarId <= CalendarId.LAST_CALENDAR, "[CultureData.GetCalendar] Expect calendarId to be in a valid range"); // arrays are 0 based, calendarIds are 1 based - int calendarIndex = calendarId - 1; + int calendarIndex = (int)calendarId - 1; // Have to have calendars - if (calendars == null) + if (_calendars == null) { - calendars = new CalendarData[CalendarData.MAX_CALENDARS]; + _calendars = new CalendarData[CalendarData.MAX_CALENDARS]; } // we need the following local variable to avoid returning null // when another thread creates a new array of CalendarData (above) // right after we insert the newly created CalendarData (below) - CalendarData calendarData = calendars[calendarIndex]; + CalendarData calendarData = _calendars[calendarIndex]; // Make sure that calendar has data - if (calendarData == null || UseUserOverride) + if (calendarData == null) { - Debug.Assert(this.sWindowsName != null, "[CultureData.GetCalendar] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already"); - calendarData = new CalendarData(this.sWindowsName, calendarId, this.UseUserOverride); - calendars[calendarIndex] = calendarData; + Debug.Assert(_sWindowsName != null, "[CultureData.GetCalendar] Expected _sWindowsName to be populated by already"); + calendarData = new CalendarData(_sWindowsName, calendarId, this.UseUserOverride); + _calendars[calendarIndex] = calendarData; } return calendarData; @@ -1913,13 +1932,13 @@ namespace System.Globalization { get { - if (this.iReadingLayout == undef) + if (_iReadingLayout == undef) { - Debug.Assert(this.sRealName != null, "[CultureData.IsRightToLeft] Expected this.sRealName to be populated by COMNlsInfo::nativeInitCultureData already"); - this.iReadingLayout = DoGetLocaleInfoInt(LOCALE_IREADINGLAYOUT); + Debug.Assert(_sRealName != null, "[CultureData.IsRightToLeft] Expected _sRealName to be populated by already"); + _iReadingLayout = GetLocaleInfo(LocaleNumberData.ReadingLayout); } - return (this.iReadingLayout); + return (_iReadingLayout); } } @@ -1934,23 +1953,10 @@ namespace System.Globalization { get { - if (this.sTextInfo == null) - { - // LOCALE_SSORTLOCALE is broken in Win7 for Alt sorts. - // It is also not supported downlevel without culture.dll. - if (IsNeutralCulture || IsSupplementalCustomCulture) - { - string sortLocale = DoGetLocaleInfo(LOCALE_SSORTLOCALE); - this.sTextInfo = GetCultureData(sortLocale, bUseOverrides).SNAME; - } - - if (this.sTextInfo == null) - { - this.sTextInfo = this.SNAME; // removes alternate sort - } - } - - return this.sTextInfo; + // Note: Custom cultures might point at another culture's textinfo, however windows knows how + // to redirect it to the desired textinfo culture, so this is OK. + Debug.Assert(_sRealName != null, "[CultureData.STEXTINFO] Expected _sRealName to be populated by already"); + return (_sRealName); } } @@ -1959,24 +1965,8 @@ namespace System.Globalization { get { - if (this.sCompareInfo == null) - { - // LOCALE_SSORTLOCALE is broken in Win7 for Alt sorts. - // It is also not supported downlevel without culture.dll. - // We really only need it for the custom locale case though - // since for all other cases, it is the same as sWindowsName - if (IsSupplementalCustomCulture) - { - this.sCompareInfo = DoGetLocaleInfo(LOCALE_SSORTLOCALE); - } - - if (this.sCompareInfo == null) - { - this.sCompareInfo = this.sWindowsName; - } - } - - return this.sCompareInfo; + Debug.Assert(_sRealName != null, "[CultureData.SCOMPAREINFO] Expected _sRealName to be populated by already"); + return (_sRealName); } } @@ -1988,16 +1978,15 @@ namespace System.Globalization } } - internal int IDEFAULTANSICODEPAGE // default ansi code page ID (ACP) { get { - if (this.iDefaultAnsiCodePage == undef) + if (_iDefaultAnsiCodePage == undef) { - this.iDefaultAnsiCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTANSICODEPAGE); + _iDefaultAnsiCodePage = GetAnsiCodePage(_sRealName); } - return this.iDefaultAnsiCodePage; + return _iDefaultAnsiCodePage; } } @@ -2005,11 +1994,11 @@ namespace System.Globalization { get { - if (this.iDefaultOemCodePage == undef) + if (_iDefaultOemCodePage == undef) { - this.iDefaultOemCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTCODEPAGE); + _iDefaultOemCodePage = GetOemCodePage(_sRealName); } - return this.iDefaultOemCodePage; + return _iDefaultOemCodePage; } } @@ -2017,11 +2006,11 @@ namespace System.Globalization { get { - if (this.iDefaultMacCodePage == undef) + if (_iDefaultMacCodePage == undef) { - this.iDefaultMacCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTMACCODEPAGE); + _iDefaultMacCodePage = GetMacCodePage(_sRealName); } - return this.iDefaultMacCodePage; + return _iDefaultMacCodePage; } } @@ -2029,54 +2018,33 @@ namespace System.Globalization { get { - if (this.iDefaultEbcdicCodePage == undef) + if (_iDefaultEbcdicCodePage == undef) { - this.iDefaultEbcdicCodePage = DoGetLocaleInfoInt(LOCALE_IDEFAULTEBCDICCODEPAGE); + _iDefaultEbcdicCodePage = GetEbcdicCodePage(_sRealName); } - return this.iDefaultEbcdicCodePage; + return _iDefaultEbcdicCodePage; } } - // Obtain locale name from LCID - // NOTE: This will get neutral names, unlike the OS API - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern int LocaleNameToLCID(String localeName); - - // These are desktop only, not coreclr - // locale ID (0409), including sort information internal int ILANGUAGE { get { - if (this.iLanguage == 0) + if (_iLanguage == 0) { - Debug.Assert(this.sRealName != null, "[CultureData.ILANGUAGE] Expected this.sRealName to be populated by COMNlsInfo::nativeInitCultureData already"); - this.iLanguage = LocaleNameToLCID(this.sRealName); + Debug.Assert(_sRealName != null, "[CultureData.ILANGUAGE] Expected this.sRealName to be populated already"); + _iLanguage = LocaleNameToLCID(_sRealName); } - return this.iLanguage; + return _iLanguage; } } - internal bool IsWin32Installed - { - get { return this.bWin32Installed; } - } - - internal bool IsFramework - { - get { return this.bFramework; } - } - - //////////////////// - // Derived properties // - //////////////////// - internal bool IsNeutralCulture { get { - // NlsInfo::nativeInitCultureData told us if we're neutral or not - return this.bNeutral; + // InitCultureData told us if we're neutral or not + return _bNeutral; } } @@ -2093,7 +2061,13 @@ namespace System.Globalization { get { - int defaultCalId = DoGetLocaleInfoInt(LOCALE_ICALENDARTYPE); + if (GlobalizationMode.Invariant) + { + return CultureInfo.GetCalendarInstance(CalendarIds[0]); + } + + CalendarId defaultCalId = (CalendarId)GetLocaleInfo(LocaleNumberData.CalendarType); + if (defaultCalId == 0) { defaultCalId = this.CalendarIds[0]; @@ -2104,29 +2078,29 @@ namespace System.Globalization } // All of our era names - internal String[] EraNames(int calendarId) + internal String[] EraNames(CalendarId calendarId) { Debug.Assert(calendarId > 0, "[CultureData.saEraNames] Expected Calendar.ID > 0"); return this.GetCalendar(calendarId).saEraNames; } - internal String[] AbbrevEraNames(int calendarId) + internal String[] AbbrevEraNames(CalendarId calendarId) { Debug.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0"); return this.GetCalendar(calendarId).saAbbrevEraNames; } - internal String[] AbbreviatedEnglishEraNames(int calendarId) + internal String[] AbbreviatedEnglishEraNames(CalendarId calendarId) { Debug.Assert(calendarId > 0, "[CultureData.saAbbrevEraNames] Expected Calendar.ID > 0"); return this.GetCalendar(calendarId).saAbbrevEnglishEraNames; } - // String array DEFAULTS - // Note: GetDTFIOverrideValues does the user overrides for these, so we don't have to. + //// String array DEFAULTS + //// Note: GetDTFIOverrideValues does the user overrides for these, so we don't have to. // Time separator (derived from time format) @@ -2134,23 +2108,23 @@ namespace System.Globalization { get { - if (sTimeSeparator == null || UseUserOverride) + if (_sTimeSeparator == null) { - string longTimeFormat = ReescapeWin32String(DoGetLocaleInfo(LOCALE_STIMEFORMAT)); + string longTimeFormat = GetTimeFormatString(); if (String.IsNullOrEmpty(longTimeFormat)) { longTimeFormat = LongTimes[0]; } // Compute STIME from time format - sTimeSeparator = GetTimeSeparator(longTimeFormat); + _sTimeSeparator = GetTimeSeparator(longTimeFormat); } - return sTimeSeparator; + return _sTimeSeparator; } } // Date separator (derived from short date format) - internal String DateSeparator(int calendarId) + internal String DateSeparator(CalendarId calendarId) { return GetDateSeparator(ShortDates(calendarId)[0]); } @@ -2178,11 +2152,11 @@ namespace System.Globalization // always build a stringbuilder because we need to remove the ' or \. // //////////////////////////////////////////////////////////////////////////// - static private String UnescapeNlsString(String str, int start, int end) + private static String UnescapeNlsString(String str, int start, int end) { - Contract.Requires(str != null); - Contract.Requires(start >= 0); - Contract.Requires(end >= 0); + Debug.Assert(str != null); + Debug.Assert(start >= 0); + Debug.Assert(end >= 0); StringBuilder result = null; for (int i = start; i < str.Length && i <= end; i++) @@ -2221,102 +2195,7 @@ namespace System.Globalization return (result.ToString()); } - //////////////////////////////////////////////////////////////////////////// - // - // Reescape a Win32 style quote string as a NLS+ style quoted string - // - // This is also the escaping style used by custom culture data files - // - // NLS+ uses \ to escape the next character, whether in a quoted string or - // not, so we always have to change \ to \\. - // - // NLS+ uses \' to escape a quote inside a quoted string so we have to change - // '' to \' (if inside a quoted string) - // - // We don't build the stringbuilder unless we find something to change - //////////////////////////////////////////////////////////////////////////// - static internal String ReescapeWin32String(String str) - { - // If we don't have data, then don't try anything - if (str == null) - return null; - - StringBuilder result = null; - - bool inQuote = false; - for (int i = 0; i < str.Length; i++) - { - // Look for quote - if (str[i] == '\'') - { - // Already in quote? - if (inQuote) - { - // See another single quote. Is this '' of 'fred''s' or '''', or is it an ending quote? - if (i + 1 < str.Length && str[i + 1] == '\'') - { - // Found another ', so we have ''. Need to add \' instead. - // 1st make sure we have our stringbuilder - if (result == null) - result = new StringBuilder(str, 0, i, str.Length * 2); - - // Append a \' and keep going (so we don't turn off quote mode) - result.Append("\\'"); - i++; - continue; - } - - // Turning off quote mode, fall through to add it - inQuote = false; - } - else - { - // Found beginning quote, fall through to add it - inQuote = true; - } - } - // Is there a single \ character? - else if (str[i] == '\\') - { - // Found a \, need to change it to \\ - // 1st make sure we have our stringbuilder - if (result == null) - result = new StringBuilder(str, 0, i, str.Length * 2); - - // Append our \\ to the string & continue - result.Append("\\\\"); - continue; - } - - // If we have a builder we need to add our character - if (result != null) - result.Append(str[i]); - } - - // Unchanged string? , just return input string - if (result == null) - return str; - - // String changed, need to use the builder - return result.ToString(); - } - - static internal String[] ReescapeWin32Strings(String[] array) - { - if (array != null) - { - for (int i = 0; i < array.Length; i++) - { - array[i] = ReescapeWin32String(array[i]); - } - } - - return array; - } - - // NOTE: this method is used through reflection by System.Globalization.CultureXmlParser.ReadDateElement() - // and breaking changes here will not show up at build time, only at run time. - static private String GetTimeSeparator(String format) + private static String GetTimeSeparator(String format) { // Time format separator (ie: : in 12:39:00) // @@ -2329,9 +2208,7 @@ namespace System.Globalization return GetSeparator(format, "Hhms"); } - // NOTE: this method is used through reflection by System.Globalization.CultureXmlParser.ReadDateElement() - // and breaking changes here will not show up at build time, only at run time. - static private String GetDateSeparator(String format) + private static String GetDateSeparator(String format) { // Date format separator (ie: / in 9/1/03) // @@ -2413,128 +2290,61 @@ namespace System.Globalization return -1; } - string DoGetLocaleInfo(uint lctype) - { - Debug.Assert(this.sWindowsName != null, "[CultureData.DoGetLocaleInfo] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already"); - return DoGetLocaleInfo(this.sWindowsName, lctype); - } - - // For LOCALE_SPARENT we need the option of using the "real" name (forcing neutral names) instead of the - // "windows" name, which can be specific for downlevel (< windows 7) os's. - string DoGetLocaleInfo(string localeName, uint lctype) - { - // Fix lctype if we don't want overrides - if (!UseUserOverride) - { - lctype |= LOCALE_NOUSEROVERRIDE; - } - - // Ask OS for data - Debug.Assert(localeName != null, "[CultureData.DoGetLocaleInfo] Expected localeName to be not be null"); - string result = CultureInfo.nativeGetLocaleInfoEx(localeName, lctype); - if (result == null) - { - // Failed, just use empty string - result = String.Empty; - } - - return result; - } - - int DoGetLocaleInfoInt(uint lctype) - { - // Fix lctype if we don't want overrides - if (!UseUserOverride) - { - lctype |= LOCALE_NOUSEROVERRIDE; - } - - // Ask OS for data, note that we presume it returns success, so we have to know that - // sWindowsName is valid before calling. - Debug.Assert(this.sWindowsName != null, "[CultureData.DoGetLocaleInfoInt] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already"); - int result = CultureInfo.nativeGetLocaleInfoExInt(this.sWindowsName, lctype); - - return result; - } - - String[] DoEnumTimeFormats() - { - // Note that this gets overrides for us all the time - Debug.Assert(this.sWindowsName != null, "[CultureData.DoEnumTimeFormats] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already"); - String[] result = ReescapeWin32Strings(nativeEnumTimeFormats(this.sWindowsName, 0, UseUserOverride)); - - return result; - } - - String[] DoEnumShortTimeFormats() - { - // Note that this gets overrides for us all the time - Debug.Assert(this.sWindowsName != null, "[CultureData.DoEnumShortTimeFormats] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already"); - String[] result = ReescapeWin32Strings(nativeEnumTimeFormats(this.sWindowsName, TIME_NOSECONDS, UseUserOverride)); - - return result; - } - - ///////////////// - // Static Helpers // - //////////////// internal static bool IsCustomCultureId(int cultureId) { - if (cultureId == CultureInfo.LOCALE_CUSTOM_DEFAULT || cultureId == CultureInfo.LOCALE_CUSTOM_UNSPECIFIED) - return true; - - return false; + return (cultureId == CultureInfo.LOCALE_CUSTOM_DEFAULT || cultureId == CultureInfo.LOCALE_CUSTOM_UNSPECIFIED); } - //////////////////////////////////////////////////////////////////////////// - // - // Parameters: - // calendarValueOnly Retrieve the values which are affected by the calendar change of DTFI. - // This will cause values like longTimePattern not be retrieved since it is - // not affected by the Calendar property in DTFI. - // - //////////////////////////////////////////////////////////////////////////// internal void GetNFIValues(NumberFormatInfo nfi) { - if (this.IsInvariantCulture) + if (GlobalizationMode.Invariant || this.IsInvariantCulture) { - nfi.positiveSign = this.sPositiveSign; - nfi.negativeSign = this.sNegativeSign; - - nfi.nativeDigits = this.saNativeDigits; - nfi.digitSubstitution = this.iDigitSubstitution; + // FUTURE: NumberFormatInfo already has default values for many of these fields. Can we not do this? + nfi.positiveSign = _sPositiveSign; + nfi.negativeSign = _sNegativeSign; - nfi.numberGroupSeparator = this.sThousandSeparator; - nfi.numberDecimalSeparator = this.sDecimalSeparator; - nfi.numberDecimalDigits = this.iDigits; - nfi.numberNegativePattern = this.iNegativeNumber; + nfi.numberGroupSeparator = _sThousandSeparator; + nfi.numberDecimalSeparator = _sDecimalSeparator; + nfi.numberDecimalDigits = _iDigits; + nfi.numberNegativePattern = _iNegativeNumber; - nfi.currencySymbol = this.sCurrency; - nfi.currencyGroupSeparator = this.sMonetaryThousand; - nfi.currencyDecimalSeparator = this.sMonetaryDecimal; - nfi.currencyDecimalDigits = this.iCurrencyDigits; - nfi.currencyNegativePattern = this.iNegativeCurrency; - nfi.currencyPositivePattern = this.iCurrency; + nfi.currencySymbol = _sCurrency; + nfi.currencyGroupSeparator = _sMonetaryThousand; + nfi.currencyDecimalSeparator = _sMonetaryDecimal; + nfi.currencyDecimalDigits = _iCurrencyDigits; + nfi.currencyNegativePattern = _iNegativeCurrency; + nfi.currencyPositivePattern = _iCurrency; } else { - // - // We don't have information for the following four. All cultures use - // the same value of the number formatting values. - // - // PercentDecimalDigits - // PercentDecimalSeparator - // PercentGroupSize - // PercentGroupSeparator - // + Debug.Assert(_sWindowsName != null, "[CultureData.GetNFIValues] Expected _sWindowsName to be populated by already"); + // String values + nfi.positiveSign = GetLocaleInfo(LocaleStringData.PositiveSign); + nfi.negativeSign = GetLocaleInfo(LocaleStringData.NegativeSign); - // - // Ask native side for our data. - // - Debug.Assert(this.sWindowsName != null, "[CultureData.GetNFIValues] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already"); - CultureData.nativeGetNumberFormatInfoValues(this.sWindowsName, nfi, UseUserOverride); - } + nfi.numberDecimalSeparator = GetLocaleInfo(LocaleStringData.DecimalSeparator); + nfi.numberGroupSeparator = GetLocaleInfo(LocaleStringData.ThousandSeparator); + nfi.currencyGroupSeparator = GetLocaleInfo(LocaleStringData.MonetaryThousandSeparator); + nfi.currencyDecimalSeparator = GetLocaleInfo(LocaleStringData.MonetaryDecimalSeparator); + nfi.currencySymbol = GetLocaleInfo(LocaleStringData.MonetarySymbol); + + // Numeric values + nfi.numberDecimalDigits = GetLocaleInfo(LocaleNumberData.FractionalDigitsCount); + nfi.currencyDecimalDigits = GetLocaleInfo(LocaleNumberData.MonetaryFractionalDigitsCount); + nfi.currencyPositivePattern = GetLocaleInfo(LocaleNumberData.PositiveMonetaryNumberFormat); + nfi.currencyNegativePattern = GetLocaleInfo(LocaleNumberData.NegativeMonetaryNumberFormat); + nfi.numberNegativePattern = GetLocaleInfo(LocaleNumberData.NegativeNumberFormat); + // LOCALE_SNATIVEDIGITS (array of 10 single character strings). + string digits = GetLocaleInfo(LocaleStringData.Digits); + nfi.nativeDigits = new string[10]; + for (int i = 0; i < nfi.nativeDigits.Length; i++) + { + nfi.nativeDigits[i] = new string(digits[i], 1); + } + + nfi.digitSubstitution = GetDigitSubstitution(_sRealName); + } // // Gather additional data @@ -2569,289 +2379,188 @@ namespace System.Globalization //Special case for Italian. The currency decimal separator in the control panel is the empty string. When the user //specifies C4 as the currency format, this results in the number apparently getting multiplied by 10000 because the - //decimal point doesn't show up. We'll just workaround this here because our default currency format will never use nfi. + //decimal point doesn't show up. We'll just hack this here because our default currency format will never use nfi. if (nfi.currencyDecimalSeparator == null || nfi.currencyDecimalSeparator.Length == 0) { nfi.currencyDecimalSeparator = nfi.numberDecimalSeparator; } } - static private int ConvertFirstDayOfWeekMonToSun(int iTemp) - { - // Convert Mon-Sun to Sun-Sat format - iTemp++; - if (iTemp > 6) - { - // Wrap Sunday and convert invalid data to Sunday - iTemp = 0; - } - return iTemp; - } - // Helper // This is ONLY used for caching names and shouldn't be used for anything else internal static string AnsiToLower(string testString) { - StringBuilder sb = new StringBuilder(testString.Length); - - for (int ich = 0; ich < testString.Length; ich++) + int index = 0; + + while (index<testString.Length && (testString[index]<'A' || testString[index]>'Z' )) { - char ch = testString[ich]; - sb.Append(ch <= 'Z' && ch >= 'A' ? (char)(ch - 'A' + 'a') : ch); + index++; } - - return (sb.ToString()); - } - - // If we get a group from windows, then its in 3;0 format with the 0 backwards - // of how NLS+ uses it (ie: if the string has a 0, then the int[] shouldn't and vice versa) - // EXCEPT in the case where the list only contains 0 in which NLS and NLS+ have the same meaning. - static private int[] ConvertWin32GroupString(String win32Str) - { - // None of these cases make any sense - if (win32Str == null || win32Str.Length == 0) + if (index >= testString.Length) { - return (new int[] { 3 }); + return testString; // we didn't really change the string } - - if (win32Str[0] == '0') + + StringBuilder sb = new StringBuilder(testString.Length); + for (int i=0; i<index; i++) { - return (new int[] { 0 }); + sb.Append(testString[i]); } - // Since its in n;n;n;n;n format, we can always get the length quickly - int[] values; - if (win32Str[win32Str.Length - 1] == '0') - { - // Trailing 0 gets dropped. 1;0 -> 1 - values = new int[(win32Str.Length / 2)]; - } - else - { - // Need extra space for trailing zero 1 -> 1;0 - values = new int[(win32Str.Length / 2) + 2]; - values[values.Length - 1] = 0; - } + sb.Append((char) (testString[index] -'A' + 'a')); - int i; - int j; - for (i = 0, j = 0; i < win32Str.Length && j < values.Length; i += 2, j++) + for (int ich = index+1; ich < testString.Length; ich++) { - // Note that this # shouldn't ever be zero, 'cause 0 is only at end - // But we'll test because its registry that could be anything - if (win32Str[i] < '1' || win32Str[i] > '9') - return new int[] { 3 }; - - values[j] = (int)(win32Str[i] - '0'); + char ch = testString[ich]; + sb.Append(ch <= 'Z' && ch >= 'A' ? (char)(ch - 'A' + 'a') : ch); } - return (values); + return (sb.ToString()); } - // LCTYPES for GetLocaleInfo - private const uint LOCALE_NOUSEROVERRIDE = 0x80000000; // do not use user overrides - private const uint LOCALE_RETURN_NUMBER = 0x20000000; // return number instead of string - - // Modifier for genitive names - private const uint LOCALE_RETURN_GENITIVE_NAMES = 0x10000000; //Flag to return the Genitive forms of month names - - // - // The following LCTypes are mutually exclusive in that they may NOT - // be used in combination with each other. - // - - // - // These are the various forms of the name of the locale: - // - private const uint LOCALE_SLOCALIZEDDISPLAYNAME = 0x00000002; // localized name of locale, eg "German (Germany)" in UI language - private const uint LOCALE_SENGLISHDISPLAYNAME = 0x00000072; // Display name (language + country usually) in English, eg "German (Germany)" - private const uint LOCALE_SNATIVEDISPLAYNAME = 0x00000073; // Display name in native locale language, eg "Deutsch (Deutschland) - - private const uint LOCALE_SLOCALIZEDLANGUAGENAME = 0x0000006f; // Language Display Name for a language, eg "German" in UI language - private const uint LOCALE_SENGLISHLANGUAGENAME = 0x00001001; // English name of language, eg "German" - private const uint LOCALE_SNATIVELANGUAGENAME = 0x00000004; // native name of language, eg "Deutsch" - - private const uint LOCALE_SLOCALIZEDCOUNTRYNAME = 0x00000006; // localized name of country, eg "Germany" in UI language - private const uint LOCALE_SENGLISHCOUNTRYNAME = 0x00001002; // English name of country, eg "Germany" - private const uint LOCALE_SNATIVECOUNTRYNAME = 0x00000008; // native name of country, eg "Deutschland" - - - // private const uint LOCALE_ILANGUAGE =0x00000001; // language id // Don't use, use NewApis::LocaleNameToLCID instead (GetLocaleInfo doesn't return neutrals) - - // private const uint LOCALE_SLANGUAGE =LOCALE_SLOCALIZEDDISPLAYNAME; // localized name of language (use LOCALE_SLOCALIZEDDISPLAYNAME instead) - // private const uint LOCALE_SENGLANGUAGE =LOCALE_SENGLISHLANGUAGENAME; // English name of language (use LOCALE_SENGLISHLANGUAGENAME instead) - private const uint LOCALE_SABBREVLANGNAME = 0x00000003; // abbreviated language name - // private const uint LOCALE_SNATIVELANGNAME =LOCALE_SNATIVELANGUAGENAME; // native name of language (use LOCALE_SNATIVELANGUAGENAME instead) - - private const uint LOCALE_ICOUNTRY = 0x00000005; // country code - // private const uint LOCALE_SCOUNTRY =LOCALE_SLOCALIZEDCOUNTRYNAME; // localized name of country (use LOCALE_SLOCALIZEDCOUNTRYNAME instead) - // private const uint LOCALE_SENGCOUNTRY =LOCALE_SENGLISHCOUNTRYNAME; // English name of country (use LOCALE_SENGLISHCOUNTRYNAME instead) - private const uint LOCALE_SABBREVCTRYNAME = 0x00000007; // abbreviated country name - // private const uint LOCALE_SNATIVECTRYNAME =LOCALE_SNATIVECOUNTRYNAME; // native name of country ( use LOCALE_SNATIVECOUNTRYNAME instead) - private const uint LOCALE_IGEOID = 0x0000005B; // geographical location id - - private const uint LOCALE_IDEFAULTLANGUAGE = 0x00000009; // default language id - private const uint LOCALE_IDEFAULTCOUNTRY = 0x0000000A; // default country code - private const uint LOCALE_IDEFAULTCODEPAGE = 0x0000000B; // default oem code page - private const uint LOCALE_IDEFAULTANSICODEPAGE = 0x00001004; // default ansi code page - private const uint LOCALE_IDEFAULTMACCODEPAGE = 0x00001011; // default mac code page - - private const uint LOCALE_SLIST = 0x0000000C; // list item separator - private const uint LOCALE_IMEASURE = 0x0000000D; // 0 = metric, 1 = US - - private const uint LOCALE_SDECIMAL = 0x0000000E; // decimal separator - private const uint LOCALE_STHOUSAND = 0x0000000F; // thousand separator - private const uint LOCALE_SGROUPING = 0x00000010; // digit grouping - private const uint LOCALE_IDIGITS = 0x00000011; // number of fractional digits - private const uint LOCALE_ILZERO = 0x00000012; // leading zeros for decimal - private const uint LOCALE_INEGNUMBER = 0x00001010; // negative number mode - private const uint LOCALE_SNATIVEDIGITS = 0x00000013; // native digits for 0-9 - - private const uint LOCALE_SCURRENCY = 0x00000014; // local monetary symbol - private const uint LOCALE_SINTLSYMBOL = 0x00000015; // uintl monetary symbol - private const uint LOCALE_SMONDECIMALSEP = 0x00000016; // monetary decimal separator - private const uint LOCALE_SMONTHOUSANDSEP = 0x00000017; // monetary thousand separator - private const uint LOCALE_SMONGROUPING = 0x00000018; // monetary grouping - private const uint LOCALE_ICURRDIGITS = 0x00000019; // # local monetary digits - private const uint LOCALE_IINTLCURRDIGITS = 0x0000001A; // # uintl monetary digits - private const uint LOCALE_ICURRENCY = 0x0000001B; // positive currency mode - private const uint LOCALE_INEGCURR = 0x0000001C; // negative currency mode - - private const uint LOCALE_SDATE = 0x0000001D; // date separator (derived from LOCALE_SSHORTDATE, use that instead) - private const uint LOCALE_STIME = 0x0000001E; // time separator (derived from LOCALE_STIMEFORMAT, use that instead) - private const uint LOCALE_SSHORTDATE = 0x0000001F; // short date format string - private const uint LOCALE_SLONGDATE = 0x00000020; // long date format string - private const uint LOCALE_STIMEFORMAT = 0x00001003; // time format string - private const uint LOCALE_IDATE = 0x00000021; // short date format ordering (derived from LOCALE_SSHORTDATE, use that instead) - private const uint LOCALE_ILDATE = 0x00000022; // long date format ordering (derived from LOCALE_SLONGDATE, use that instead) - private const uint LOCALE_ITIME = 0x00000023; // time format specifier (derived from LOCALE_STIMEFORMAT, use that instead) - private const uint LOCALE_ITIMEMARKPOSN = 0x00001005; // time marker position (derived from LOCALE_STIMEFORMAT, use that instead) - private const uint LOCALE_ICENTURY = 0x00000024; // century format specifier (short date, LOCALE_SSHORTDATE is preferred) - private const uint LOCALE_ITLZERO = 0x00000025; // leading zeros in time field (derived from LOCALE_STIMEFORMAT, use that instead) - private const uint LOCALE_IDAYLZERO = 0x00000026; // leading zeros in day field (short date, LOCALE_SSHORTDATE is preferred) - private const uint LOCALE_IMONLZERO = 0x00000027; // leading zeros in month field (short date, LOCALE_SSHORTDATE is preferred) - private const uint LOCALE_S1159 = 0x00000028; // AM designator - private const uint LOCALE_S2359 = 0x00000029; // PM designator - - private const uint LOCALE_ICALENDARTYPE = 0x00001009; // type of calendar specifier - private const uint LOCALE_IOPTIONALCALENDAR = 0x0000100B; // additional calendar types specifier - private const uint LOCALE_IFIRSTDAYOFWEEK = 0x0000100C; // first day of week specifier - private const uint LOCALE_IFIRSTWEEKOFYEAR = 0x0000100D; // first week of year specifier - - private const uint LOCALE_SDAYNAME1 = 0x0000002A; // long name for Monday - private const uint LOCALE_SDAYNAME2 = 0x0000002B; // long name for Tuesday - private const uint LOCALE_SDAYNAME3 = 0x0000002C; // long name for Wednesday - private const uint LOCALE_SDAYNAME4 = 0x0000002D; // long name for Thursday - private const uint LOCALE_SDAYNAME5 = 0x0000002E; // long name for Friday - private const uint LOCALE_SDAYNAME6 = 0x0000002F; // long name for Saturday - private const uint LOCALE_SDAYNAME7 = 0x00000030; // long name for Sunday - private const uint LOCALE_SABBREVDAYNAME1 = 0x00000031; // abbreviated name for Monday - private const uint LOCALE_SABBREVDAYNAME2 = 0x00000032; // abbreviated name for Tuesday - private const uint LOCALE_SABBREVDAYNAME3 = 0x00000033; // abbreviated name for Wednesday - private const uint LOCALE_SABBREVDAYNAME4 = 0x00000034; // abbreviated name for Thursday - private const uint LOCALE_SABBREVDAYNAME5 = 0x00000035; // abbreviated name for Friday - private const uint LOCALE_SABBREVDAYNAME6 = 0x00000036; // abbreviated name for Saturday - private const uint LOCALE_SABBREVDAYNAME7 = 0x00000037; // abbreviated name for Sunday - private const uint LOCALE_SMONTHNAME1 = 0x00000038; // long name for January - private const uint LOCALE_SMONTHNAME2 = 0x00000039; // long name for February - private const uint LOCALE_SMONTHNAME3 = 0x0000003A; // long name for March - private const uint LOCALE_SMONTHNAME4 = 0x0000003B; // long name for April - private const uint LOCALE_SMONTHNAME5 = 0x0000003C; // long name for May - private const uint LOCALE_SMONTHNAME6 = 0x0000003D; // long name for June - private const uint LOCALE_SMONTHNAME7 = 0x0000003E; // long name for July - private const uint LOCALE_SMONTHNAME8 = 0x0000003F; // long name for August - private const uint LOCALE_SMONTHNAME9 = 0x00000040; // long name for September - private const uint LOCALE_SMONTHNAME10 = 0x00000041; // long name for October - private const uint LOCALE_SMONTHNAME11 = 0x00000042; // long name for November - private const uint LOCALE_SMONTHNAME12 = 0x00000043; // long name for December - private const uint LOCALE_SMONTHNAME13 = 0x0000100E; // long name for 13th month (if exists) - private const uint LOCALE_SABBREVMONTHNAME1 = 0x00000044; // abbreviated name for January - private const uint LOCALE_SABBREVMONTHNAME2 = 0x00000045; // abbreviated name for February - private const uint LOCALE_SABBREVMONTHNAME3 = 0x00000046; // abbreviated name for March - private const uint LOCALE_SABBREVMONTHNAME4 = 0x00000047; // abbreviated name for April - private const uint LOCALE_SABBREVMONTHNAME5 = 0x00000048; // abbreviated name for May - private const uint LOCALE_SABBREVMONTHNAME6 = 0x00000049; // abbreviated name for June - private const uint LOCALE_SABBREVMONTHNAME7 = 0x0000004A; // abbreviated name for July - private const uint LOCALE_SABBREVMONTHNAME8 = 0x0000004B; // abbreviated name for August - private const uint LOCALE_SABBREVMONTHNAME9 = 0x0000004C; // abbreviated name for September - private const uint LOCALE_SABBREVMONTHNAME10 = 0x0000004D; // abbreviated name for October - private const uint LOCALE_SABBREVMONTHNAME11 = 0x0000004E; // abbreviated name for November - private const uint LOCALE_SABBREVMONTHNAME12 = 0x0000004F; // abbreviated name for December - private const uint LOCALE_SABBREVMONTHNAME13 = 0x0000100F; // abbreviated name for 13th month (if exists) - - private const uint LOCALE_SPOSITIVESIGN = 0x00000050; // positive sign - private const uint LOCALE_SNEGATIVESIGN = 0x00000051; // negative sign - private const uint LOCALE_IPOSSIGNPOSN = 0x00000052; // positive sign position (derived from INEGCURR) - private const uint LOCALE_INEGSIGNPOSN = 0x00000053; // negative sign position (derived from INEGCURR) - private const uint LOCALE_IPOSSYMPRECEDES = 0x00000054; // mon sym precedes pos amt (derived from ICURRENCY) - private const uint LOCALE_IPOSSEPBYSPACE = 0x00000055; // mon sym sep by space from pos amt (derived from ICURRENCY) - private const uint LOCALE_INEGSYMPRECEDES = 0x00000056; // mon sym precedes neg amt (derived from INEGCURR) - private const uint LOCALE_INEGSEPBYSPACE = 0x00000057; // mon sym sep by space from neg amt (derived from INEGCURR) - - private const uint LOCALE_FONTSIGNATURE = 0x00000058; // font signature - private const uint LOCALE_SISO639LANGNAME = 0x00000059; // ISO abbreviated language name - private const uint LOCALE_SISO3166CTRYNAME = 0x0000005A; // ISO abbreviated country name - - private const uint LOCALE_IDEFAULTEBCDICCODEPAGE = 0x00001012; // default ebcdic code page - private const uint LOCALE_IPAPERSIZE = 0x0000100A; // 1 = letter, 5 = legal, 8 = a3, 9 = a4 - private const uint LOCALE_SENGCURRNAME = 0x00001007; // english name of currency - private const uint LOCALE_SNATIVECURRNAME = 0x00001008; // native name of currency - private const uint LOCALE_SYEARMONTH = 0x00001006; // year month format string - private const uint LOCALE_SSORTNAME = 0x00001013; // sort name - private const uint LOCALE_IDIGITSUBSTITUTION = 0x00001014; // 0 = context, 1 = none, 2 = national - - private const uint LOCALE_SNAME = 0x0000005c; // locale name (with sort info) (ie: de-DE_phoneb) - private const uint LOCALE_SDURATION = 0x0000005d; // time duration format - private const uint LOCALE_SKEYBOARDSTOINSTALL = 0x0000005e; // (windows only) keyboards to install - private const uint LOCALE_SSHORTESTDAYNAME1 = 0x00000060; // Shortest day name for Monday - private const uint LOCALE_SSHORTESTDAYNAME2 = 0x00000061; // Shortest day name for Tuesday - private const uint LOCALE_SSHORTESTDAYNAME3 = 0x00000062; // Shortest day name for Wednesday - private const uint LOCALE_SSHORTESTDAYNAME4 = 0x00000063; // Shortest day name for Thursday - private const uint LOCALE_SSHORTESTDAYNAME5 = 0x00000064; // Shortest day name for Friday - private const uint LOCALE_SSHORTESTDAYNAME6 = 0x00000065; // Shortest day name for Saturday - private const uint LOCALE_SSHORTESTDAYNAME7 = 0x00000066; // Shortest day name for Sunday - private const uint LOCALE_SISO639LANGNAME2 = 0x00000067; // 3 character ISO abbreviated language name - private const uint LOCALE_SISO3166CTRYNAME2 = 0x00000068; // 3 character ISO country name - private const uint LOCALE_SNAN = 0x00000069; // Not a Number - private const uint LOCALE_SPOSINFINITY = 0x0000006a; // + Infinity - private const uint LOCALE_SNEGINFINITY = 0x0000006b; // - Infinity - private const uint LOCALE_SSCRIPTS = 0x0000006c; // Typical scripts in the locale - private const uint LOCALE_SPARENT = 0x0000006d; // Fallback name for resources - private const uint LOCALE_SCONSOLEFALLBACKNAME = 0x0000006e; // Fallback name for within the console - // private const uint LOCALE_SLANGDISPLAYNAME =LOCALE_SLOCALIZEDLANGUAGENAME; // Language Display Name for a language (use LOCALE_SLOCALIZEDLANGUAGENAME instead) - - // Windows 7 LCTYPES - private const uint LOCALE_IREADINGLAYOUT = 0x00000070; // Returns one of the following 4 reading layout values: - // 0 - Left to right (eg en-US) - // 1 - Right to left (eg arabic locales) - // 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales) - // 3 - Vertical top to bottom with columns proceeding to the right - private const uint LOCALE_INEUTRAL = 0x00000071; // Returns 0 for specific cultures, 1 for neutral cultures. - private const uint LOCALE_INEGATIVEPERCENT = 0x00000074; // Returns 0-11 for the negative percent format - private const uint LOCALE_IPOSITIVEPERCENT = 0x00000075; // Returns 0-3 for the positive percent formatIPOSITIVEPERCENT - private const uint LOCALE_SPERCENT = 0x00000076; // Returns the percent symbol - private const uint LOCALE_SPERMILLE = 0x00000077; // Returns the permille (U+2030) symbol - private const uint LOCALE_SMONTHDAY = 0x00000078; // Returns the preferred month/day format - private const uint LOCALE_SSHORTTIME = 0x00000079; // Returns the preferred short time format (ie: no seconds, just h:mm) - private const uint LOCALE_SOPENTYPELANGUAGETAG = 0x0000007a; // Open type language tag, eg: "latn" or "dflt" - private const uint LOCALE_SSORTLOCALE = 0x0000007b; // Name of locale to use for sorting/collation/casing behavior. - - // Time formats enumerations - internal const uint TIME_NOSECONDS = 0x00000002; // Don't use seconds (get short time format for enumtimeformats on win7+) - - // Get our initial minimal culture data (name, parent, etc.) - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern bool nativeInitCultureData(CultureData cultureData); - - // Grab the NumberFormatInfo data - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern bool nativeGetNumberFormatInfoValues(String localeName, NumberFormatInfo nfi, bool useUserOverride); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern String[] nativeEnumTimeFormats(String localeName, uint dwFlags, bool useUserOverride); - - [SuppressUnmanagedCodeSecurityAttribute()] - [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] - internal static extern int nativeEnumCultureNames(int cultureTypes, ObjectHandleOnStack retStringArray); + /// <remarks> + /// The numeric values of the enum members match their Win32 counterparts. The CultureData Win32 PAL implementation + /// takes a dependency on this fact, in order to prevent having to construct a mapping from internal values to LCTypes. + /// </remarks> + private enum LocaleStringData : uint + { + /// <summary>localized name of locale, eg "German (Germany)" in UI language (coresponds to LOCALE_SLOCALIZEDDISPLAYNAME)</summary> + LocalizedDisplayName = 0x00000002, + /// <summary>Display name (language + country usually) in English, eg "German (Germany)" (coresponds to LOCALE_SENGLISHDISPLAYNAME)</summary> + EnglishDisplayName = 0x00000072, + /// <summary>Display name in native locale language, eg "Deutsch (Deutschland) (coresponds to LOCALE_SNATIVEDISPLAYNAME)</summary> + NativeDisplayName = 0x00000073, + /// <summary>Language Display Name for a language, eg "German" in UI language (coresponds to LOCALE_SLOCALIZEDLANGUAGENAME)</summary> + LocalizedLanguageName = 0x0000006f, + /// <summary>English name of language, eg "German" (coresponds to LOCALE_SENGLISHLANGUAGENAME)</summary> + EnglishLanguageName = 0x00001001, + /// <summary>native name of language, eg "Deutsch" (coresponds to LOCALE_SNATIVELANGUAGENAME)</summary> + NativeLanguageName = 0x00000004, + /// <summary>localized name of country, eg "Germany" in UI language (coresponds to LOCALE_SLOCALIZEDCOUNTRYNAME)</summary> + LocalizedCountryName = 0x00000006, + /// <summary>English name of country, eg "Germany" (coresponds to LOCALE_SENGLISHCOUNTRYNAME)</summary> + EnglishCountryName = 0x00001002, + /// <summary>native name of country, eg "Deutschland" (coresponds to LOCALE_SNATIVECOUNTRYNAME)</summary> + NativeCountryName = 0x00000008, + /// <summary>abbreviated language name (coresponds to LOCALE_SABBREVLANGNAME)</summary> + AbbreviatedWindowsLanguageName = 0x00000003, + /// <summary>list item separator (coresponds to LOCALE_SLIST)</summary> + ListSeparator = 0x0000000C, + /// <summary>decimal separator (coresponds to LOCALE_SDECIMAL)</summary> + DecimalSeparator = 0x0000000E, + /// <summary>thousand separator (coresponds to LOCALE_STHOUSAND)</summary> + ThousandSeparator = 0x0000000F, + /// <summary>digit grouping (coresponds to LOCALE_SGROUPING)</summary> + Digits = 0x00000013, + /// <summary>local monetary symbol (coresponds to LOCALE_SCURRENCY)</summary> + MonetarySymbol = 0x00000014, + /// <summary>English currency name (coresponds to LOCALE_SENGCURRNAME)</summary> + CurrencyEnglishName = 0x00001007, + /// <summary>Native currency name (coresponds to LOCALE_SNATIVECURRNAME)</summary> + CurrencyNativeName = 0x00001008, + /// <summary>uintl monetary symbol (coresponds to LOCALE_SINTLSYMBOL)</summary> + Iso4217MonetarySymbol = 0x00000015, + /// <summary>monetary decimal separator (coresponds to LOCALE_SMONDECIMALSEP)</summary> + MonetaryDecimalSeparator = 0x00000016, + /// <summary>monetary thousand separator (coresponds to LOCALE_SMONTHOUSANDSEP)</summary> + MonetaryThousandSeparator = 0x00000017, + /// <summary>AM designator (coresponds to LOCALE_S1159)</summary> + AMDesignator = 0x00000028, + /// <summary>PM designator (coresponds to LOCALE_S2359)</summary> + PMDesignator = 0x00000029, + /// <summary>positive sign (coresponds to LOCALE_SPOSITIVESIGN)</summary> + PositiveSign = 0x00000050, + /// <summary>negative sign (coresponds to LOCALE_SNEGATIVESIGN)</summary> + NegativeSign = 0x00000051, + /// <summary>ISO abbreviated language name (coresponds to LOCALE_SISO639LANGNAME)</summary> + Iso639LanguageTwoLetterName = 0x00000059, + /// <summary>ISO abbreviated country name (coresponds to LOCALE_SISO639LANGNAME2)</summary> + Iso639LanguageThreeLetterName = 0x00000067, + /// <summary>ISO abbreviated language name (coresponds to LOCALE_SISO639LANGNAME)</summary> + Iso639LanguageName = 0x00000059, + /// <summary>ISO abbreviated country name (coresponds to LOCALE_SISO3166CTRYNAME)</summary> + Iso3166CountryName = 0x0000005A, + /// <summary>3 letter ISO country code (coresponds to LOCALE_SISO3166CTRYNAME2)</summary> + Iso3166CountryName2 = 0x00000068, // 3 character ISO country name + /// <summary>Not a Number (coresponds to LOCALE_SNAN)</summary> + NaNSymbol = 0x00000069, + /// <summary>+ Infinity (coresponds to LOCALE_SPOSINFINITY)</summary> + PositiveInfinitySymbol = 0x0000006a, + /// <summary>- Infinity (coresponds to LOCALE_SNEGINFINITY)</summary> + NegativeInfinitySymbol = 0x0000006b, + /// <summary>Fallback name for resources (coresponds to LOCALE_SPARENT)</summary> + ParentName = 0x0000006d, + /// <summary>Fallback name for within the console (coresponds to LOCALE_SCONSOLEFALLBACKNAME)</summary> + ConsoleFallbackName = 0x0000006e, + /// <summary>Returns the percent symbol (coresponds to LOCALE_SPERCENT)</summary> + PercentSymbol = 0x00000076, + /// <summary>Returns the permille (U+2030) symbol (coresponds to LOCALE_SPERMILLE)</summary> + PerMilleSymbol = 0x00000077 + } + + /// <remarks> + /// The numeric values of the enum members match their Win32 counterparts. The CultureData Win32 PAL implementation + /// takes a dependency on this fact, in order to prevent having to construct a mapping from internal values to LCTypes. + /// </remarks> + private enum LocaleGroupingData : uint + { + /// <summary>digit grouping (coresponds to LOCALE_SGROUPING)</summary> + Digit = 0x00000010, + /// <summary>monetary grouping (coresponds to LOCALE_SMONGROUPING)</summary> + Monetary = 0x00000018, + } + + /// <remarks> + /// The numeric values of the enum members match their Win32 counterparts. The CultureData Win32 PAL implementation + /// takes a dependency on this fact, in order to prevent having to construct a mapping from internal values to LCTypes. + /// </remarks> + private enum LocaleNumberData : uint + { + /// <summary>language id (coresponds to LOCALE_ILANGUAGE)</summary> + LanguageId = 0x00000001, + /// <summary>geographical location id, (coresponds to LOCALE_IGEOID)</summary> + GeoId = 0x0000005B, + /// <summary>0 = context, 1 = none, 2 = national (coresponds to LOCALE_IDIGITSUBSTITUTION)</summary> + DigitSubstitution = 0x00001014, + /// <summary>0 = metric, 1 = US (coresponds to LOCALE_IMEASURE)</summary> + MeasurementSystem = 0x0000000D, + /// <summary>number of fractional digits (coresponds to LOCALE_IDIGITS)</summary> + FractionalDigitsCount = 0x00000011, + /// <summary>negative number mode (coresponds to LOCALE_INEGNUMBER)</summary> + NegativeNumberFormat = 0x00001010, + /// <summary># local monetary digits (coresponds to LOCALE_ICURRDIGITS)</summary> + MonetaryFractionalDigitsCount = 0x00000019, + /// <summary>positive currency mode (coresponds to LOCALE_ICURRENCY)</summary> + PositiveMonetaryNumberFormat = 0x0000001B, + /// <summary>negative currency mode (coresponds to LOCALE_INEGCURR)</summary> + NegativeMonetaryNumberFormat = 0x0000001C, + /// <summary>type of calendar specifier (coresponds to LOCALE_ICALENDARTYPE)</summary> + CalendarType = 0x00001009, + /// <summary>first day of week specifier (coresponds to LOCALE_IFIRSTDAYOFWEEK)</summary> + FirstDayOfWeek = 0x0000100C, + /// <summary>first week of year specifier (coresponds to LOCALE_IFIRSTWEEKOFYEAR)</summary> + FirstWeekOfYear = 0x0000100D, + /// <summary> + /// Returns one of the following 4 reading layout values: + /// 0 - Left to right (eg en-US) + /// 1 - Right to left (eg arabic locales) + /// 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales) + /// 3 - Vertical top to bottom with columns proceeding to the right + /// (coresponds to LOCALE_IREADINGLAYOUT) + /// </summary> + ReadingLayout = 0x00000070, + /// <summary>Returns 0-11 for the negative percent format (coresponds to LOCALE_INEGATIVEPERCENT)</summary> + NegativePercentFormat = 0x00000074, + /// <summary>Returns 0-3 for the positive percent format (coresponds to LOCALE_IPOSITIVEPERCENT)</summary> + PositivePercentFormat = 0x00000075, + /// <summary>default ansi code page (coresponds to LOCALE_IDEFAULTCODEPAGE)</summary> + OemCodePage = 0x0000000B, + /// <summary>default ansi code page (coresponds to LOCALE_IDEFAULTANSICODEPAGE)</summary> + AnsiCodePage = 0x00001004, + /// <summary>default mac code page (coresponds to LOCALE_IDEFAULTMACCODEPAGE)</summary> + MacCodePage = 0x00001011, + /// <summary>default ebcdic code page (coresponds to LOCALE_IDEFAULTEBCDICCODEPAGE)</summary> + EbcdicCodePage = 0x00001012, + } } } diff --git a/src/mscorlib/src/System/Globalization/CultureInfo.Unix.cs b/src/mscorlib/src/System/Globalization/CultureInfo.Unix.cs new file mode 100644 index 0000000000..7a8a9fd08d --- /dev/null +++ b/src/mscorlib/src/System/Globalization/CultureInfo.Unix.cs @@ -0,0 +1,128 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Threading; + +namespace System.Globalization +{ + public partial class CultureInfo : IFormatProvider + { + private static CultureInfo GetUserDefaultCultureCacheOverride() + { + return null; // ICU doesn't provide a user override + } + + internal static CultureInfo GetUserDefaultCulture() + { + if (GlobalizationMode.Invariant) + return CultureInfo.InvariantCulture; + + CultureInfo cultureInfo = null; + string localeName; + if (CultureData.GetDefaultLocaleName(out localeName)) + { + cultureInfo = GetCultureByName(localeName, true); + cultureInfo._isReadOnly = true; + } + else + { + cultureInfo = CultureInfo.InvariantCulture; + } + + return cultureInfo; + } + + private static CultureInfo GetUserDefaultUILanguage() + { + return GetUserDefaultCulture(); + } + + //////////////////////////////////////////////////////////////////////// + // + // CurrentCulture + // + // This instance provides methods based on the current user settings. + // These settings are volatile and may change over the lifetime of the + // thread. + // + //////////////////////////////////////////////////////////////////////// + + // + // We use the following order to return CurrentCulture and CurrentUICulture + // o use current thread culture if the user already set one using CurrentCulture/CurrentUICulture + // o use thread culture if the user already set one using DefaultThreadCurrentCulture + // or DefaultThreadCurrentUICulture + // o Use NLS default user culture + // o Use NLS default system culture + // o Use Invariant culture + // + public static CultureInfo CurrentCulture + { + get + { + if (Thread.m_CurrentCulture != null) + { + return Thread.m_CurrentCulture; + } + + CultureInfo ci = s_DefaultThreadCurrentCulture; + if (ci != null) + { + return ci; + } + + // if s_userDefaultCulture == null means CultureInfo statics didn't get initialized yet. this can happen if there early static + // method get executed which eventually hit the cultureInfo code while CultureInfo statics didn’t get chance to initialize + if (s_userDefaultCulture == null) + { + Init(); + } + + Debug.Assert(s_userDefaultCulture != null); + return s_userDefaultCulture; + } + + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (s_asyncLocalCurrentCulture == null) + { + Interlocked.CompareExchange(ref s_asyncLocalCurrentCulture, new AsyncLocal<CultureInfo>(AsyncLocalSetCurrentCulture), null); + } + s_asyncLocalCurrentCulture.Value = value; + } + } + + public static CultureInfo CurrentUICulture + { + get + { + return GetCurrentUICultureNoAppX(); + } + + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + CultureInfo.VerifyCultureName(value, true); + if (s_asyncLocalCurrentUICulture == null) + { + Interlocked.CompareExchange(ref s_asyncLocalCurrentUICulture, new AsyncLocal<CultureInfo>(AsyncLocalSetCurrentUICulture), null); + } + + // this one will set s_currentThreadUICulture too + s_asyncLocalCurrentUICulture.Value = value; + } + } + + } +} diff --git a/src/mscorlib/src/System/Globalization/CultureInfo.Windows.cs b/src/mscorlib/src/System/Globalization/CultureInfo.Windows.cs new file mode 100644 index 0000000000..e33874e760 --- /dev/null +++ b/src/mscorlib/src/System/Globalization/CultureInfo.Windows.cs @@ -0,0 +1,278 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; + +#if ENABLE_WINRT +using Internal.Runtime.Augments; +#endif + +using System.Threading; +#if FEATURE_APPX +using System.Resources; +#endif + +namespace System.Globalization +{ + public partial class CultureInfo : IFormatProvider + { +#if FEATURE_APPX + // When running under AppX, we use this to get some information about the language list + private static volatile WindowsRuntimeResourceManagerBase s_WindowsRuntimeResourceManager; + + [ThreadStatic] + private static bool ts_IsDoingAppXCultureInfoLookup; +#endif + + /// <summary> + /// Gets the default user culture from WinRT, if available. + /// </summary> + /// <remarks> + /// This method may return null, if there is no default user culture or if WinRT isn't available. + /// </remarks> + private static CultureInfo GetUserDefaultCultureCacheOverride() + { +#if ENABLE_WINRT + WinRTInteropCallbacks callbacks = WinRTInterop.UnsafeCallbacks; + if (callbacks != null && callbacks.IsAppxModel()) + { + return (CultureInfo)callbacks.GetUserDefaultCulture(); + } +#endif + + return null; + } + + internal static CultureInfo GetUserDefaultCulture() + { + if (GlobalizationMode.Invariant) + return CultureInfo.InvariantCulture; + + const uint LOCALE_SNAME = 0x0000005c; + const string LOCALE_NAME_USER_DEFAULT = null; + const string LOCALE_NAME_SYSTEM_DEFAULT = "!x-sys-default-locale"; + + string strDefault = CultureData.GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SNAME); + if (strDefault == null) + { + strDefault = CultureData.GetLocaleInfoEx(LOCALE_NAME_SYSTEM_DEFAULT, LOCALE_SNAME); + + if (strDefault == null) + { + // If system default doesn't work, use invariant + return CultureInfo.InvariantCulture; + } + } + + CultureInfo temp = GetCultureByName(strDefault, true); + + temp._isReadOnly = true; + + return temp; + } + + private static CultureInfo GetUserDefaultUILanguage() + { + if (GlobalizationMode.Invariant) + return CultureInfo.InvariantCulture; + + const uint MUI_LANGUAGE_NAME = 0x8; // Use ISO language (culture) name convention + uint langCount = 0; + uint bufLen = 0; + + if (Interop.Kernel32.GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, out langCount, null, ref bufLen)) + { + char [] languages = new char[bufLen]; + if (Interop.Kernel32.GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, out langCount, languages, ref bufLen)) + { + int index = 0; + while (languages[index] != (char) 0 && index<languages.Length) + { + index++; + } + + CultureInfo temp = GetCultureByName(new String(languages, 0, index), true); + temp._isReadOnly = true; + return temp; + } + } + + return GetUserDefaultCulture(); + } + + //////////////////////////////////////////////////////////////////////// + // + // CurrentCulture + // + // This instance provides methods based on the current user settings. + // These settings are volatile and may change over the lifetime of the + // thread. + // + //////////////////////////////////////////////////////////////////////// + + // + // We use the following order to return CurrentCulture and CurrentUICulture + // o Use WinRT to return the current user profile language + // o use current thread culture if the user already set one using CurrentCulture/CurrentUICulture + // o use thread culture if the user already set one using DefaultThreadCurrentCulture + // or DefaultThreadCurrentUICulture + // o Use NLS default user culture + // o Use NLS default system culture + // o Use Invariant culture + // + public static CultureInfo CurrentCulture + { + get + { +#if FEATURE_APPX + if (AppDomain.IsAppXModel()) + { + CultureInfo culture = GetCultureInfoForUserPreferredLanguageInAppX(); + if (culture != null) + return culture; + } +#endif + CultureInfo ci = GetUserDefaultCultureCacheOverride(); + if (ci != null) + { + return ci; + } + + if (Thread.m_CurrentCulture != null) + { + return Thread.m_CurrentCulture; + } + + ci = s_DefaultThreadCurrentCulture; + if (ci != null) + { + return ci; + } + + // if s_userDefaultCulture == null means CultureInfo statics didn't get initialized yet. this can happen if there early static + // method get executed which eventually hit the cultureInfo code while CultureInfo statics didn’t get chance to initialize + if (s_userDefaultCulture == null) + { + Init(); + } + + Debug.Assert(s_userDefaultCulture != null); + return s_userDefaultCulture; + } + + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + +#if FEATURE_APPX + if (AppDomain.IsAppXModel()) + { + if (SetCultureInfoForUserPreferredLanguageInAppX(value)) + { + // successfully set the culture, otherwise fallback to legacy path + return; + } + } +#endif + if (s_asyncLocalCurrentCulture == null) + { + Interlocked.CompareExchange(ref s_asyncLocalCurrentCulture, new AsyncLocal<CultureInfo>(AsyncLocalSetCurrentCulture), null); + } + s_asyncLocalCurrentCulture.Value = value; + } + } + + public static CultureInfo CurrentUICulture + { + get + { +#if FEATURE_APPX + if (AppDomain.IsAppXModel()) + { + CultureInfo culture = GetCultureInfoForUserPreferredLanguageInAppX(); + if (culture != null) + return culture; + } +#endif + return GetCurrentUICultureNoAppX(); + } + + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + CultureInfo.VerifyCultureName(value, true); +#if FEATURE_APPX + if (AppDomain.IsAppXModel()) + { + if (SetCultureInfoForUserPreferredLanguageInAppX(value)) + { + // successfully set the culture, otherwise fallback to legacy path + return; + } + } +#endif + if (s_asyncLocalCurrentUICulture == null) + { + Interlocked.CompareExchange(ref s_asyncLocalCurrentUICulture, new AsyncLocal<CultureInfo>(AsyncLocalSetCurrentUICulture), null); + } + + // this one will set s_currentThreadUICulture too + s_asyncLocalCurrentUICulture.Value = value; + } + } + +#if FEATURE_APPX + internal static CultureInfo GetCultureInfoForUserPreferredLanguageInAppX() + { + // If a call to GetCultureInfoForUserPreferredLanguageInAppX() generated a recursive + // call to itself, return null, since we don't want to stack overflow. For example, + // this can happen if some code in this method ends up calling CultureInfo.CurrentCulture + // (which is common on check'd build because of BCLDebug logging which calls Int32.ToString()). + // In this case, returning null will mean CultureInfo.CurrentCulture gets the default Win32 + // value, which should be fine. + if (ts_IsDoingAppXCultureInfoLookup) + { + return null; + } + + CultureInfo toReturn = null; + + try + { + ts_IsDoingAppXCultureInfoLookup = true; + + if (s_WindowsRuntimeResourceManager == null) + { + s_WindowsRuntimeResourceManager = ResourceManager.GetWinRTResourceManager(); + } + + toReturn = s_WindowsRuntimeResourceManager.GlobalResourceContextBestFitCultureInfo; + } + finally + { + ts_IsDoingAppXCultureInfoLookup = false; + } + + return toReturn; + } + + internal static bool SetCultureInfoForUserPreferredLanguageInAppX(CultureInfo ci) + { + if (s_WindowsRuntimeResourceManager == null) + { + s_WindowsRuntimeResourceManager = ResourceManager.GetWinRTResourceManager(); + } + + return s_WindowsRuntimeResourceManager.SetGlobalResourceContextDefaultCulture(ci); + } +#endif + } +} diff --git a/src/mscorlib/src/System/Globalization/CultureInfo.cs b/src/mscorlib/src/System/Globalization/CultureInfo.cs index ba61c146f6..60938defac 100644 --- a/src/mscorlib/src/System/Globalization/CultureInfo.cs +++ b/src/mscorlib/src/System/Globalization/CultureInfo.cs @@ -26,24 +26,27 @@ // //////////////////////////////////////////////////////////////////////////// -namespace System.Globalization { - using System; - using System.Security; - using System.Threading; - using System.Collections; - using System.Runtime; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Runtime.Serialization; - using System.Runtime.Versioning; - using System.Reflection; - using Microsoft.Win32; - using System.Diagnostics; - using System.Diagnostics.Contracts; - using System.Resources; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Runtime.Serialization; +using System.Threading; + +namespace System.Globalization +{ +#if CORECLR + using StringCultureInfoDictionary = Dictionary<string, CultureInfo>; + using StringLcidDictionary = Dictionary<int, CultureInfo>; + + using Lock = Object; +#else + using StringCultureInfoDictionary = LowLevelDictionary<string, CultureInfo>; + using StringLcidDictionary = LowLevelDictionary<int, CultureInfo>; +#endif [Serializable] - public partial class CultureInfo : ICloneable, IFormatProvider { + public partial class CultureInfo : IFormatProvider, ICloneable + { //--------------------------------------------------------------------// // Internal Information // //--------------------------------------------------------------------// @@ -53,38 +56,32 @@ namespace System.Globalization { //--------------------------------------------------------------------// // We use an RFC4646 type string to construct CultureInfo. - // This string is stored in m_name and is authoritative. - // We use the m_cultureData to get the data for our object - - // WARNING - // WARNING: All member fields declared here must also be in ndp/clr/src/vm/object.h - // WARNING: They aren't really private because object.h can access them, but other C# stuff cannot - // WARNING: The type loader will rearrange class member offsets so the mscorwks!CultureInfoBaseObject - // WARNING: must be manually structured to match the true loaded class layout - // WARNING - internal bool m_isReadOnly; - internal CompareInfo compareInfo; - internal TextInfo textInfo; + // This string is stored in _name and is authoritative. + // We use the _cultureData to get the data for our object + + private bool _isReadOnly; + private CompareInfo compareInfo; + private TextInfo textInfo; internal NumberFormatInfo numInfo; internal DateTimeFormatInfo dateTimeInfo; - internal Calendar calendar; - [OptionalField(VersionAdded = 1)] - internal int m_dataItem; // NEVER USED, DO NOT USE THIS! (Serialized in Whidbey/Everett) - [OptionalField(VersionAdded = 1)] - internal int cultureID = 0x007f; // NEVER USED, DO NOT USE THIS! (Serialized in Whidbey/Everett) + private Calendar calendar; // // The CultureData instance that we are going to read data from. // For supported culture, this will be the CultureData instance that read data from mscorlib assembly. // For customized culture, this will be the CultureData instance that read data from user customized culture binary file. // - [NonSerialized]internal CultureData m_cultureData; - - [NonSerialized]internal bool m_isInherited; - [NonSerialized]private CultureInfo m_consoleFallbackCulture; + [NonSerialized] + internal CultureData _cultureData; + + [NonSerialized] + internal bool _isInherited; + + [NonSerialized] + private CultureInfo _consoleFallbackCulture; // Names are confusing. Here are 3 names we have: // - // new CultureInfo() m_name m_nonSortName m_sortName + // new CultureInfo() _name _nonSortName _sortName // en-US en-US en-US en-US // de-de_phoneb de-DE_phoneb de-DE de-DE_phoneb // fj-fj (custom) fj-FJ fj-FJ en-US (if specified sort is en-US) @@ -96,17 +93,18 @@ namespace System.Globalization { // Note that the name used to be serialized for Everett; it is now serialized // because alernate sorts can have alternate names. // This has a de-DE, de-DE_phoneb or fj-FJ style name - internal string m_name; + internal string _name; // This will hold the non sorting name to be returned from CultureInfo.Name property. // This has a de-DE style name even for de-DE_phoneb type cultures - [NonSerialized]private string m_nonSortName; + [NonSerialized] + private string _nonSortName; // This will hold the sorting name to be returned from CultureInfo.SortName property. // This might be completely unrelated to the culture name if a custom culture. Ie en-US for fj-FJ. // Otherwise its the sort name, ie: de-DE or de-DE_phoneb - [NonSerialized]private string m_sortName; - + [NonSerialized] + private string _sortName; //--------------------------------------------------------------------// // @@ -117,184 +115,74 @@ namespace System.Globalization { //Get the current user default culture. This one is almost always used, so we create it by default. private static volatile CultureInfo s_userDefaultCulture; + //The culture used in the user interface. This is mostly used to load correct localized resources. + private static volatile CultureInfo s_userDefaultUICulture; // // All of the following will be created on demand. // + // WARNING: We allow diagnostic tools to directly inspect these three members (s_InvariantCultureInfo, s_DefaultThreadCurrentUICulture and s_DefaultThreadCurrentCulture) + // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details. + // Please do not change the type, the name, or the semantic usage of this member without understanding the implication for tools. + // Get in touch with the diagnostics team if you have questions. + //The Invariant culture; private static volatile CultureInfo s_InvariantCultureInfo; - //The culture used in the user interface. This is mostly used to load correct localized resources. - private static volatile CultureInfo s_userDefaultUICulture; - - //This is the UI culture used to install the OS. - private static volatile CultureInfo s_InstalledUICultureInfo; - //These are defaults that we use if a thread has not opted into having an explicit culture private static volatile CultureInfo s_DefaultThreadCurrentUICulture; private static volatile CultureInfo s_DefaultThreadCurrentCulture; - //This is a cache of all previously created cultures. Valid keys are LCIDs or the name. We use two hashtables to track them, - // depending on how they are called. - private static volatile Hashtable s_LcidCachedCultures; - private static volatile Hashtable s_NameCachedCultures; + internal static AsyncLocal<CultureInfo> s_asyncLocalCurrentCulture; + internal static AsyncLocal<CultureInfo> s_asyncLocalCurrentUICulture; -#if FEATURE_APPX - // When running under AppX, we use this to get some information about the language list - private static volatile WindowsRuntimeResourceManagerBase s_WindowsRuntimeResourceManager; + internal static void AsyncLocalSetCurrentCulture(AsyncLocalValueChangedArgs<CultureInfo> args) + { + Thread.m_CurrentCulture = args.CurrentValue; + } - [ThreadStatic] - private static bool ts_IsDoingAppXCultureInfoLookup; -#endif + internal static void AsyncLocalSetCurrentUICulture(AsyncLocalValueChangedArgs<CultureInfo> args) + { + Thread.m_CurrentUICulture = args.CurrentValue; + } + + private static readonly Lock _lock = new Lock(); + private static volatile StringCultureInfoDictionary s_NameCachedCultures; + private static volatile StringLcidDictionary s_LcidCachedCultures; //The parent culture. - [NonSerialized]private CultureInfo m_parent; + [NonSerialized] + private CultureInfo _parent; // LOCALE constants of interest to us internally and privately for LCID functions // (ie: avoid using these and use names if possible) - internal const int LOCALE_NEUTRAL = 0x0000; - private const int LOCALE_USER_DEFAULT = 0x0400; - private const int LOCALE_SYSTEM_DEFAULT = 0x0800; - internal const int LOCALE_CUSTOM_DEFAULT = 0x0c00; - internal const int LOCALE_CUSTOM_UNSPECIFIED = 0x1000; - internal const int LOCALE_INVARIANT = 0x007F; - private const int LOCALE_TRADITIONAL_SPANISH = 0x040a; + internal const int LOCALE_NEUTRAL = 0x0000; + private const int LOCALE_USER_DEFAULT = 0x0400; + private const int LOCALE_SYSTEM_DEFAULT = 0x0800; + internal const int LOCALE_CUSTOM_UNSPECIFIED = 0x1000; + internal const int LOCALE_CUSTOM_DEFAULT = 0x0c00; + internal const int LOCALE_INVARIANT = 0x007F; // // The CultureData instance that reads the data provided by our CultureData class. // - //Using a field initializer rather than a static constructor so that the whole class can be lazy - //init. + // Using a field initializer rather than a static constructor so that the whole class can be lazy + // init. private static readonly bool init = Init(); private static bool Init() { - - if (s_InvariantCultureInfo == null) + if (s_InvariantCultureInfo == null) { CultureInfo temp = new CultureInfo("", false); - temp.m_isReadOnly = true; + temp._isReadOnly = true; s_InvariantCultureInfo = temp; } - // First we set it to Invariant in case someone needs it before we're done finding it. - // For example, if we throw an exception in InitUserDefaultCulture, we will still need an valid - // s_userDefaultCulture to be used in Thread.CurrentCulture. - s_userDefaultCulture = s_userDefaultUICulture = s_InvariantCultureInfo; - s_userDefaultCulture = InitUserDefaultCulture(); - s_userDefaultUICulture = InitUserDefaultUICulture(); + s_userDefaultCulture = GetUserDefaultCulture(); + s_userDefaultUICulture = GetUserDefaultUILanguage(); return true; } - static CultureInfo InitUserDefaultCulture() - { - String strDefault = GetDefaultLocaleName(LOCALE_USER_DEFAULT); - if (strDefault == null) - { - strDefault = GetDefaultLocaleName(LOCALE_SYSTEM_DEFAULT); - - if (strDefault == null) - { - // If system default doesn't work, keep using the invariant - return (CultureInfo.InvariantCulture); - } - } - CultureInfo temp |