| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659 |
- // Licensed to the .NET Foundation under one or more agreements.
- // The .NET Foundation licenses this file to you under the MIT license.
- // See the LICENSE file in the project root for more information.
- using System.Diagnostics;
- using System.Runtime.CompilerServices;
- namespace System
- {
- /// <summary>Methods for parsing numbers and strings.</summary>
- internal static class ParseNumbers
- {
- internal const int LeftAlign = 0x0001;
- internal const int RightAlign = 0x0004;
- internal const int PrefixSpace = 0x0008;
- internal const int PrintSign = 0x0010;
- internal const int PrintBase = 0x0020;
- internal const int PrintAsI1 = 0x0040;
- internal const int PrintAsI2 = 0x0080;
- internal const int PrintAsI4 = 0x0100;
- internal const int TreatAsUnsigned = 0x0200;
- internal const int TreatAsI1 = 0x0400;
- internal const int TreatAsI2 = 0x0800;
- internal const int IsTight = 0x1000;
- internal const int NoSpace = 0x2000;
- internal const int PrintRadixBase = 0x4000;
- private const int MinRadix = 2;
- private const int MaxRadix = 36;
- public static unsafe long StringToLong(ReadOnlySpan<char> s, int radix, int flags)
- {
- int pos = 0;
- return StringToLong(s, radix, flags, ref pos);
- }
- public static long StringToLong(ReadOnlySpan<char> s, int radix, int flags, ref int currPos)
- {
- int i = currPos;
- // Do some radix checking.
- // A radix of -1 says to use whatever base is spec'd on the number.
- // Parse in Base10 until we figure out what the base actually is.
- int r = (-1 == radix) ? 10 : radix;
- if (r != 2 && r != 10 && r != 8 && r != 16)
- throw new ArgumentException(SR.Arg_InvalidBase, nameof(radix));
- int length = s.Length;
- if (i < 0 || i >= length)
- throw new ArgumentOutOfRangeException(SR.ArgumentOutOfRange_Index);
- // Get rid of the whitespace and then check that we've still got some digits to parse.
- if (((flags & IsTight) == 0) && ((flags & NoSpace) == 0))
- {
- EatWhiteSpace(s, ref i);
- if (i == length)
- throw new FormatException(SR.Format_EmptyInputString);
- }
- // Check for a sign
- int sign = 1;
- if (s[i] == '-')
- {
- if (r != 10)
- throw new ArgumentException(SR.Arg_CannotHaveNegativeValue);
- if ((flags & TreatAsUnsigned) != 0)
- throw new OverflowException(SR.Overflow_NegativeUnsigned);
- sign = -1;
- i++;
- }
- else if (s[i] == '+')
- {
- i++;
- }
- if ((radix == -1 || radix == 16) && (i + 1 < length) && s[i] == '0')
- {
- if (s[i + 1] == 'x' || s[i + 1] == 'X')
- {
- r = 16;
- i += 2;
- }
- }
- int grabNumbersStart = i;
- long result = GrabLongs(r, s, ref i, (flags & TreatAsUnsigned) != 0);
- // Check if they passed us a string with no parsable digits.
- if (i == grabNumbersStart)
- throw new FormatException(SR.Format_NoParsibleDigits);
- if ((flags & IsTight) != 0)
- {
- //If we've got effluvia left at the end of the string, complain.
- if (i < length)
- throw new FormatException(SR.Format_ExtraJunkAtEnd);
- }
- // Put the current index back into the correct place.
- currPos = i;
- // Return the value properly signed.
- if ((ulong)result == 0x8000000000000000 && sign == 1 && r == 10 && ((flags & TreatAsUnsigned) == 0))
- Number.ThrowOverflowException(TypeCode.Int64);
- if (r == 10)
- {
- result *= sign;
- }
- return result;
- }
- public static int StringToInt(ReadOnlySpan<char> s, int radix, int flags)
- {
- int pos = 0;
- return StringToInt(s, radix, flags, ref pos);
- }
- public static int StringToInt(ReadOnlySpan<char> s, int radix, int flags, ref int currPos)
- {
- // They're requied to tell me where to start parsing.
- int i = currPos;
- // Do some radix checking.
- // A radix of -1 says to use whatever base is spec'd on the number.
- // Parse in Base10 until we figure out what the base actually is.
- int r = (-1 == radix) ? 10 : radix;
- if (r != 2 && r != 10 && r != 8 && r != 16)
- throw new ArgumentException(SR.Arg_InvalidBase, nameof(radix));
- int length = s.Length;
- if (i < 0 || i >= length)
- throw new ArgumentOutOfRangeException(SR.ArgumentOutOfRange_Index);
- // Get rid of the whitespace and then check that we've still got some digits to parse.
- if (((flags & IsTight) == 0) && ((flags & NoSpace) == 0))
- {
- EatWhiteSpace(s, ref i);
- if (i == length)
- throw new FormatException(SR.Format_EmptyInputString);
- }
- // Check for a sign
- int sign = 1;
- if (s[i] == '-')
- {
- if (r != 10)
- throw new ArgumentException(SR.Arg_CannotHaveNegativeValue);
- if ((flags & TreatAsUnsigned) != 0)
- throw new OverflowException(SR.Overflow_NegativeUnsigned);
- sign = -1;
- i++;
- }
- else if (s[i] == '+')
- {
- i++;
- }
- // Consume the 0x if we're in an unknown base or in base-16.
- if ((radix == -1 || radix == 16) && (i + 1 < length) && s[i] == '0')
- {
- if (s[i + 1] == 'x' || s[i + 1] == 'X')
- {
- r = 16;
- i += 2;
- }
- }
- int grabNumbersStart = i;
- int result = GrabInts(r, s, ref i, ((flags & TreatAsUnsigned) != 0));
- // Check if they passed us a string with no parsable digits.
- if (i == grabNumbersStart)
- throw new FormatException(SR.Format_NoParsibleDigits);
- if ((flags & IsTight) != 0)
- {
- // If we've got effluvia left at the end of the string, complain.
- if (i < length)
- throw new FormatException(SR.Format_ExtraJunkAtEnd);
- }
- // Put the current index back into the correct place.
- currPos = i;
- // Return the value properly signed.
- if ((flags & TreatAsI1) != 0)
- {
- if ((uint)result > 0xFF)
- Number.ThrowOverflowException(TypeCode.SByte);
- }
- else if ((flags & TreatAsI2) != 0)
- {
- if ((uint)result > 0xFFFF)
- Number.ThrowOverflowException(TypeCode.Int16);
- }
- else if ((uint)result == 0x80000000 && sign == 1 && r == 10 && ((flags & TreatAsUnsigned) == 0))
- {
- Number.ThrowOverflowException(TypeCode.Int32);
- }
- if (r == 10)
- {
- result *= sign;
- }
- return result;
- }
- public static string IntToString(int n, int radix, int width, char paddingChar, int flags)
- {
- Span<char> buffer = stackalloc char[66]; // Longest possible string length for an integer in binary notation with prefix
- if (radix < MinRadix || radix > MaxRadix)
- throw new ArgumentException(SR.Arg_InvalidBase, nameof(radix));
- // If the number is negative, make it positive and remember the sign.
- // If the number is MIN_VALUE, this will still be negative, so we'll have to
- // special case this later.
- bool isNegative = false;
- uint l;
- if (n < 0)
- {
- isNegative = true;
- // For base 10, write out -num, but other bases write out the
- // 2's complement bit pattern
- l = (10 == radix) ? (uint)-n : (uint)n;
- }
- else
- {
- l = (uint)n;
- }
- // The conversion to a uint will sign extend the number. In order to ensure
- // that we only get as many bits as we expect, we chop the number.
- if ((flags & PrintAsI1) != 0)
- {
- l &= 0xFF;
- }
- else if ((flags & PrintAsI2) != 0)
- {
- l &= 0xFFFF;
- }
- // Special case the 0.
- int index;
- if (0 == l)
- {
- buffer[0] = '0';
- index = 1;
- }
- else
- {
- index = 0;
- for (int i = 0; i < buffer.Length; i++) // for(...;i<buffer.Length;...) loop instead of do{...}while(l!=0) to help JIT eliminate span bounds checks
- {
- uint div = l / (uint)radix; // TODO https://github.com/dotnet/coreclr/issues/3439
- uint charVal = l - (div * (uint)radix);
- l = div;
- buffer[i] = (charVal < 10) ?
- (char)(charVal + '0') :
- (char)(charVal + 'a' - 10);
- if (l == 0)
- {
- index = i + 1;
- break;
- }
- }
- Debug.Assert(l == 0, $"Expected {l} == 0");
- }
- // If they want the base, append that to the string (in reverse order)
- if (radix != 10 && ((flags & PrintBase) != 0))
- {
- if (16 == radix)
- {
- buffer[index++] = 'x';
- buffer[index++] = '0';
- }
- else if (8 == radix)
- {
- buffer[index++] = '0';
- }
- }
- if (10 == radix)
- {
- // If it was negative, append the sign, else if they requested, add the '+'.
- // If they requested a leading space, put it on.
- if (isNegative)
- {
- buffer[index++] = '-';
- }
- else if ((flags & PrintSign) != 0)
- {
- buffer[index++] = '+';
- }
- else if ((flags & PrefixSpace) != 0)
- {
- buffer[index++] = ' ';
- }
- }
- // Figure out the size of and allocate the resulting string
- string result = string.FastAllocateString(Math.Max(width, index));
- unsafe
- {
- // Put the characters into the string in reverse order.
- // Fill the remaining space, if there is any, with the correct padding character.
- fixed (char* resultPtr = result)
- {
- char* p = resultPtr;
- int padding = result.Length - index;
- if ((flags & LeftAlign) != 0)
- {
- for (int i = 0; i < padding; i++)
- {
- *p++ = paddingChar;
- }
- for (int i = 0; i < index; i++)
- {
- *p++ = buffer[index - i - 1];
- }
- }
- else
- {
- for (int i = 0; i < index; i++)
- {
- *p++ = buffer[index - i - 1];
- }
- for (int i = 0; i < padding; i++)
- {
- *p++ = paddingChar;
- }
- }
- Debug.Assert((p - resultPtr) == result.Length, $"Expected {p - resultPtr} == {result.Length}");
- }
- }
- return result;
- }
- public static string LongToString(long n, int radix, int width, char paddingChar, int flags)
- {
- Span<char> buffer = stackalloc char[67]; // Longest possible string length for an integer in binary notation with prefix
- if (radix < MinRadix || radix > MaxRadix)
- throw new ArgumentException(SR.Arg_InvalidBase, nameof(radix));
- //If the number is negative, make it positive and remember the sign.
- ulong ul;
- bool isNegative = false;
- if (n < 0)
- {
- isNegative = true;
- // For base 10, write out -num, but other bases write out the
- // 2's complement bit pattern
- ul = (10 == radix) ? (ulong)(-n) : (ulong)n;
- }
- else
- {
- ul = (ulong)n;
- }
- if ((flags & PrintAsI1) != 0)
- {
- ul = ul & 0xFF;
- }
- else if ((flags & PrintAsI2) != 0)
- {
- ul = ul & 0xFFFF;
- }
- else if ((flags & PrintAsI4) != 0)
- {
- ul = ul & 0xFFFFFFFF;
- }
- //Special case the 0.
- int index;
- if (0 == ul)
- {
- buffer[0] = '0';
- index = 1;
- }
- else
- {
- index = 0;
- for (int i = 0; i < buffer.Length; i++) // for loop instead of do{...}while(l!=0) to help JIT eliminate span bounds checks
- {
- ulong div = ul / (ulong)radix; // TODO https://github.com/dotnet/coreclr/issues/3439
- int charVal = (int)(ul - (div * (ulong)radix));
- ul = div;
- buffer[i] = (charVal < 10) ?
- (char)(charVal + '0') :
- (char)(charVal + 'a' - 10);
- if (ul == 0)
- {
- index = i + 1;
- break;
- }
- }
- Debug.Assert(ul == 0, $"Expected {ul} == 0");
- }
- //If they want the base, append that to the string (in reverse order)
- if (radix != 10 && ((flags & PrintBase) != 0))
- {
- if (16 == radix)
- {
- buffer[index++] = 'x';
- buffer[index++] = '0';
- }
- else if (8 == radix)
- {
- buffer[index++] = '0';
- }
- else if ((flags & PrintRadixBase) != 0)
- {
- buffer[index++] = '#';
- buffer[index++] = (char)((radix % 10) + '0');
- buffer[index++] = (char)((radix / 10) + '0');
- }
- }
- if (10 == radix)
- {
- //If it was negative, append the sign.
- if (isNegative)
- {
- buffer[index++] = '-';
- }
- //else if they requested, add the '+';
- else if ((flags & PrintSign) != 0)
- {
- buffer[index++] = '+';
- }
- //If they requested a leading space, put it on.
- else if ((flags & PrefixSpace) != 0)
- {
- buffer[index++] = ' ';
- }
- }
- // Figure out the size of and allocate the resulting string
- string result = string.FastAllocateString(Math.Max(width, index));
- unsafe
- {
- // Put the characters into the string in reverse order.
- // Fill the remaining space, if there is any, with the correct padding character.
- fixed (char* resultPtr = result)
- {
- char* p = resultPtr;
- int padding = result.Length - index;
- if ((flags & LeftAlign) != 0)
- {
- for (int i = 0; i < padding; i++)
- {
- *p++ = paddingChar;
- }
- for (int i = 0; i < index; i++)
- {
- *p++ = buffer[index - i - 1];
- }
- }
- else
- {
- for (int i = 0; i < index; i++)
- {
- *p++ = buffer[index - i - 1];
- }
- for (int i = 0; i < padding; i++)
- {
- *p++ = paddingChar;
- }
- }
- Debug.Assert((p - resultPtr) == result.Length, $"Expected {p - resultPtr} == {result.Length}");
- }
- }
- return result;
- }
- private static void EatWhiteSpace(ReadOnlySpan<char> s, ref int i)
- {
- int localIndex = i;
- for (; localIndex < s.Length && char.IsWhiteSpace(s[localIndex]); localIndex++);
- i = localIndex;
- }
- private static long GrabLongs(int radix, ReadOnlySpan<char> s, ref int i, bool isUnsigned)
- {
- ulong result = 0;
- ulong maxVal;
- // Allow all non-decimal numbers to set the sign bit.
- if (radix == 10 && !isUnsigned)
- {
- maxVal = 0x7FFFFFFFFFFFFFFF / 10;
- // Read all of the digits and convert to a number
- while (i < s.Length && IsDigit(s[i], radix, out int value))
- {
- // Check for overflows - this is sufficient & correct.
- if (result > maxVal || ((long)result) < 0)
- {
- Number.ThrowOverflowException(TypeCode.Int64);
- }
- result = result * (ulong)radix + (ulong)value;
- i++;
- }
- if ((long)result < 0 && result != 0x8000000000000000)
- {
- Number.ThrowOverflowException(TypeCode.Int64);
- }
- }
- else
- {
- Debug.Assert(radix == 2 || radix == 8 || radix == 10 || radix == 16);
- maxVal =
- radix == 10 ? 0xffffffffffffffff / 10 :
- radix == 16 ? 0xffffffffffffffff / 16 :
- radix == 8 ? 0xffffffffffffffff / 8 :
- 0xffffffffffffffff / 2;
- // Read all of the digits and convert to a number
- while (i < s.Length && IsDigit(s[i], radix, out int value))
- {
- // Check for overflows - this is sufficient & correct.
- if (result > maxVal)
- {
- Number.ThrowOverflowException(TypeCode.UInt64);
- }
- ulong temp = result * (ulong)radix + (ulong)value;
- if (temp < result) // this means overflow as well
- {
- Number.ThrowOverflowException(TypeCode.UInt64);
- }
- result = temp;
- i++;
- }
- }
- return (long)result;
- }
- private static int GrabInts(int radix, ReadOnlySpan<char> s, ref int i, bool isUnsigned)
- {
- uint result = 0;
- uint maxVal;
- // Allow all non-decimal numbers to set the sign bit.
- if (radix == 10 && !isUnsigned)
- {
- maxVal = (0x7FFFFFFF / 10);
- // Read all of the digits and convert to a number
- while (i < s.Length && IsDigit(s[i], radix, out int value))
- {
- // Check for overflows - this is sufficient & correct.
- if (result > maxVal || (int)result < 0)
- {
- Number.ThrowOverflowException(TypeCode.Int32);
- }
- result = result * (uint)radix + (uint)value;
- i++;
- }
- if ((int)result < 0 && result != 0x80000000)
- {
- Number.ThrowOverflowException(TypeCode.Int32);
- }
- }
- else
- {
- Debug.Assert(radix == 2 || radix == 8 || radix == 10 || radix == 16);
- maxVal =
- radix == 10 ? 0xffffffff / 10 :
- radix == 16 ? 0xffffffff / 16 :
- radix == 8 ? 0xffffffff / 8 :
- 0xffffffff / 2;
- // Read all of the digits and convert to a number
- while (i < s.Length && IsDigit(s[i], radix, out int value))
- {
- // Check for overflows - this is sufficient & correct.
- if (result > maxVal)
- {
- Number.ThrowOverflowException(TypeCode.UInt32);
- }
- uint temp = result * (uint)radix + (uint)value;
- if (temp < result) // this means overflow as well
- {
- Number.ThrowOverflowException(TypeCode.UInt32);
- }
- result = temp;
- i++;
- }
- }
- return (int)result;
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static bool IsDigit(char c, int radix, out int result)
- {
- int tmp;
- if ((uint)(c - '0') <= 9)
- {
- result = tmp = c - '0';
- }
- else if ((uint)(c - 'A') <= 'Z' - 'A')
- {
- result = tmp = c - 'A' + 10;
- }
- else if ((uint)(c - 'a') <= 'z' - 'a')
- {
- result = tmp = c - 'a' + 10;
- }
- else
- {
- result = -1;
- return false;
- }
- return tmp < radix;
- }
- }
- }
|