2007-05-23 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / corlib / System / Int32.cs
1 //
2 // System.Int32.cs
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //
7 // (C) Ximian, Inc.  http://www.ximian.com
8 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 using System.Globalization;
31 using System.Threading;
32
33 namespace System {
34         
35         [Serializable]
36 #if NET_2_0
37         [System.Runtime.InteropServices.ComVisible (true)]
38 #endif
39         public struct Int32 : IFormattable, IConvertible, IComparable
40 #if NET_2_0
41                 , IComparable<Int32>, IEquatable <Int32>
42 #endif
43         {
44
45                 public const int MaxValue = 0x7fffffff;
46                 public const int MinValue = -2147483648;
47                 
48                 // This field is looked up by name in the runtime
49                 internal int m_value;
50
51                 public int CompareTo (object v)
52                 {
53                         if (v == null)
54                                 return 1;
55                         
56                         if (!(v is System.Int32))
57                                 throw new ArgumentException (Locale.GetText ("Value is not a System.Int32"));
58
59                         int xv = (int) v;
60                         if (m_value == xv)
61                                 return 0;
62                         if (m_value > xv)
63                                 return 1;
64                         else
65                                 return -1;
66                 }
67
68                 public override bool Equals (object o)
69                 {
70                         if (!(o is System.Int32))
71                                 return false;
72
73                         return ((int) o) == m_value;
74                 }
75
76                 public override int GetHashCode ()
77                 {
78                         return m_value;
79                 }
80
81 #if NET_2_0
82                 public int CompareTo (int value)
83                 {
84                         if (m_value == value)
85                                 return 0;
86                         if (m_value > value)
87                                 return 1;
88                         else
89                                 return -1;
90                 }
91
92                 public bool Equals (int value)
93                 {
94                         return value == m_value;
95                 }
96 #endif
97
98                 internal static bool Parse (string s, bool tryParse, out int result, out Exception exc)
99                 {
100                         int val = 0;
101                         int len;
102                         int i, sign = 1;
103                         bool digits_seen = false;
104
105                         result = 0;
106                         exc = null;
107
108                         if (s == null) {
109                                 if (!tryParse)
110                                         exc = new ArgumentNullException ("s");
111                                 return false;
112                         }
113
114                         len = s.Length;
115
116                         char c;
117                         for (i = 0; i < len; i++){
118                                 c = s [i];
119                                 if (!Char.IsWhiteSpace (c))
120                                         break;
121                         }
122                         
123                         if (i == len) {
124                                 if (!tryParse)
125                                         exc = GetFormatException ();
126                                 return false;
127                         }
128
129                         c = s [i];
130                         if (c == '+')
131                                 i++;
132                         else if (c == '-'){
133                                 sign = -1;
134                                 i++;
135                         }
136                         
137                         for (; i < len; i++){
138                                 c = s [i];
139
140                                 if (c == '\0') {
141                                         i = len;
142                                         continue;
143                                 }
144                                 
145                                 if (c >= '0' && c <= '9'){
146                                         try {
147                                                 val = checked (val * 10 + (c - '0') * sign);
148                                                 digits_seen = true;
149                                         } catch (OverflowException e) {
150                                                 exc = e;
151                                                 return false;
152                                         }
153                                 } else {
154                                         if (Char.IsWhiteSpace (c)){
155                                                 for (i++; i < len; i++){
156                                                         if (!Char.IsWhiteSpace (s [i])) {
157                                                                 if (!tryParse)
158                                                                         exc = GetFormatException ();
159                                                                 return false;
160                         
161                                                         }
162                                                 }
163                                                 break;
164                                         } else {
165                                                 if (!tryParse)
166                                                         exc = GetFormatException ();
167                                                 return false;
168                                         }
169                                 }
170                         }
171                         if (!digits_seen) {
172                                 if (!tryParse)
173                                         exc = GetFormatException ();
174                                 return false;
175                         }
176
177                         result = val;
178
179                         return true;
180                 }
181
182                 public static int Parse (string s, IFormatProvider fp)
183                 {
184                         return Parse (s, NumberStyles.Integer, fp);
185                 }
186
187                 public static int Parse (string s, NumberStyles style)
188                 {
189                         return Parse (s, style, null);
190                 }
191
192                 internal static bool CheckStyle (NumberStyles style, bool tryParse, ref Exception exc)
193                 {
194                         if ((style & NumberStyles.AllowHexSpecifier) != 0) {
195                                 NumberStyles ne = style ^ NumberStyles.AllowHexSpecifier;
196                                 if ((ne & NumberStyles.AllowLeadingWhite) != 0)
197                                         ne ^= NumberStyles.AllowLeadingWhite;
198                                 if ((ne & NumberStyles.AllowTrailingWhite) != 0)
199                                         ne ^= NumberStyles.AllowTrailingWhite;
200                                 if (ne != 0) {
201                                         if (!tryParse)
202                                                 exc = new ArgumentException (
203                                                         "With AllowHexSpecifier only " + 
204                                                         "AllowLeadingWhite and AllowTrailingWhite " + 
205                                                         "are permitted.");
206                                         return false;
207                                 }
208                         }
209
210                         return true;
211                 }
212                 
213                 internal static bool JumpOverWhite (ref int pos, string s, bool reportError, bool tryParse, ref Exception exc)
214                 {
215                         while (pos < s.Length && Char.IsWhiteSpace (s [pos]))
216                                 pos++;
217
218                         if (reportError && pos >= s.Length) {
219                                 if (!tryParse)
220                                         exc = GetFormatException ();
221                                 return false;
222                         }
223
224                         return true;
225                 }
226
227                 internal static void FindSign (ref int pos, string s, NumberFormatInfo nfi, 
228                                       ref bool foundSign, ref bool negative)
229                 {
230                         if ((pos + nfi.NegativeSign.Length) <= s.Length &&
231                                 s.IndexOf (nfi.NegativeSign, pos, nfi.NegativeSign.Length) == pos) {
232                                 negative = true;
233                                 foundSign = true;
234                                 pos += nfi.NegativeSign.Length;
235                         } 
236                         else if ((pos + nfi.PositiveSign.Length) < s.Length &&
237                                 s.IndexOf (nfi.PositiveSign, pos, nfi.PositiveSign.Length) == pos) {
238                                 negative = false;
239                                 pos += nfi.PositiveSign.Length;
240                                 foundSign = true;
241                         } 
242                 }
243
244                 internal static void FindCurrency (ref int pos,
245                                                  string s, 
246                                                  NumberFormatInfo nfi,
247                                                  ref bool foundCurrency)
248                 {
249                         if ((pos + nfi.CurrencySymbol.Length) <= s.Length &&
250                              s.Substring (pos, nfi.CurrencySymbol.Length) == nfi.CurrencySymbol) {
251                                 foundCurrency = true;
252                                 pos += nfi.CurrencySymbol.Length;
253                         } 
254                 }
255
256                 internal static bool FindExponent (ref int pos, string s)
257                 {
258                                 int i = s.IndexOfAny(new char [] {'e', 'E'}, pos);
259                                 if (i < 0)
260                                                 return false;
261                                 if (++i == s.Length)
262                                                 return false;
263                                 if (s [i] == '+' || s [i] == '-')
264                                                 if (++i == s.Length)
265                                                                 return false;
266                                 if (!Char.IsDigit (s [i]))
267                                                 return false;
268                                 for (; i < s.Length; ++i)
269                                                 if (!Char.IsDigit (s [i])) 
270                                                                 break;
271                                 pos = i;
272                                 return true;
273                 }
274
275                 internal static bool FindOther (ref int pos,
276                                               string s, 
277                                               string other)
278                 {
279                         if ((pos + other.Length) <= s.Length &&
280                              s.Substring (pos, other.Length) == other) {
281                                 pos += other.Length;
282                                 return true;
283                         } 
284
285                         return false;
286                 }
287
288                 internal static bool ValidDigit (char e, bool allowHex)
289                 {
290                         if (allowHex)
291                                 return Char.IsDigit (e) || (e >= 'A' && e <= 'F') || (e >= 'a' && e <= 'f');
292
293                         return Char.IsDigit (e);
294                 }
295                 
296                 internal static Exception GetFormatException ()
297                 {
298                         return new FormatException ("Input string was not in the correct format");
299                 }
300                 
301                 internal static bool Parse (string s, NumberStyles style, IFormatProvider fp, bool tryParse, out int result, out Exception exc)
302                 {
303                         result = 0;
304                         exc = null;
305
306                         if (s == null) {
307                                 if (!tryParse)
308                                         exc = GetFormatException ();
309                                 return false;
310                         }
311                         
312                         if (s == null) {
313                                 if (!tryParse)
314                                         exc = new ArgumentNullException ();
315                                 return false;
316                         }
317
318                         if (s.Length == 0) {
319                                 if (!tryParse)
320                                         exc = GetFormatException ();
321                                 return false;
322                         }
323
324                         NumberFormatInfo nfi;
325                         if (fp != null) {
326                                 Type typeNFI = typeof (System.Globalization.NumberFormatInfo);
327                                 nfi = (NumberFormatInfo) fp.GetFormat (typeNFI);
328                         }
329                         else
330                                 nfi = Thread.CurrentThread.CurrentCulture.NumberFormat;
331
332                         if (!CheckStyle (style, tryParse, ref exc))
333                                 return false;
334
335                         bool AllowCurrencySymbol = (style & NumberStyles.AllowCurrencySymbol) != 0;
336                         bool AllowHexSpecifier = (style & NumberStyles.AllowHexSpecifier) != 0;
337                         bool AllowThousands = (style & NumberStyles.AllowThousands) != 0;
338                         bool AllowDecimalPoint = (style & NumberStyles.AllowDecimalPoint) != 0;
339                         bool AllowParentheses = (style & NumberStyles.AllowParentheses) != 0;
340                         bool AllowTrailingSign = (style & NumberStyles.AllowTrailingSign) != 0;
341                         bool AllowLeadingSign = (style & NumberStyles.AllowLeadingSign) != 0;
342                         bool AllowTrailingWhite = (style & NumberStyles.AllowTrailingWhite) != 0;
343                         bool AllowLeadingWhite = (style & NumberStyles.AllowLeadingWhite) != 0;
344                         bool AllowExponent = (style & NumberStyles.AllowExponent) != 0;
345
346                         int pos = 0;
347
348                         if (AllowLeadingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
349                                 return false;
350
351                         bool foundOpenParentheses = false;
352                         bool negative = false;
353                         bool foundSign = false;
354                         bool foundCurrency = false;
355
356                         // Pre-number stuff
357                         if (AllowParentheses && s [pos] == '(') {
358                                 foundOpenParentheses = true;
359                                 foundSign = true;
360                                 negative = true; // MS always make the number negative when there parentheses
361                                                  // even when NumberFormatInfo.NumberNegativePattern != 0!!!
362                                 pos++;
363                                 if (AllowLeadingWhite && !!JumpOverWhite (ref pos, s, true, tryParse, ref exc))
364                                         return false;
365
366                                 if (s.Substring (pos, nfi.NegativeSign.Length) == nfi.NegativeSign) {
367                                         if (!tryParse)
368                                                 exc = GetFormatException ();
369                                         return false;
370                                 }
371                                 
372                                 if (s.Substring (pos, nfi.PositiveSign.Length) == nfi.PositiveSign) {
373                                         if (!tryParse)
374                                                 exc = GetFormatException ();
375                                         return false;
376                                 }
377                         }
378
379                         if (AllowLeadingSign && !foundSign) {
380                                 // Sign + Currency
381                                 FindSign (ref pos, s, nfi, ref foundSign, ref negative);
382                                 if (foundSign) {
383                                         if (AllowLeadingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
384                                                 return false;
385                                         if (AllowCurrencySymbol) {
386                                                 FindCurrency (ref pos, s, nfi,
387                                                               ref foundCurrency);
388                                                 if (foundCurrency && AllowLeadingWhite &&
389                                                                 !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
390                                                         return false;
391                                         }
392                                 }
393                         }
394                         
395                         if (AllowCurrencySymbol && !foundCurrency) {
396                                 // Currency + sign
397                                 FindCurrency (ref pos, s, nfi, ref foundCurrency);
398                                 if (foundCurrency) {
399                                         if (AllowLeadingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
400                                                 return false;
401                                         if (foundCurrency) {
402                                                 if (!foundSign && AllowLeadingSign) {
403                                                         FindSign (ref pos, s, nfi, ref foundSign,
404                                                                   ref negative);
405                                                         if (foundSign && AllowLeadingWhite &&
406                                                                         !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
407                                                                 return false;
408                                                 }
409                                         }
410                                 }
411                         }
412
413                         int number = 0;
414                         int nDigits = 0;
415                         bool decimalPointFound = false;
416                         int digitValue;
417                         char hexDigit;
418                                 
419                         // Number stuff
420                         do {
421
422                                 if (!ValidDigit (s [pos], AllowHexSpecifier)) {
423                                         if (AllowThousands &&
424                                             FindOther (ref pos, s, nfi.NumberGroupSeparator))
425                                             continue;
426                                         else
427                                         if (!decimalPointFound && AllowDecimalPoint &&
428                                             FindOther (ref pos, s, nfi.NumberDecimalSeparator)) {
429                                             decimalPointFound = true;
430                                             continue;
431                                         }
432
433                                         break;
434                                 }
435                                 else if (AllowHexSpecifier) {
436                                         nDigits++;
437                                         hexDigit = s [pos++];
438                                         if (Char.IsDigit (hexDigit))
439                                                 digitValue = (int) (hexDigit - '0');
440                                         else if (Char.IsLower (hexDigit))
441                                                 digitValue = (int) (hexDigit - 'a' + 10);
442                                         else
443                                                 digitValue = (int) (hexDigit - 'A' + 10);
444
445                                         uint unumber = (uint)number;
446                                         try {
447                                                 number = (int)checked (unumber * 16u + (uint)digitValue);
448                                         } catch (OverflowException e) {
449                                                 exc = e;
450                                                 return false;
451                                         }
452                                 }
453                                 else if (decimalPointFound) {
454                                         nDigits++;
455                                         // Allows decimal point as long as it's only 
456                                         // followed by zeroes.
457                                         if (s [pos++] != '0') {
458                                                 if (!tryParse)
459                                                         exc = new OverflowException ("Value too large or too " +
460                                                                         "small.");
461                                                 return false;
462                                         }
463                                 }
464                                 else {
465                                         nDigits++;
466
467                                         try {
468                                                 // Calculations done as negative
469                                                 // (abs (MinValue) > abs (MaxValue))
470                                                 number = checked (
471                                                         number * 10 - 
472                                                         (int) (s [pos++] - '0')
473                                                         );
474                                         } catch (OverflowException) {
475                                                 if (!tryParse)
476                                                         exc = new OverflowException ("Value too large or too " +
477                                                                         "small.");
478                                                 return false;
479                                         }
480                                 }
481                         } while (pos < s.Length);
482
483                         // Post number stuff
484                         if (nDigits == 0) {
485                                 if (!tryParse)
486                                         exc = GetFormatException ();
487                                 return false;
488                         }
489
490                         if (AllowExponent) 
491                                         FindExponent(ref pos, s);
492
493                         if (AllowTrailingSign && !foundSign) {
494                                 // Sign + Currency
495                                 FindSign (ref pos, s, nfi, ref foundSign, ref negative);
496                                 if (foundSign) {
497                                         if (AllowTrailingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
498                                                 return false;
499                                         if (AllowCurrencySymbol)
500                                                 FindCurrency (ref pos, s, nfi,
501                                                               ref foundCurrency);
502                                 }
503                         }
504                         
505                         if (AllowCurrencySymbol && !foundCurrency) {
506                                 // Currency + sign
507                                 FindCurrency (ref pos, s, nfi, ref foundCurrency);
508                                 if (foundCurrency) {
509                                         if (AllowTrailingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
510                                                 return false;
511                                         if (!foundSign && AllowTrailingSign)
512                                                 FindSign (ref pos, s, nfi, ref foundSign,
513                                                           ref negative);
514                                 }
515                         }
516                         
517                         if (AllowTrailingWhite && pos < s.Length && !JumpOverWhite (ref pos, s, false, tryParse, ref exc))
518                                 return false;
519
520                         if (foundOpenParentheses) {
521                                 if (pos >= s.Length || s [pos++] != ')') {
522                                         if (!tryParse)
523                                                 exc = GetFormatException ();
524                                         return false;
525                                 }
526                                 if (AllowTrailingWhite && pos < s.Length &&
527                                                 !JumpOverWhite (ref pos, s, false, tryParse, ref exc))
528                                         return false;
529                         }
530
531                         if (pos < s.Length && s [pos] != '\u0000') {
532                                 if (!tryParse)
533                                         exc = GetFormatException ();
534                                 return false;
535                         }
536                         
537                         if (!negative && !AllowHexSpecifier)
538                                 number = checked (-number);
539
540                         result = number;
541
542                         return true;
543                 }
544
545                 public static int Parse (string s) 
546                 {
547                         Exception exc;
548                         int res;
549
550                         if (!Parse (s, false, out res, out exc))
551                                 throw exc;
552
553                         return res;
554                 }
555
556                 public static int Parse (string s, NumberStyles style, IFormatProvider fp) 
557                 {
558                         Exception exc;
559                         int res;
560
561                         if (!Parse (s, style, fp, false, out res, out exc))
562                                 throw exc;
563
564                         return res;
565                 }
566
567 #if NET_2_0
568                 public static bool TryParse (string s, out int result) 
569                 {
570                         Exception exc;
571                         
572                         if (!Parse (s, true, out result, out exc)) {
573                                 result = 0;
574                                 return false;
575                         }
576
577                         return true;
578                 }
579
580                 public static bool TryParse (string s, NumberStyles style, IFormatProvider provider, out int result) 
581                 {
582                         Exception exc;
583                         if (!Parse (s, style, provider, true, out result, out exc)) {
584                                 result = 0;
585                                 return false;
586                         }
587
588                         return true;
589                 }
590 #endif
591
592                 public override string ToString ()
593                 {
594                         return NumberFormatter.FormatGeneral (new NumberFormatter.NumberStore (m_value));
595                 }
596
597                 public string ToString (IFormatProvider fp)
598                 {
599                         return NumberFormatter.FormatGeneral (new NumberFormatter.NumberStore (m_value), fp);
600                 }
601
602                 public string ToString (string format)
603                 {
604                         return ToString (format, null);
605                 }
606
607                 public string ToString (string format, IFormatProvider fp )
608                 {
609                         NumberFormatInfo nfi = NumberFormatInfo.GetInstance( fp );
610                         return NumberFormatter.NumberToString (format, m_value, nfi);
611                 }
612
613                 // =========== IConvertible Methods =========== //
614
615                 public TypeCode GetTypeCode ()
616                 {
617                         return TypeCode.Int32;
618                 }
619                 
620                 bool IConvertible.ToBoolean  (IFormatProvider provider)
621                 {
622                         return System.Convert.ToBoolean (m_value);
623                 }
624                 byte IConvertible.ToByte     (IFormatProvider provider)
625                 {
626                         return System.Convert.ToByte (m_value);
627                 }
628                 char IConvertible.ToChar     (IFormatProvider provider)
629                 {
630                         return System.Convert.ToChar (m_value);
631                 }
632                 DateTime IConvertible.ToDateTime (IFormatProvider provider)
633                 {
634                         return System.Convert.ToDateTime (m_value);
635                 }
636                 decimal IConvertible.ToDecimal  (IFormatProvider provider)
637                 {
638                         return System.Convert.ToDecimal (m_value);
639                 }
640                 double IConvertible.ToDouble   (IFormatProvider provider)
641                 {
642                         return System.Convert.ToDouble (m_value);
643                 }
644                 short IConvertible.ToInt16    (IFormatProvider provider)
645                 {
646                         return System.Convert.ToInt16 (m_value);
647                 }
648                 int IConvertible.ToInt32    (IFormatProvider provider)
649                 {
650                         return m_value;
651                 }
652                 long IConvertible.ToInt64    (IFormatProvider provider)
653                 {
654                         return System.Convert.ToInt64 (m_value);
655                 }
656
657                 sbyte IConvertible.ToSByte    (IFormatProvider provider)
658                 {
659                         return System.Convert.ToSByte (m_value);
660                 }
661                 float IConvertible.ToSingle   (IFormatProvider provider)
662                 {
663                         return System.Convert.ToSingle (m_value);
664                 }
665
666                 object IConvertible.ToType     (Type conversionType, IFormatProvider provider)
667                 {
668                         return System.Convert.ToType (m_value, conversionType, provider);
669                 }
670                 
671                 ushort IConvertible.ToUInt16   (IFormatProvider provider)
672                 {
673                         return System.Convert.ToUInt16 (m_value);
674                 }
675
676                 uint IConvertible.ToUInt32   (IFormatProvider provider)
677                 {
678                         return System.Convert.ToUInt32 (m_value);
679                 }
680                 ulong IConvertible.ToUInt64   (IFormatProvider provider)
681                 {
682                         return System.Convert.ToUInt64 (m_value);
683                 }
684         }
685 }