minor fix for bug 9520:
[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 = (NumberFormatInfo) fp.GetFormat (typeNFI);
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                         bool decimalPointFound = false;
297                         int digitValue;
298                         char hexDigit;
299                         int exponent = 0;
300                                 
301                         // Number stuff
302                         do {
303
304                                 if (!Int32.ValidDigit (s [pos], AllowHexSpecifier)) {
305                                         if (AllowThousands &&
306                                             (Int32.FindOther (ref pos, s, nfi.NumberGroupSeparator)
307                                                 || Int32.FindOther (ref pos, s, nfi.CurrencyGroupSeparator)))
308                                             continue;
309                                         else
310                                         if (!decimalPointFound && AllowDecimalPoint &&
311                                             (Int32.FindOther (ref pos, s, nfi.NumberDecimalSeparator)
312                                                 || Int32.FindOther (ref pos, s, nfi.CurrencyDecimalSeparator))) {
313                                             decimalPointFound = true;
314                                             continue;
315                                         }
316
317                                         break;
318                                 }
319                                 if (AllowHexSpecifier) {
320                                         nDigits++;
321                                         hexDigit = s [pos++];
322                                         if (Char.IsDigit (hexDigit))
323                                                 digitValue = (int) (hexDigit - '0');
324                                         else if (Char.IsLower (hexDigit))
325                                                 digitValue = (int) (hexDigit - 'a' + 10);
326                                         else
327                                                 digitValue = (int) (hexDigit - 'A' + 10);
328
329                                         ulong unumber = (ulong)number;
330                                         
331                                         // IMPROVME: We could avoid catching OverflowException
332                                         try {
333                                                 number = (long)checked(unumber * 16ul + (ulong)digitValue);
334                                         } catch (OverflowException e){
335                                                 if (!tryParse)
336                                                         exc = e;
337                                                 return false;
338                                         }
339                                 }
340                                 else if (decimalPointFound) {
341                                         nDigits++;
342                                         // Allows decimal point as long as it's only 
343                                         // followed by zeroes.
344                                         if (s [pos++] != '0') {
345                                                 if (!tryParse)
346                                                         exc = new OverflowException ("Value too large or too " +
347                                                                         "small.");
348                                                 return false;
349                                         }
350                                 }
351                                 else {
352                                         nDigits++;
353
354                                         try {
355                                                 // Calculations done as negative
356                                                 // (abs (MinValue) > abs (MaxValue))
357                                                 number = checked (
358                                                         number * 10 - 
359                                                         (long) (s [pos++] - '0')
360                                                         );
361                                         } catch (OverflowException) {
362                                                 if (!tryParse)
363                                                         exc = new OverflowException ("Value too large or too " +
364                                                                         "small.");
365                                                 return false;
366                                         }
367                                 }
368                         } while (pos < s.Length);
369
370                         // Post number stuff
371                         if (nDigits == 0) {
372                                 if (!tryParse)
373                                         exc = Int32.GetFormatException ();
374                                 return false;
375                         }
376
377                         if (AllowExponent)
378                                 if (Int32.FindExponent (ref pos, s, ref exponent, tryParse, ref exc) && exc != null)
379                                         return false;
380
381                         if (AllowTrailingSign && !foundSign) {
382                                 // Sign + Currency
383                                 Int32.FindSign (ref pos, s, nfi, ref foundSign, ref negative);
384                                 if (foundSign && pos < s.Length) {
385                                         if (AllowTrailingWhite && !Int32.JumpOverWhite (ref pos, s, true, tryParse, ref exc))
386                                                 return false;
387                                 }
388                         }
389                         
390                         if (AllowCurrencySymbol && !foundCurrency) {
391                                 if (AllowTrailingWhite && pos < s.Length && !Int32.JumpOverWhite (ref pos, s, false, tryParse, ref exc))
392                                         return false;
393                                 
394                                 // Currency + sign
395                                 Int32.FindCurrency (ref pos, s, nfi, ref foundCurrency);
396                                 if (foundCurrency && pos < s.Length) {
397                                         if (AllowTrailingWhite && !Int32.JumpOverWhite (ref pos, s, true, tryParse, ref exc))
398                                                 return false;
399                                         if (!foundSign && AllowTrailingSign)
400                                                 Int32.FindSign (ref pos, s, nfi, ref foundSign,
401                                                                 ref negative);
402                                 }
403                         }
404                         
405                         if (AllowTrailingWhite && pos < s.Length && !Int32.JumpOverWhite (ref pos, s, false, tryParse, ref exc))
406                                 return false;
407
408                         if (foundOpenParentheses) {
409                                 if (pos >= s.Length || s [pos++] != ')') {
410                                         if (!tryParse)
411                                                 exc = Int32.GetFormatException ();
412                                         return false;
413                                 }
414                                 if (AllowTrailingWhite && pos < s.Length && !Int32.JumpOverWhite (ref pos, s, false, tryParse, ref exc))
415                                         return false;
416                         }
417
418                         if (pos < s.Length && s [pos] != '\u0000') {
419                                 if (!tryParse)
420                                         exc = Int32.GetFormatException ();
421                                 return false;
422                         }
423
424                         if (!negative && !AllowHexSpecifier){
425                                 try {
426                                         number = checked (-number);
427                                 } catch (OverflowException e){
428                                         if (!tryParse)
429                                                 exc = e;
430                                         return false;
431                                 }
432                         }
433
434                         // result *= 10^exponent
435                         if (exponent > 0) {
436                                 // Reduce the risk of throwing an overflow exc
437                                 double res = checked (Math.Pow (10, exponent) * number);
438                                 if (res < Int32.MinValue || res > Int32.MaxValue) {
439                                         if (!tryParse)
440                                                 exc = new OverflowException ("Value too large or too small.");
441                                         return false;
442                                 }
443
444                                 number = (long) res;
445                         }
446
447                         result = number;
448                         return true;
449                 }
450
451                 public static long Parse (string s) 
452                 {
453                         Exception exc;
454                         long res;
455
456                         if (!Parse (s, false, out res, out exc))
457                                 throw exc;
458
459                         return res;
460                 }
461
462                 public static long Parse (string s, NumberStyles style, IFormatProvider provider) 
463                 {
464                         Exception exc;
465                         long res;
466
467                         if (!Parse (s, style, provider, false, out res, out exc))
468                                 throw exc;
469
470                         return res;
471                 }
472
473                 public static bool TryParse (string s, out long result) 
474                 {
475                         Exception exc;
476                         if (!Parse (s, true, out result, out exc)) {
477                                 result = 0;
478                                 return false;
479                         }
480
481                         return true;
482                 }
483
484                 public static bool TryParse (string s, NumberStyles style, IFormatProvider provider, out long result) 
485                 {
486                         Exception exc;
487                         if (!Parse (s, style, provider, true, out result, out exc)) {
488                                 result = 0;
489                                 return false;
490                         }
491
492                         return true;
493                 }
494
495                 public override string ToString ()
496                 {
497                         return NumberFormatter.NumberToString (m_value, null);
498                 }
499
500                 public string ToString (IFormatProvider provider)
501                 {
502                         return NumberFormatter.NumberToString (m_value, provider);
503                 }
504
505                 public string ToString (string format)
506                 {
507                         return ToString (format, null);
508                 }
509
510                 public string ToString (string format, IFormatProvider provider)
511                 {
512                         return NumberFormatter.NumberToString (format, m_value, provider);
513                 }
514
515                 // =========== IConvertible Methods =========== //
516
517                 public TypeCode GetTypeCode ()
518                 {
519                         return TypeCode.Int64;
520                 }
521
522                 bool IConvertible.ToBoolean (IFormatProvider provider)
523                 {
524                         return System.Convert.ToBoolean (m_value);
525                 }
526
527                 byte IConvertible.ToByte (IFormatProvider provider)
528                 {
529                         return System.Convert.ToByte (m_value);
530                 }
531
532                 char IConvertible.ToChar (IFormatProvider provider)
533                 {
534                         return System.Convert.ToChar (m_value);
535                 }
536
537                 DateTime IConvertible.ToDateTime (IFormatProvider provider)
538                 {
539                         return System.Convert.ToDateTime (m_value);
540                 }
541
542                 decimal IConvertible.ToDecimal (IFormatProvider provider)
543                 {
544                         return System.Convert.ToDecimal (m_value);
545                 }
546
547                 double IConvertible.ToDouble (IFormatProvider provider)
548                 {
549                         return System.Convert.ToDouble (m_value);
550                 }
551
552                 short IConvertible.ToInt16 (IFormatProvider provider)
553                 {
554                         return System.Convert.ToInt16 (m_value);
555                 }
556
557                 int IConvertible.ToInt32 (IFormatProvider provider)
558                 {
559                         return System.Convert.ToInt32 (m_value);
560                 }
561
562                 long IConvertible.ToInt64 (IFormatProvider provider)
563                 {
564                         return m_value;
565                 }
566
567                 sbyte IConvertible.ToSByte (IFormatProvider provider)
568                 {
569                         return System.Convert.ToSByte (m_value);
570                 }
571
572                 float IConvertible.ToSingle (IFormatProvider provider)
573                 {
574                         return System.Convert.ToSingle (m_value);
575                 }
576
577                 object IConvertible.ToType (Type targetType, IFormatProvider provider)
578                 {
579                         if (targetType == null)
580                                 throw new ArgumentNullException ("targetType");
581                         return System.Convert.ToType (m_value, targetType, provider, false);
582                 }
583
584                 ushort IConvertible.ToUInt16 (IFormatProvider provider)
585                 {
586                         return System.Convert.ToUInt16 (m_value);
587                 }
588
589                 uint IConvertible.ToUInt32 (IFormatProvider provider)
590                 {
591                         return System.Convert.ToUInt32 (m_value);
592                 }
593
594                 ulong IConvertible.ToUInt64 (IFormatProvider provider)
595                 {
596                         return System.Convert.ToUInt64 (m_value);
597                 }
598         }
599 }