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