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)
284 if (pos >= s.Length || (s [pos] != 'e' && s[pos] != 'E')) {
291 exc = tryParse ? null : GetFormatException ();
295 // negative exponent not valid for Int32
297 exc = tryParse ? null : new OverflowException ("Value too large or too small.");
301 if (s [i] == '+' && ++i == s.Length) {
302 exc = tryParse ? null : GetFormatException ();
306 long exp = 0; // temp long value
307 for (; i < s.Length; i++) {
308 if (!Char.IsDigit (s [i])) {
309 exc = tryParse ? null : GetFormatException ();
313 // Reduce the risk of throwing an overflow exc
314 exp = checked (exp * 10 - (int) (s [i] - '0'));
315 if (exp < Int32.MinValue || exp > Int32.MaxValue) {
316 exc = tryParse ? null : new OverflowException ("Value too large or too small.");
321 // exp value saved as 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)
346 return Char.IsDigit (e) || (e >= 'A' && e <= 'F') || (e >= 'a' && e <= 'f');
348 return Char.IsDigit (e);
351 internal static Exception GetFormatException ()
353 return new FormatException ("Input string was not in the correct format");
356 internal static bool Parse (string s, NumberStyles style, IFormatProvider fp, bool tryParse, out int result, out Exception exc)
363 exc = new ArgumentNullException ("s");
369 exc = GetFormatException ();
373 NumberFormatInfo nfi = null;
375 Type typeNFI = typeof (System.Globalization.NumberFormatInfo);
376 nfi = (NumberFormatInfo) fp.GetFormat (typeNFI);
379 nfi = Thread.CurrentThread.CurrentCulture.NumberFormat;
381 if (!CheckStyle (style, tryParse, ref exc))
384 bool AllowCurrencySymbol = (style & NumberStyles.AllowCurrencySymbol) != 0;
385 bool AllowHexSpecifier = (style & NumberStyles.AllowHexSpecifier) != 0;
386 bool AllowThousands = (style & NumberStyles.AllowThousands) != 0;
387 bool AllowDecimalPoint = (style & NumberStyles.AllowDecimalPoint) != 0;
388 bool AllowParentheses = (style & NumberStyles.AllowParentheses) != 0;
389 bool AllowTrailingSign = (style & NumberStyles.AllowTrailingSign) != 0;
390 bool AllowLeadingSign = (style & NumberStyles.AllowLeadingSign) != 0;
391 bool AllowTrailingWhite = (style & NumberStyles.AllowTrailingWhite) != 0;
392 bool AllowLeadingWhite = (style & NumberStyles.AllowLeadingWhite) != 0;
393 bool AllowExponent = (style & NumberStyles.AllowExponent) != 0;
397 if (AllowLeadingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
400 bool foundOpenParentheses = false;
401 bool negative = false;
402 bool foundSign = false;
403 bool foundCurrency = false;
406 if (AllowParentheses && s [pos] == '(') {
407 foundOpenParentheses = true;
409 negative = true; // MS always make the number negative when there parentheses
410 // even when NumberFormatInfo.NumberNegativePattern != 0!!!
412 if (AllowLeadingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
415 if (s.Substring (pos, nfi.NegativeSign.Length) == nfi.NegativeSign) {
417 exc = GetFormatException ();
421 if (s.Substring (pos, nfi.PositiveSign.Length) == nfi.PositiveSign) {
423 exc = GetFormatException ();
428 if (AllowLeadingSign && !foundSign) {
430 FindSign (ref pos, s, nfi, ref foundSign, ref negative);
432 if (AllowLeadingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
434 if (AllowCurrencySymbol) {
435 FindCurrency (ref pos, s, nfi,
437 if (foundCurrency && AllowLeadingWhite &&
438 !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
444 if (AllowCurrencySymbol && !foundCurrency) {
446 FindCurrency (ref pos, s, nfi, ref foundCurrency);
448 if (AllowLeadingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
451 if (!foundSign && AllowLeadingSign) {
452 FindSign (ref pos, s, nfi, ref foundSign,
454 if (foundSign && AllowLeadingWhite &&
455 !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
464 int decimalPointPos = -1;
469 while (pos < s.Length) {
471 if (!ValidDigit (s [pos], AllowHexSpecifier)) {
472 if (AllowThousands &&
473 (FindOther (ref pos, s, nfi.NumberGroupSeparator)
474 || FindOther (ref pos, s, nfi.CurrencyGroupSeparator)))
477 if (AllowDecimalPoint && decimalPointPos < 0 &&
478 (FindOther (ref pos, s, nfi.NumberDecimalSeparator)
479 || FindOther (ref pos, s, nfi.CurrencyDecimalSeparator))) {
480 decimalPointPos = nDigits;
489 if (AllowHexSpecifier) {
490 hexDigit = s [pos++];
491 if (Char.IsDigit (hexDigit))
492 digitValue = (int) (hexDigit - '0');
493 else if (Char.IsLower (hexDigit))
494 digitValue = (int) (hexDigit - 'a' + 10);
496 digitValue = (int) (hexDigit - 'A' + 10);
498 uint unumber = (uint)number;
500 if ((unumber & 0xf0000000) != 0)
503 number = (int) (unumber * 16u + (uint) digitValue);
505 number = (int)checked (unumber * 16u + (uint)digitValue);
512 // Calculations done as negative
513 // (abs (MinValue) > abs (MaxValue))
514 number = checked (number * 10 - (int) (s [pos++] - '0'));
515 } catch (OverflowException) {
517 exc = new OverflowException ("Value too large or too small.");
525 exc = GetFormatException ();
531 if (FindExponent (ref pos, s, ref exponent, tryParse, ref exc) && exc != null)
534 if (AllowTrailingSign && !foundSign) {
536 FindSign (ref pos, s, nfi, ref foundSign, ref negative);
537 if (foundSign && pos < s.Length) {
538 if (AllowTrailingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
543 if (AllowCurrencySymbol && !foundCurrency) {
544 if (AllowTrailingWhite && pos < s.Length && !JumpOverWhite (ref pos, s, false, tryParse, ref exc))
548 FindCurrency (ref pos, s, nfi, ref foundCurrency);
549 if (foundCurrency && pos < s.Length) {
550 if (AllowTrailingWhite && !JumpOverWhite (ref pos, s, true, tryParse, ref exc))
552 if (!foundSign && AllowTrailingSign)
553 FindSign (ref pos, s, nfi, ref foundSign,
558 if (AllowTrailingWhite && pos < s.Length && !JumpOverWhite (ref pos, s, false, tryParse, ref exc))
561 if (foundOpenParentheses) {
562 if (pos >= s.Length || s [pos++] != ')') {
564 exc = GetFormatException ();
567 if (AllowTrailingWhite && pos < s.Length && !JumpOverWhite (ref pos, s, false, tryParse, ref exc))
571 if (pos < s.Length && s [pos] != '\u0000') {
573 exc = GetFormatException ();
577 if (!negative && !AllowHexSpecifier){
579 long lval = -((long)number);
581 if (lval < MinValue || lval > MaxValue)
585 number = checked (-number);
588 if (decimalPointPos >= 0)
589 exponent = exponent - nDigits + decimalPointPos;
593 // Any non-zero values after decimal point are not allowed
596 number = Math.DivRem (number, (int) Math.Pow (10, -exponent), out remainder);
597 if (remainder != 0) {
599 exc = new OverflowException ("Value too large or too small.");
602 } else if (exponent > 0) {
604 // result *= 10^exponent
606 // Reduce the risk of throwing an overflow exc
608 double res = checked (Math.Pow (10, exponent) * number);
609 if (res < MinValue || res > MaxValue) {
611 exc = new OverflowException ("Value too large or too small.");
622 public static int Parse (string s)
627 if (!Parse (s, false, out res, out exc))
633 public static int Parse (string s, NumberStyles style, IFormatProvider provider)
638 if (!Parse (s, style, provider, false, out res, out exc))
644 public static bool TryParse (string s, out int result)
647 if (!Parse (s, true, out result, out exc)) {
655 public static bool TryParse (string s, NumberStyles style, IFormatProvider provider, out int result)
658 if (!Parse (s, style, provider, true, out result, out exc)) {
666 public override string ToString ()
668 return NumberFormatter.NumberToString (m_value, null);
671 public string ToString (IFormatProvider provider)
673 return NumberFormatter.NumberToString (m_value, provider);
676 public string ToString (string format)
678 return ToString (format, null);
681 public string ToString (string format, IFormatProvider provider)
683 return NumberFormatter.NumberToString (format, m_value, provider);
686 // =========== IConvertible Methods =========== //
688 public TypeCode GetTypeCode ()
690 return TypeCode.Int32;
693 bool IConvertible.ToBoolean (IFormatProvider provider)
695 return System.Convert.ToBoolean (m_value);
698 byte IConvertible.ToByte (IFormatProvider provider)
700 return System.Convert.ToByte (m_value);
703 char IConvertible.ToChar (IFormatProvider provider)
705 return System.Convert.ToChar (m_value);
708 DateTime IConvertible.ToDateTime (IFormatProvider provider)
710 return System.Convert.ToDateTime (m_value);
713 decimal IConvertible.ToDecimal (IFormatProvider provider)
715 return System.Convert.ToDecimal (m_value);
718 double IConvertible.ToDouble (IFormatProvider provider)
720 return System.Convert.ToDouble (m_value);
723 short IConvertible.ToInt16 (IFormatProvider provider)
725 return System.Convert.ToInt16 (m_value);
728 int IConvertible.ToInt32 (IFormatProvider provider)
733 long IConvertible.ToInt64 (IFormatProvider provider)
735 return System.Convert.ToInt64 (m_value);
738 sbyte IConvertible.ToSByte (IFormatProvider provider)
740 return System.Convert.ToSByte (m_value);
743 float IConvertible.ToSingle (IFormatProvider provider)
745 return System.Convert.ToSingle (m_value);
748 object IConvertible.ToType (Type targetType, IFormatProvider provider)
750 if (targetType == null)
751 throw new ArgumentNullException ("targetType");
752 return System.Convert.ToType (m_value, targetType, provider, false);
755 ushort IConvertible.ToUInt16 (IFormatProvider provider)
757 return System.Convert.ToUInt16 (m_value);
760 uint IConvertible.ToUInt32 (IFormatProvider provider)
762 return System.Convert.ToUInt32 (m_value);
765 ulong IConvertible.ToUInt64 (IFormatProvider provider)
767 return System.Convert.ToUInt64 (m_value);