7f24b4029d094840d7d183a3c02347e704566f77
[platform/upstream/coreclr.git] / src / System.Private.CoreLib / shared / System / Char.cs
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 /*============================================================
6 **
7 **
8 **
9 ** Purpose: This is the value class representing a Unicode character
10 ** Char methods until we create this functionality.
11 **
12 **
13 ===========================================================*/
14
15 using System.Diagnostics;
16 using System.Globalization;
17 using System.Runtime.InteropServices;
18
19 namespace System
20 {
21     [Serializable]
22     [StructLayout(LayoutKind.Sequential)]
23     [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] 
24     public readonly struct Char : IComparable, IComparable<char>, IEquatable<char>, IConvertible
25     {
26         //
27         // Member Variables
28         //
29         private readonly char m_value; // Do not rename (binary serialization)
30
31         //
32         // Public Constants
33         //
34         // The maximum character value.
35         public const char MaxValue = (char)0xFFFF;
36         // The minimum character value.
37         public const char MinValue = (char)0x00;
38
39         // Unicode category values from Unicode U+0000 ~ U+00FF. Store them in byte[] array to save space.
40         private static ReadOnlySpan<byte> CategoryForLatin1 => new byte[] { // uses C# compiler's optimization for static byte[] data
41             (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control,    // 0000 - 0007
42             (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control,    // 0008 - 000F
43             (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control,    // 0010 - 0017
44             (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control,    // 0018 - 001F
45             (byte)UnicodeCategory.SpaceSeparator, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.CurrencySymbol, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation,    // 0020 - 0027
46             (byte)UnicodeCategory.OpenPunctuation, (byte)UnicodeCategory.ClosePunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.DashPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation,    // 0028 - 002F
47             (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber,    // 0030 - 0037
48             (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.OtherPunctuation,    // 0038 - 003F
49             (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter,    // 0040 - 0047
50             (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter,    // 0048 - 004F
51             (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter,    // 0050 - 0057
52             (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.OpenPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.ClosePunctuation, (byte)UnicodeCategory.ModifierSymbol, (byte)UnicodeCategory.ConnectorPunctuation,    // 0058 - 005F
53             (byte)UnicodeCategory.ModifierSymbol, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter,    // 0060 - 0067
54             (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter,    // 0068 - 006F
55             (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter,    // 0070 - 0077
56             (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.OpenPunctuation, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.ClosePunctuation, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.Control,    // 0078 - 007F
57             (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control,    // 0080 - 0087
58             (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control,    // 0088 - 008F
59             (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control,    // 0090 - 0097
60             (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control,    // 0098 - 009F
61             (byte)UnicodeCategory.SpaceSeparator, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.CurrencySymbol, (byte)UnicodeCategory.CurrencySymbol, (byte)UnicodeCategory.CurrencySymbol, (byte)UnicodeCategory.CurrencySymbol, (byte)UnicodeCategory.OtherSymbol, (byte)UnicodeCategory.OtherSymbol,    // 00A0 - 00A7
62             (byte)UnicodeCategory.ModifierSymbol, (byte)UnicodeCategory.OtherSymbol, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.InitialQuotePunctuation, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.DashPunctuation, (byte)UnicodeCategory.OtherSymbol, (byte)UnicodeCategory.ModifierSymbol,    // 00A8 - 00AF
63             (byte)UnicodeCategory.OtherSymbol, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.ModifierSymbol, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.OtherSymbol, (byte)UnicodeCategory.OtherPunctuation,    // 00B0 - 00B7
64             (byte)UnicodeCategory.ModifierSymbol, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.FinalQuotePunctuation, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.OtherPunctuation,    // 00B8 - 00BF
65             (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter,    // 00C0 - 00C7
66             (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter,    // 00C8 - 00CF
67             (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.MathSymbol,    // 00D0 - 00D7
68             (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.LowercaseLetter,    // 00D8 - 00DF
69             (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter,    // 00E0 - 00E7
70             (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter,    // 00E8 - 00EF
71             (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.MathSymbol,    // 00F0 - 00F7
72             (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter,    // 00F8 - 00FF
73         };
74
75         // Return true for all characters below or equal U+00ff, which is ASCII + Latin-1 Supplement.
76         private static bool IsLatin1(char ch)
77         {
78             return (uint)ch <= '\x00ff';
79         }
80
81         // Return true for all characters below or equal U+007f, which is ASCII.
82         private static bool IsAscii(char ch)
83         {
84             return (uint)ch <= '\x007f';
85         }
86
87         // Return the Unicode category for Unicode character <= 0x00ff.      
88         private static UnicodeCategory GetLatin1UnicodeCategory(char ch)
89         {
90             Debug.Assert(IsLatin1(ch), "char.GetLatin1UnicodeCategory(): ch should be <= 007f");
91             return (UnicodeCategory)CategoryForLatin1[(int)ch];
92         }
93
94         //
95         // Private Constants
96         //
97
98         //
99         // Overriden Instance Methods
100         //
101
102         // Calculate a hashcode for a 2 byte Unicode character.
103         public override int GetHashCode()
104         {
105             return (int)m_value | ((int)m_value << 16);
106         }
107
108         // Used for comparing two boxed Char objects.
109         //
110         public override bool Equals(object obj)
111         {
112             if (!(obj is char))
113             {
114                 return false;
115             }
116             return (m_value == ((char)obj).m_value);
117         }
118
119         [System.Runtime.Versioning.NonVersionable]
120         public bool Equals(char obj)
121         {
122             return m_value == obj;
123         }
124
125         // Compares this object to another object, returning an integer that
126         // indicates the relationship. 
127         // Returns a value less than zero if this  object
128         // null is considered to be less than any instance.
129         // If object is not of type Char, this method throws an ArgumentException.
130         //
131         public int CompareTo(object value)
132         {
133             if (value == null)
134             {
135                 return 1;
136             }
137             if (!(value is char))
138             {
139                 throw new ArgumentException(SR.Arg_MustBeChar);
140             }
141
142             return (m_value - ((char)value).m_value);
143         }
144
145         public int CompareTo(char value)
146         {
147             return (m_value - value);
148         }
149
150         // Overrides System.Object.ToString.
151         public override string ToString()
152         {
153             return char.ToString(m_value);
154         }
155
156         public string ToString(IFormatProvider provider)
157         {
158             return char.ToString(m_value);
159         }
160
161         //
162         // Formatting Methods
163         //
164
165         /*===================================ToString===================================
166         **This static methods takes a character and returns the String representation of it.
167         ==============================================================================*/
168         // Provides a string representation of a character.
169         public static string ToString(char c) => string.CreateFromChar(c);
170
171         public static char Parse(string s)
172         {
173             if (s == null)
174             {
175                 throw new ArgumentNullException(nameof(s));
176             }
177
178             if (s.Length != 1)
179             {
180                 throw new FormatException(SR.Format_NeedSingleChar);
181             }
182             return s[0];
183         }
184
185         public static bool TryParse(string s, out char result)
186         {
187             result = '\0';
188             if (s == null)
189             {
190                 return false;
191             }
192             if (s.Length != 1)
193             {
194                 return false;
195             }
196             result = s[0];
197             return true;
198         }
199
200         //
201         // Static Methods
202         //
203         /*=================================ISDIGIT======================================
204         **A wrapper for char.  Returns a boolean indicating whether    **
205         **character c is considered to be a digit.                                    **
206         ==============================================================================*/
207         // Determines whether a character is a digit.
208         public static bool IsDigit(char c)
209         {
210             if (IsLatin1(c))
211             {
212                 return IsInRange(c, '0', '9');
213             }
214             return (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.DecimalDigitNumber);
215         }
216
217         private static bool IsInRange(char c, char min, char max) => (uint)(c - min) <= (uint)(max - min);
218
219         private static bool IsInRange(UnicodeCategory c, UnicodeCategory min, UnicodeCategory max) => (uint)(c - min) <= (uint)(max - min);
220
221         /*=================================CheckLetter=====================================
222         ** Check if the specified UnicodeCategory belongs to the letter categories.
223         ==============================================================================*/
224         internal static bool CheckLetter(UnicodeCategory uc)
225         {
226             return IsInRange(uc, UnicodeCategory.UppercaseLetter, UnicodeCategory.OtherLetter);
227         }
228
229         /*=================================ISLETTER=====================================
230         **A wrapper for char.  Returns a boolean indicating whether    **
231         **character c is considered to be a letter.                                   **
232         ==============================================================================*/
233         // Determines whether a character is a letter.
234         public static bool IsLetter(char c)
235         {
236             if (IsLatin1(c))
237             {
238                 if (IsAscii(c))
239                 {
240                     c |= (char)0x20;
241                     return IsInRange(c, 'a', 'z');
242                 }
243                 return (CheckLetter(GetLatin1UnicodeCategory(c)));
244             }
245             return (CheckLetter(CharUnicodeInfo.GetUnicodeCategory(c)));
246         }
247
248         private static bool IsWhiteSpaceLatin1(char c)
249         {
250             // There are characters which belong to UnicodeCategory.Control but are considered as white spaces.
251             // We use code point comparisons for these characters here as a temporary fix.
252
253             // U+0009 = <control> HORIZONTAL TAB
254             // U+000a = <control> LINE FEED
255             // U+000b = <control> VERTICAL TAB
256             // U+000c = <contorl> FORM FEED
257             // U+000d = <control> CARRIAGE RETURN
258             // U+0085 = <control> NEXT LINE
259             // U+00a0 = NO-BREAK SPACE
260             return
261                 c == ' ' ||
262                 (uint)(c - '\x0009') <= ('\x000d' - '\x0009') || // (c >= '\x0009' && c <= '\x000d')
263                 c == '\x00a0' ||
264                 c == '\x0085';
265         }
266
267         /*===============================ISWHITESPACE===================================
268         **A wrapper for char.  Returns a boolean indicating whether    **
269         **character c is considered to be a whitespace character.                     **
270         ==============================================================================*/
271         // Determines whether a character is whitespace.
272         public static bool IsWhiteSpace(char c)
273         {
274             if (IsLatin1(c))
275             {
276                 return (IsWhiteSpaceLatin1(c));
277             }
278             return CheckSeparator(CharUnicodeInfo.GetUnicodeCategory(c));
279         }
280
281
282         /*===================================IsUpper====================================
283         **Arguments: c -- the characater to be checked.
284         **Returns:  True if c is an uppercase character.
285         ==============================================================================*/
286         // Determines whether a character is upper-case.
287         public static bool IsUpper(char c)
288         {
289             if (IsLatin1(c))
290             {
291                 if (IsAscii(c))
292                 {
293                     return IsInRange(c, 'A', 'Z');
294                 }
295                 return (GetLatin1UnicodeCategory(c) == UnicodeCategory.UppercaseLetter);
296             }
297             return (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.UppercaseLetter);
298         }
299
300         /*===================================IsLower====================================
301         **Arguments: c -- the characater to be checked.
302         **Returns:  True if c is an lowercase character.
303         ==============================================================================*/
304         // Determines whether a character is lower-case.
305         public static bool IsLower(char c)
306         {
307             if (IsLatin1(c))
308             {
309                 if (IsAscii(c))
310                 {
311                     return IsInRange(c, 'a', 'z');
312                 }
313                 return (GetLatin1UnicodeCategory(c) == UnicodeCategory.LowercaseLetter);
314             }
315             return (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.LowercaseLetter);
316         }
317
318         internal static bool CheckPunctuation(UnicodeCategory uc)
319         {
320             return IsInRange(uc, UnicodeCategory.ConnectorPunctuation, UnicodeCategory.OtherPunctuation);
321         }
322
323
324         /*================================IsPunctuation=================================
325         **Arguments: c -- the characater to be checked.
326         **Returns:  True if c is an punctuation mark
327         ==============================================================================*/
328         // Determines whether a character is a punctuation mark.
329         public static bool IsPunctuation(char c)
330         {
331             if (IsLatin1(c))
332             {
333                 return (CheckPunctuation(GetLatin1UnicodeCategory(c)));
334             }
335             return (CheckPunctuation(CharUnicodeInfo.GetUnicodeCategory(c)));
336         }
337
338         /*=================================CheckLetterOrDigit=====================================
339         ** Check if the specified UnicodeCategory belongs to the letter or digit categories.
340         ==============================================================================*/
341         internal static bool CheckLetterOrDigit(UnicodeCategory uc)
342         {
343             return CheckLetter(uc) || uc == UnicodeCategory.DecimalDigitNumber;
344         }
345
346         // Determines whether a character is a letter or a digit.
347         public static bool IsLetterOrDigit(char c)
348         {
349             if (IsLatin1(c))
350             {
351                 return (CheckLetterOrDigit(GetLatin1UnicodeCategory(c)));
352             }
353             return (CheckLetterOrDigit(CharUnicodeInfo.GetUnicodeCategory(c)));
354         }
355
356         /*===================================ToUpper====================================
357         **
358         ==============================================================================*/
359         // Converts a character to upper-case for the specified culture.
360         // <;<;Not fully implemented>;>;
361         public static char ToUpper(char c, CultureInfo culture)
362         {
363             if (culture == null)
364                 throw new ArgumentNullException(nameof(culture));
365             return culture.TextInfo.ToUpper(c);
366         }
367
368         /*=================================TOUPPER======================================
369         **A wrapper for char.ToUpperCase.  Converts character c to its **
370         **uppercase equivalent.  If c is already an uppercase character or is not an  **
371         **alphabetic, nothing happens.                                                **
372         ==============================================================================*/
373         // Converts a character to upper-case for the default culture.
374         //
375         public static char ToUpper(char c)
376         {
377             return CultureInfo.CurrentCulture.TextInfo.ToUpper(c);
378         }
379
380
381         // Converts a character to upper-case for invariant culture.
382         public static char ToUpperInvariant(char c)
383         {
384             return CultureInfo.InvariantCulture.TextInfo.ToUpper(c);
385         }
386
387
388         /*===================================ToLower====================================
389         **
390         ==============================================================================*/
391         // Converts a character to lower-case for the specified culture.
392         // <;<;Not fully implemented>;>;
393         public static char ToLower(char c, CultureInfo culture)
394         {
395             if (culture == null)
396                 throw new ArgumentNullException(nameof(culture));
397             return culture.TextInfo.ToLower(c);
398         }
399
400         /*=================================TOLOWER======================================
401         **A wrapper for char.ToLowerCase.  Converts character c to its **
402         **lowercase equivalent.  If c is already a lowercase character or is not an   **
403         **alphabetic, nothing happens.                                                **
404         ==============================================================================*/
405         // Converts a character to lower-case for the default culture.
406         public static char ToLower(char c)
407         {
408             return CultureInfo.CurrentCulture.TextInfo.ToLower(c);
409         }
410
411
412         // Converts a character to lower-case for invariant culture.
413         public static char ToLowerInvariant(char c)
414         {
415             return CultureInfo.InvariantCulture.TextInfo.ToLower(c);
416         }
417
418
419         //
420         // IConvertible implementation
421         //    
422         public TypeCode GetTypeCode()
423         {
424             return TypeCode.Char;
425         }
426
427
428         bool IConvertible.ToBoolean(IFormatProvider provider)
429         {
430             throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "Boolean"));
431         }
432
433         char IConvertible.ToChar(IFormatProvider provider)
434         {
435             return m_value;
436         }
437
438         sbyte IConvertible.ToSByte(IFormatProvider provider)
439         {
440             return Convert.ToSByte(m_value);
441         }
442
443         byte IConvertible.ToByte(IFormatProvider provider)
444         {
445             return Convert.ToByte(m_value);
446         }
447
448         short IConvertible.ToInt16(IFormatProvider provider)
449         {
450             return Convert.ToInt16(m_value);
451         }
452
453         ushort IConvertible.ToUInt16(IFormatProvider provider)
454         {
455             return Convert.ToUInt16(m_value);
456         }
457
458         int IConvertible.ToInt32(IFormatProvider provider)
459         {
460             return Convert.ToInt32(m_value);
461         }
462
463         uint IConvertible.ToUInt32(IFormatProvider provider)
464         {
465             return Convert.ToUInt32(m_value);
466         }
467
468         long IConvertible.ToInt64(IFormatProvider provider)
469         {
470             return Convert.ToInt64(m_value);
471         }
472
473         ulong IConvertible.ToUInt64(IFormatProvider provider)
474         {
475             return Convert.ToUInt64(m_value);
476         }
477
478         float IConvertible.ToSingle(IFormatProvider provider)
479         {
480             throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "Single"));
481         }
482
483         double IConvertible.ToDouble(IFormatProvider provider)
484         {
485             throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "Double"));
486         }
487
488         decimal IConvertible.ToDecimal(IFormatProvider provider)
489         {
490             throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "Decimal"));
491         }
492
493         DateTime IConvertible.ToDateTime(IFormatProvider provider)
494         {
495             throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "DateTime"));
496         }
497
498         object IConvertible.ToType(Type type, IFormatProvider provider)
499         {
500             return Convert.DefaultToType((IConvertible)this, type, provider);
501         }
502         public static bool IsControl(char c)
503         {
504             if (IsLatin1(c))
505             {
506                 return (GetLatin1UnicodeCategory(c) == UnicodeCategory.Control);
507             }
508             return (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.Control);
509         }
510
511         public static bool IsControl(string s, int index)
512         {
513             if (s == null)
514                 throw new ArgumentNullException(nameof(s));
515             if (((uint)index) >= ((uint)s.Length))
516             {
517                 throw new ArgumentOutOfRangeException(nameof(index));
518             }
519             char c = s[index];
520             if (IsLatin1(c))
521             {
522                 return (GetLatin1UnicodeCategory(c) == UnicodeCategory.Control);
523             }
524             return (CharUnicodeInfo.GetUnicodeCategory(s, index) == UnicodeCategory.Control);
525         }
526
527
528         public static bool IsDigit(string s, int index)
529         {
530             if (s == null)
531                 throw new ArgumentNullException(nameof(s));
532             if (((uint)index) >= ((uint)s.Length))
533             {
534                 throw new ArgumentOutOfRangeException(nameof(index));
535             }
536             char c = s[index];
537             if (IsLatin1(c))
538             {
539                 return IsInRange(c, '0', '9');
540             }
541             return (CharUnicodeInfo.GetUnicodeCategory(s, index) == UnicodeCategory.DecimalDigitNumber);
542         }
543
544         public static bool IsLetter(string s, int index)
545         {
546             if (s == null)
547                 throw new ArgumentNullException(nameof(s));
548             if (((uint)index) >= ((uint)s.Length))
549             {
550                 throw new ArgumentOutOfRangeException(nameof(index));
551             }
552             char c = s[index];
553             if (IsLatin1(c))
554             {
555                 if (IsAscii(c))
556                 {
557                     c |= (char)0x20;
558                     return IsInRange(c, 'a', 'z');
559                 }
560                 return (CheckLetter(GetLatin1UnicodeCategory(c)));
561             }
562             return (CheckLetter(CharUnicodeInfo.GetUnicodeCategory(s, index)));
563         }
564
565         public static bool IsLetterOrDigit(string s, int index)
566         {
567             if (s == null)
568                 throw new ArgumentNullException(nameof(s));
569             if (((uint)index) >= ((uint)s.Length))
570             {
571                 throw new ArgumentOutOfRangeException(nameof(index));
572             }
573             char c = s[index];
574             if (IsLatin1(c))
575             {
576                 return CheckLetterOrDigit(GetLatin1UnicodeCategory(c));
577             }
578             return CheckLetterOrDigit(CharUnicodeInfo.GetUnicodeCategory(s, index));
579         }
580
581         public static bool IsLower(string s, int index)
582         {
583             if (s == null)
584                 throw new ArgumentNullException(nameof(s));
585             if (((uint)index) >= ((uint)s.Length))
586             {
587                 throw new ArgumentOutOfRangeException(nameof(index));
588             }
589             char c = s[index];
590             if (IsLatin1(c))
591             {
592                 if (IsAscii(c))
593                 {
594                     return IsInRange(c, 'a', 'z');
595                 }
596                 return (GetLatin1UnicodeCategory(c) == UnicodeCategory.LowercaseLetter);
597             }
598
599             return (CharUnicodeInfo.GetUnicodeCategory(s, index) == UnicodeCategory.LowercaseLetter);
600         }
601
602         /*=================================CheckNumber=====================================
603         ** Check if the specified UnicodeCategory belongs to the number categories.
604         ==============================================================================*/
605
606         internal static bool CheckNumber(UnicodeCategory uc)
607         {
608             return IsInRange(uc, UnicodeCategory.DecimalDigitNumber, UnicodeCategory.OtherNumber);
609         }
610
611         public static bool IsNumber(char c)
612         {
613             if (IsLatin1(c))
614             {
615                 if (IsAscii(c))
616                 {
617                     return IsInRange(c, '0', '9');
618                 }
619                 return (CheckNumber(GetLatin1UnicodeCategory(c)));
620             }
621             return (CheckNumber(CharUnicodeInfo.GetUnicodeCategory(c)));
622         }
623
624         public static bool IsNumber(string s, int index)
625         {
626             if (s == null)
627                 throw new ArgumentNullException(nameof(s));
628             if (((uint)index) >= ((uint)s.Length))
629             {
630                 throw new ArgumentOutOfRangeException(nameof(index));
631             }
632             char c = s[index];
633             if (IsLatin1(c))
634             {
635                 if (IsAscii(c))
636                 {
637                     return IsInRange(c, '0', '9');
638                 }
639                 return (CheckNumber(GetLatin1UnicodeCategory(c)));
640             }
641             return (CheckNumber(CharUnicodeInfo.GetUnicodeCategory(s, index)));
642         }
643
644         ////////////////////////////////////////////////////////////////////////
645         //
646         //  IsPunctuation
647         //
648         //  Determines if the given character is a punctuation character.
649         //
650         ////////////////////////////////////////////////////////////////////////
651
652         public static bool IsPunctuation(string s, int index)
653         {
654             if (s == null)
655                 throw new ArgumentNullException(nameof(s));
656             if (((uint)index) >= ((uint)s.Length))
657             {
658                 throw new ArgumentOutOfRangeException(nameof(index));
659             }
660             char c = s[index];
661             if (IsLatin1(c))
662             {
663                 return (CheckPunctuation(GetLatin1UnicodeCategory(c)));
664             }
665             return (CheckPunctuation(CharUnicodeInfo.GetUnicodeCategory(s, index)));
666         }
667
668
669         /*================================= CheckSeparator ============================
670         ** Check if the specified UnicodeCategory belongs to the seprator categories.
671         ==============================================================================*/
672
673         internal static bool CheckSeparator(UnicodeCategory uc)
674         {
675             return IsInRange(uc, UnicodeCategory.SpaceSeparator, UnicodeCategory.ParagraphSeparator);
676         }
677
678         private static bool IsSeparatorLatin1(char c)
679         {
680             // U+00a0 = NO-BREAK SPACE
681             // There is no LineSeparator or ParagraphSeparator in Latin 1 range.
682             return (c == '\x0020' || c == '\x00a0');
683         }
684
685         public static bool IsSeparator(char c)
686         {
687             if (IsLatin1(c))
688             {
689                 return (IsSeparatorLatin1(c));
690             }
691             return (CheckSeparator(CharUnicodeInfo.GetUnicodeCategory(c)));
692         }
693
694         public static bool IsSeparator(string s, int index)
695         {
696             if (s == null)
697                 throw new ArgumentNullException(nameof(s));
698             if (((uint)index) >= ((uint)s.Length))
699             {
700                 throw new ArgumentOutOfRangeException(nameof(index));
701             }
702             char c = s[index];
703             if (IsLatin1(c))
704             {
705                 return (IsSeparatorLatin1(c));
706             }
707             return (CheckSeparator(CharUnicodeInfo.GetUnicodeCategory(s, index)));
708         }
709
710         public static bool IsSurrogate(char c)
711         {
712             return IsInRange(c, CharUnicodeInfo.HIGH_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END);
713         }
714
715         public static bool IsSurrogate(string s, int index)
716         {
717             if (s == null)
718             {
719                 throw new ArgumentNullException(nameof(s));
720             }
721             if (((uint)index) >= ((uint)s.Length))
722             {
723                 throw new ArgumentOutOfRangeException(nameof(index));
724             }
725             return (IsSurrogate(s[index]));
726         }
727
728         /*================================= CheckSymbol ============================
729          ** Check if the specified UnicodeCategory belongs to the symbol categories.
730          ==============================================================================*/
731
732         internal static bool CheckSymbol(UnicodeCategory uc)
733         {
734             return IsInRange(uc, UnicodeCategory.MathSymbol, UnicodeCategory.OtherSymbol);
735         }
736
737         public static bool IsSymbol(char c)
738         {
739             if (IsLatin1(c))
740             {
741                 return (CheckSymbol(GetLatin1UnicodeCategory(c)));
742             }
743             return (CheckSymbol(CharUnicodeInfo.GetUnicodeCategory(c)));
744         }
745
746         public static bool IsSymbol(string s, int index)
747         {
748             if (s == null)
749                 throw new ArgumentNullException(nameof(s));
750             if (((uint)index) >= ((uint)s.Length))
751             {
752                 throw new ArgumentOutOfRangeException(nameof(index));
753             }
754             char c = s[index];
755             if (IsLatin1(c))
756             {
757                 return (CheckSymbol(GetLatin1UnicodeCategory(c)));
758             }
759             return (CheckSymbol(CharUnicodeInfo.GetUnicodeCategory(s, index)));
760         }
761
762
763         public static bool IsUpper(string s, int index)
764         {
765             if (s == null)
766                 throw new ArgumentNullException(nameof(s));
767             if (((uint)index) >= ((uint)s.Length))
768             {
769                 throw new ArgumentOutOfRangeException(nameof(index));
770             }
771             char c = s[index];
772             if (IsLatin1(c))
773             {
774                 if (IsAscii(c))
775                 {
776                     return IsInRange(c, 'A', 'Z');
777                 }
778                 return (GetLatin1UnicodeCategory(c) == UnicodeCategory.UppercaseLetter);
779             }
780
781             return (CharUnicodeInfo.GetUnicodeCategory(s, index) == UnicodeCategory.UppercaseLetter);
782         }
783
784         public static bool IsWhiteSpace(string s, int index)
785         {
786             if (s == null)
787                 throw new ArgumentNullException(nameof(s));
788             if (((uint)index) >= ((uint)s.Length))
789             {
790                 throw new ArgumentOutOfRangeException(nameof(index));
791             }
792
793             if (IsLatin1(s[index]))
794             {
795                 return IsWhiteSpaceLatin1(s[index]);
796             }
797
798             return CheckSeparator(CharUnicodeInfo.GetUnicodeCategory(s, index));
799         }
800
801         public static UnicodeCategory GetUnicodeCategory(char c)
802         {
803             if (IsLatin1(c))
804             {
805                 return (GetLatin1UnicodeCategory(c));
806             }
807             return CharUnicodeInfo.GetUnicodeCategory((int)c);
808         }
809
810         public static UnicodeCategory GetUnicodeCategory(string s, int index)
811         {
812             if (s == null)
813                 throw new ArgumentNullException(nameof(s));
814             if (((uint)index) >= ((uint)s.Length))
815             {
816                 throw new ArgumentOutOfRangeException(nameof(index));
817             }
818             if (IsLatin1(s[index]))
819             {
820                 return (GetLatin1UnicodeCategory(s[index]));
821             }
822             return CharUnicodeInfo.InternalGetUnicodeCategory(s, index);
823         }
824
825         public static double GetNumericValue(char c)
826         {
827             return CharUnicodeInfo.GetNumericValue(c);
828         }
829
830         public static double GetNumericValue(string s, int index)
831         {
832             if (s == null)
833                 throw new ArgumentNullException(nameof(s));
834             if (((uint)index) >= ((uint)s.Length))
835             {
836                 throw new ArgumentOutOfRangeException(nameof(index));
837             }
838             return CharUnicodeInfo.GetNumericValue(s, index);
839         }
840
841
842         /*================================= IsHighSurrogate ============================
843          ** Check if a char is a high surrogate.
844          ==============================================================================*/
845         public static bool IsHighSurrogate(char c)
846         {
847             return IsInRange(c, CharUnicodeInfo.HIGH_SURROGATE_START, CharUnicodeInfo.HIGH_SURROGATE_END);
848         }
849
850         public static bool IsHighSurrogate(string s, int index)
851         {
852             if (s == null)
853             {
854                 throw new ArgumentNullException(nameof(s));
855             }
856             if (index < 0 || index >= s.Length)
857             {
858                 throw new ArgumentOutOfRangeException(nameof(index));
859             }
860             return (IsHighSurrogate(s[index]));
861         }
862
863         /*================================= IsLowSurrogate ============================
864          ** Check if a char is a low surrogate.
865          ==============================================================================*/
866         public static bool IsLowSurrogate(char c)
867         {
868             return IsInRange(c, CharUnicodeInfo.LOW_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END);
869         }
870
871         public static bool IsLowSurrogate(string s, int index)
872         {
873             if (s == null)
874             {
875                 throw new ArgumentNullException(nameof(s));
876             }
877             if (index < 0 || index >= s.Length)
878             {
879                 throw new ArgumentOutOfRangeException(nameof(index));
880             }
881             return (IsLowSurrogate(s[index]));
882         }
883
884         /*================================= IsSurrogatePair ============================
885          ** Check if the string specified by the index starts with a surrogate pair.
886          ==============================================================================*/
887         public static bool IsSurrogatePair(string s, int index)
888         {
889             if (s == null)
890             {
891                 throw new ArgumentNullException(nameof(s));
892             }
893             if (index < 0 || index >= s.Length)
894             {
895                 throw new ArgumentOutOfRangeException(nameof(index));
896             }
897             if (index + 1 < s.Length)
898             {
899                 return (IsSurrogatePair(s[index], s[index + 1]));
900             }
901             return (false);
902         }
903
904         public static bool IsSurrogatePair(char highSurrogate, char lowSurrogate)
905         {
906             return IsHighSurrogate(highSurrogate) && IsLowSurrogate(lowSurrogate);
907         }
908
909         internal const int UNICODE_PLANE00_END = 0x00ffff;
910         // The starting codepoint for Unicode plane 1.  Plane 1 contains 0x010000 ~ 0x01ffff.
911         internal const int UNICODE_PLANE01_START = 0x10000;
912         // The end codepoint for Unicode plane 16.  This is the maximum code point value allowed for Unicode.
913         // Plane 16 contains 0x100000 ~ 0x10ffff.
914         internal const int UNICODE_PLANE16_END = 0x10ffff;
915
916
917
918         /*================================= ConvertFromUtf32 ============================
919          ** Convert an UTF32 value into a surrogate pair.
920          ==============================================================================*/
921
922         public static string ConvertFromUtf32(int utf32)
923         {
924             // For UTF32 values from U+00D800 ~ U+00DFFF, we should throw.  They
925             // are considered as irregular code unit sequence, but they are not illegal.
926             if (((uint)utf32 > UNICODE_PLANE16_END) || (utf32 >= CharUnicodeInfo.HIGH_SURROGATE_START && utf32 <= CharUnicodeInfo.LOW_SURROGATE_END))
927             {
928                 throw new ArgumentOutOfRangeException(nameof(utf32), SR.ArgumentOutOfRange_InvalidUTF32);
929             }
930
931             if (utf32 < UNICODE_PLANE01_START)
932             {
933                 // This is a BMP character.
934                 return (char.ToString((char)utf32));
935             }
936
937             unsafe
938             {
939                 // This is a supplementary character.  Convert it to a surrogate pair in UTF-16.
940                 utf32 -= UNICODE_PLANE01_START;
941                 uint surrogate = 0; // allocate 2 chars worth of stack space
942                 char* address = (char*)&surrogate;
943                 address[0] = (char)((utf32 / 0x400) + (int)CharUnicodeInfo.HIGH_SURROGATE_START);
944                 address[1] = (char)((utf32 % 0x400) + (int)CharUnicodeInfo.LOW_SURROGATE_START);
945                 return new string(address, 0, 2);
946             }
947         }
948
949
950         /*=============================ConvertToUtf32===================================
951         ** Convert a surrogate pair to UTF32 value                                    
952         ==============================================================================*/
953
954         public static int ConvertToUtf32(char highSurrogate, char lowSurrogate)
955         {
956             if (!IsHighSurrogate(highSurrogate))
957             {
958                 throw new ArgumentOutOfRangeException(nameof(highSurrogate), SR.ArgumentOutOfRange_InvalidHighSurrogate);
959             }
960             if (!IsLowSurrogate(lowSurrogate))
961             {
962                 throw new ArgumentOutOfRangeException(nameof(lowSurrogate), SR.ArgumentOutOfRange_InvalidLowSurrogate);
963             }
964             return (((highSurrogate - CharUnicodeInfo.HIGH_SURROGATE_START) * 0x400) + (lowSurrogate - CharUnicodeInfo.LOW_SURROGATE_START) + UNICODE_PLANE01_START);
965         }
966
967         /*=============================ConvertToUtf32===================================
968         ** Convert a character or a surrogate pair starting at index of the specified string 
969         ** to UTF32 value.
970         ** The char pointed by index should be a surrogate pair or a BMP character.
971         ** This method throws if a high-surrogate is not followed by a low surrogate.
972         ** This method throws if a low surrogate is seen without preceding a high-surrogate.
973         ==============================================================================*/
974
975         public static int ConvertToUtf32(string s, int index)
976         {
977             if (s == null)
978             {
979                 throw new ArgumentNullException(nameof(s));
980             }
981
982             if (index < 0 || index >= s.Length)
983             {
984                 throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index);
985             }
986             // Check if the character at index is a high surrogate.
987             int temp1 = (int)s[index] - CharUnicodeInfo.HIGH_SURROGATE_START;
988             if (temp1 >= 0 && temp1 <= 0x7ff)
989             {
990                 // Found a surrogate char.
991                 if (temp1 <= 0x3ff)
992                 {
993                     // Found a high surrogate.
994                     if (index < s.Length - 1)
995                     {
996                         int temp2 = (int)s[index + 1] - CharUnicodeInfo.LOW_SURROGATE_START;
997                         if (temp2 >= 0 && temp2 <= 0x3ff)
998                         {
999                             // Found a low surrogate.
1000                             return ((temp1 * 0x400) + temp2 + UNICODE_PLANE01_START);
1001                         }
1002                         else
1003                         {
1004                             throw new ArgumentException(SR.Format(SR.Argument_InvalidHighSurrogate, index), nameof(s));
1005                         }
1006                     }
1007                     else
1008                     {
1009                         // Found a high surrogate at the end of the string.
1010                         throw new ArgumentException(SR.Format(SR.Argument_InvalidHighSurrogate, index), nameof(s));
1011                     }
1012                 }
1013                 else
1014                 {
1015                     // Find a low surrogate at the character pointed by index.
1016                     throw new ArgumentException(SR.Format(SR.Argument_InvalidLowSurrogate, index), nameof(s));
1017                 }
1018             }
1019             // Not a high-surrogate or low-surrogate. Genereate the UTF32 value for the BMP characters.
1020             return ((int)s[index]);
1021         }
1022     }
1023 }