Revert "Remove not necessary type forwarded from attributes when type comes from...
[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 readonly byte[] s_categoryForLatin1 = {
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 (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 (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)(s_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 (c >= '0' && c <= '9');
213             }
214             return (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.DecimalDigitNumber);
215         }
216
217
218         /*=================================CheckLetter=====================================
219         ** Check if the specified UnicodeCategory belongs to the letter categories.
220         ==============================================================================*/
221         internal static bool CheckLetter(UnicodeCategory uc)
222         {
223             switch (uc)
224             {
225                 case (UnicodeCategory.UppercaseLetter):
226                 case (UnicodeCategory.LowercaseLetter):
227                 case (UnicodeCategory.TitlecaseLetter):
228                 case (UnicodeCategory.ModifierLetter):
229                 case (UnicodeCategory.OtherLetter):
230                     return (true);
231             }
232             return (false);
233         }
234
235         /*=================================ISLETTER=====================================
236         **A wrapper for char.  Returns a boolean indicating whether    **
237         **character c is considered to be a letter.                                   **
238         ==============================================================================*/
239         // Determines whether a character is a letter.
240         public static bool IsLetter(char c)
241         {
242             if (IsLatin1(c))
243             {
244                 if (IsAscii(c))
245                 {
246                     c |= (char)0x20;
247                     return ((c >= 'a' && c <= 'z'));
248                 }
249                 return (CheckLetter(GetLatin1UnicodeCategory(c)));
250             }
251             return (CheckLetter(CharUnicodeInfo.GetUnicodeCategory(c)));
252         }
253
254         private static bool IsWhiteSpaceLatin1(char c)
255         {
256             // There are characters which belong to UnicodeCategory.Control but are considered as white spaces.
257             // We use code point comparisons for these characters here as a temporary fix.
258
259             // U+0009 = <control> HORIZONTAL TAB
260             // U+000a = <control> LINE FEED
261             // U+000b = <control> VERTICAL TAB
262             // U+000c = <contorl> FORM FEED
263             // U+000d = <control> CARRIAGE RETURN
264             // U+0085 = <control> NEXT LINE
265             // U+00a0 = NO-BREAK SPACE
266             return
267                 c == ' ' ||
268                 (uint)(c - '\x0009') <= ('\x000d' - '\x0009') || // (c >= '\x0009' && c <= '\x000d')
269                 c == '\x00a0' ||
270                 c == '\x0085';
271         }
272
273         /*===============================ISWHITESPACE===================================
274         **A wrapper for char.  Returns a boolean indicating whether    **
275         **character c is considered to be a whitespace character.                     **
276         ==============================================================================*/
277         // Determines whether a character is whitespace.
278         public static bool IsWhiteSpace(char c)
279         {
280             if (IsLatin1(c))
281             {
282                 return (IsWhiteSpaceLatin1(c));
283             }
284             return CharUnicodeInfo.IsWhiteSpace(c);
285         }
286
287
288         /*===================================IsUpper====================================
289         **Arguments: c -- the characater to be checked.
290         **Returns:  True if c is an uppercase character.
291         ==============================================================================*/
292         // Determines whether a character is upper-case.
293         public static bool IsUpper(char c)
294         {
295             if (IsLatin1(c))
296             {
297                 if (IsAscii(c))
298                 {
299                     return (c >= 'A' && c <= 'Z');
300                 }
301                 return (GetLatin1UnicodeCategory(c) == UnicodeCategory.UppercaseLetter);
302             }
303             return (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.UppercaseLetter);
304         }
305
306         /*===================================IsLower====================================
307         **Arguments: c -- the characater to be checked.
308         **Returns:  True if c is an lowercase character.
309         ==============================================================================*/
310         // Determines whether a character is lower-case.
311         public static bool IsLower(char c)
312         {
313             if (IsLatin1(c))
314             {
315                 if (IsAscii(c))
316                 {
317                     return (c >= 'a' && c <= 'z');
318                 }
319                 return (GetLatin1UnicodeCategory(c) == UnicodeCategory.LowercaseLetter);
320             }
321             return (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.LowercaseLetter);
322         }
323
324         internal static bool CheckPunctuation(UnicodeCategory uc)
325         {
326             switch (uc)
327             {
328                 case UnicodeCategory.ConnectorPunctuation:
329                 case UnicodeCategory.DashPunctuation:
330                 case UnicodeCategory.OpenPunctuation:
331                 case UnicodeCategory.ClosePunctuation:
332                 case UnicodeCategory.InitialQuotePunctuation:
333                 case UnicodeCategory.FinalQuotePunctuation:
334                 case UnicodeCategory.OtherPunctuation:
335                     return (true);
336             }
337             return (false);
338         }
339
340
341         /*================================IsPunctuation=================================
342         **Arguments: c -- the characater to be checked.
343         **Returns:  True if c is an punctuation mark
344         ==============================================================================*/
345         // Determines whether a character is a punctuation mark.
346         public static bool IsPunctuation(char c)
347         {
348             if (IsLatin1(c))
349             {
350                 return (CheckPunctuation(GetLatin1UnicodeCategory(c)));
351             }
352             return (CheckPunctuation(CharUnicodeInfo.GetUnicodeCategory(c)));
353         }
354
355         /*=================================CheckLetterOrDigit=====================================
356         ** Check if the specified UnicodeCategory belongs to the letter or digit categories.
357         ==============================================================================*/
358         internal static bool CheckLetterOrDigit(UnicodeCategory uc)
359         {
360             switch (uc)
361             {
362                 case UnicodeCategory.UppercaseLetter:
363                 case UnicodeCategory.LowercaseLetter:
364                 case UnicodeCategory.TitlecaseLetter:
365                 case UnicodeCategory.ModifierLetter:
366                 case UnicodeCategory.OtherLetter:
367                 case UnicodeCategory.DecimalDigitNumber:
368                     return (true);
369             }
370             return (false);
371         }
372
373         // Determines whether a character is a letter or a digit.
374         public static bool IsLetterOrDigit(char c)
375         {
376             if (IsLatin1(c))
377             {
378                 return (CheckLetterOrDigit(GetLatin1UnicodeCategory(c)));
379             }
380             return (CheckLetterOrDigit(CharUnicodeInfo.GetUnicodeCategory(c)));
381         }
382
383         /*===================================ToUpper====================================
384         **
385         ==============================================================================*/
386         // Converts a character to upper-case for the specified culture.
387         // <;<;Not fully implemented>;>;
388         public static char ToUpper(char c, CultureInfo culture)
389         {
390             if (culture == null)
391                 throw new ArgumentNullException(nameof(culture));
392             return culture.TextInfo.ToUpper(c);
393         }
394
395         /*=================================TOUPPER======================================
396         **A wrapper for char.ToUpperCase.  Converts character c to its **
397         **uppercase equivalent.  If c is already an uppercase character or is not an  **
398         **alphabetic, nothing happens.                                                **
399         ==============================================================================*/
400         // Converts a character to upper-case for the default culture.
401         //
402         public static char ToUpper(char c)
403         {
404             return CultureInfo.CurrentCulture.TextInfo.ToUpper(c);
405         }
406
407
408         // Converts a character to upper-case for invariant culture.
409         public static char ToUpperInvariant(char c)
410         {
411             return CultureInfo.InvariantCulture.TextInfo.ToUpper(c);
412         }
413
414
415         /*===================================ToLower====================================
416         **
417         ==============================================================================*/
418         // Converts a character to lower-case for the specified culture.
419         // <;<;Not fully implemented>;>;
420         public static char ToLower(char c, CultureInfo culture)
421         {
422             if (culture == null)
423                 throw new ArgumentNullException(nameof(culture));
424             return culture.TextInfo.ToLower(c);
425         }
426
427         /*=================================TOLOWER======================================
428         **A wrapper for char.ToLowerCase.  Converts character c to its **
429         **lowercase equivalent.  If c is already a lowercase character or is not an   **
430         **alphabetic, nothing happens.                                                **
431         ==============================================================================*/
432         // Converts a character to lower-case for the default culture.
433         public static char ToLower(char c)
434         {
435             return CultureInfo.CurrentCulture.TextInfo.ToLower(c);
436         }
437
438
439         // Converts a character to lower-case for invariant culture.
440         public static char ToLowerInvariant(char c)
441         {
442             return CultureInfo.InvariantCulture.TextInfo.ToLower(c);
443         }
444
445
446         //
447         // IConvertible implementation
448         //    
449         public TypeCode GetTypeCode()
450         {
451             return TypeCode.Char;
452         }
453
454
455         bool IConvertible.ToBoolean(IFormatProvider provider)
456         {
457             throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "Boolean"));
458         }
459
460         char IConvertible.ToChar(IFormatProvider provider)
461         {
462             return m_value;
463         }
464
465         sbyte IConvertible.ToSByte(IFormatProvider provider)
466         {
467             return Convert.ToSByte(m_value);
468         }
469
470         byte IConvertible.ToByte(IFormatProvider provider)
471         {
472             return Convert.ToByte(m_value);
473         }
474
475         short IConvertible.ToInt16(IFormatProvider provider)
476         {
477             return Convert.ToInt16(m_value);
478         }
479
480         ushort IConvertible.ToUInt16(IFormatProvider provider)
481         {
482             return Convert.ToUInt16(m_value);
483         }
484
485         int IConvertible.ToInt32(IFormatProvider provider)
486         {
487             return Convert.ToInt32(m_value);
488         }
489
490         uint IConvertible.ToUInt32(IFormatProvider provider)
491         {
492             return Convert.ToUInt32(m_value);
493         }
494
495         long IConvertible.ToInt64(IFormatProvider provider)
496         {
497             return Convert.ToInt64(m_value);
498         }
499
500         ulong IConvertible.ToUInt64(IFormatProvider provider)
501         {
502             return Convert.ToUInt64(m_value);
503         }
504
505         float IConvertible.ToSingle(IFormatProvider provider)
506         {
507             throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "Single"));
508         }
509
510         double IConvertible.ToDouble(IFormatProvider provider)
511         {
512             throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "Double"));
513         }
514
515         decimal IConvertible.ToDecimal(IFormatProvider provider)
516         {
517             throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "Decimal"));
518         }
519
520         DateTime IConvertible.ToDateTime(IFormatProvider provider)
521         {
522             throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "DateTime"));
523         }
524
525         object IConvertible.ToType(Type type, IFormatProvider provider)
526         {
527             return Convert.DefaultToType((IConvertible)this, type, provider);
528         }
529         public static bool IsControl(char c)
530         {
531             if (IsLatin1(c))
532             {
533                 return (GetLatin1UnicodeCategory(c) == UnicodeCategory.Control);
534             }
535             return (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.Control);
536         }
537
538         public static bool IsControl(string s, int index)
539         {
540             if (s == null)
541                 throw new ArgumentNullException(nameof(s));
542             if (((uint)index) >= ((uint)s.Length))
543             {
544                 throw new ArgumentOutOfRangeException(nameof(index));
545             }
546             char c = s[index];
547             if (IsLatin1(c))
548             {
549                 return (GetLatin1UnicodeCategory(c) == UnicodeCategory.Control);
550             }
551             return (CharUnicodeInfo.GetUnicodeCategory(s, index) == UnicodeCategory.Control);
552         }
553
554
555         public static bool IsDigit(string s, int index)
556         {
557             if (s == null)
558                 throw new ArgumentNullException(nameof(s));
559             if (((uint)index) >= ((uint)s.Length))
560             {
561                 throw new ArgumentOutOfRangeException(nameof(index));
562             }
563             char c = s[index];
564             if (IsLatin1(c))
565             {
566                 return (c >= '0' && c <= '9');
567             }
568             return (CharUnicodeInfo.GetUnicodeCategory(s, index) == UnicodeCategory.DecimalDigitNumber);
569         }
570
571         public static bool IsLetter(string s, int index)
572         {
573             if (s == null)
574                 throw new ArgumentNullException(nameof(s));
575             if (((uint)index) >= ((uint)s.Length))
576             {
577                 throw new ArgumentOutOfRangeException(nameof(index));
578             }
579             char c = s[index];
580             if (IsLatin1(c))
581             {
582                 if (IsAscii(c))
583                 {
584                     c |= (char)0x20;
585                     return ((c >= 'a' && c <= 'z'));
586                 }
587                 return (CheckLetter(GetLatin1UnicodeCategory(c)));
588             }
589             return (CheckLetter(CharUnicodeInfo.GetUnicodeCategory(s, index)));
590         }
591
592         public static bool IsLetterOrDigit(string s, int index)
593         {
594             if (s == null)
595                 throw new ArgumentNullException(nameof(s));
596             if (((uint)index) >= ((uint)s.Length))
597             {
598                 throw new ArgumentOutOfRangeException(nameof(index));
599             }
600             char c = s[index];
601             if (IsLatin1(c))
602             {
603                 return CheckLetterOrDigit(GetLatin1UnicodeCategory(c));
604             }
605             return CheckLetterOrDigit(CharUnicodeInfo.GetUnicodeCategory(s, index));
606         }
607
608         public static bool IsLower(string s, int index)
609         {
610             if (s == null)
611                 throw new ArgumentNullException(nameof(s));
612             if (((uint)index) >= ((uint)s.Length))
613             {
614                 throw new ArgumentOutOfRangeException(nameof(index));
615             }
616             char c = s[index];
617             if (IsLatin1(c))
618             {
619                 if (IsAscii(c))
620                 {
621                     return (c >= 'a' && c <= 'z');
622                 }
623                 return (GetLatin1UnicodeCategory(c) == UnicodeCategory.LowercaseLetter);
624             }
625
626             return (CharUnicodeInfo.GetUnicodeCategory(s, index) == UnicodeCategory.LowercaseLetter);
627         }
628
629         /*=================================CheckNumber=====================================
630         ** Check if the specified UnicodeCategory belongs to the number categories.
631         ==============================================================================*/
632
633         internal static bool CheckNumber(UnicodeCategory uc)
634         {
635             switch (uc)
636             {
637                 case (UnicodeCategory.DecimalDigitNumber):
638                 case (UnicodeCategory.LetterNumber):
639                 case (UnicodeCategory.OtherNumber):
640                     return (true);
641             }
642             return (false);
643         }
644
645         public static bool IsNumber(char c)
646         {
647             if (IsLatin1(c))
648             {
649                 if (IsAscii(c))
650                 {
651                     return (c >= '0' && c <= '9');
652                 }
653                 return (CheckNumber(GetLatin1UnicodeCategory(c)));
654             }
655             return (CheckNumber(CharUnicodeInfo.GetUnicodeCategory(c)));
656         }
657
658         public static bool IsNumber(string s, int index)
659         {
660             if (s == null)
661                 throw new ArgumentNullException(nameof(s));
662             if (((uint)index) >= ((uint)s.Length))
663             {
664                 throw new ArgumentOutOfRangeException(nameof(index));
665             }
666             char c = s[index];
667             if (IsLatin1(c))
668             {
669                 if (IsAscii(c))
670                 {
671                     return (c >= '0' && c <= '9');
672                 }
673                 return (CheckNumber(GetLatin1UnicodeCategory(c)));
674             }
675             return (CheckNumber(CharUnicodeInfo.GetUnicodeCategory(s, index)));
676         }
677
678         ////////////////////////////////////////////////////////////////////////
679         //
680         //  IsPunctuation
681         //
682         //  Determines if the given character is a punctuation character.
683         //
684         ////////////////////////////////////////////////////////////////////////
685
686         public static bool IsPunctuation(string s, int index)
687         {
688             if (s == null)
689                 throw new ArgumentNullException(nameof(s));
690             if (((uint)index) >= ((uint)s.Length))
691             {
692                 throw new ArgumentOutOfRangeException(nameof(index));
693             }
694             char c = s[index];
695             if (IsLatin1(c))
696             {
697                 return (CheckPunctuation(GetLatin1UnicodeCategory(c)));
698             }
699             return (CheckPunctuation(CharUnicodeInfo.GetUnicodeCategory(s, index)));
700         }
701
702
703         /*================================= CheckSeparator ============================
704         ** Check if the specified UnicodeCategory belongs to the seprator categories.
705         ==============================================================================*/
706
707         internal static bool CheckSeparator(UnicodeCategory uc)
708         {
709             switch (uc)
710             {
711                 case UnicodeCategory.SpaceSeparator:
712                 case UnicodeCategory.LineSeparator:
713                 case UnicodeCategory.ParagraphSeparator:
714                     return (true);
715             }
716             return (false);
717         }
718
719         private static bool IsSeparatorLatin1(char c)
720         {
721             // U+00a0 = NO-BREAK SPACE
722             // There is no LineSeparator or ParagraphSeparator in Latin 1 range.
723             return (c == '\x0020' || c == '\x00a0');
724         }
725
726         public static bool IsSeparator(char c)
727         {
728             if (IsLatin1(c))
729             {
730                 return (IsSeparatorLatin1(c));
731             }
732             return (CheckSeparator(CharUnicodeInfo.GetUnicodeCategory(c)));
733         }
734
735         public static bool IsSeparator(string s, int index)
736         {
737             if (s == null)
738                 throw new ArgumentNullException(nameof(s));
739             if (((uint)index) >= ((uint)s.Length))
740             {
741                 throw new ArgumentOutOfRangeException(nameof(index));
742             }
743             char c = s[index];
744             if (IsLatin1(c))
745             {
746                 return (IsSeparatorLatin1(c));
747             }
748             return (CheckSeparator(CharUnicodeInfo.GetUnicodeCategory(s, index)));
749         }
750
751         public static bool IsSurrogate(char c)
752         {
753             return (c >= HIGH_SURROGATE_START && c <= LOW_SURROGATE_END);
754         }
755
756         public static bool IsSurrogate(string s, int index)
757         {
758             if (s == null)
759             {
760                 throw new ArgumentNullException(nameof(s));
761             }
762             if (((uint)index) >= ((uint)s.Length))
763             {
764                 throw new ArgumentOutOfRangeException(nameof(index));
765             }
766             return (IsSurrogate(s[index]));
767         }
768
769         /*================================= CheckSymbol ============================
770          ** Check if the specified UnicodeCategory belongs to the symbol categories.
771          ==============================================================================*/
772
773         internal static bool CheckSymbol(UnicodeCategory uc)
774         {
775             switch (uc)
776             {
777                 case (UnicodeCategory.MathSymbol):
778                 case (UnicodeCategory.CurrencySymbol):
779                 case (UnicodeCategory.ModifierSymbol):
780                 case (UnicodeCategory.OtherSymbol):
781                     return (true);
782             }
783             return (false);
784         }
785
786         public static bool IsSymbol(char c)
787         {
788             if (IsLatin1(c))
789             {
790                 return (CheckSymbol(GetLatin1UnicodeCategory(c)));
791             }
792             return (CheckSymbol(CharUnicodeInfo.GetUnicodeCategory(c)));
793         }
794
795         public static bool IsSymbol(string s, int index)
796         {
797             if (s == null)
798                 throw new ArgumentNullException(nameof(s));
799             if (((uint)index) >= ((uint)s.Length))
800             {
801                 throw new ArgumentOutOfRangeException(nameof(index));
802             }
803             char c = s[index];
804             if (IsLatin1(c))
805             {
806                 return (CheckSymbol(GetLatin1UnicodeCategory(c)));
807             }
808             return (CheckSymbol(CharUnicodeInfo.GetUnicodeCategory(s, index)));
809         }
810
811
812         public static bool IsUpper(string s, int index)
813         {
814             if (s == null)
815                 throw new ArgumentNullException(nameof(s));
816             if (((uint)index) >= ((uint)s.Length))
817             {
818                 throw new ArgumentOutOfRangeException(nameof(index));
819             }
820             char c = s[index];
821             if (IsLatin1(c))
822             {
823                 if (IsAscii(c))
824                 {
825                     return (c >= 'A' && c <= 'Z');
826                 }
827                 return (GetLatin1UnicodeCategory(c) == UnicodeCategory.UppercaseLetter);
828             }
829
830             return (CharUnicodeInfo.GetUnicodeCategory(s, index) == UnicodeCategory.UppercaseLetter);
831         }
832
833         public static bool IsWhiteSpace(string s, int index)
834         {
835             if (s == null)
836                 throw new ArgumentNullException(nameof(s));
837             if (((uint)index) >= ((uint)s.Length))
838             {
839                 throw new ArgumentOutOfRangeException(nameof(index));
840             }
841
842             if (IsLatin1(s[index]))
843             {
844                 return IsWhiteSpaceLatin1(s[index]);
845             }
846
847             return CharUnicodeInfo.IsWhiteSpace(s, index);
848         }
849
850         public static UnicodeCategory GetUnicodeCategory(char c)
851         {
852             if (IsLatin1(c))
853             {
854                 return (GetLatin1UnicodeCategory(c));
855             }
856             return CharUnicodeInfo.GetUnicodeCategory((int)c);
857         }
858
859         public static UnicodeCategory GetUnicodeCategory(string s, int index)
860         {
861             if (s == null)
862                 throw new ArgumentNullException(nameof(s));
863             if (((uint)index) >= ((uint)s.Length))
864             {
865                 throw new ArgumentOutOfRangeException(nameof(index));
866             }
867             if (IsLatin1(s[index]))
868             {
869                 return (GetLatin1UnicodeCategory(s[index]));
870             }
871             return CharUnicodeInfo.InternalGetUnicodeCategory(s, index);
872         }
873
874         public static double GetNumericValue(char c)
875         {
876             return CharUnicodeInfo.GetNumericValue(c);
877         }
878
879         public static double GetNumericValue(string s, int index)
880         {
881             if (s == null)
882                 throw new ArgumentNullException(nameof(s));
883             if (((uint)index) >= ((uint)s.Length))
884             {
885                 throw new ArgumentOutOfRangeException(nameof(index));
886             }
887             return CharUnicodeInfo.GetNumericValue(s, index);
888         }
889
890
891         /*================================= IsHighSurrogate ============================
892          ** Check if a char is a high surrogate.
893          ==============================================================================*/
894         public static bool IsHighSurrogate(char c)
895         {
896             return ((c >= CharUnicodeInfo.HIGH_SURROGATE_START) && (c <= CharUnicodeInfo.HIGH_SURROGATE_END));
897         }
898
899         public static bool IsHighSurrogate(string s, int index)
900         {
901             if (s == null)
902             {
903                 throw new ArgumentNullException(nameof(s));
904             }
905             if (index < 0 || index >= s.Length)
906             {
907                 throw new ArgumentOutOfRangeException(nameof(index));
908             }
909             return (IsHighSurrogate(s[index]));
910         }
911
912         /*================================= IsLowSurrogate ============================
913          ** Check if a char is a low surrogate.
914          ==============================================================================*/
915         public static bool IsLowSurrogate(char c)
916         {
917             return ((c >= CharUnicodeInfo.LOW_SURROGATE_START) && (c <= CharUnicodeInfo.LOW_SURROGATE_END));
918         }
919
920         public static bool IsLowSurrogate(string s, int index)
921         {
922             if (s == null)
923             {
924                 throw new ArgumentNullException(nameof(s));
925             }
926             if (index < 0 || index >= s.Length)
927             {
928                 throw new ArgumentOutOfRangeException(nameof(index));
929             }
930             return (IsLowSurrogate(s[index]));
931         }
932
933         /*================================= IsSurrogatePair ============================
934          ** Check if the string specified by the index starts with a surrogate pair.
935          ==============================================================================*/
936         public static bool IsSurrogatePair(string s, int index)
937         {
938             if (s == null)
939             {
940                 throw new ArgumentNullException(nameof(s));
941             }
942             if (index < 0 || index >= s.Length)
943             {
944                 throw new ArgumentOutOfRangeException(nameof(index));
945             }
946             if (index + 1 < s.Length)
947             {
948                 return (IsSurrogatePair(s[index], s[index + 1]));
949             }
950             return (false);
951         }
952
953         public static bool IsSurrogatePair(char highSurrogate, char lowSurrogate)
954         {
955             return ((highSurrogate >= CharUnicodeInfo.HIGH_SURROGATE_START && highSurrogate <= CharUnicodeInfo.HIGH_SURROGATE_END) &&
956                     (lowSurrogate >= CharUnicodeInfo.LOW_SURROGATE_START && lowSurrogate <= CharUnicodeInfo.LOW_SURROGATE_END));
957         }
958
959         internal const int UNICODE_PLANE00_END = 0x00ffff;
960         // The starting codepoint for Unicode plane 1.  Plane 1 contains 0x010000 ~ 0x01ffff.
961         internal const int UNICODE_PLANE01_START = 0x10000;
962         // The end codepoint for Unicode plane 16.  This is the maximum code point value allowed for Unicode.
963         // Plane 16 contains 0x100000 ~ 0x10ffff.
964         internal const int UNICODE_PLANE16_END = 0x10ffff;
965
966         internal const int HIGH_SURROGATE_START = 0x00d800;
967         internal const int LOW_SURROGATE_END = 0x00dfff;
968
969
970
971         /*================================= ConvertFromUtf32 ============================
972          ** Convert an UTF32 value into a surrogate pair.
973          ==============================================================================*/
974
975         public static string ConvertFromUtf32(int utf32)
976         {
977             // For UTF32 values from U+00D800 ~ U+00DFFF, we should throw.  They
978             // are considered as irregular code unit sequence, but they are not illegal.
979             if ((utf32 < 0 || utf32 > UNICODE_PLANE16_END) || (utf32 >= HIGH_SURROGATE_START && utf32 <= LOW_SURROGATE_END))
980             {
981                 throw new ArgumentOutOfRangeException(nameof(utf32), SR.ArgumentOutOfRange_InvalidUTF32);
982             }
983
984             if (utf32 < UNICODE_PLANE01_START)
985             {
986                 // This is a BMP character.
987                 return (char.ToString((char)utf32));
988             }
989
990             unsafe
991             {
992                 // This is a supplementary character.  Convert it to a surrogate pair in UTF-16.
993                 utf32 -= UNICODE_PLANE01_START;
994                 uint surrogate = 0; // allocate 2 chars worth of stack space
995                 char* address = (char*)&surrogate;
996                 address[0] = (char)((utf32 / 0x400) + (int)CharUnicodeInfo.HIGH_SURROGATE_START);
997                 address[1] = (char)((utf32 % 0x400) + (int)CharUnicodeInfo.LOW_SURROGATE_START);
998                 return new string(address, 0, 2);
999             }
1000         }
1001
1002
1003         /*=============================ConvertToUtf32===================================
1004         ** Convert a surrogate pair to UTF32 value                                    
1005         ==============================================================================*/
1006
1007         public static int ConvertToUtf32(char highSurrogate, char lowSurrogate)
1008         {
1009             if (!IsHighSurrogate(highSurrogate))
1010             {
1011                 throw new ArgumentOutOfRangeException(nameof(highSurrogate), SR.ArgumentOutOfRange_InvalidHighSurrogate);
1012             }
1013             if (!IsLowSurrogate(lowSurrogate))
1014             {
1015                 throw new ArgumentOutOfRangeException(nameof(lowSurrogate), SR.ArgumentOutOfRange_InvalidLowSurrogate);
1016             }
1017             return (((highSurrogate - CharUnicodeInfo.HIGH_SURROGATE_START) * 0x400) + (lowSurrogate - CharUnicodeInfo.LOW_SURROGATE_START) + UNICODE_PLANE01_START);
1018         }
1019
1020         /*=============================ConvertToUtf32===================================
1021         ** Convert a character or a surrogate pair starting at index of the specified string 
1022         ** to UTF32 value.
1023         ** The char pointed by index should be a surrogate pair or a BMP character.
1024         ** This method throws if a high-surrogate is not followed by a low surrogate.
1025         ** This method throws if a low surrogate is seen without preceding a high-surrogate.
1026         ==============================================================================*/
1027
1028         public static int ConvertToUtf32(string s, int index)
1029         {
1030             if (s == null)
1031             {
1032                 throw new ArgumentNullException(nameof(s));
1033             }
1034
1035             if (index < 0 || index >= s.Length)
1036             {
1037                 throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index);
1038             }
1039             // Check if the character at index is a high surrogate.
1040             int temp1 = (int)s[index] - CharUnicodeInfo.HIGH_SURROGATE_START;
1041             if (temp1 >= 0 && temp1 <= 0x7ff)
1042             {
1043                 // Found a surrogate char.
1044                 if (temp1 <= 0x3ff)
1045                 {
1046                     // Found a high surrogate.
1047                     if (index < s.Length - 1)
1048                     {
1049                         int temp2 = (int)s[index + 1] - CharUnicodeInfo.LOW_SURROGATE_START;
1050                         if (temp2 >= 0 && temp2 <= 0x3ff)
1051                         {
1052                             // Found a low surrogate.
1053                             return ((temp1 * 0x400) + temp2 + UNICODE_PLANE01_START);
1054                         }
1055                         else
1056                         {
1057                             throw new ArgumentException(SR.Format(SR.Argument_InvalidHighSurrogate, index), nameof(s));
1058                         }
1059                     }
1060                     else
1061                     {
1062                         // Found a high surrogate at the end of the string.
1063                         throw new ArgumentException(SR.Format(SR.Argument_InvalidHighSurrogate, index), nameof(s));
1064                     }
1065                 }
1066                 else
1067                 {
1068                     // Find a low surrogate at the character pointed by index.
1069                     throw new ArgumentException(SR.Format(SR.Argument_InvalidLowSurrogate, index), nameof(s));
1070                 }
1071             }
1072             // Not a high-surrogate or low-surrogate. Genereate the UTF32 value for the BMP characters.
1073             return ((int)s[index]);
1074         }
1075     }
1076 }