Number.Parsing.cs 75 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Diagnostics;
  5. using System.Diagnostics.CodeAnalysis;
  6. using System.Globalization;
  7. using System.Runtime.CompilerServices;
  8. using System.Runtime.InteropServices;
  9. using Internal.Runtime.CompilerServices;
  10. namespace System
  11. {
  12. // The Parse methods provided by the numeric classes convert a
  13. // string to a numeric value. The optional style parameter specifies the
  14. // permitted style of the numeric string. It must be a combination of bit flags
  15. // from the NumberStyles enumeration. The optional info parameter
  16. // specifies the NumberFormatInfo instance to use when parsing the
  17. // string. If the info parameter is null or omitted, the numeric
  18. // formatting information is obtained from the current culture.
  19. //
  20. // Numeric strings produced by the Format methods using the Currency,
  21. // Decimal, Engineering, Fixed point, General, or Number standard formats
  22. // (the C, D, E, F, G, and N format specifiers) are guaranteed to be parseable
  23. // by the Parse methods if the NumberStyles.Any style is
  24. // specified. Note, however, that the Parse methods do not accept
  25. // NaNs or Infinities.
  26. internal static partial class Number
  27. {
  28. private const int Int32Precision = 10;
  29. private const int UInt32Precision = Int32Precision;
  30. private const int Int64Precision = 19;
  31. private const int UInt64Precision = 20;
  32. private const int DoubleMaxExponent = 309;
  33. private const int DoubleMinExponent = -324;
  34. private const int FloatingPointMaxExponent = DoubleMaxExponent;
  35. private const int FloatingPointMinExponent = DoubleMinExponent;
  36. private const int SingleMaxExponent = 39;
  37. private const int SingleMinExponent = -45;
  38. /// <summary>Map from an ASCII char to its hex value, e.g. arr['b'] == 11. 0xFF means it's not a hex digit.</summary>
  39. internal static ReadOnlySpan<byte> CharToHexLookup => new byte[]
  40. {
  41. 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 15
  42. 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 31
  43. 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 47
  44. 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 63
  45. 0xFF, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 79
  46. 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 95
  47. 0xFF, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf // 102
  48. };
  49. private static unsafe bool TryNumberToInt32(ref NumberBuffer number, ref int value)
  50. {
  51. number.CheckConsistency();
  52. int i = number.Scale;
  53. if (i > Int32Precision || i < number.DigitsCount)
  54. {
  55. return false;
  56. }
  57. byte* p = number.GetDigitsPointer();
  58. Debug.Assert(p != null);
  59. int n = 0;
  60. while (--i >= 0)
  61. {
  62. if ((uint)n > (0x7FFFFFFF / 10))
  63. {
  64. return false;
  65. }
  66. n *= 10;
  67. if (*p != '\0')
  68. {
  69. n += (*p++ - '0');
  70. }
  71. }
  72. if (number.IsNegative)
  73. {
  74. n = -n;
  75. if (n > 0)
  76. {
  77. return false;
  78. }
  79. }
  80. else
  81. {
  82. if (n < 0)
  83. {
  84. return false;
  85. }
  86. }
  87. value = n;
  88. return true;
  89. }
  90. private static unsafe bool TryNumberToInt64(ref NumberBuffer number, ref long value)
  91. {
  92. number.CheckConsistency();
  93. int i = number.Scale;
  94. if (i > Int64Precision || i < number.DigitsCount)
  95. {
  96. return false;
  97. }
  98. byte* p = number.GetDigitsPointer();
  99. Debug.Assert(p != null);
  100. long n = 0;
  101. while (--i >= 0)
  102. {
  103. if ((ulong)n > (0x7FFFFFFFFFFFFFFF / 10))
  104. {
  105. return false;
  106. }
  107. n *= 10;
  108. if (*p != '\0')
  109. {
  110. n += (*p++ - '0');
  111. }
  112. }
  113. if (number.IsNegative)
  114. {
  115. n = -n;
  116. if (n > 0)
  117. {
  118. return false;
  119. }
  120. }
  121. else
  122. {
  123. if (n < 0)
  124. {
  125. return false;
  126. }
  127. }
  128. value = n;
  129. return true;
  130. }
  131. private static unsafe bool TryNumberToUInt32(ref NumberBuffer number, ref uint value)
  132. {
  133. number.CheckConsistency();
  134. int i = number.Scale;
  135. if (i > UInt32Precision || i < number.DigitsCount || number.IsNegative)
  136. {
  137. return false;
  138. }
  139. byte* p = number.GetDigitsPointer();
  140. Debug.Assert(p != null);
  141. uint n = 0;
  142. while (--i >= 0)
  143. {
  144. if (n > (0xFFFFFFFF / 10))
  145. {
  146. return false;
  147. }
  148. n *= 10;
  149. if (*p != '\0')
  150. {
  151. uint newN = n + (uint)(*p++ - '0');
  152. // Detect an overflow here...
  153. if (newN < n)
  154. {
  155. return false;
  156. }
  157. n = newN;
  158. }
  159. }
  160. value = n;
  161. return true;
  162. }
  163. private static unsafe bool TryNumberToUInt64(ref NumberBuffer number, ref ulong value)
  164. {
  165. number.CheckConsistency();
  166. int i = number.Scale;
  167. if (i > UInt64Precision || i < number.DigitsCount || number.IsNegative)
  168. {
  169. return false;
  170. }
  171. byte* p = number.GetDigitsPointer();
  172. Debug.Assert(p != null);
  173. ulong n = 0;
  174. while (--i >= 0)
  175. {
  176. if (n > (0xFFFFFFFFFFFFFFFF / 10))
  177. {
  178. return false;
  179. }
  180. n *= 10;
  181. if (*p != '\0')
  182. {
  183. ulong newN = n + (ulong)(*p++ - '0');
  184. // Detect an overflow here...
  185. if (newN < n)
  186. {
  187. return false;
  188. }
  189. n = newN;
  190. }
  191. }
  192. value = n;
  193. return true;
  194. }
  195. internal static int ParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
  196. {
  197. ParsingStatus status = TryParseInt32(value, styles, info, out int result);
  198. if (status != ParsingStatus.OK)
  199. {
  200. ThrowOverflowOrFormatException(status, TypeCode.Int32);
  201. }
  202. return result;
  203. }
  204. internal static long ParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
  205. {
  206. ParsingStatus status = TryParseInt64(value, styles, info, out long result);
  207. if (status != ParsingStatus.OK)
  208. {
  209. ThrowOverflowOrFormatException(status, TypeCode.Int64);
  210. }
  211. return result;
  212. }
  213. internal static uint ParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
  214. {
  215. ParsingStatus status = TryParseUInt32(value, styles, info, out uint result);
  216. if (status != ParsingStatus.OK)
  217. {
  218. ThrowOverflowOrFormatException(status, TypeCode.UInt32);
  219. }
  220. return result;
  221. }
  222. internal static ulong ParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
  223. {
  224. ParsingStatus status = TryParseUInt64(value, styles, info, out ulong result);
  225. if (status != ParsingStatus.OK)
  226. {
  227. ThrowOverflowOrFormatException(status, TypeCode.UInt64);
  228. }
  229. return result;
  230. }
  231. private static unsafe bool TryParseNumber(ref char* str, char* strEnd, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
  232. {
  233. Debug.Assert(str != null);
  234. Debug.Assert(strEnd != null);
  235. Debug.Assert(str <= strEnd);
  236. Debug.Assert((styles & NumberStyles.AllowHexSpecifier) == 0);
  237. const int StateSign = 0x0001;
  238. const int StateParens = 0x0002;
  239. const int StateDigits = 0x0004;
  240. const int StateNonZero = 0x0008;
  241. const int StateDecimal = 0x0010;
  242. const int StateCurrency = 0x0020;
  243. Debug.Assert(number.DigitsCount == 0);
  244. Debug.Assert(number.Scale == 0);
  245. Debug.Assert(!number.IsNegative);
  246. Debug.Assert(!number.HasNonZeroTail);
  247. number.CheckConsistency();
  248. string decSep; // decimal separator from NumberFormatInfo.
  249. string groupSep; // group separator from NumberFormatInfo.
  250. string? currSymbol = null; // currency symbol from NumberFormatInfo.
  251. bool parsingCurrency = false;
  252. if ((styles & NumberStyles.AllowCurrencySymbol) != 0)
  253. {
  254. currSymbol = info.CurrencySymbol;
  255. // 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.
  256. // The values of decSep are setup to use the correct relevant separator (currency in the if part and decimal in the else part).
  257. decSep = info.CurrencyDecimalSeparator;
  258. groupSep = info.CurrencyGroupSeparator;
  259. parsingCurrency = true;
  260. }
  261. else
  262. {
  263. decSep = info.NumberDecimalSeparator;
  264. groupSep = info.NumberGroupSeparator;
  265. }
  266. int state = 0;
  267. char* p = str;
  268. char ch = p < strEnd ? *p : '\0';
  269. char* next;
  270. while (true)
  271. {
  272. // Eat whitespace unless we've found a sign which isn't followed by a currency symbol.
  273. // "-Kr 1231.47" is legal but "- 1231.47" is not.
  274. if (!IsWhite(ch) || (styles & NumberStyles.AllowLeadingWhite) == 0 || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && info.NumberNegativePattern != 2)))
  275. {
  276. 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))))
  277. {
  278. state |= StateSign;
  279. p = next - 1;
  280. }
  281. else if (ch == '(' && ((styles & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0))
  282. {
  283. state |= StateSign | StateParens;
  284. number.IsNegative = true;
  285. }
  286. else if (currSymbol != null && (next = MatchChars(p, strEnd, currSymbol)) != null)
  287. {
  288. state |= StateCurrency;
  289. currSymbol = null;
  290. // We already found the currency symbol. There should not be more currency symbols. Set
  291. // currSymbol to NULL so that we won't search it again in the later code path.
  292. p = next - 1;
  293. }
  294. else
  295. {
  296. break;
  297. }
  298. }
  299. ch = ++p < strEnd ? *p : '\0';
  300. }
  301. int digCount = 0;
  302. int digEnd = 0;
  303. int maxDigCount = number.Digits.Length - 1;
  304. while (true)
  305. {
  306. if (IsDigit(ch))
  307. {
  308. state |= StateDigits;
  309. if (ch != '0' || (state & StateNonZero) != 0)
  310. {
  311. if (digCount < maxDigCount)
  312. {
  313. number.Digits[digCount++] = (byte)(ch);
  314. if ((ch != '0') || (number.Kind != NumberBufferKind.Integer))
  315. {
  316. digEnd = digCount;
  317. }
  318. }
  319. else if (ch != '0')
  320. {
  321. // For decimal and binary floating-point numbers, we only
  322. // need to store digits up to maxDigCount. However, we still
  323. // need to keep track of whether any additional digits past
  324. // maxDigCount were non-zero, as that can impact rounding
  325. // for an input that falls evenly between two representable
  326. // results.
  327. number.HasNonZeroTail = true;
  328. }
  329. if ((state & StateDecimal) == 0)
  330. {
  331. number.Scale++;
  332. }
  333. state |= StateNonZero;
  334. }
  335. else if ((state & StateDecimal) != 0)
  336. {
  337. number.Scale--;
  338. }
  339. }
  340. 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))
  341. {
  342. state |= StateDecimal;
  343. p = next - 1;
  344. }
  345. 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))
  346. {
  347. p = next - 1;
  348. }
  349. else
  350. {
  351. break;
  352. }
  353. ch = ++p < strEnd ? *p : '\0';
  354. }
  355. bool negExp = false;
  356. number.DigitsCount = digEnd;
  357. number.Digits[digEnd] = (byte)('\0');
  358. if ((state & StateDigits) != 0)
  359. {
  360. if ((ch == 'E' || ch == 'e') && ((styles & NumberStyles.AllowExponent) != 0))
  361. {
  362. char* temp = p;
  363. ch = ++p < strEnd ? *p : '\0';
  364. if ((next = MatchChars(p, strEnd, info._positiveSign)) != null)
  365. {
  366. ch = (p = next) < strEnd ? *p : '\0';
  367. }
  368. else if ((next = MatchChars(p, strEnd, info._negativeSign)) != null)
  369. {
  370. ch = (p = next) < strEnd ? *p : '\0';
  371. negExp = true;
  372. }
  373. if (IsDigit(ch))
  374. {
  375. int exp = 0;
  376. do
  377. {
  378. exp = exp * 10 + (ch - '0');
  379. ch = ++p < strEnd ? *p : '\0';
  380. if (exp > 1000)
  381. {
  382. exp = 9999;
  383. while (IsDigit(ch))
  384. {
  385. ch = ++p < strEnd ? *p : '\0';
  386. }
  387. }
  388. } while (IsDigit(ch));
  389. if (negExp)
  390. {
  391. exp = -exp;
  392. }
  393. number.Scale += exp;
  394. }
  395. else
  396. {
  397. p = temp;
  398. ch = p < strEnd ? *p : '\0';
  399. }
  400. }
  401. while (true)
  402. {
  403. if (!IsWhite(ch) || (styles & NumberStyles.AllowTrailingWhite) == 0)
  404. {
  405. 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))))
  406. {
  407. state |= StateSign;
  408. p = next - 1;
  409. }
  410. else if (ch == ')' && ((state & StateParens) != 0))
  411. {
  412. state &= ~StateParens;
  413. }
  414. else if (currSymbol != null && (next = MatchChars(p, strEnd, currSymbol)) != null)
  415. {
  416. currSymbol = null;
  417. p = next - 1;
  418. }
  419. else
  420. {
  421. break;
  422. }
  423. }
  424. ch = ++p < strEnd ? *p : '\0';
  425. }
  426. if ((state & StateParens) == 0)
  427. {
  428. if ((state & StateNonZero) == 0)
  429. {
  430. if (number.Kind != NumberBufferKind.Decimal)
  431. {
  432. number.Scale = 0;
  433. }
  434. if ((number.Kind == NumberBufferKind.Integer) && (state & StateDecimal) == 0)
  435. {
  436. number.IsNegative = false;
  437. }
  438. }
  439. str = p;
  440. return true;
  441. }
  442. }
  443. str = p;
  444. return false;
  445. }
  446. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  447. internal static ParsingStatus TryParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
  448. {
  449. if ((styles & ~NumberStyles.Integer) == 0)
  450. {
  451. // Optimized path for the common case of anything that's allowed for integer style.
  452. return TryParseInt32IntegerStyle(value, styles, info, out result);
  453. }
  454. if ((styles & NumberStyles.AllowHexSpecifier) != 0)
  455. {
  456. result = 0;
  457. return TryParseUInt32HexNumberStyle(value, styles, out Unsafe.As<int, uint>(ref result));
  458. }
  459. return TryParseInt32Number(value, styles, info, out result);
  460. }
  461. private static unsafe ParsingStatus TryParseInt32Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
  462. {
  463. result = 0;
  464. byte* pDigits = stackalloc byte[Int32NumberBufferLength];
  465. NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength);
  466. if (!TryStringToNumber(value, styles, ref number, info))
  467. {
  468. return ParsingStatus.Failed;
  469. }
  470. if (!TryNumberToInt32(ref number, ref result))
  471. {
  472. return ParsingStatus.Overflow;
  473. }
  474. return ParsingStatus.OK;
  475. }
  476. /// <summary>Parses int limited to styles that make up NumberStyles.Integer.</summary>
  477. internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
  478. {
  479. Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
  480. if (value.IsEmpty)
  481. goto FalseExit;
  482. int index = 0;
  483. int num = value[0];
  484. // Skip past any whitespace at the beginning.
  485. if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
  486. {
  487. do
  488. {
  489. index++;
  490. if ((uint)index >= (uint)value.Length)
  491. goto FalseExit;
  492. num = value[index];
  493. }
  494. while (IsWhite(num));
  495. }
  496. // Parse leading sign.
  497. int sign = 1;
  498. if ((styles & NumberStyles.AllowLeadingSign) != 0)
  499. {
  500. if (info.HasInvariantNumberSigns)
  501. {
  502. if (num == '-')
  503. {
  504. sign = -1;
  505. index++;
  506. if ((uint)index >= (uint)value.Length)
  507. goto FalseExit;
  508. num = value[index];
  509. }
  510. else if (num == '+')
  511. {
  512. index++;
  513. if ((uint)index >= (uint)value.Length)
  514. goto FalseExit;
  515. num = value[index];
  516. }
  517. }
  518. else
  519. {
  520. value = value.Slice(index);
  521. index = 0;
  522. string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
  523. if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
  524. {
  525. index += positiveSign.Length;
  526. if ((uint)index >= (uint)value.Length)
  527. goto FalseExit;
  528. num = value[index];
  529. }
  530. else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
  531. {
  532. sign = -1;
  533. index += negativeSign.Length;
  534. if ((uint)index >= (uint)value.Length)
  535. goto FalseExit;
  536. num = value[index];
  537. }
  538. }
  539. }
  540. bool overflow = false;
  541. int answer = 0;
  542. if (IsDigit(num))
  543. {
  544. // Skip past leading zeros.
  545. if (num == '0')
  546. {
  547. do
  548. {
  549. index++;
  550. if ((uint)index >= (uint)value.Length)
  551. goto DoneAtEnd;
  552. num = value[index];
  553. } while (num == '0');
  554. if (!IsDigit(num))
  555. goto HasTrailingChars;
  556. }
  557. // Parse most digits, up to the potential for overflow, which can't happen until after 9 digits.
  558. answer = num - '0'; // first digit
  559. index++;
  560. for (int i = 0; i < 8; i++) // next 8 digits can't overflow
  561. {
  562. if ((uint)index >= (uint)value.Length)
  563. goto DoneAtEnd;
  564. num = value[index];
  565. if (!IsDigit(num))
  566. goto HasTrailingChars;
  567. index++;
  568. answer = 10 * answer + num - '0';
  569. }
  570. if ((uint)index >= (uint)value.Length)
  571. goto DoneAtEnd;
  572. num = value[index];
  573. if (!IsDigit(num))
  574. goto HasTrailingChars;
  575. index++;
  576. // Potential overflow now processing the 10th digit.
  577. overflow = answer > int.MaxValue / 10;
  578. answer = answer * 10 + num - '0';
  579. overflow |= (uint)answer > int.MaxValue + (((uint)sign) >> 31);
  580. if ((uint)index >= (uint)value.Length)
  581. goto DoneAtEndButPotentialOverflow;
  582. // At this point, we're either overflowing or hitting a formatting error.
  583. // Format errors take precedence for compatibility.
  584. num = value[index];
  585. while (IsDigit(num))
  586. {
  587. overflow = true;
  588. index++;
  589. if ((uint)index >= (uint)value.Length)
  590. goto OverflowExit;
  591. num = value[index];
  592. }
  593. goto HasTrailingChars;
  594. }
  595. goto FalseExit;
  596. DoneAtEndButPotentialOverflow:
  597. if (overflow)
  598. {
  599. goto OverflowExit;
  600. }
  601. DoneAtEnd:
  602. result = answer * sign;
  603. ParsingStatus status = ParsingStatus.OK;
  604. Exit:
  605. return status;
  606. FalseExit: // parsing failed
  607. result = 0;
  608. status = ParsingStatus.Failed;
  609. goto Exit;
  610. OverflowExit:
  611. result = 0;
  612. status = ParsingStatus.Overflow;
  613. goto Exit;
  614. HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
  615. // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
  616. if (IsWhite(num))
  617. {
  618. if ((styles & NumberStyles.AllowTrailingWhite) == 0)
  619. goto FalseExit;
  620. for (index++; index < value.Length; index++)
  621. {
  622. if (!IsWhite(value[index]))
  623. break;
  624. }
  625. if ((uint)index >= (uint)value.Length)
  626. goto DoneAtEndButPotentialOverflow;
  627. }
  628. if (!TrailingZeros(value, index))
  629. goto FalseExit;
  630. goto DoneAtEndButPotentialOverflow;
  631. }
  632. /// <summary>Parses long inputs limited to styles that make up NumberStyles.Integer.</summary>
  633. internal static ParsingStatus TryParseInt64IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
  634. {
  635. Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
  636. if (value.IsEmpty)
  637. goto FalseExit;
  638. int index = 0;
  639. int num = value[0];
  640. // Skip past any whitespace at the beginning.
  641. if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
  642. {
  643. do
  644. {
  645. index++;
  646. if ((uint)index >= (uint)value.Length)
  647. goto FalseExit;
  648. num = value[index];
  649. }
  650. while (IsWhite(num));
  651. }
  652. // Parse leading sign.
  653. int sign = 1;
  654. if ((styles & NumberStyles.AllowLeadingSign) != 0)
  655. {
  656. if (info.HasInvariantNumberSigns)
  657. {
  658. if (num == '-')
  659. {
  660. sign = -1;
  661. index++;
  662. if ((uint)index >= (uint)value.Length)
  663. goto FalseExit;
  664. num = value[index];
  665. }
  666. else if (num == '+')
  667. {
  668. index++;
  669. if ((uint)index >= (uint)value.Length)
  670. goto FalseExit;
  671. num = value[index];
  672. }
  673. }
  674. else
  675. {
  676. value = value.Slice(index);
  677. index = 0;
  678. string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
  679. if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
  680. {
  681. index += positiveSign.Length;
  682. if ((uint)index >= (uint)value.Length)
  683. goto FalseExit;
  684. num = value[index];
  685. }
  686. else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
  687. {
  688. sign = -1;
  689. index += negativeSign.Length;
  690. if ((uint)index >= (uint)value.Length)
  691. goto FalseExit;
  692. num = value[index];
  693. }
  694. }
  695. }
  696. bool overflow = false;
  697. long answer = 0;
  698. if (IsDigit(num))
  699. {
  700. // Skip past leading zeros.
  701. if (num == '0')
  702. {
  703. do
  704. {
  705. index++;
  706. if ((uint)index >= (uint)value.Length)
  707. goto DoneAtEnd;
  708. num = value[index];
  709. } while (num == '0');
  710. if (!IsDigit(num))
  711. goto HasTrailingChars;
  712. }
  713. // Parse most digits, up to the potential for overflow, which can't happen until after 18 digits.
  714. answer = num - '0'; // first digit
  715. index++;
  716. for (int i = 0; i < 17; i++) // next 17 digits can't overflow
  717. {
  718. if ((uint)index >= (uint)value.Length)
  719. goto DoneAtEnd;
  720. num = value[index];
  721. if (!IsDigit(num))
  722. goto HasTrailingChars;
  723. index++;
  724. answer = 10 * answer + num - '0';
  725. }
  726. if ((uint)index >= (uint)value.Length)
  727. goto DoneAtEnd;
  728. num = value[index];
  729. if (!IsDigit(num))
  730. goto HasTrailingChars;
  731. index++;
  732. // Potential overflow now processing the 19th digit.
  733. overflow = answer > long.MaxValue / 10;
  734. answer = answer * 10 + num - '0';
  735. overflow |= (ulong)answer > (ulong)long.MaxValue + (((uint)sign) >> 31);
  736. if ((uint)index >= (uint)value.Length)
  737. goto DoneAtEndButPotentialOverflow;
  738. // At this point, we're either overflowing or hitting a formatting error.
  739. // Format errors take precedence for compatibility.
  740. num = value[index];
  741. while (IsDigit(num))
  742. {
  743. overflow = true;
  744. index++;
  745. if ((uint)index >= (uint)value.Length)
  746. goto OverflowExit;
  747. num = value[index];
  748. }
  749. goto HasTrailingChars;
  750. }
  751. goto FalseExit;
  752. DoneAtEndButPotentialOverflow:
  753. if (overflow)
  754. {
  755. goto OverflowExit;
  756. }
  757. DoneAtEnd:
  758. result = answer * sign;
  759. ParsingStatus status = ParsingStatus.OK;
  760. Exit:
  761. return status;
  762. FalseExit: // parsing failed
  763. result = 0;
  764. status = ParsingStatus.Failed;
  765. goto Exit;
  766. OverflowExit:
  767. result = 0;
  768. status = ParsingStatus.Overflow;
  769. goto Exit;
  770. HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
  771. // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
  772. if (IsWhite(num))
  773. {
  774. if ((styles & NumberStyles.AllowTrailingWhite) == 0)
  775. goto FalseExit;
  776. for (index++; index < value.Length; index++)
  777. {
  778. if (!IsWhite(value[index]))
  779. break;
  780. }
  781. if ((uint)index >= (uint)value.Length)
  782. goto DoneAtEndButPotentialOverflow;
  783. }
  784. if (!TrailingZeros(value, index))
  785. goto FalseExit;
  786. goto DoneAtEndButPotentialOverflow;
  787. }
  788. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  789. internal static ParsingStatus TryParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
  790. {
  791. if ((styles & ~NumberStyles.Integer) == 0)
  792. {
  793. // Optimized path for the common case of anything that's allowed for integer style.
  794. return TryParseInt64IntegerStyle(value, styles, info, out result);
  795. }
  796. if ((styles & NumberStyles.AllowHexSpecifier) != 0)
  797. {
  798. result = 0;
  799. return TryParseUInt64HexNumberStyle(value, styles, out Unsafe.As<long, ulong>(ref result));
  800. }
  801. return TryParseInt64Number(value, styles, info, out result);
  802. }
  803. private static unsafe ParsingStatus TryParseInt64Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
  804. {
  805. result = 0;
  806. byte* pDigits = stackalloc byte[Int64NumberBufferLength];
  807. NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength);
  808. if (!TryStringToNumber(value, styles, ref number, info))
  809. {
  810. return ParsingStatus.Failed;
  811. }
  812. if (!TryNumberToInt64(ref number, ref result))
  813. {
  814. return ParsingStatus.Overflow;
  815. }
  816. return ParsingStatus.OK;
  817. }
  818. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  819. internal static ParsingStatus TryParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
  820. {
  821. if ((styles & ~NumberStyles.Integer) == 0)
  822. {
  823. // Optimized path for the common case of anything that's allowed for integer style.
  824. return TryParseUInt32IntegerStyle(value, styles, info, out result);
  825. }
  826. if ((styles & NumberStyles.AllowHexSpecifier) != 0)
  827. {
  828. return TryParseUInt32HexNumberStyle(value, styles, out result);
  829. }
  830. return TryParseUInt32Number(value, styles, info, out result);
  831. }
  832. private static unsafe ParsingStatus TryParseUInt32Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
  833. {
  834. result = 0;
  835. byte* pDigits = stackalloc byte[UInt32NumberBufferLength];
  836. NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength);
  837. if (!TryStringToNumber(value, styles, ref number, info))
  838. {
  839. return ParsingStatus.Failed;
  840. }
  841. if (!TryNumberToUInt32(ref number, ref result))
  842. {
  843. return ParsingStatus.Overflow;
  844. }
  845. return ParsingStatus.OK;
  846. }
  847. /// <summary>Parses uint limited to styles that make up NumberStyles.Integer.</summary>
  848. internal static ParsingStatus TryParseUInt32IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
  849. {
  850. Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
  851. if (value.IsEmpty)
  852. goto FalseExit;
  853. int index = 0;
  854. int num = value[0];
  855. // Skip past any whitespace at the beginning.
  856. if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
  857. {
  858. do
  859. {
  860. index++;
  861. if ((uint)index >= (uint)value.Length)
  862. goto FalseExit;
  863. num = value[index];
  864. }
  865. while (IsWhite(num));
  866. }
  867. // Parse leading sign.
  868. bool overflow = false;
  869. if ((styles & NumberStyles.AllowLeadingSign) != 0)
  870. {
  871. if (info.HasInvariantNumberSigns)
  872. {
  873. if (num == '+')
  874. {
  875. index++;
  876. if ((uint)index >= (uint)value.Length)
  877. goto FalseExit;
  878. num = value[index];
  879. }
  880. else if (num == '-')
  881. {
  882. overflow = true;
  883. index++;
  884. if ((uint)index >= (uint)value.Length)
  885. goto FalseExit;
  886. num = value[index];
  887. }
  888. }
  889. else
  890. {
  891. value = value.Slice(index);
  892. index = 0;
  893. string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
  894. if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
  895. {
  896. index += positiveSign.Length;
  897. if ((uint)index >= (uint)value.Length)
  898. goto FalseExit;
  899. num = value[index];
  900. }
  901. else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
  902. {
  903. overflow = true;
  904. index += negativeSign.Length;
  905. if ((uint)index >= (uint)value.Length)
  906. goto FalseExit;
  907. num = value[index];
  908. }
  909. }
  910. }
  911. int answer = 0;
  912. if (IsDigit(num))
  913. {
  914. // Skip past leading zeros.
  915. if (num == '0')
  916. {
  917. do
  918. {
  919. index++;
  920. if ((uint)index >= (uint)value.Length)
  921. goto DoneAtEnd;
  922. num = value[index];
  923. } while (num == '0');
  924. if (!IsDigit(num))
  925. goto HasTrailingCharsZero;
  926. }
  927. // Parse most digits, up to the potential for overflow, which can't happen until after 9 digits.
  928. answer = num - '0'; // first digit
  929. index++;
  930. for (int i = 0; i < 8; i++) // next 8 digits can't overflow
  931. {
  932. if ((uint)index >= (uint)value.Length)
  933. goto DoneAtEndButPotentialOverflow;
  934. num = value[index];
  935. if (!IsDigit(num))
  936. goto HasTrailingChars;
  937. index++;
  938. answer = 10 * answer + num - '0';
  939. }
  940. if ((uint)index >= (uint)value.Length)
  941. goto DoneAtEndButPotentialOverflow;
  942. num = value[index];
  943. if (!IsDigit(num))
  944. goto HasTrailingChars;
  945. index++;
  946. // Potential overflow now processing the 10th digit.
  947. overflow |= (uint)answer > uint.MaxValue / 10 || ((uint)answer == uint.MaxValue / 10 && num > '5');
  948. answer = answer * 10 + num - '0';
  949. if ((uint)index >= (uint)value.Length)
  950. goto DoneAtEndButPotentialOverflow;
  951. // At this point, we're either overflowing or hitting a formatting error.
  952. // Format errors take precedence for compatibility.
  953. num = value[index];
  954. while (IsDigit(num))
  955. {
  956. overflow = true;
  957. index++;
  958. if ((uint)index >= (uint)value.Length)
  959. goto OverflowExit;
  960. num = value[index];
  961. }
  962. goto HasTrailingChars;
  963. }
  964. goto FalseExit;
  965. DoneAtEndButPotentialOverflow:
  966. if (overflow)
  967. {
  968. goto OverflowExit;
  969. }
  970. DoneAtEnd:
  971. result = (uint)answer;
  972. ParsingStatus status = ParsingStatus.OK;
  973. Exit:
  974. return status;
  975. FalseExit: // parsing failed
  976. result = 0;
  977. status = ParsingStatus.Failed;
  978. goto Exit;
  979. OverflowExit:
  980. result = 0;
  981. status = ParsingStatus.Overflow;
  982. goto Exit;
  983. HasTrailingCharsZero:
  984. overflow = false;
  985. HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
  986. // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
  987. if (IsWhite(num))
  988. {
  989. if ((styles & NumberStyles.AllowTrailingWhite) == 0)
  990. goto FalseExit;
  991. for (index++; index < value.Length; index++)
  992. {
  993. if (!IsWhite(value[index]))
  994. break;
  995. }
  996. if ((uint)index >= (uint)value.Length)
  997. goto DoneAtEndButPotentialOverflow;
  998. }
  999. if (!TrailingZeros(value, index))
  1000. goto FalseExit;
  1001. goto DoneAtEndButPotentialOverflow;
  1002. }
  1003. /// <summary>Parses uint limited to styles that make up NumberStyles.HexNumber.</summary>
  1004. private static ParsingStatus TryParseUInt32HexNumberStyle(ReadOnlySpan<char> value, NumberStyles styles, out uint result)
  1005. {
  1006. Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format");
  1007. if (value.IsEmpty)
  1008. goto FalseExit;
  1009. int index = 0;
  1010. int num = value[0];
  1011. // Skip past any whitespace at the beginning.
  1012. if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
  1013. {
  1014. do
  1015. {
  1016. index++;
  1017. if ((uint)index >= (uint)value.Length)
  1018. goto FalseExit;
  1019. num = value[index];
  1020. }
  1021. while (IsWhite(num));
  1022. }
  1023. bool overflow = false;
  1024. uint answer = 0;
  1025. ReadOnlySpan<byte> charToHexLookup = CharToHexLookup;
  1026. if ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
  1027. {
  1028. // Skip past leading zeros.
  1029. if (num == '0')
  1030. {
  1031. do
  1032. {
  1033. index++;
  1034. if ((uint)index >= (uint)value.Length)
  1035. goto DoneAtEnd;
  1036. num = value[index];
  1037. } while (num == '0');
  1038. if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF)
  1039. goto HasTrailingChars;
  1040. }
  1041. // Parse up through 8 digits, as no overflow is possible
  1042. answer = charToHexLookup[num]; // first digit
  1043. index++;
  1044. for (int i = 0; i < 7; i++) // next 7 digits can't overflow
  1045. {
  1046. if ((uint)index >= (uint)value.Length)
  1047. goto DoneAtEnd;
  1048. num = value[index];
  1049. uint numValue;
  1050. if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
  1051. goto HasTrailingChars;
  1052. index++;
  1053. answer = 16 * answer + numValue;
  1054. }
  1055. // If there's another digit, it's an overflow.
  1056. if ((uint)index >= (uint)value.Length)
  1057. goto DoneAtEnd;
  1058. num = value[index];
  1059. if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF)
  1060. goto HasTrailingChars;
  1061. // At this point, we're either overflowing or hitting a formatting error.
  1062. // Format errors take precedence for compatibility. Read through any remaining digits.
  1063. do
  1064. {
  1065. index++;
  1066. if ((uint)index >= (uint)value.Length)
  1067. goto OverflowExit;
  1068. num = value[index];
  1069. } while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF);
  1070. overflow = true;
  1071. goto HasTrailingChars;
  1072. }
  1073. goto FalseExit;
  1074. DoneAtEndButPotentialOverflow:
  1075. if (overflow)
  1076. {
  1077. goto OverflowExit;
  1078. }
  1079. DoneAtEnd:
  1080. result = answer;
  1081. ParsingStatus status = ParsingStatus.OK;
  1082. Exit:
  1083. return status;
  1084. FalseExit: // parsing failed
  1085. result = 0;
  1086. status = ParsingStatus.Failed;
  1087. goto Exit;
  1088. OverflowExit:
  1089. result = 0;
  1090. status = ParsingStatus.Overflow;
  1091. goto Exit;
  1092. HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
  1093. // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
  1094. if (IsWhite(num))
  1095. {
  1096. if ((styles & NumberStyles.AllowTrailingWhite) == 0)
  1097. goto FalseExit;
  1098. for (index++; index < value.Length; index++)
  1099. {
  1100. if (!IsWhite(value[index]))
  1101. break;
  1102. }
  1103. if ((uint)index >= (uint)value.Length)
  1104. goto DoneAtEndButPotentialOverflow;
  1105. }
  1106. if (!TrailingZeros(value, index))
  1107. goto FalseExit;
  1108. goto DoneAtEndButPotentialOverflow;
  1109. }
  1110. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1111. internal static ParsingStatus TryParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
  1112. {
  1113. if ((styles & ~NumberStyles.Integer) == 0)
  1114. {
  1115. // Optimized path for the common case of anything that's allowed for integer style.
  1116. return TryParseUInt64IntegerStyle(value, styles, info, out result);
  1117. }
  1118. if ((styles & NumberStyles.AllowHexSpecifier) != 0)
  1119. {
  1120. return TryParseUInt64HexNumberStyle(value, styles, out result);
  1121. }
  1122. return TryParseUInt64Number(value, styles, info, out result);
  1123. }
  1124. private static unsafe ParsingStatus TryParseUInt64Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
  1125. {
  1126. result = 0;
  1127. byte* pDigits = stackalloc byte[UInt64NumberBufferLength];
  1128. NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt64NumberBufferLength);
  1129. if (!TryStringToNumber(value, styles, ref number, info))
  1130. {
  1131. return ParsingStatus.Failed;
  1132. }
  1133. if (!TryNumberToUInt64(ref number, ref result))
  1134. {
  1135. return ParsingStatus.Overflow;
  1136. }
  1137. return ParsingStatus.OK;
  1138. }
  1139. /// <summary>Parses ulong limited to styles that make up NumberStyles.Integer.</summary>
  1140. internal static ParsingStatus TryParseUInt64IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
  1141. {
  1142. Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
  1143. if (value.IsEmpty)
  1144. goto FalseExit;
  1145. int index = 0;
  1146. int num = value[0];
  1147. // Skip past any whitespace at the beginning.
  1148. if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
  1149. {
  1150. do
  1151. {
  1152. index++;
  1153. if ((uint)index >= (uint)value.Length)
  1154. goto FalseExit;
  1155. num = value[index];
  1156. }
  1157. while (IsWhite(num));
  1158. }
  1159. // Parse leading sign.
  1160. bool overflow = false;
  1161. if ((styles & NumberStyles.AllowLeadingSign) != 0)
  1162. {
  1163. if (info.HasInvariantNumberSigns)
  1164. {
  1165. if (num == '+')
  1166. {
  1167. index++;
  1168. if ((uint)index >= (uint)value.Length)
  1169. goto FalseExit;
  1170. num = value[index];
  1171. }
  1172. else if (num == '-')
  1173. {
  1174. overflow = true;
  1175. index++;
  1176. if ((uint)index >= (uint)value.Length)
  1177. goto FalseExit;
  1178. num = value[index];
  1179. }
  1180. }
  1181. else
  1182. {
  1183. value = value.Slice(index);
  1184. index = 0;
  1185. string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
  1186. if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
  1187. {
  1188. index += positiveSign.Length;
  1189. if ((uint)index >= (uint)value.Length)
  1190. goto FalseExit;
  1191. num = value[index];
  1192. }
  1193. else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
  1194. {
  1195. overflow = true;
  1196. index += negativeSign.Length;
  1197. if ((uint)index >= (uint)value.Length)
  1198. goto FalseExit;
  1199. num = value[index];
  1200. }
  1201. }
  1202. }
  1203. long answer = 0;
  1204. if (IsDigit(num))
  1205. {
  1206. // Skip past leading zeros.
  1207. if (num == '0')
  1208. {
  1209. do
  1210. {
  1211. index++;
  1212. if ((uint)index >= (uint)value.Length)
  1213. goto DoneAtEnd;
  1214. num = value[index];
  1215. } while (num == '0');
  1216. if (!IsDigit(num))
  1217. goto HasTrailingCharsZero;
  1218. }
  1219. // Parse most digits, up to the potential for overflow, which can't happen until after 19 digits.
  1220. answer = num - '0'; // first digit
  1221. index++;
  1222. for (int i = 0; i < 18; i++) // next 18 digits can't overflow
  1223. {
  1224. if ((uint)index >= (uint)value.Length)
  1225. goto DoneAtEndButPotentialOverflow;
  1226. num = value[index];
  1227. if (!IsDigit(num))
  1228. goto HasTrailingChars;
  1229. index++;
  1230. answer = 10 * answer + num - '0';
  1231. }
  1232. if ((uint)index >= (uint)value.Length)
  1233. goto DoneAtEndButPotentialOverflow;
  1234. num = value[index];
  1235. if (!IsDigit(num))
  1236. goto HasTrailingChars;
  1237. index++;
  1238. // Potential overflow now processing the 20th digit.
  1239. overflow |= (ulong)answer > ulong.MaxValue / 10 || ((ulong)answer == ulong.MaxValue / 10 && num > '5');
  1240. answer = answer * 10 + num - '0';
  1241. if ((uint)index >= (uint)value.Length)
  1242. goto DoneAtEndButPotentialOverflow;
  1243. // At this point, we're either overflowing or hitting a formatting error.
  1244. // Format errors take precedence for compatibility.
  1245. num = value[index];
  1246. while (IsDigit(num))
  1247. {
  1248. overflow = true;
  1249. index++;
  1250. if ((uint)index >= (uint)value.Length)
  1251. goto OverflowExit;
  1252. num = value[index];
  1253. }
  1254. goto HasTrailingChars;
  1255. }
  1256. goto FalseExit;
  1257. DoneAtEndButPotentialOverflow:
  1258. if (overflow)
  1259. {
  1260. goto OverflowExit;
  1261. }
  1262. DoneAtEnd:
  1263. result = (ulong)answer;
  1264. ParsingStatus status = ParsingStatus.OK;
  1265. Exit:
  1266. return status;
  1267. FalseExit: // parsing failed
  1268. result = 0;
  1269. status = ParsingStatus.Failed;
  1270. goto Exit;
  1271. OverflowExit:
  1272. result = 0;
  1273. status = ParsingStatus.Overflow;
  1274. goto Exit;
  1275. HasTrailingCharsZero:
  1276. overflow = false;
  1277. HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
  1278. // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
  1279. if (IsWhite(num))
  1280. {
  1281. if ((styles & NumberStyles.AllowTrailingWhite) == 0)
  1282. goto FalseExit;
  1283. for (index++; index < value.Length; index++)
  1284. {
  1285. if (!IsWhite(value[index]))
  1286. break;
  1287. }
  1288. if ((uint)index >= (uint)value.Length)
  1289. goto DoneAtEndButPotentialOverflow;
  1290. }
  1291. if (!TrailingZeros(value, index))
  1292. goto FalseExit;
  1293. goto DoneAtEndButPotentialOverflow;
  1294. }
  1295. /// <summary>Parses ulong limited to styles that make up NumberStyles.HexNumber.</summary>
  1296. private static ParsingStatus TryParseUInt64HexNumberStyle(ReadOnlySpan<char> value, NumberStyles styles, out ulong result)
  1297. {
  1298. Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format");
  1299. if (value.IsEmpty)
  1300. goto FalseExit;
  1301. int index = 0;
  1302. int num = value[0];
  1303. // Skip past any whitespace at the beginning.
  1304. if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
  1305. {
  1306. do
  1307. {
  1308. index++;
  1309. if ((uint)index >= (uint)value.Length)
  1310. goto FalseExit;
  1311. num = value[index];
  1312. }
  1313. while (IsWhite(num));
  1314. }
  1315. bool overflow = false;
  1316. ulong answer = 0;
  1317. ReadOnlySpan<byte> charToHexLookup = CharToHexLookup;
  1318. if ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
  1319. {
  1320. // Skip past leading zeros.
  1321. if (num == '0')
  1322. {
  1323. do
  1324. {
  1325. index++;
  1326. if ((uint)index >= (uint)value.Length)
  1327. goto DoneAtEnd;
  1328. num = value[index];
  1329. } while (num == '0');
  1330. if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF)
  1331. goto HasTrailingChars;
  1332. }
  1333. // Parse up through 16 digits, as no overflow is possible
  1334. answer = charToHexLookup[num]; // first digit
  1335. index++;
  1336. for (int i = 0; i < 15; i++) // next 15 digits can't overflow
  1337. {
  1338. if ((uint)index >= (uint)value.Length)
  1339. goto DoneAtEnd;
  1340. num = value[index];
  1341. uint numValue;
  1342. if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF)
  1343. goto HasTrailingChars;
  1344. index++;
  1345. answer = 16 * answer + numValue;
  1346. }
  1347. // If there's another digit, it's an overflow.
  1348. if ((uint)index >= (uint)value.Length)
  1349. goto DoneAtEnd;
  1350. num = value[index];
  1351. if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF)
  1352. goto HasTrailingChars;
  1353. // At this point, we're either overflowing or hitting a formatting error.
  1354. // Format errors take precedence for compatibility. Read through any remaining digits.
  1355. do
  1356. {
  1357. index++;
  1358. if ((uint)index >= (uint)value.Length)
  1359. goto OverflowExit;
  1360. num = value[index];
  1361. } while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF);
  1362. overflow = true;
  1363. goto HasTrailingChars;
  1364. }
  1365. goto FalseExit;
  1366. DoneAtEndButPotentialOverflow:
  1367. if (overflow)
  1368. {
  1369. goto OverflowExit;
  1370. }
  1371. DoneAtEnd:
  1372. result = answer;
  1373. ParsingStatus status = ParsingStatus.OK;
  1374. Exit:
  1375. return status;
  1376. FalseExit: // parsing failed
  1377. result = 0;
  1378. status = ParsingStatus.Failed;
  1379. goto Exit;
  1380. OverflowExit:
  1381. result = 0;
  1382. status = ParsingStatus.Overflow;
  1383. goto Exit;
  1384. HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
  1385. // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
  1386. if (IsWhite(num))
  1387. {
  1388. if ((styles & NumberStyles.AllowTrailingWhite) == 0)
  1389. goto FalseExit;
  1390. for (index++; index < value.Length; index++)
  1391. {
  1392. if (!IsWhite(value[index]))
  1393. break;
  1394. }
  1395. if ((uint)index >= (uint)value.Length)
  1396. goto DoneAtEndButPotentialOverflow;
  1397. }
  1398. if (!TrailingZeros(value, index))
  1399. goto FalseExit;
  1400. goto DoneAtEndButPotentialOverflow;
  1401. }
  1402. internal static decimal ParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
  1403. {
  1404. ParsingStatus status = TryParseDecimal(value, styles, info, out decimal result);
  1405. if (status != ParsingStatus.OK)
  1406. {
  1407. ThrowOverflowOrFormatException(status, TypeCode.Decimal);
  1408. }
  1409. return result;
  1410. }
  1411. internal static unsafe bool TryNumberToDecimal(ref NumberBuffer number, ref decimal value)
  1412. {
  1413. number.CheckConsistency();
  1414. byte* p = number.GetDigitsPointer();
  1415. int e = number.Scale;
  1416. bool sign = number.IsNegative;
  1417. uint c = *p;
  1418. if (c == 0)
  1419. {
  1420. // 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
  1421. // the scale to 0 if the scale was previously positive (previously, such cases were unparsable to a bug.)
  1422. value = new decimal(0, 0, 0, sign, (byte)Math.Clamp(-e, 0, 28));
  1423. return true;
  1424. }
  1425. if (e > DecimalPrecision)
  1426. return false;
  1427. ulong low64 = 0;
  1428. while (e > -28)
  1429. {
  1430. e--;
  1431. low64 *= 10;
  1432. low64 += c - '0';
  1433. c = *++p;
  1434. if (low64 >= ulong.MaxValue / 10)
  1435. break;
  1436. if (c == 0)
  1437. {
  1438. while (e > 0)
  1439. {
  1440. e--;
  1441. low64 *= 10;
  1442. if (low64 >= ulong.MaxValue / 10)
  1443. break;
  1444. }
  1445. break;
  1446. }
  1447. }
  1448. uint high = 0;
  1449. while ((e > 0 || (c != 0 && e > -28)) &&
  1450. (high < uint.MaxValue / 10 || (high == uint.MaxValue / 10 && (low64 < 0x99999999_99999999 || (low64 == 0x99999999_99999999 && c <= '5')))))
  1451. {
  1452. // multiply by 10
  1453. ulong tmpLow = (uint)low64 * 10UL;
  1454. ulong tmp64 = (uint)(low64 >> 32) * 10UL + (tmpLow >> 32);
  1455. low64 = (uint)tmpLow + (tmp64 << 32);
  1456. high = (uint)(tmp64 >> 32) + high * 10;
  1457. if (c != 0)
  1458. {
  1459. c -= '0';
  1460. low64 += c;
  1461. if (low64 < c)
  1462. high++;
  1463. c = *++p;
  1464. }
  1465. e--;
  1466. }
  1467. if (c >= '5')
  1468. {
  1469. if ((c == '5') && ((low64 & 1) == 0))
  1470. {
  1471. c = *++p;
  1472. bool hasZeroTail = !number.HasNonZeroTail;
  1473. // We might still have some additional digits, in which case they need
  1474. // to be considered as part of hasZeroTail. Some examples of this are:
  1475. // * 3.0500000000000000000001e-27
  1476. // * 3.05000000000000000000001e-27
  1477. // In these cases, we will have processed 3 and 0, and ended on 5. The
  1478. // buffer, however, will still contain a number of trailing zeros and
  1479. // a trailing non-zero number.
  1480. while ((c != 0) && hasZeroTail)
  1481. {
  1482. hasZeroTail &= (c == '0');
  1483. c = *++p;
  1484. }
  1485. // We should either be at the end of the stream or have a non-zero tail
  1486. Debug.Assert((c == 0) || !hasZeroTail);
  1487. if (hasZeroTail)
  1488. {
  1489. // When the next digit is 5, the number is even, and all following
  1490. // digits are zero we don't need to round.
  1491. goto NoRounding;
  1492. }
  1493. }
  1494. if (++low64 == 0 && ++high == 0)
  1495. {
  1496. low64 = 0x99999999_9999999A;
  1497. high = uint.MaxValue / 10;
  1498. e++;
  1499. }
  1500. }
  1501. NoRounding:
  1502. if (e > 0)
  1503. return false;
  1504. if (e <= -DecimalPrecision)
  1505. {
  1506. // Parsing a large scale zero can give you more precision than fits in the decimal.
  1507. // This should only happen for actual zeros or very small numbers that round to zero.
  1508. value = new decimal(0, 0, 0, sign, DecimalPrecision - 1);
  1509. }
  1510. else
  1511. {
  1512. value = new decimal((int)low64, (int)(low64 >> 32), (int)high, sign, (byte)-e);
  1513. }
  1514. return true;
  1515. }
  1516. internal static double ParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
  1517. {
  1518. if (!TryParseDouble(value, styles, info, out double result))
  1519. {
  1520. ThrowOverflowOrFormatException(ParsingStatus.Failed);
  1521. }
  1522. return result;
  1523. }
  1524. internal static float ParseSingle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
  1525. {
  1526. if (!TryParseSingle(value, styles, info, out float result))
  1527. {
  1528. ThrowOverflowOrFormatException(ParsingStatus.Failed);
  1529. }
  1530. return result;
  1531. }
  1532. internal static unsafe ParsingStatus TryParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out decimal result)
  1533. {
  1534. byte* pDigits = stackalloc byte[DecimalNumberBufferLength];
  1535. NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, pDigits, DecimalNumberBufferLength);
  1536. result = 0;
  1537. if (!TryStringToNumber(value, styles, ref number, info))
  1538. {
  1539. return ParsingStatus.Failed;
  1540. }
  1541. if (!TryNumberToDecimal(ref number, ref result))
  1542. {
  1543. return ParsingStatus.Overflow;
  1544. }
  1545. return ParsingStatus.OK;
  1546. }
  1547. internal static unsafe bool TryParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out double result)
  1548. {
  1549. byte* pDigits = stackalloc byte[DoubleNumberBufferLength];
  1550. NumberBuffer number = new NumberBuffer(NumberBufferKind.FloatingPoint, pDigits, DoubleNumberBufferLength);
  1551. if (!TryStringToNumber(value, styles, ref number, info))
  1552. {
  1553. ReadOnlySpan<char> valueTrim = value.Trim();
  1554. // This code would be simpler if we only had the concept of `InfinitySymbol`, but
  1555. // we don't so we'll check the existing cases first and then handle `PositiveSign` +
  1556. // `PositiveInfinitySymbol` and `PositiveSign/NegativeSign` + `NaNSymbol` last.
  1557. if (valueTrim.EqualsOrdinalIgnoreCase(info.PositiveInfinitySymbol))
  1558. {
  1559. result = double.PositiveInfinity;
  1560. }
  1561. else if (valueTrim.EqualsOrdinalIgnoreCase(info.NegativeInfinitySymbol))
  1562. {
  1563. result = double.NegativeInfinity;
  1564. }
  1565. else if (valueTrim.EqualsOrdinalIgnoreCase(info.NaNSymbol))
  1566. {
  1567. result = double.NaN;
  1568. }
  1569. else if (valueTrim.StartsWith(info.PositiveSign, StringComparison.OrdinalIgnoreCase))
  1570. {
  1571. valueTrim = valueTrim.Slice(info.PositiveSign.Length);
  1572. if (valueTrim.EqualsOrdinalIgnoreCase(info.PositiveInfinitySymbol))
  1573. {
  1574. result = double.PositiveInfinity;
  1575. }
  1576. else if (valueTrim.EqualsOrdinalIgnoreCase(info.NaNSymbol))
  1577. {
  1578. result = double.NaN;
  1579. }
  1580. else
  1581. {
  1582. result = 0;
  1583. return false;
  1584. }
  1585. }
  1586. else if (valueTrim.StartsWith(info.NegativeSign, StringComparison.OrdinalIgnoreCase) &&
  1587. valueTrim.Slice(info.NegativeSign.Length).EqualsOrdinalIgnoreCase(info.NaNSymbol))
  1588. {
  1589. result = double.NaN;
  1590. }
  1591. else
  1592. {
  1593. result = 0;
  1594. return false; // We really failed
  1595. }
  1596. }
  1597. else
  1598. {
  1599. result = NumberToDouble(ref number);
  1600. }
  1601. return true;
  1602. }
  1603. internal static unsafe bool TryParseSingle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out float result)
  1604. {
  1605. byte* pDigits = stackalloc byte[SingleNumberBufferLength];
  1606. NumberBuffer number = new NumberBuffer(NumberBufferKind.FloatingPoint, pDigits, SingleNumberBufferLength);
  1607. if (!TryStringToNumber(value, styles, ref number, info))
  1608. {
  1609. ReadOnlySpan<char> valueTrim = value.Trim();
  1610. // This code would be simpler if we only had the concept of `InfinitySymbol`, but
  1611. // we don't so we'll check the existing cases first and then handle `PositiveSign` +
  1612. // `PositiveInfinitySymbol` and `PositiveSign/NegativeSign` + `NaNSymbol` last.
  1613. //
  1614. // Additionally, since some cultures ("wo") actually define `PositiveInfinitySymbol`
  1615. // to include `PositiveSign`, we need to check whether `PositiveInfinitySymbol` fits
  1616. // that case so that we don't start parsing things like `++infini`.
  1617. if (valueTrim.EqualsOrdinalIgnoreCase(info.PositiveInfinitySymbol))
  1618. {
  1619. result = float.PositiveInfinity;
  1620. }
  1621. else if (valueTrim.EqualsOrdinalIgnoreCase(info.NegativeInfinitySymbol))
  1622. {
  1623. result = float.NegativeInfinity;
  1624. }
  1625. else if (valueTrim.EqualsOrdinalIgnoreCase(info.NaNSymbol))
  1626. {
  1627. result = float.NaN;
  1628. }
  1629. else if (valueTrim.StartsWith(info.PositiveSign, StringComparison.OrdinalIgnoreCase))
  1630. {
  1631. valueTrim = valueTrim.Slice(info.PositiveSign.Length);
  1632. if (!info.PositiveInfinitySymbol.StartsWith(info.PositiveSign, StringComparison.OrdinalIgnoreCase) && valueTrim.EqualsOrdinalIgnoreCase(info.PositiveInfinitySymbol))
  1633. {
  1634. result = float.PositiveInfinity;
  1635. }
  1636. else if (!info.NaNSymbol.StartsWith(info.PositiveSign, StringComparison.OrdinalIgnoreCase) && valueTrim.EqualsOrdinalIgnoreCase(info.NaNSymbol))
  1637. {
  1638. result = float.NaN;
  1639. }
  1640. else
  1641. {
  1642. result = 0;
  1643. return false;
  1644. }
  1645. }
  1646. else if (valueTrim.StartsWith(info.NegativeSign, StringComparison.OrdinalIgnoreCase) &&
  1647. !info.NaNSymbol.StartsWith(info.NegativeSign, StringComparison.OrdinalIgnoreCase) &&
  1648. valueTrim.Slice(info.NegativeSign.Length).EqualsOrdinalIgnoreCase(info.NaNSymbol))
  1649. {
  1650. result = float.NaN;
  1651. }
  1652. else
  1653. {
  1654. result = 0;
  1655. return false; // We really failed
  1656. }
  1657. }
  1658. else
  1659. {
  1660. result = NumberToSingle(ref number);
  1661. }
  1662. return true;
  1663. }
  1664. internal static unsafe bool TryStringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
  1665. {
  1666. Debug.Assert(info != null);
  1667. fixed (char* stringPointer = &MemoryMarshal.GetReference(value))
  1668. {
  1669. char* p = stringPointer;
  1670. if (!TryParseNumber(ref p, p + value.Length, styles, ref number, info)
  1671. || ((int)(p - stringPointer) < value.Length && !TrailingZeros(value, (int)(p - stringPointer))))
  1672. {
  1673. number.CheckConsistency();
  1674. return false;
  1675. }
  1676. }
  1677. number.CheckConsistency();
  1678. return true;
  1679. }
  1680. private static bool TrailingZeros(ReadOnlySpan<char> value, int index)
  1681. {
  1682. // For compatibility, we need to allow trailing zeros at the end of a number string
  1683. for (int i = index; (uint)i < (uint)value.Length; i++)
  1684. {
  1685. if (value[i] != '\0')
  1686. {
  1687. return false;
  1688. }
  1689. }
  1690. return true;
  1691. }
  1692. private static bool IsSpaceReplacingChar(char c) => c == '\u00a0' || c == '\u202f';
  1693. private static unsafe char* MatchChars(char* p, char* pEnd, string value)
  1694. {
  1695. Debug.Assert(p != null && pEnd != null && p <= pEnd && value != null);
  1696. fixed (char* stringPointer = value)
  1697. {
  1698. char* str = stringPointer;
  1699. if (*str != '\0')
  1700. {
  1701. // We only hurt the failure case
  1702. // This fix is for French or Kazakh cultures. Since a user cannot type 0xA0 or 0x202F as a
  1703. // space character we use 0x20 space character instead to mean the same.
  1704. while (true)
  1705. {
  1706. char cp = p < pEnd ? *p : '\0';
  1707. if (cp != *str && !(IsSpaceReplacingChar(*str) && cp == '\u0020'))
  1708. {
  1709. break;
  1710. }
  1711. p++;
  1712. str++;
  1713. if (*str == '\0')
  1714. return p;
  1715. }
  1716. }
  1717. }
  1718. return null;
  1719. }
  1720. // Ternary op is a workaround for https://github.com/dotnet/coreclr/issues/914
  1721. private static bool IsWhite(int ch) => ch == 0x20 || (uint)(ch - 0x09) <= (0x0D - 0x09) ? true : false;
  1722. private static bool IsDigit(int ch) => ((uint)ch - '0') <= 9;
  1723. internal enum ParsingStatus
  1724. {
  1725. OK,
  1726. Failed,
  1727. Overflow
  1728. }
  1729. [DoesNotReturn]
  1730. internal static void ThrowOverflowOrFormatException(ParsingStatus status, TypeCode type = 0) => throw GetException(status, type);
  1731. [DoesNotReturn]
  1732. internal static void ThrowOverflowException(TypeCode type) => throw GetException(ParsingStatus.Overflow, type);
  1733. private static Exception GetException(ParsingStatus status, TypeCode type)
  1734. {
  1735. if (status == ParsingStatus.Failed)
  1736. return new FormatException(SR.Format_InvalidString);
  1737. string s;
  1738. switch (type)
  1739. {
  1740. case TypeCode.SByte:
  1741. s = SR.Overflow_SByte;
  1742. break;
  1743. case TypeCode.Byte:
  1744. s = SR.Overflow_Byte;
  1745. break;
  1746. case TypeCode.Int16:
  1747. s = SR.Overflow_Int16;
  1748. break;
  1749. case TypeCode.UInt16:
  1750. s = SR.Overflow_UInt16;
  1751. break;
  1752. case TypeCode.Int32:
  1753. s = SR.Overflow_Int32;
  1754. break;
  1755. case TypeCode.UInt32:
  1756. s = SR.Overflow_UInt32;
  1757. break;
  1758. case TypeCode.Int64:
  1759. s = SR.Overflow_Int64;
  1760. break;
  1761. case TypeCode.UInt64:
  1762. s = SR.Overflow_UInt64;
  1763. break;
  1764. default:
  1765. Debug.Assert(type == TypeCode.Decimal);
  1766. s = SR.Overflow_Decimal;
  1767. break;
  1768. }
  1769. return new OverflowException(s);
  1770. }
  1771. internal static double NumberToDouble(ref NumberBuffer number)
  1772. {
  1773. number.CheckConsistency();
  1774. double result;
  1775. if ((number.DigitsCount == 0) || (number.Scale < DoubleMinExponent))
  1776. {
  1777. result = 0;
  1778. }
  1779. else if (number.Scale > DoubleMaxExponent)
  1780. {
  1781. result = double.PositiveInfinity;
  1782. }
  1783. else
  1784. {
  1785. ulong bits = NumberToFloatingPointBits(ref number, in FloatingPointInfo.Double);
  1786. result = BitConverter.Int64BitsToDouble((long)(bits));
  1787. }
  1788. return number.IsNegative ? -result : result;
  1789. }
  1790. internal static float NumberToSingle(ref NumberBuffer number)
  1791. {
  1792. number.CheckConsistency();
  1793. float result;
  1794. if ((number.DigitsCount == 0) || (number.Scale < SingleMinExponent))
  1795. {
  1796. result = 0;
  1797. }
  1798. else if (number.Scale > SingleMaxExponent)
  1799. {
  1800. result = float.PositiveInfinity;
  1801. }
  1802. else
  1803. {
  1804. uint bits = (uint)(NumberToFloatingPointBits(ref number, in FloatingPointInfo.Single));
  1805. result = BitConverter.Int32BitsToSingle((int)(bits));
  1806. }
  1807. return number.IsNegative ? -result : result;
  1808. }
  1809. }
  1810. }