Number.Parsing.cs 75 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022
  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 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 == false);
  246. Debug.Assert(number.HasNonZeroTail == false);
  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. uint numValue;
  1012. // Skip past any whitespace at the beginning.
  1013. if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
  1014. {
  1015. do
  1016. {
  1017. index++;
  1018. if ((uint)index >= (uint)value.Length)
  1019. goto FalseExit;
  1020. num = value[index];
  1021. }
  1022. while (IsWhite(num));
  1023. }
  1024. bool overflow = false;
  1025. uint answer = 0;
  1026. ReadOnlySpan<byte> charToHexLookup = CharToHexLookup;
  1027. if ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
  1028. {
  1029. // Skip past leading zeros.
  1030. if (num == '0')
  1031. {
  1032. do
  1033. {
  1034. index++;
  1035. if ((uint)index >= (uint)value.Length)
  1036. goto DoneAtEnd;
  1037. num = value[index];
  1038. } while (num == '0');
  1039. if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF)
  1040. goto HasTrailingChars;
  1041. }
  1042. // Parse up through 8 digits, as no overflow is possible
  1043. answer = charToHexLookup[num]; // first digit
  1044. index++;
  1045. for (int i = 0; i < 7; i++) // next 7 digits can't overflow
  1046. {
  1047. if ((uint)index >= (uint)value.Length)
  1048. goto DoneAtEnd;
  1049. num = value[index];
  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 || (numValue = 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. uint numValue;
  1304. // Skip past any whitespace at the beginning.
  1305. if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
  1306. {
  1307. do
  1308. {
  1309. index++;
  1310. if ((uint)index >= (uint)value.Length)
  1311. goto FalseExit;
  1312. num = value[index];
  1313. }
  1314. while (IsWhite(num));
  1315. }
  1316. bool overflow = false;
  1317. ulong answer = 0;
  1318. ReadOnlySpan<byte> charToHexLookup = CharToHexLookup;
  1319. if ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
  1320. {
  1321. // Skip past leading zeros.
  1322. if (num == '0')
  1323. {
  1324. do
  1325. {
  1326. index++;
  1327. if ((uint)index >= (uint)value.Length)
  1328. goto DoneAtEnd;
  1329. num = value[index];
  1330. } while (num == '0');
  1331. if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF)
  1332. goto HasTrailingChars;
  1333. }
  1334. // Parse up through 16 digits, as no overflow is possible
  1335. answer = charToHexLookup[num]; // first digit
  1336. index++;
  1337. for (int i = 0; i < 15; i++) // next 15 digits can't overflow
  1338. {
  1339. if ((uint)index >= (uint)value.Length)
  1340. goto DoneAtEnd;
  1341. num = value[index];
  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 || (numValue = 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. }