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