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