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