Redesign System/NumberFormatter to improve primitive numeric types ToString performance.
[mono.git] / mcs / class / corlib / System / UInt64.cs
1 //
2 // System.UInt64.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         [CLSCompliant (false)]
37 #if NET_2_0
38         [System.Runtime.InteropServices.ComVisible (true)]
39 #endif
40         public struct UInt64 : IFormattable, IConvertible, IComparable
41 #if NET_2_0
42                 , IComparable<UInt64>, IEquatable <UInt64>
43 #endif
44         {
45                 public const ulong MaxValue = 0xffffffffffffffff;
46                 public const ulong MinValue = 0;
47
48                 internal ulong m_value;
49
50                 public int CompareTo (object value)
51                 {
52                         if (value == null)
53                                 return 1;
54
55                         if (!(value is System.UInt64))
56                                 throw new ArgumentException (Locale.GetText ("Value is not a System.UInt64."));
57
58                         if (this.m_value == (ulong) value)
59                                 return 0;
60
61                         if (this.m_value < (ulong) value)
62                                 return -1;
63
64                         return 1;
65                 }
66
67                 public override bool Equals (object obj)
68                 {
69                         if (!(obj is System.UInt64))
70                                 return false;
71
72                         return ((ulong) obj) == m_value;
73                 }
74
75                 public override int GetHashCode ()
76                 {
77                         return (int)(m_value & 0xffffffff) ^ (int)(m_value >> 32);
78                 }
79
80 #if NET_2_0
81                 public int CompareTo (ulong value)
82                 {
83                         if (m_value == value)
84                                 return 0;
85                         if (m_value > value)
86                                 return 1;
87                         else
88                                 return -1;
89                 }
90
91                 public bool Equals (ulong value)
92                 {
93                         return value == m_value;
94                 }
95 #endif
96
97                 [CLSCompliant (false)]
98                 public static ulong Parse (string s)
99                 {
100                         return Parse (s, NumberStyles.Integer, null);
101                 }
102
103                 [CLSCompliant (false)]
104                 public static ulong Parse (string s, IFormatProvider provider)
105                 {
106                         return Parse (s, NumberStyles.Integer, provider);
107                 }
108
109                 [CLSCompliant (false)]
110                 public static ulong Parse (string s, NumberStyles style)
111                 {
112                         return Parse (s, style, null);
113                 }
114
115                 internal static bool Parse (string s, NumberStyles style, IFormatProvider provider, bool tryParse, out ulong result, out Exception exc)
116                 {
117                         result = 0;
118                         exc = null;
119
120                         if (s == null) {
121                                 if (!tryParse)
122                                         exc = new ArgumentNullException ("s");
123                                 return false;
124                         }
125
126                         if (s.Length == 0) {
127                                 if (!tryParse)
128                                         exc = Int32.GetFormatException ();
129                                 return false;
130                         }
131
132                         NumberFormatInfo nfi;
133                         if (provider != null) {
134                                 Type typeNFI = typeof (NumberFormatInfo);
135                                 nfi = (NumberFormatInfo) provider.GetFormat (typeNFI);
136                         }
137                         else
138                                 nfi = Thread.CurrentThread.CurrentCulture.NumberFormat;
139
140                         if (!Int32.CheckStyle (style, tryParse, ref exc))
141                                 return false;
142
143                         bool AllowCurrencySymbol = (style & NumberStyles.AllowCurrencySymbol) != 0;
144                         bool AllowHexSpecifier = (style & NumberStyles.AllowHexSpecifier) != 0;
145                         bool AllowThousands = (style & NumberStyles.AllowThousands) != 0;
146                         bool AllowDecimalPoint = (style & NumberStyles.AllowDecimalPoint) != 0;
147                         bool AllowParentheses = (style & NumberStyles.AllowParentheses) != 0;
148                         bool AllowTrailingSign = (style & NumberStyles.AllowTrailingSign) != 0;
149                         bool AllowLeadingSign = (style & NumberStyles.AllowLeadingSign) != 0;
150                         bool AllowTrailingWhite = (style & NumberStyles.AllowTrailingWhite) != 0;
151                         bool AllowLeadingWhite = (style & NumberStyles.AllowLeadingWhite) != 0;
152
153                         int pos = 0;
154
155                         if (AllowLeadingWhite && !Int32.JumpOverWhite (ref pos, s, true, tryParse, ref exc))
156                                 return false;
157
158                         bool foundOpenParentheses = false;
159                         bool negative = false;
160                         bool foundSign = false;
161                         bool foundCurrency = false;
162
163                         // Pre-number stuff
164                         if (AllowParentheses && s [pos] == '(') {
165                                 foundOpenParentheses = true;
166                                 foundSign = true;
167                                 negative = true; // MS always make the number negative when there parentheses
168                                                  // even when NumberFormatInfo.NumberNegativePattern != 0!!!
169                                 pos++;
170                                 if (AllowLeadingWhite && !Int32.JumpOverWhite (ref pos, s, true, tryParse, ref exc))
171                                         return false;
172
173                                 if (s.Substring (pos, nfi.NegativeSign.Length) == nfi.NegativeSign) {
174                                         if (!tryParse)
175                                                 exc = Int32.GetFormatException ();
176                                         return false;
177                                 }
178                                 if (s.Substring (pos, nfi.PositiveSign.Length) == nfi.PositiveSign) {
179                                         if (!tryParse)
180                                                 exc = Int32.GetFormatException ();
181                                         return false;
182                                 }
183                         }
184
185                         if (AllowLeadingSign && !foundSign) {
186                                 // Sign + Currency
187                                 Int32.FindSign (ref pos, s, nfi, ref foundSign, ref negative);
188                                 if (foundSign) {
189                                         if (AllowLeadingWhite && !Int32.JumpOverWhite (ref pos, s, true, tryParse, ref exc))
190                                                 return false;
191                                         if (AllowCurrencySymbol) {
192                                                 Int32.FindCurrency (ref pos, s, nfi,
193                                                                     ref foundCurrency);
194                                                 if (foundCurrency && AllowLeadingWhite &&
195                                                                 !Int32.JumpOverWhite (ref pos, s, true, tryParse, ref exc))
196                                                         return false;
197                                         }
198                                 }
199                         }
200
201                         if (AllowCurrencySymbol && !foundCurrency) {
202                                 // Currency + sign
203                                 Int32.FindCurrency (ref pos, s, nfi, ref foundCurrency);
204                                 if (foundCurrency) {
205                                         if (AllowLeadingWhite && !Int32.JumpOverWhite (ref pos, s, true, tryParse, ref exc))
206                                                 return false;
207                                         if (foundCurrency) {
208                                                 if (!foundSign && AllowLeadingSign) {
209                                                         Int32.FindSign (ref pos, s, nfi, ref foundSign,
210                                                                         ref negative);
211                                                         if (foundSign && AllowLeadingWhite &&
212                                                                         !Int32.JumpOverWhite (ref pos, s, true, tryParse, ref exc))
213                                                                 return false;
214                                                 }
215                                         }
216                                 }
217                         }
218
219                         ulong number = 0;
220                         int nDigits = 0;
221                         bool decimalPointFound = false;
222                         ulong digitValue;
223                         char hexDigit;
224
225                         // Number stuff
226                         // Just the same as Int32, but this one adds instead of substract
227                         do {
228
229                                 if (!Int32.ValidDigit (s [pos], AllowHexSpecifier)) {
230                                         if (AllowThousands && Int32.FindOther (ref pos, s, nfi.NumberGroupSeparator))
231                                                 continue;
232                                         else
233                                                 if (!decimalPointFound && AllowDecimalPoint && 
234                                                     Int32.FindOther (ref pos, s, nfi.NumberDecimalSeparator)) {
235                                                         decimalPointFound = true;
236                                                         continue;
237                                                 }
238                                         break;
239                                 }
240                                 else if (AllowHexSpecifier) {
241                                         nDigits++;
242                                         hexDigit = s [pos++];
243                                         if (Char.IsDigit (hexDigit))
244                                                 digitValue = (ulong) (hexDigit - '0');
245                                         else if (Char.IsLower (hexDigit))
246                                                 digitValue = (ulong) (hexDigit - 'a' + 10);
247                                         else
248                                                 digitValue = (ulong) (hexDigit - 'A' + 10);
249
250                                         if (tryParse){
251                                                 // Any number above 32 will do 
252                                                 bool can_overflow = number > 0xffff;
253
254                                                 number = number * 16 + digitValue;
255
256                                                 if (can_overflow && number < 16)
257                                                         return false;
258                                         } else
259                                                 number = checked (number * 16 + digitValue);
260                                 }
261                                 else if (decimalPointFound) {
262                                         nDigits++;
263                                         // Allows decimal point as long as it's only 
264                                         // followed by zeroes.
265                                         if (s [pos++] != '0') {
266                                                 if (!tryParse)
267                                                         exc = new OverflowException (Locale.GetText ("Value too large or too small."));
268                                                 return false;
269                                         }
270                                 }
271                                 else {
272                                         nDigits++;
273
274                                         try {
275                                                 number = checked (number * 10 + (ulong) (s [pos++] - '0'));
276                                         }
277                                         catch (OverflowException) {
278                                                 if (!tryParse)
279                                                         exc = new OverflowException (Locale.GetText ("Value too large or too small."));
280                                                 return false;
281                                         }
282                                 }
283                         } while (pos < s.Length);
284
285                         // Post number stuff
286                         if (nDigits == 0) {
287                                 if (!tryParse)
288                                         exc = Int32.GetFormatException ();
289                                 return false;
290                         }
291
292                         if (AllowTrailingSign && !foundSign) {
293                                 // Sign + Currency
294                                 Int32.FindSign (ref pos, s, nfi, ref foundSign, ref negative);
295                                 if (foundSign) {
296                                         if (AllowTrailingWhite && !Int32.JumpOverWhite (ref pos, s, true, tryParse, ref exc))
297                                                 return false;
298                                         if (AllowCurrencySymbol)
299                                                 Int32.FindCurrency (ref pos, s, nfi, ref foundCurrency);
300                                 }
301                         }
302
303                         if (AllowCurrencySymbol && !foundCurrency) {
304                                 // Currency + sign
305                                 Int32.FindCurrency (ref pos, s, nfi, ref foundCurrency);
306                                 if (foundCurrency) {
307                                         if (AllowTrailingWhite && !Int32.JumpOverWhite (ref pos, s, true, tryParse, ref exc))
308                                                 return false;
309                                         if (!foundSign && AllowTrailingSign)
310                                                 Int32.FindSign (ref pos, s, nfi, ref foundSign, ref negative);
311                                 }
312                         }
313
314                         if (AllowTrailingWhite && pos < s.Length && !Int32.JumpOverWhite (ref pos, s, false, tryParse, ref exc))
315                                 return false;
316
317                         if (foundOpenParentheses) {
318                                 if (pos >= s.Length || s [pos++] != ')') {
319                                         if (!tryParse)
320                                                 exc = Int32.GetFormatException ();
321                                         return false;
322                                 }
323                                 if (AllowTrailingWhite && pos < s.Length && !Int32.JumpOverWhite (ref pos, s, false, tryParse, ref exc))
324                                         return false;
325                         }
326
327                         if (pos < s.Length && s [pos] != '\u0000') {
328                                 if (!tryParse)
329                                         exc = Int32.GetFormatException ();
330                                 return false;
331                         }
332
333                         // -0 is legal but other negative values are not
334                         if (negative && (number > 0)) {
335                                 if (!tryParse)
336                                         exc = new OverflowException (
337                                             Locale.GetText ("Negative number"));
338                                 return false;
339                         }
340
341                         result = number;
342                         return true;
343                 }
344
345                 [CLSCompliant (false)]
346                 public static ulong Parse (string s, NumberStyles style, IFormatProvider fp) 
347                 {
348                         Exception exc;
349                         ulong res;
350
351                         if (!Parse (s, style, fp, false, out res, out exc))
352                                 throw exc;
353
354                         return res;
355                 }
356
357
358 #if NET_2_0
359                 [CLSCompliant (false)]
360                 public static bool TryParse (string s, out ulong result) 
361                 {
362                         Exception exc;
363                         if (!Parse (s, NumberStyles.Integer, null, true, out result, out exc)) {
364                                 result = 0;
365                                 return false;
366                         }
367
368                         return true;
369                 }
370
371                 [CLSCompliant (false)]
372                 public static bool TryParse (string s, NumberStyles style, IFormatProvider provider, out ulong result) 
373                 {
374                         Exception exc;
375                         if (!Parse (s, style, provider, true, out result, out exc)) {
376                                 result = 0;
377                                 return false;
378                         }
379
380                         return true;
381                 }
382 #endif
383
384                 public override string ToString ()
385                 {
386                         return new NumberFormatter(null, m_value).FormatDecimal(-1, null);
387                 }
388
389                 public string ToString (IFormatProvider provider)
390                 {
391                         NumberFormatInfo nfi = NumberFormatInfo.GetInstance (provider);
392                         return new NumberFormatter(null, m_value).FormatDecimal(-1, nfi);
393                 }
394
395                 public string ToString (string format)
396                 {
397                         return ToString (format, null);
398                 }
399
400                 public string ToString (string format, IFormatProvider provider)
401                 {
402                         NumberFormatInfo nfi = NumberFormatInfo.GetInstance (provider);
403                         return NumberFormatter.NumberToString (format, m_value, nfi);
404                 }
405
406                 // =========== IConvertible Methods =========== //
407                 public TypeCode GetTypeCode ()
408                 {
409                         return TypeCode.UInt64;
410                 }
411
412                 bool IConvertible.ToBoolean (IFormatProvider provider)
413                 {
414                         return System.Convert.ToBoolean (m_value);
415                 }
416
417                 byte IConvertible.ToByte (IFormatProvider provider)
418                 {
419                         return System.Convert.ToByte (m_value);
420                 }
421
422                 char IConvertible.ToChar (IFormatProvider provider)
423                 {
424                         return System.Convert.ToChar (m_value);
425                 }
426
427                 DateTime IConvertible.ToDateTime (IFormatProvider provider)
428                 {
429                         return System.Convert.ToDateTime (m_value);
430                 }
431
432                 decimal IConvertible.ToDecimal (IFormatProvider provider)
433                 {
434                         return System.Convert.ToDecimal (m_value);
435                 }
436
437                 double IConvertible.ToDouble (IFormatProvider provider)
438                 {
439                         return System.Convert.ToDouble (m_value);
440                 }
441
442                 short IConvertible.ToInt16 (IFormatProvider provider)
443                 {
444                         return System.Convert.ToInt16 (m_value);
445                 }
446
447                 int IConvertible.ToInt32 (IFormatProvider provider)
448                 {
449                         return System.Convert.ToInt32 (m_value);
450                 }
451
452                 long IConvertible.ToInt64 (IFormatProvider provider)
453                 {
454                         return System.Convert.ToInt64 (m_value);
455                 }
456
457                 sbyte IConvertible.ToSByte(IFormatProvider provider)
458                 {
459                         return System.Convert.ToSByte (m_value);
460                 }
461
462                 float IConvertible.ToSingle (IFormatProvider provider)
463                 {
464                         return System.Convert.ToSingle (m_value);
465                 }
466
467                 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
468                 {
469                         return System.Convert.ToType (m_value, conversionType, provider);
470                 }
471
472                 ushort IConvertible.ToUInt16 (IFormatProvider provider)
473                 {
474                         return System.Convert.ToUInt16 (m_value);
475                 }
476
477                 uint IConvertible.ToUInt32 (IFormatProvider provider)
478                 {
479                         return System.Convert.ToUInt32 (m_value);
480                 }
481
482                 ulong IConvertible.ToUInt64 (IFormatProvider provider)
483                 {
484                         return m_value;
485                 }
486         }
487 }