5 // Miguel de Icaza (miguel@ximian.com)
6 // Marek Safar (marek.safar@gmail.com)
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)
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:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
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.
32 using System.Globalization;
33 using System.Threading;
38 [System.Runtime.InteropServices.ComVisible (true)]
39 public struct Int32 : IFormattable, IConvertible, IComparable, IComparable<Int32>, IEquatable <Int32>
42 public const int MaxValue = 0x7fffffff;
43 public const int MinValue = -2147483648;
45 // This field is looked up by name in the runtime
48 public int CompareTo (object value)
53 if (!(value is System.Int32))
54 throw new ArgumentException (Locale.GetText ("Value is not a System.Int32"));
65 public override bool Equals (object obj)
67 if (!(obj is System.Int32))
70 return ((int) obj) == m_value;
73 public override int GetHashCode ()
78 public int CompareTo (int value)
88 public bool Equals (int obj)
90 return obj == m_value;
93 internal static bool ProcessTrailingWhitespace (bool tryParse, string s, int position, ref Exception exc)
97 for (int i = position; i < len; i++){
100 if (c != 0 && !Char.IsWhiteSpace (c)){
102 exc = GetFormatException ();
109 internal static bool Parse (string s, bool tryParse, out int result, out Exception exc)
114 bool digits_seen = false;
118 NumberFormatInfo nfi = Thread.CurrentThread.CurrentCulture.NumberFormat;
122 exc = new ArgumentNullException ("s");
129 for (i = 0; i < len; i++){
131 if (!Char.IsWhiteSpace (c))
137 exc = GetFormatException ();
141 var ps_length = nfi.PositiveSign.Length;
142 var ns_length = nfi.NegativeSign.Length;
143 if (len > ps_length && string.CompareOrdinalUnchecked (s, i, ns_length, nfi.PositiveSign, 0, ps_length) == 0)
145 else if (len > ns_length && string.CompareOrdinalUnchecked (s, i, ns_length, nfi.NegativeSign, 0, ns_length) == 0) {
150 for (; i < len; i++){
158 if (c >= '0' && c <= '9'){
159 byte d = (byte) (c - '0');
161 if (val > (MaxValue/10))
164 if (val == (MaxValue/10)){
165 if ((d > (MaxValue % 10)) && (sign == 1 || (d > ((MaxValue % 10) + 1))))
168 val = (val * sign * 10) - d;
170 val = (val * 10) + d;
172 if (ProcessTrailingWhitespace (tryParse, s, i + 1, ref exc)){
181 } else if (!ProcessTrailingWhitespace (tryParse, s, i, ref exc))
186 exc = GetFormatException ();
199 exc = new OverflowException ("Value is too large");
203 public static int Parse (string s, IFormatProvider provider)
205 return Parse (s, NumberStyles.Integer, provider);
208 public static int Parse (string s, NumberStyles style)
210 return Parse (s, style, null);
213 internal static bool CheckStyle (NumberStyles style, bool tryParse, ref Exception exc)
215 if ((style & NumberStyles.AllowHexSpecifier) != 0) {
216 NumberStyles ne = style ^ NumberStyles.AllowHexSpecifier;
217 if ((ne & NumberStyles.AllowLeadingWhite) != 0)
218 ne ^= NumberStyles.AllowLeadingWhite;
219 if ((ne & NumberStyles.AllowTrailingWhite) != 0)
220 ne ^= NumberStyles.AllowTrailingWhite;
223 exc = new ArgumentException (
224 "With AllowHexSpecifier only " +
225 "AllowLeadingWhite and AllowTrailingWhite " +
229 } else if ((uint) style > (uint) NumberStyles.Any){
231 exc = new ArgumentException ("Not a valid number style");
238 internal static bool JumpOverWhite (ref int pos, string s, bool reportError, bool tryParse, ref Exception exc)
240 while (pos < s.Length && Char.IsWhiteSpace (s [pos]))
243 if (reportError && pos >= s.Length) {
245 exc = GetFormatException ();
252 internal static void FindSign (ref int pos, string s, NumberFormatInfo nfi,
253 ref bool foundSign, ref bool negative)
255 if ((pos + nfi.NegativeSign.Length) <= s.Length &&
256 s.IndexOfOrdinalUnchecked (nfi.NegativeSign, pos, nfi.NegativeSign.Length) == pos) {
259 pos += nfi.NegativeSign.Length;
260 } else if ((pos + nfi.PositiveSign.Length) <= s.Length &&
261 s.IndexOfOrdinalUnchecked (nfi.PositiveSign, pos, nfi.PositiveSign.Length) == pos) {
263 pos += nfi.PositiveSign.Length;
268 internal static void FindCurrency (ref int pos,
270 NumberFormatInfo nfi,
271 ref bool foundCurrency)
273 if ((pos + nfi.CurrencySymbol.Length) <= s.Length &&
274 s.Substring (pos, nfi.CurrencySymbol.Length) == nfi.CurrencySymbol) {
275 foundCurrency = true;
276 pos += nfi.CurrencySymbol.Length;
280 internal static bool FindExponent (ref int pos, string s, ref int exponent, bool tryParse, ref Exception exc)
285 if (pos >= s.Length || (s [pos] != 'e' && s[pos] != 'E')) {
292 exc = tryParse ? null : GetFormatException ();
296 neg = (s [i] == '-');
300 if (s [i] == '+' && ++i == s.Length) {
301 exc = tryParse ? null : GetFormatException ();
305 long exp = 0; // temp long value
306 for (; i < s.Length; i++) {
307 if (!Char.IsDigit (s [i])) {
308 exc = tryParse ? null : GetFormatException ();
312 // Reduce the risk of throwing an overflow exc
313 exp = checked (exp * 10 - (int) (s [i] - '0'));
314 if (exp < Int32.MinValue || exp > Int32.MaxValue) {
315 exc = tryParse ? null : new OverflowException ("Value too large or too small.");
320 // exp value saved as negative, and neg tracks whether we had a negative
330 internal static bool FindOther (ref int pos,
334 if ((pos + other.Length) <= s.Length &&
335 s.Substring (pos, other.Length) == other) {
343 internal static bool ValidDigit (char e, bool allowHex)
345 return (e >= '0' && e <= '9') || (allowHex && ((e >= 'A' && e <= 'F') || (e >= 'a' && e <= 'f')));
348 internal static Exception GetFormatException ()
350 return new FormatException ("Input string was not in the correct format");
353 internal static bool Parse (string s, NumberStyles style, IFormatProvider fp, bool tryParse, out int result, out Exception exc)
360 exc = new ArgumentNullException ("s");
366 exc = GetFormatException ();
370 NumberFormatInfo nfi = null;
372 Type typeNFI = typeof (System.Globalization.NumberFormatInfo);
373 nfi = fp.GetFormat (typeNFI) as NumberFormatInfo;
376 nfi = Thread.CurrentThread.CurrentCulture.NumberFormat;
378 if (!CheckStyle (style, tryParse, ref exc))
381 bool AllowCurrencySymbol = (style & NumberStyles.AllowCurrencySymbol) != 0;
382 bool AllowHexSpecifier = (style & NumberStyles.AllowHexSpecifier) != 0;
383 bool AllowThousands = (style & NumberStyles.AllowThousands) != 0;
384 bool AllowDecimalPoint = (style & NumberStyles.AllowDecimalPoint) != 0;
385 bool AllowParentheses = (style & NumberStyles.AllowParentheses) != 0;
386 bool AllowTrailingSign = (style & NumberStyles.AllowTrailingSign) != 0;
387 bool AllowLeadingSign = (style & NumberStyles.AllowLeadingSign) != 0;
388 bool AllowTrailingWhite = (style & NumberStyles.AllowTrailingWhite) != 0;
389 bool AllowLeadingWhite = (style & NumberStyles.AllowLeadingWhite) != 0;
390 bool AllowExponent = (style & NumberStyles.AllowExponent) != 0;
394 if (AllowLeadingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
397 bool foundOpenParentheses = false;
398 bool negative = false;
399 bool foundSign = false;
400 bool foundCurrency = false;
403 if (AllowParentheses && s [pos] == '(') {
404 foundOpenParentheses = true;
406 negative = true; // MS always make the number negative when there parentheses
407 // even when NumberFormatInfo.NumberNegativePattern != 0!!!
409 if (AllowLeadingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
412 if (s.Substring (pos, nfi.NegativeSign.Length) == nfi.NegativeSign) {
414 exc = GetFormatException ();
418 if (s.Substring (pos, nfi.PositiveSign.Length) == nfi.PositiveSign) {
420 exc = GetFormatException ();
425 if (AllowLeadingSign && !foundSign) {
427 FindSign (ref pos, s, nfi, ref foundSign, ref negative);
429 if (AllowLeadingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
431 if (AllowCurrencySymbol) {
432 FindCurrency (ref pos, s, nfi,
434 if (foundCurrency && AllowLeadingWhite &&
435 !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
441 if (AllowCurrencySymbol && !foundCurrency) {
443 FindCurrency (ref pos, s, nfi, ref foundCurrency);
445 if (AllowLeadingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
448 if (!foundSign && AllowLeadingSign) {
449 FindSign (ref pos, s, nfi, ref foundSign,
451 if (foundSign && AllowLeadingWhite &&
452 !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
461 int decimalPointPos = -1;
466 while (pos < s.Length) {
468 if (!ValidDigit (s [pos], AllowHexSpecifier)) {
469 if (AllowThousands &&
470 (FindOther (ref pos, s, nfi.NumberGroupSeparator)
471 || FindOther (ref pos, s, nfi.CurrencyGroupSeparator)))
474 if (AllowDecimalPoint && decimalPointPos < 0 &&
475 (FindOther (ref pos, s, nfi.NumberDecimalSeparator)
476 || FindOther (ref pos, s, nfi.CurrencyDecimalSeparator))) {
477 decimalPointPos = nDigits;
486 if (AllowHexSpecifier) {
487 hexDigit = s [pos++];
488 if (Char.IsDigit (hexDigit))
489 digitValue = (int) (hexDigit - '0');
490 else if (Char.IsLower (hexDigit))
491 digitValue = (int) (hexDigit - 'a' + 10);
493 digitValue = (int) (hexDigit - 'A' + 10);
495 uint unumber = (uint)number;
497 if ((unumber & 0xf0000000) != 0)
500 number = (int) (unumber * 16u + (uint) digitValue);
502 number = (int)checked (unumber * 16u + (uint)digitValue);
509 // Calculations done as negative
510 // (abs (MinValue) > abs (MaxValue))
511 number = checked (number * 10 - (int) (s [pos++] - '0'));
512 } catch (OverflowException) {
514 exc = new OverflowException ("Value too large or too small.");
522 exc = GetFormatException ();
528 if (FindExponent (ref pos, s, ref exponent, tryParse, ref exc) && exc != null)
531 if (AllowTrailingSign && !foundSign) {
533 FindSign (ref pos, s, nfi, ref foundSign, ref negative);
534 if (foundSign && pos < s.Length) {
535 if (AllowTrailingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
540 if (AllowCurrencySymbol && !foundCurrency) {
541 if (AllowTrailingWhite && pos < s.Length && !JumpOverWhite (ref pos, s, false, tryParse, ref exc))
545 FindCurrency (ref pos, s, nfi, ref foundCurrency);
546 if (foundCurrency && pos < s.Length) {
547 if (AllowTrailingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
549 if (!foundSign && AllowTrailingSign)
550 FindSign (ref pos, s, nfi, ref foundSign,
555 if (AllowTrailingWhite && pos < s.Length && !JumpOverWhite (ref pos, s, false, tryParse, ref exc))
558 if (foundOpenParentheses) {
559 if (pos >= s.Length || s [pos++] != ')') {
561 exc = GetFormatException ();
564 if (AllowTrailingWhite && pos < s.Length && !JumpOverWhite (ref pos, s, false, tryParse, ref exc))
568 if (pos < s.Length && s [pos] != '\u0000') {
570 exc = GetFormatException ();
574 if (!negative && !AllowHexSpecifier){
576 long lval = -((long)number);
578 if (lval < MinValue || lval > MaxValue)
582 number = checked (-number);
585 if (decimalPointPos >= 0)
586 exponent = exponent - nDigits + decimalPointPos;
590 // Any non-zero values after decimal point are not allowed
593 number = Math.DivRem (number, (int) Math.Pow (10, -exponent), out remainder);
594 if (remainder != 0) {
596 exc = new OverflowException ("Value too large or too small.");
599 } else if (exponent > 0) {
601 // result *= 10^exponent
603 // Reduce the risk of throwing an overflow exc
605 double res = checked (Math.Pow (10, exponent) * number);
606 if (res < MinValue || res > MaxValue) {
608 exc = new OverflowException ("Value too large or too small.");
619 public static int Parse (string s)
624 if (!Parse (s, false, out res, out exc))
630 public static int Parse (string s, NumberStyles style, IFormatProvider provider)
635 if (!Parse (s, style, provider, false, out res, out exc))
641 public static bool TryParse (string s, out int result)
644 if (!Parse (s, true, out result, out exc)) {
652 public static bool TryParse (string s, NumberStyles style, IFormatProvider provider, out int result)
655 if (!Parse (s, style, provider, true, out result, out exc)) {
663 public override string ToString ()
665 return NumberFormatter.NumberToString (m_value, null);
668 public string ToString (IFormatProvider provider)
670 return NumberFormatter.NumberToString (m_value, provider);
673 public string ToString (string format)
675 return ToString (format, null);
678 public string ToString (string format, IFormatProvider provider)
680 return NumberFormatter.NumberToString (format, m_value, provider);
683 // =========== IConvertible Methods =========== //
685 public TypeCode GetTypeCode ()
687 return TypeCode.Int32;
690 bool IConvertible.ToBoolean (IFormatProvider provider)
692 return System.Convert.ToBoolean (m_value);
695 byte IConvertible.ToByte (IFormatProvider provider)
697 return System.Convert.ToByte (m_value);
700 char IConvertible.ToChar (IFormatProvider provider)
702 return System.Convert.ToChar (m_value);
705 DateTime IConvertible.ToDateTime (IFormatProvider provider)
707 return System.Convert.ToDateTime (m_value);
710 decimal IConvertible.ToDecimal (IFormatProvider provider)
712 return System.Convert.ToDecimal (m_value);
715 double IConvertible.ToDouble (IFormatProvider provider)
717 return System.Convert.ToDouble (m_value);
720 short IConvertible.ToInt16 (IFormatProvider provider)
722 return System.Convert.ToInt16 (m_value);
725 int IConvertible.ToInt32 (IFormatProvider provider)
730 long IConvertible.ToInt64 (IFormatProvider provider)
732 return System.Convert.ToInt64 (m_value);
735 sbyte IConvertible.ToSByte (IFormatProvider provider)
737 return System.Convert.ToSByte (m_value);
740 float IConvertible.ToSingle (IFormatProvider provider)
742 return System.Convert.ToSingle (m_value);
745 object IConvertible.ToType (Type targetType, IFormatProvider provider)
747 if (targetType == null)
748 throw new ArgumentNullException ("targetType");
749 return System.Convert.ToType (m_value, targetType, provider, false);
752 ushort IConvertible.ToUInt16 (IFormatProvider provider)
754 return System.Convert.ToUInt16 (m_value);
757 uint IConvertible.ToUInt32 (IFormatProvider provider)
759 return System.Convert.ToUInt32 (m_value);
762 ulong IConvertible.ToUInt64 (IFormatProvider provider)
764 return System.Convert.ToUInt64 (m_value);