| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022 |
- // 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.Diagnostics.CodeAnalysis;
- using System.Globalization;
- using System.Runtime.CompilerServices;
- using System.Runtime.InteropServices;
- using Internal.Runtime.CompilerServices;
- namespace System
- {
- // The Parse methods provided by the numeric classes convert a
- // string to a numeric value. The optional style parameter specifies the
- // permitted style of the numeric string. It must be a combination of bit flags
- // from the NumberStyles enumeration. The optional info parameter
- // specifies the NumberFormatInfo instance to use when parsing the
- // string. If the info parameter is null or omitted, the numeric
- // formatting information is obtained from the current culture.
- //
- // Numeric strings produced by the Format methods using the Currency,
- // Decimal, Engineering, Fixed point, General, or Number standard formats
- // (the C, D, E, F, G, and N format specifiers) are guaranteed to be parseable
- // by the Parse methods if the NumberStyles.Any style is
- // specified. Note, however, that the Parse methods do not accept
- // NaNs or Infinities.
- internal partial class Number
- {
- private const int Int32Precision = 10;
- private const int UInt32Precision = Int32Precision;
- private const int Int64Precision = 19;
- private const int UInt64Precision = 20;
- private const int DoubleMaxExponent = 309;
- private const int DoubleMinExponent = -324;
- private const int FloatingPointMaxExponent = DoubleMaxExponent;
- private const int FloatingPointMinExponent = DoubleMinExponent;
- private const int SingleMaxExponent = 39;
- private const int SingleMinExponent = -45;
- /// <summary>Map from an ASCII char to its hex value, e.g. arr['b'] == 11. 0xFF means it's not a hex digit.</summary>
- internal static ReadOnlySpan<byte> CharToHexLookup => new byte[]
- {
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 15
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 31
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 47
- 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 63
- 0xFF, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 79
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 95
- 0xFF, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf // 102
- };
- private static unsafe bool TryNumberToInt32(ref NumberBuffer number, ref int value)
- {
- number.CheckConsistency();
- int i = number.Scale;
- if (i > Int32Precision || i < number.DigitsCount)
- {
- return false;
- }
- byte* p = number.GetDigitsPointer();
- Debug.Assert(p != null);
- int n = 0;
- while (--i >= 0)
- {
- if ((uint)n > (0x7FFFFFFF / 10))
- {
- return false;
- }
- n *= 10;
- if (*p != '\0')
- {
- n += (*p++ - '0');
- }
- }
- if (number.IsNegative)
- {
- n = -n;
- if (n > 0)
- {
- return false;
- }
- }
- else
- {
- if (n < 0)
- {
- return false;
- }
- }
- value = n;
- return true;
- }
- private static unsafe bool TryNumberToInt64(ref NumberBuffer number, ref long value)
- {
- number.CheckConsistency();
- int i = number.Scale;
- if (i > Int64Precision || i < number.DigitsCount)
- {
- return false;
- }
- byte* p = number.GetDigitsPointer();
- Debug.Assert(p != null);
- long n = 0;
- while (--i >= 0)
- {
- if ((ulong)n > (0x7FFFFFFFFFFFFFFF / 10))
- {
- return false;
- }
- n *= 10;
- if (*p != '\0')
- {
- n += (*p++ - '0');
- }
- }
- if (number.IsNegative)
- {
- n = -n;
- if (n > 0)
- {
- return false;
- }
- }
- else
- {
- if (n < 0)
- {
- return false;
- }
- }
- value = n;
- return true;
- }
- private static unsafe bool TryNumberToUInt32(ref NumberBuffer number, ref uint value)
- {
- number.CheckConsistency();
- int i = number.Scale;
- if (i > UInt32Precision || i < number.DigitsCount || number.IsNegative)
- {
- return false;
- }
- byte* p = number.GetDigitsPointer();
- Debug.Assert(p != null);
- uint n = 0;
- while (--i >= 0)
- {
- if (n > (0xFFFFFFFF / 10))
- {
- return false;
- }
- n *= 10;
- if (*p != '\0')
- {
- uint newN = n + (uint)(*p++ - '0');
- // Detect an overflow here...
- if (newN < n)
- {
- return false;
- }
- n = newN;
- }
- }
- value = n;
- return true;
- }
- private static unsafe bool TryNumberToUInt64(ref NumberBuffer number, ref ulong value)
- {
- number.CheckConsistency();
- int i = number.Scale;
- if (i > UInt64Precision || i < number.DigitsCount || number.IsNegative)
- {
- return false;
- }
- byte* p = number.GetDigitsPointer();
- Debug.Assert(p != null);
- ulong n = 0;
- while (--i >= 0)
- {
- if (n > (0xFFFFFFFFFFFFFFFF / 10))
- {
- return false;
- }
- n *= 10;
- if (*p != '\0')
- {
- ulong newN = n + (ulong)(*p++ - '0');
- // Detect an overflow here...
- if (newN < n)
- {
- return false;
- }
- n = newN;
- }
- }
- value = n;
- return true;
- }
- internal static int ParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
- {
- ParsingStatus status = TryParseInt32(value, styles, info, out int result);
- if (status != ParsingStatus.OK)
- {
- ThrowOverflowOrFormatException(status, TypeCode.Int32);
- }
- return result;
- }
- internal static long ParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
- {
- ParsingStatus status = TryParseInt64(value, styles, info, out long result);
- if (status != ParsingStatus.OK)
- {
- ThrowOverflowOrFormatException(status, TypeCode.Int64);
- }
- return result;
- }
- internal static uint ParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
- {
- ParsingStatus status = TryParseUInt32(value, styles, info, out uint result);
- if (status != ParsingStatus.OK)
- {
- ThrowOverflowOrFormatException(status, TypeCode.UInt32);
- }
- return result;
- }
- internal static ulong ParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
- {
- ParsingStatus status = TryParseUInt64(value, styles, info, out ulong result);
- if (status != ParsingStatus.OK)
- {
- ThrowOverflowOrFormatException(status, TypeCode.UInt64);
- }
- return result;
- }
- private static unsafe bool TryParseNumber(ref char* str, char* strEnd, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
- {
- Debug.Assert(str != null);
- Debug.Assert(strEnd != null);
- Debug.Assert(str <= strEnd);
- Debug.Assert((styles & NumberStyles.AllowHexSpecifier) == 0);
- const int StateSign = 0x0001;
- const int StateParens = 0x0002;
- const int StateDigits = 0x0004;
- const int StateNonZero = 0x0008;
- const int StateDecimal = 0x0010;
- const int StateCurrency = 0x0020;
- Debug.Assert(number.DigitsCount == 0);
- Debug.Assert(number.Scale == 0);
- Debug.Assert(number.IsNegative == false);
- Debug.Assert(number.HasNonZeroTail == false);
- number.CheckConsistency();
- string decSep; // decimal separator from NumberFormatInfo.
- string groupSep; // group separator from NumberFormatInfo.
- string? currSymbol = null; // currency symbol from NumberFormatInfo.
- bool parsingCurrency = false;
- if ((styles & NumberStyles.AllowCurrencySymbol) != 0)
- {
- currSymbol = info.CurrencySymbol;
- // The idea here is to match the currency separators and on failure match the number separators to keep the perf of VB's IsNumeric fast.
- // The values of decSep are setup to use the correct relevant separator (currency in the if part and decimal in the else part).
- decSep = info.CurrencyDecimalSeparator;
- groupSep = info.CurrencyGroupSeparator;
- parsingCurrency = true;
- }
- else
- {
- decSep = info.NumberDecimalSeparator;
- groupSep = info.NumberGroupSeparator;
- }
- int state = 0;
- char* p = str;
- char ch = p < strEnd ? *p : '\0';
- char* next;
- while (true)
- {
- // Eat whitespace unless we've found a sign which isn't followed by a currency symbol.
- // "-Kr 1231.47" is legal but "- 1231.47" is not.
- if (!IsWhite(ch) || (styles & NumberStyles.AllowLeadingWhite) == 0 || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && info.NumberNegativePattern != 2)))
- {
- if ((((styles & NumberStyles.AllowLeadingSign) != 0) && (state & StateSign) == 0) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || ((next = MatchChars(p, strEnd, info.NegativeSign)) != null && (number.IsNegative = true))))
- {
- state |= StateSign;
- p = next - 1;
- }
- else if (ch == '(' && ((styles & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0))
- {
- state |= StateSign | StateParens;
- number.IsNegative = true;
- }
- else if (currSymbol != null && (next = MatchChars(p, strEnd, currSymbol)) != null)
- {
- state |= StateCurrency;
- currSymbol = null;
- // We already found the currency symbol. There should not be more currency symbols. Set
- // currSymbol to NULL so that we won't search it again in the later code path.
- p = next - 1;
- }
- else
- {
- break;
- }
- }
- ch = ++p < strEnd ? *p : '\0';
- }
- int digCount = 0;
- int digEnd = 0;
- int maxDigCount = number.Digits.Length - 1;
- while (true)
- {
- if (IsDigit(ch))
- {
- state |= StateDigits;
- if (ch != '0' || (state & StateNonZero) != 0)
- {
- if (digCount < maxDigCount)
- {
- number.Digits[digCount++] = (byte)(ch);
- if ((ch != '0') || (number.Kind != NumberBufferKind.Integer))
- {
- digEnd = digCount;
- }
- }
- else if (ch != '0')
- {
- // For decimal and binary floating-point numbers, we only
- // need to store digits up to maxDigCount. However, we still
- // need to keep track of whether any additional digits past
- // maxDigCount were non-zero, as that can impact rounding
- // for an input that falls evenly between two representable
- // results.
- number.HasNonZeroTail = true;
- }
- if ((state & StateDecimal) == 0)
- {
- number.Scale++;
- }
- state |= StateNonZero;
- }
- else if ((state & StateDecimal) != 0)
- {
- number.Scale--;
- }
- }
- else if (((styles & NumberStyles.AllowDecimalPoint) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, strEnd, decSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, strEnd, info.NumberDecimalSeparator)) != null))
- {
- state |= StateDecimal;
- p = next - 1;
- }
- else if (((styles & NumberStyles.AllowThousands) != 0) && ((state & StateDigits) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, strEnd, groupSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, strEnd, info.NumberGroupSeparator)) != null))
- {
- p = next - 1;
- }
- else
- {
- break;
- }
- ch = ++p < strEnd ? *p : '\0';
- }
- bool negExp = false;
- number.DigitsCount = digEnd;
- number.Digits[digEnd] = (byte)('\0');
- if ((state & StateDigits) != 0)
- {
- if ((ch == 'E' || ch == 'e') && ((styles & NumberStyles.AllowExponent) != 0))
- {
- char* temp = p;
- ch = ++p < strEnd ? *p : '\0';
- if ((next = MatchChars(p, strEnd, info._positiveSign)) != null)
- {
- ch = (p = next) < strEnd ? *p : '\0';
- }
- else if ((next = MatchChars(p, strEnd, info._negativeSign)) != null)
- {
- ch = (p = next) < strEnd ? *p : '\0';
- negExp = true;
- }
- if (IsDigit(ch))
- {
- int exp = 0;
- do
- {
- exp = exp * 10 + (ch - '0');
- ch = ++p < strEnd ? *p : '\0';
- if (exp > 1000)
- {
- exp = 9999;
- while (IsDigit(ch))
- {
- ch = ++p < strEnd ? *p : '\0';
- }
- }
- } while (IsDigit(ch));
- if (negExp)
- {
- exp = -exp;
- }
- number.Scale += exp;
- }
- else
- {
- p = temp;
- ch = p < strEnd ? *p : '\0';
- }
- }
- while (true)
- {
- if (!IsWhite(ch) || (styles & NumberStyles.AllowTrailingWhite) == 0)
- {
- if (((styles & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0)) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || (((next = MatchChars(p, strEnd, info.NegativeSign)) != null) && (number.IsNegative = true))))
- {
- state |= StateSign;
- p = next - 1;
- }
- else if (ch == ')' && ((state & StateParens) != 0))
- {
- state &= ~StateParens;
- }
- else if (currSymbol != null && (next = MatchChars(p, strEnd, currSymbol)) != null)
- {
- currSymbol = null;
- p = next - 1;
- }
- else
- {
- break;
- }
- }
- ch = ++p < strEnd ? *p : '\0';
- }
- if ((state & StateParens) == 0)
- {
- if ((state & StateNonZero) == 0)
- {
- if (number.Kind != NumberBufferKind.Decimal)
- {
- number.Scale = 0;
- }
- if ((number.Kind == NumberBufferKind.Integer) && (state & StateDecimal) == 0)
- {
- number.IsNegative = false;
- }
- }
- str = p;
- return true;
- }
- }
- str = p;
- return false;
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal static ParsingStatus TryParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
- {
- if ((styles & ~NumberStyles.Integer) == 0)
- {
- // Optimized path for the common case of anything that's allowed for integer style.
- return TryParseInt32IntegerStyle(value, styles, info, out result);
- }
- if ((styles & NumberStyles.AllowHexSpecifier) != 0)
- {
- result = 0;
- return TryParseUInt32HexNumberStyle(value, styles, out Unsafe.As<int, uint>(ref result));
- }
- return TryParseInt32Number(value, styles, info, out result);
- }
- private static unsafe ParsingStatus TryParseInt32Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
- {
- result = 0;
- byte* pDigits = stackalloc byte[Int32NumberBufferLength];
- NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength);
- if (!TryStringToNumber(value, styles, ref number, info))
- {
- return ParsingStatus.Failed;
- }
- if (!TryNumberToInt32(ref number, ref result))
- {
- return ParsingStatus.Overflow;
- }
- return ParsingStatus.OK;
- }
- /// <summary>Parses int limited to styles that make up NumberStyles.Integer.</summary>
- internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
- {
- Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
- if (value.IsEmpty)
- goto FalseExit;
- int index = 0;
- int num = value[0];
- // Skip past any whitespace at the beginning.
- if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
- {
- do
- {
- index++;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- while (IsWhite(num));
- }
- // Parse leading sign.
- int sign = 1;
- if ((styles & NumberStyles.AllowLeadingSign) != 0)
- {
- if (info.HasInvariantNumberSigns)
- {
- if (num == '-')
- {
- sign = -1;
- index++;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- else if (num == '+')
- {
- index++;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- }
- else
- {
- value = value.Slice(index);
- index = 0;
- string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
- if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
- {
- index += positiveSign.Length;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
- {
- sign = -1;
- index += negativeSign.Length;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- }
- }
- bool overflow = false;
- int answer = 0;
- if (IsDigit(num))
- {
- // Skip past leading zeros.
- if (num == '0')
- {
- do
- {
- index++;
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEnd;
- num = value[index];
- } while (num == '0');
- if (!IsDigit(num))
- goto HasTrailingChars;
- }
- // Parse most digits, up to the potential for overflow, which can't happen until after 9 digits.
- answer = num - '0'; // first digit
- index++;
- for (int i = 0; i < 8; i++) // next 8 digits can't overflow
- {
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEnd;
- num = value[index];
- if (!IsDigit(num))
- goto HasTrailingChars;
- index++;
- answer = 10 * answer + num - '0';
- }
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEnd;
- num = value[index];
- if (!IsDigit(num))
- goto HasTrailingChars;
- index++;
- // Potential overflow now processing the 10th digit.
- overflow = answer > int.MaxValue / 10;
- answer = answer * 10 + num - '0';
- overflow |= (uint)answer > int.MaxValue + (((uint)sign) >> 31);
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
- // At this point, we're either overflowing or hitting a formatting error.
- // Format errors take precedence for compatibility.
- num = value[index];
- while (IsDigit(num))
- {
- overflow = true;
- index++;
- if ((uint)index >= (uint)value.Length)
- goto OverflowExit;
- num = value[index];
- }
- goto HasTrailingChars;
- }
- goto FalseExit;
- DoneAtEndButPotentialOverflow:
- if (overflow)
- {
- goto OverflowExit;
- }
- DoneAtEnd:
- result = answer * sign;
- ParsingStatus status = ParsingStatus.OK;
- Exit:
- return status;
- FalseExit: // parsing failed
- result = 0;
- status = ParsingStatus.Failed;
- goto Exit;
- OverflowExit:
- result = 0;
- status = ParsingStatus.Overflow;
- goto Exit;
- HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
- // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
- if (IsWhite(num))
- {
- if ((styles & NumberStyles.AllowTrailingWhite) == 0)
- goto FalseExit;
- for (index++; index < value.Length; index++)
- {
- if (!IsWhite(value[index]))
- break;
- }
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
- }
- if (!TrailingZeros(value, index))
- goto FalseExit;
- goto DoneAtEndButPotentialOverflow;
- }
- /// <summary>Parses long inputs limited to styles that make up NumberStyles.Integer.</summary>
- internal static ParsingStatus TryParseInt64IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
- {
- Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
- if (value.IsEmpty)
- goto FalseExit;
- int index = 0;
- int num = value[0];
- // Skip past any whitespace at the beginning.
- if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
- {
- do
- {
- index++;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- while (IsWhite(num));
- }
- // Parse leading sign.
- int sign = 1;
- if ((styles & NumberStyles.AllowLeadingSign) != 0)
- {
- if (info.HasInvariantNumberSigns)
- {
- if (num == '-')
- {
- sign = -1;
- index++;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- else if (num == '+')
- {
- index++;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- }
- else
- {
- value = value.Slice(index);
- index = 0;
- string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
- if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
- {
- index += positiveSign.Length;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
- {
- sign = -1;
- index += negativeSign.Length;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- }
- }
- bool overflow = false;
- long answer = 0;
- if (IsDigit(num))
- {
- // Skip past leading zeros.
- if (num == '0')
- {
- do
- {
- index++;
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEnd;
- num = value[index];
- } while (num == '0');
- if (!IsDigit(num))
- goto HasTrailingChars;
- }
- // Parse most digits, up to the potential for overflow, which can't happen until after 18 digits.
- answer = num - '0'; // first digit
- index++;
- for (int i = 0; i < 17; i++) // next 17 digits can't overflow
- {
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEnd;
- num = value[index];
- if (!IsDigit(num))
- goto HasTrailingChars;
- index++;
- answer = 10 * answer + num - '0';
- }
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEnd;
- num = value[index];
- if (!IsDigit(num))
- goto HasTrailingChars;
- index++;
- // Potential overflow now processing the 19th digit.
- overflow = answer > long.MaxValue / 10;
- answer = answer * 10 + num - '0';
- overflow |= (ulong)answer > (ulong)long.MaxValue + (((uint)sign) >> 31);
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
- // At this point, we're either overflowing or hitting a formatting error.
- // Format errors take precedence for compatibility.
- num = value[index];
- while (IsDigit(num))
- {
- overflow = true;
- index++;
- if ((uint)index >= (uint)value.Length)
- goto OverflowExit;
- num = value[index];
- }
- goto HasTrailingChars;
- }
- goto FalseExit;
- DoneAtEndButPotentialOverflow:
- if (overflow)
- {
- goto OverflowExit;
- }
- DoneAtEnd:
- result = answer * sign;
- ParsingStatus status = ParsingStatus.OK;
- Exit:
- return status;
- FalseExit: // parsing failed
- result = 0;
- status = ParsingStatus.Failed;
- goto Exit;
- OverflowExit:
- result = 0;
- status = ParsingStatus.Overflow;
- goto Exit;
- HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
- // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
- if (IsWhite(num))
- {
- if ((styles & NumberStyles.AllowTrailingWhite) == 0)
- goto FalseExit;
- for (index++; index < value.Length; index++)
- {
- if (!IsWhite(value[index]))
- break;
- }
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
- }
- if (!TrailingZeros(value, index))
- goto FalseExit;
- goto DoneAtEndButPotentialOverflow;
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal static ParsingStatus TryParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
- {
- if ((styles & ~NumberStyles.Integer) == 0)
- {
- // Optimized path for the common case of anything that's allowed for integer style.
- return TryParseInt64IntegerStyle(value, styles, info, out result);
- }
- if ((styles & NumberStyles.AllowHexSpecifier) != 0)
- {
- result = 0;
- return TryParseUInt64HexNumberStyle(value, styles, out Unsafe.As<long, ulong>(ref result));
- }
- return TryParseInt64Number(value, styles, info, out result);
- }
- private static unsafe ParsingStatus TryParseInt64Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
- {
- result = 0;
- byte* pDigits = stackalloc byte[Int64NumberBufferLength];
- NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength);
- if (!TryStringToNumber(value, styles, ref number, info))
- {
- return ParsingStatus.Failed;
- }
- if (!TryNumberToInt64(ref number, ref result))
- {
- return ParsingStatus.Overflow;
- }
- return ParsingStatus.OK;
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal static ParsingStatus TryParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
- {
- if ((styles & ~NumberStyles.Integer) == 0)
- {
- // Optimized path for the common case of anything that's allowed for integer style.
- return TryParseUInt32IntegerStyle(value, styles, info, out result);
- }
- if ((styles & NumberStyles.AllowHexSpecifier) != 0)
- {
- return TryParseUInt32HexNumberStyle(value, styles, out result);
- }
- return TryParseUInt32Number(value, styles, info, out result);
- }
- private static unsafe ParsingStatus TryParseUInt32Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
- {
- result = 0;
- byte* pDigits = stackalloc byte[UInt32NumberBufferLength];
- NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength);
- if (!TryStringToNumber(value, styles, ref number, info))
- {
- return ParsingStatus.Failed;
- }
- if (!TryNumberToUInt32(ref number, ref result))
- {
- return ParsingStatus.Overflow;
- }
- return ParsingStatus.OK;
- }
- /// <summary>Parses uint limited to styles that make up NumberStyles.Integer.</summary>
- internal static ParsingStatus TryParseUInt32IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
- {
- Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
- if (value.IsEmpty)
- goto FalseExit;
- int index = 0;
- int num = value[0];
- // Skip past any whitespace at the beginning.
- if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
- {
- do
- {
- index++;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- while (IsWhite(num));
- }
- // Parse leading sign.
- bool overflow = false;
- if ((styles & NumberStyles.AllowLeadingSign) != 0)
- {
- if (info.HasInvariantNumberSigns)
- {
- if (num == '+')
- {
- index++;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- else if (num == '-')
- {
- overflow = true;
- index++;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- }
- else
- {
- value = value.Slice(index);
- index = 0;
- string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
- if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
- {
- index += positiveSign.Length;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
- {
- overflow = true;
- index += negativeSign.Length;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- }
- }
- int answer = 0;
- if (IsDigit(num))
- {
- // Skip past leading zeros.
- if (num == '0')
- {
- do
- {
- index++;
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEnd;
- num = value[index];
- } while (num == '0');
- if (!IsDigit(num))
- goto HasTrailingCharsZero;
- }
- // Parse most digits, up to the potential for overflow, which can't happen until after 9 digits.
- answer = num - '0'; // first digit
- index++;
- for (int i = 0; i < 8; i++) // next 8 digits can't overflow
- {
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
- num = value[index];
- if (!IsDigit(num))
- goto HasTrailingChars;
- index++;
- answer = 10 * answer + num - '0';
- }
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
- num = value[index];
- if (!IsDigit(num))
- goto HasTrailingChars;
- index++;
- // Potential overflow now processing the 10th digit.
- overflow |= (uint)answer > uint.MaxValue / 10 || ((uint)answer == uint.MaxValue / 10 && num > '5');
- answer = answer * 10 + num - '0';
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
- // At this point, we're either overflowing or hitting a formatting error.
- // Format errors take precedence for compatibility.
- num = value[index];
- while (IsDigit(num))
- {
- overflow = true;
- index++;
- if ((uint)index >= (uint)value.Length)
- goto OverflowExit;
- num = value[index];
- }
- goto HasTrailingChars;
- }
- goto FalseExit;
- DoneAtEndButPotentialOverflow:
- if (overflow)
- {
- goto OverflowExit;
- }
- DoneAtEnd:
- result = (uint)answer;
- ParsingStatus status = ParsingStatus.OK;
- Exit:
- return status;
- FalseExit: // parsing failed
- result = 0;
- status = ParsingStatus.Failed;
- goto Exit;
- OverflowExit:
- result = 0;
- status = ParsingStatus.Overflow;
- goto Exit;
- HasTrailingCharsZero:
- overflow = false;
- HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
- // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
- if (IsWhite(num))
- {
- if ((styles & NumberStyles.AllowTrailingWhite) == 0)
- goto FalseExit;
- for (index++; index < value.Length; index++)
- {
- if (!IsWhite(value[index]))
- break;
- }
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
- }
- if (!TrailingZeros(value, index))
- goto FalseExit;
- goto DoneAtEndButPotentialOverflow;
- }
- /// <summary>Parses uint limited to styles that make up NumberStyles.HexNumber.</summary>
- private static ParsingStatus TryParseUInt32HexNumberStyle(ReadOnlySpan<char> value, NumberStyles styles, out uint result)
- {
- Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format");
- if (value.IsEmpty)
- goto FalseExit;
- int index = 0;
- int num = value[0];
- uint numValue;
- // Skip past any whitespace at the beginning.
- if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
- {
- do
- {
- index++;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- while (IsWhite(num));
- }
- bool overflow = false;
- uint answer = 0;
- ReadOnlySpan<byte> charToHexLookup = CharToHexLookup;
- if ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
- {
- // Skip past leading zeros.
- if (num == '0')
- {
- do
- {
- index++;
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEnd;
- num = value[index];
- } while (num == '0');
- if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF)
- goto HasTrailingChars;
- }
- // Parse up through 8 digits, as no overflow is possible
- answer = charToHexLookup[num]; // first digit
- index++;
- for (int i = 0; i < 7; i++) // next 7 digits can't overflow
- {
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEnd;
- num = value[index];
- if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
- goto HasTrailingChars;
- index++;
- answer = 16 * answer + numValue;
- }
- // If there's another digit, it's an overflow.
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEnd;
- num = value[index];
- if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
- goto HasTrailingChars;
- // At this point, we're either overflowing or hitting a formatting error.
- // Format errors take precedence for compatibility. Read through any remaining digits.
- do
- {
- index++;
- if ((uint)index >= (uint)value.Length)
- goto OverflowExit;
- num = value[index];
- } while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF);
- overflow = true;
- goto HasTrailingChars;
- }
- goto FalseExit;
- DoneAtEndButPotentialOverflow:
- if (overflow)
- {
- goto OverflowExit;
- }
- DoneAtEnd:
- result = answer;
- ParsingStatus status = ParsingStatus.OK;
- Exit:
- return status;
- FalseExit: // parsing failed
- result = 0;
- status = ParsingStatus.Failed;
- goto Exit;
- OverflowExit:
- result = 0;
- status = ParsingStatus.Overflow;
- goto Exit;
- HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
- // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
- if (IsWhite(num))
- {
- if ((styles & NumberStyles.AllowTrailingWhite) == 0)
- goto FalseExit;
- for (index++; index < value.Length; index++)
- {
- if (!IsWhite(value[index]))
- break;
- }
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
- }
- if (!TrailingZeros(value, index))
- goto FalseExit;
- goto DoneAtEndButPotentialOverflow;
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal static ParsingStatus TryParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
- {
- if ((styles & ~NumberStyles.Integer) == 0)
- {
- // Optimized path for the common case of anything that's allowed for integer style.
- return TryParseUInt64IntegerStyle(value, styles, info, out result);
- }
- if ((styles & NumberStyles.AllowHexSpecifier) != 0)
- {
- return TryParseUInt64HexNumberStyle(value, styles, out result);
- }
- return TryParseUInt64Number(value, styles, info, out result);
- }
- private static unsafe ParsingStatus TryParseUInt64Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
- {
- result = 0;
- byte* pDigits = stackalloc byte[UInt64NumberBufferLength];
- NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt64NumberBufferLength);
- if (!TryStringToNumber(value, styles, ref number, info))
- {
- return ParsingStatus.Failed;
- }
- if (!TryNumberToUInt64(ref number, ref result))
- {
- return ParsingStatus.Overflow;
- }
- return ParsingStatus.OK;
- }
- /// <summary>Parses ulong limited to styles that make up NumberStyles.Integer.</summary>
- internal static ParsingStatus TryParseUInt64IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
- {
- Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
- if (value.IsEmpty)
- goto FalseExit;
- int index = 0;
- int num = value[0];
- // Skip past any whitespace at the beginning.
- if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
- {
- do
- {
- index++;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- while (IsWhite(num));
- }
- // Parse leading sign.
- bool overflow = false;
- if ((styles & NumberStyles.AllowLeadingSign) != 0)
- {
- if (info.HasInvariantNumberSigns)
- {
- if (num == '+')
- {
- index++;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- else if (num == '-')
- {
- overflow = true;
- index++;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- }
- else
- {
- value = value.Slice(index);
- index = 0;
- string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
- if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
- {
- index += positiveSign.Length;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
- {
- overflow = true;
- index += negativeSign.Length;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- }
- }
- long answer = 0;
- if (IsDigit(num))
- {
- // Skip past leading zeros.
- if (num == '0')
- {
- do
- {
- index++;
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEnd;
- num = value[index];
- } while (num == '0');
- if (!IsDigit(num))
- goto HasTrailingCharsZero;
- }
- // Parse most digits, up to the potential for overflow, which can't happen until after 19 digits.
- answer = num - '0'; // first digit
- index++;
- for (int i = 0; i < 18; i++) // next 18 digits can't overflow
- {
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
- num = value[index];
- if (!IsDigit(num))
- goto HasTrailingChars;
- index++;
- answer = 10 * answer + num - '0';
- }
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
- num = value[index];
- if (!IsDigit(num))
- goto HasTrailingChars;
- index++;
- // Potential overflow now processing the 20th digit.
- overflow |= (ulong)answer > ulong.MaxValue / 10 || ((ulong)answer == ulong.MaxValue / 10 && num > '5');
- answer = answer * 10 + num - '0';
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
- // At this point, we're either overflowing or hitting a formatting error.
- // Format errors take precedence for compatibility.
- num = value[index];
- while (IsDigit(num))
- {
- overflow = true;
- index++;
- if ((uint)index >= (uint)value.Length)
- goto OverflowExit;
- num = value[index];
- }
- goto HasTrailingChars;
- }
- goto FalseExit;
- DoneAtEndButPotentialOverflow:
- if (overflow)
- {
- goto OverflowExit;
- }
- DoneAtEnd:
- result = (ulong)answer;
- ParsingStatus status = ParsingStatus.OK;
- Exit:
- return status;
- FalseExit: // parsing failed
- result = 0;
- status = ParsingStatus.Failed;
- goto Exit;
- OverflowExit:
- result = 0;
- status = ParsingStatus.Overflow;
- goto Exit;
- HasTrailingCharsZero:
- overflow = false;
- HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
- // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
- if (IsWhite(num))
- {
- if ((styles & NumberStyles.AllowTrailingWhite) == 0)
- goto FalseExit;
- for (index++; index < value.Length; index++)
- {
- if (!IsWhite(value[index]))
- break;
- }
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
- }
- if (!TrailingZeros(value, index))
- goto FalseExit;
- goto DoneAtEndButPotentialOverflow;
- }
- /// <summary>Parses ulong limited to styles that make up NumberStyles.HexNumber.</summary>
- private static ParsingStatus TryParseUInt64HexNumberStyle(ReadOnlySpan<char> value, NumberStyles styles, out ulong result)
- {
- Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format");
- if (value.IsEmpty)
- goto FalseExit;
- int index = 0;
- int num = value[0];
- uint numValue;
- // Skip past any whitespace at the beginning.
- if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
- {
- do
- {
- index++;
- if ((uint)index >= (uint)value.Length)
- goto FalseExit;
- num = value[index];
- }
- while (IsWhite(num));
- }
- bool overflow = false;
- ulong answer = 0;
- ReadOnlySpan<byte> charToHexLookup = CharToHexLookup;
- if ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
- {
- // Skip past leading zeros.
- if (num == '0')
- {
- do
- {
- index++;
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEnd;
- num = value[index];
- } while (num == '0');
- if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF)
- goto HasTrailingChars;
- }
- // Parse up through 16 digits, as no overflow is possible
- answer = charToHexLookup[num]; // first digit
- index++;
- for (int i = 0; i < 15; i++) // next 15 digits can't overflow
- {
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEnd;
- num = value[index];
- if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
- goto HasTrailingChars;
- index++;
- answer = 16 * answer + numValue;
- }
- // If there's another digit, it's an overflow.
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEnd;
- num = value[index];
- if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
- goto HasTrailingChars;
- // At this point, we're either overflowing or hitting a formatting error.
- // Format errors take precedence for compatibility. Read through any remaining digits.
- do
- {
- index++;
- if ((uint)index >= (uint)value.Length)
- goto OverflowExit;
- num = value[index];
- } while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF);
- overflow = true;
- goto HasTrailingChars;
- }
- goto FalseExit;
- DoneAtEndButPotentialOverflow:
- if (overflow)
- {
- goto OverflowExit;
- }
- DoneAtEnd:
- result = answer;
- ParsingStatus status = ParsingStatus.OK;
- Exit:
- return status;
- FalseExit: // parsing failed
- result = 0;
- status = ParsingStatus.Failed;
- goto Exit;
- OverflowExit:
- result = 0;
- status = ParsingStatus.Overflow;
- goto Exit;
- HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
- // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
- if (IsWhite(num))
- {
- if ((styles & NumberStyles.AllowTrailingWhite) == 0)
- goto FalseExit;
- for (index++; index < value.Length; index++)
- {
- if (!IsWhite(value[index]))
- break;
- }
- if ((uint)index >= (uint)value.Length)
- goto DoneAtEndButPotentialOverflow;
- }
- if (!TrailingZeros(value, index))
- goto FalseExit;
- goto DoneAtEndButPotentialOverflow;
- }
- internal static decimal ParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
- {
- ParsingStatus status = TryParseDecimal(value, styles, info, out decimal result);
- if (status != ParsingStatus.OK)
- {
- ThrowOverflowOrFormatException(status, TypeCode.Decimal);
- }
- return result;
- }
- internal static unsafe bool TryNumberToDecimal(ref NumberBuffer number, ref decimal value)
- {
- number.CheckConsistency();
- byte* p = number.GetDigitsPointer();
- int e = number.Scale;
- bool sign = number.IsNegative;
- uint c = *p;
- if (c == 0)
- {
- // To avoid risking an app-compat issue with pre 4.5 (where some app was illegally using Reflection to examine the internal scale bits), we'll only force
- // the scale to 0 if the scale was previously positive (previously, such cases were unparsable to a bug.)
- value = new decimal(0, 0, 0, sign, (byte)Math.Clamp(-e, 0, 28));
- return true;
- }
- if (e > DecimalPrecision)
- return false;
- ulong low64 = 0;
- while (e > -28)
- {
- e--;
- low64 *= 10;
- low64 += c - '0';
- c = *++p;
- if (low64 >= ulong.MaxValue / 10)
- break;
- if (c == 0)
- {
- while (e > 0)
- {
- e--;
- low64 *= 10;
- if (low64 >= ulong.MaxValue / 10)
- break;
- }
- break;
- }
- }
- uint high = 0;
- while ((e > 0 || (c != 0 && e > -28)) &&
- (high < uint.MaxValue / 10 || (high == uint.MaxValue / 10 && (low64 < 0x99999999_99999999 || (low64 == 0x99999999_99999999 && c <= '5')))))
- {
- // multiply by 10
- ulong tmpLow = (uint)low64 * 10UL;
- ulong tmp64 = (uint)(low64 >> 32) * 10UL + (tmpLow >> 32);
- low64 = (uint)tmpLow + (tmp64 << 32);
- high = (uint)(tmp64 >> 32) + high * 10;
- if (c != 0)
- {
- c -= '0';
- low64 += c;
- if (low64 < c)
- high++;
- c = *++p;
- }
- e--;
- }
- if (c >= '5')
- {
- if ((c == '5') && ((low64 & 1) == 0))
- {
- c = *++p;
- bool hasZeroTail = !number.HasNonZeroTail;
- // We might still have some additional digits, in which case they need
- // to be considered as part of hasZeroTail. Some examples of this are:
- // * 3.0500000000000000000001e-27
- // * 3.05000000000000000000001e-27
- // In these cases, we will have processed 3 and 0, and ended on 5. The
- // buffer, however, will still contain a number of trailing zeros and
- // a trailing non-zero number.
- while ((c != 0) && hasZeroTail)
- {
- hasZeroTail &= (c == '0');
- c = *++p;
- }
- // We should either be at the end of the stream or have a non-zero tail
- Debug.Assert((c == 0) || !hasZeroTail);
- if (hasZeroTail)
- {
- // When the next digit is 5, the number is even, and all following
- // digits are zero we don't need to round.
- goto NoRounding;
- }
- }
- if (++low64 == 0 && ++high == 0)
- {
- low64 = 0x99999999_9999999A;
- high = uint.MaxValue / 10;
- e++;
- }
- }
- NoRounding:
- if (e > 0)
- return false;
- if (e <= -DecimalPrecision)
- {
- // Parsing a large scale zero can give you more precision than fits in the decimal.
- // This should only happen for actual zeros or very small numbers that round to zero.
- value = new decimal(0, 0, 0, sign, DecimalPrecision - 1);
- }
- else
- {
- value = new decimal((int)low64, (int)(low64 >> 32), (int)high, sign, (byte)-e);
- }
- return true;
- }
- internal static double ParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
- {
- if (!TryParseDouble(value, styles, info, out double result))
- {
- ThrowOverflowOrFormatException(ParsingStatus.Failed);
- }
- return result;
- }
- internal static float ParseSingle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
- {
- if (!TryParseSingle(value, styles, info, out float result))
- {
- ThrowOverflowOrFormatException(ParsingStatus.Failed);
- }
- return result;
- }
- internal static unsafe ParsingStatus TryParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out decimal result)
- {
- byte* pDigits = stackalloc byte[DecimalNumberBufferLength];
- NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, pDigits, DecimalNumberBufferLength);
- result = 0;
- if (!TryStringToNumber(value, styles, ref number, info))
- {
- return ParsingStatus.Failed;
- }
- if (!TryNumberToDecimal(ref number, ref result))
- {
- return ParsingStatus.Overflow;
- }
- return ParsingStatus.OK;
- }
- internal static unsafe bool TryParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out double result)
- {
- byte* pDigits = stackalloc byte[DoubleNumberBufferLength];
- NumberBuffer number = new NumberBuffer(NumberBufferKind.FloatingPoint, pDigits, DoubleNumberBufferLength);
- if (!TryStringToNumber(value, styles, ref number, info))
- {
- ReadOnlySpan<char> valueTrim = value.Trim();
- // This code would be simpler if we only had the concept of `InfinitySymbol`, but
- // we don't so we'll check the existing cases first and then handle `PositiveSign` +
- // `PositiveInfinitySymbol` and `PositiveSign/NegativeSign` + `NaNSymbol` last.
- if (valueTrim.EqualsOrdinalIgnoreCase(info.PositiveInfinitySymbol))
- {
- result = double.PositiveInfinity;
- }
- else if (valueTrim.EqualsOrdinalIgnoreCase(info.NegativeInfinitySymbol))
- {
- result = double.NegativeInfinity;
- }
- else if (valueTrim.EqualsOrdinalIgnoreCase(info.NaNSymbol))
- {
- result = double.NaN;
- }
- else if (valueTrim.StartsWith(info.PositiveSign, StringComparison.OrdinalIgnoreCase))
- {
- valueTrim = valueTrim.Slice(info.PositiveSign.Length);
- if (valueTrim.EqualsOrdinalIgnoreCase(info.PositiveInfinitySymbol))
- {
- result = double.PositiveInfinity;
- }
- else if (valueTrim.EqualsOrdinalIgnoreCase(info.NaNSymbol))
- {
- result = double.NaN;
- }
- else
- {
- result = 0;
- return false;
- }
- }
- else if (valueTrim.StartsWith(info.NegativeSign, StringComparison.OrdinalIgnoreCase) &&
- valueTrim.Slice(info.NegativeSign.Length).EqualsOrdinalIgnoreCase(info.NaNSymbol))
- {
- result = double.NaN;
- }
- else
- {
- result = 0;
- return false; // We really failed
- }
- }
- else
- {
- result = NumberToDouble(ref number);
- }
- return true;
- }
- internal static unsafe bool TryParseSingle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out float result)
- {
- byte* pDigits = stackalloc byte[SingleNumberBufferLength];
- NumberBuffer number = new NumberBuffer(NumberBufferKind.FloatingPoint, pDigits, SingleNumberBufferLength);
- if (!TryStringToNumber(value, styles, ref number, info))
- {
- ReadOnlySpan<char> valueTrim = value.Trim();
- // This code would be simpler if we only had the concept of `InfinitySymbol`, but
- // we don't so we'll check the existing cases first and then handle `PositiveSign` +
- // `PositiveInfinitySymbol` and `PositiveSign/NegativeSign` + `NaNSymbol` last.
- //
- // Additionally, since some cultures ("wo") actually define `PositiveInfinitySymbol`
- // to include `PositiveSign`, we need to check whether `PositiveInfinitySymbol` fits
- // that case so that we don't start parsing things like `++infini`.
- if (valueTrim.EqualsOrdinalIgnoreCase(info.PositiveInfinitySymbol))
- {
- result = float.PositiveInfinity;
- }
- else if (valueTrim.EqualsOrdinalIgnoreCase(info.NegativeInfinitySymbol))
- {
- result = float.NegativeInfinity;
- }
- else if (valueTrim.EqualsOrdinalIgnoreCase(info.NaNSymbol))
- {
- result = float.NaN;
- }
- else if (valueTrim.StartsWith(info.PositiveSign, StringComparison.OrdinalIgnoreCase))
- {
- valueTrim = valueTrim.Slice(info.PositiveSign.Length);
- if (!info.PositiveInfinitySymbol.StartsWith(info.PositiveSign, StringComparison.OrdinalIgnoreCase) && valueTrim.EqualsOrdinalIgnoreCase(info.PositiveInfinitySymbol))
- {
- result = float.PositiveInfinity;
- }
- else if (!info.NaNSymbol.StartsWith(info.PositiveSign, StringComparison.OrdinalIgnoreCase) && valueTrim.EqualsOrdinalIgnoreCase(info.NaNSymbol))
- {
- result = float.NaN;
- }
- else
- {
- result = 0;
- return false;
- }
- }
- else if (valueTrim.StartsWith(info.NegativeSign, StringComparison.OrdinalIgnoreCase) &&
- !info.NaNSymbol.StartsWith(info.NegativeSign, StringComparison.OrdinalIgnoreCase) &&
- valueTrim.Slice(info.NegativeSign.Length).EqualsOrdinalIgnoreCase(info.NaNSymbol))
- {
- result = float.NaN;
- }
- else
- {
- result = 0;
- return false; // We really failed
- }
- }
- else
- {
- result = NumberToSingle(ref number);
- }
- return true;
- }
- internal static unsafe bool TryStringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
- {
- Debug.Assert(info != null);
- fixed (char* stringPointer = &MemoryMarshal.GetReference(value))
- {
- char* p = stringPointer;
- if (!TryParseNumber(ref p, p + value.Length, styles, ref number, info)
- || ((int)(p - stringPointer) < value.Length && !TrailingZeros(value, (int)(p - stringPointer))))
- {
- number.CheckConsistency();
- return false;
- }
- }
- number.CheckConsistency();
- return true;
- }
- private static bool TrailingZeros(ReadOnlySpan<char> value, int index)
- {
- // For compatibility, we need to allow trailing zeros at the end of a number string
- for (int i = index; (uint)i < (uint)value.Length; i++)
- {
- if (value[i] != '\0')
- {
- return false;
- }
- }
- return true;
- }
- private static bool IsSpaceReplacingChar(char c) => c == '\u00a0' || c == '\u202f';
- private static unsafe char* MatchChars(char* p, char* pEnd, string value)
- {
- Debug.Assert(p != null && pEnd != null && p <= pEnd && value != null);
- fixed (char* stringPointer = value)
- {
- char* str = stringPointer;
- if (*str != '\0')
- {
- // We only hurt the failure case
- // This fix is for French or Kazakh cultures. Since a user cannot type 0xA0 or 0x202F as a
- // space character we use 0x20 space character instead to mean the same.
- while (true)
- {
- char cp = p < pEnd ? *p : '\0';
- if (cp != *str && !(IsSpaceReplacingChar(*str) && cp == '\u0020'))
- {
- break;
- }
- p++;
- str++;
- if (*str == '\0')
- return p;
- }
- }
- }
- return null;
- }
- // Ternary op is a workaround for https://github.com/dotnet/coreclr/issues/914
- private static bool IsWhite(int ch) => ch == 0x20 || (uint)(ch - 0x09) <= (0x0D - 0x09) ? true : false;
- private static bool IsDigit(int ch) => ((uint)ch - '0') <= 9;
- internal enum ParsingStatus
- {
- OK,
- Failed,
- Overflow
- }
- [DoesNotReturn]
- internal static void ThrowOverflowOrFormatException(ParsingStatus status, TypeCode type = 0) => throw GetException(status, type);
- [DoesNotReturn]
- internal static void ThrowOverflowException(TypeCode type) => throw GetException(ParsingStatus.Overflow, type);
- private static Exception GetException(ParsingStatus status, TypeCode type)
- {
- if (status == ParsingStatus.Failed)
- return new FormatException(SR.Format_InvalidString);
- string s;
- switch (type)
- {
- case TypeCode.SByte:
- s = SR.Overflow_SByte;
- break;
- case TypeCode.Byte:
- s = SR.Overflow_Byte;
- break;
- case TypeCode.Int16:
- s = SR.Overflow_Int16;
- break;
- case TypeCode.UInt16:
- s = SR.Overflow_UInt16;
- break;
- case TypeCode.Int32:
- s = SR.Overflow_Int32;
- break;
- case TypeCode.UInt32:
- s = SR.Overflow_UInt32;
- break;
- case TypeCode.Int64:
- s = SR.Overflow_Int64;
- break;
- case TypeCode.UInt64:
- s = SR.Overflow_UInt64;
- break;
- default:
- Debug.Assert(type == TypeCode.Decimal);
- s = SR.Overflow_Decimal;
- break;
- }
- return new OverflowException(s);
- }
- internal static double NumberToDouble(ref NumberBuffer number)
- {
- number.CheckConsistency();
- double result;
- if ((number.DigitsCount == 0) || (number.Scale < DoubleMinExponent))
- {
- result = 0;
- }
- else if (number.Scale > DoubleMaxExponent)
- {
- result = double.PositiveInfinity;
- }
- else
- {
- ulong bits = NumberToFloatingPointBits(ref number, in FloatingPointInfo.Double);
- result = BitConverter.Int64BitsToDouble((long)(bits));
- }
- return number.IsNegative ? -result : result;
- }
- internal static float NumberToSingle(ref NumberBuffer number)
- {
- number.CheckConsistency();
- float result;
- if ((number.DigitsCount == 0) || (number.Scale < SingleMinExponent))
- {
- result = 0;
- }
- else if (number.Scale > SingleMaxExponent)
- {
- result = float.PositiveInfinity;
- }
- else
- {
- uint bits = (uint)(NumberToFloatingPointBits(ref number, in FloatingPointInfo.Single));
- result = BitConverter.Int32BitsToSingle((int)(bits));
- }
- return number.IsNegative ? -result : result;
- }
- }
- }
|