You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2581 lines
95 KiB

  1. #region License
  2. // Copyright (c) 2007 James Newton-King
  3. //
  4. // Permission is hereby granted, free of charge, to any person
  5. // obtaining a copy of this software and associated documentation
  6. // files (the "Software"), to deal in the Software without
  7. // restriction, including without limitation the rights to use,
  8. // copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the
  10. // Software is furnished to do so, subject to the following
  11. // conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be
  14. // included in all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  18. // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  20. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  21. // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  22. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  23. // OTHER DEALINGS IN THE SOFTWARE.
  24. #endregion
  25. using System;
  26. using System.Runtime.CompilerServices;
  27. using System.IO;
  28. using System.Globalization;
  29. #if HAVE_BIG_INTEGER
  30. using System.Numerics;
  31. #endif
  32. using Newtonsoft.Json.Utilities;
  33. namespace Newtonsoft.Json
  34. {
  35. internal enum ReadType
  36. {
  37. Read,
  38. ReadAsInt32,
  39. ReadAsInt64,
  40. ReadAsBytes,
  41. ReadAsString,
  42. ReadAsDecimal,
  43. ReadAsDateTime,
  44. #if !NET20
  45. ReadAsDateTimeOffset,
  46. #endif
  47. ReadAsDouble,
  48. ReadAsBoolean
  49. }
  50. /// <summary>
  51. /// Represents a reader that provides fast, non-cached, forward-only access to JSON text data.
  52. /// </summary>
  53. internal partial class JsonTextReader : JsonReader, IJsonLineInfo
  54. {
  55. private const char UnicodeReplacementChar = '\uFFFD';
  56. private const int MaximumJavascriptIntegerCharacterLength = 380;
  57. #if DEBUG
  58. internal int LargeBufferLength { get; set; } = int.MaxValue / 2;
  59. #else
  60. private const int LargeBufferLength = int.MaxValue / 2;
  61. #endif
  62. private readonly TextReader _reader;
  63. private char[] _chars;
  64. private int _charsUsed;
  65. private int _charPos;
  66. private int _lineStartPos;
  67. private int _lineNumber;
  68. private bool _isEndOfFile;
  69. private StringBuffer _stringBuffer;
  70. private StringReference _stringReference;
  71. private IArrayPool<char> _arrayPool;
  72. internal PropertyNameTable NameTable;
  73. /// <summary>
  74. /// Initializes a new instance of the <see cref="JsonTextReader"/> class with the specified <see cref="TextReader"/>.
  75. /// </summary>
  76. /// <param name="reader">The <see cref="TextReader"/> containing the JSON data to read.</param>
  77. public JsonTextReader(TextReader reader)
  78. {
  79. if (reader == null)
  80. {
  81. throw new ArgumentNullException(nameof(reader));
  82. }
  83. _reader = reader;
  84. _lineNumber = 1;
  85. #if HAVE_ASYNC
  86. _safeAsync = GetType() == typeof(JsonTextReader);
  87. #endif
  88. }
  89. #if DEBUG
  90. internal char[] CharBuffer
  91. {
  92. get => _chars;
  93. set => _chars = value;
  94. }
  95. internal int CharPos => _charPos;
  96. #endif
  97. /// <summary>
  98. /// Gets or sets the reader's character buffer pool.
  99. /// </summary>
  100. public IArrayPool<char> ArrayPool
  101. {
  102. get => _arrayPool;
  103. set
  104. {
  105. if (value == null)
  106. {
  107. throw new ArgumentNullException(nameof(value));
  108. }
  109. _arrayPool = value;
  110. }
  111. }
  112. private void EnsureBufferNotEmpty()
  113. {
  114. if (_stringBuffer.IsEmpty)
  115. {
  116. _stringBuffer = new StringBuffer(_arrayPool, 1024);
  117. }
  118. }
  119. private void SetNewLine(bool hasNextChar)
  120. {
  121. if (hasNextChar && _chars[_charPos] == StringUtils.LineFeed)
  122. {
  123. _charPos++;
  124. }
  125. OnNewLine(_charPos);
  126. }
  127. private void OnNewLine(int pos)
  128. {
  129. _lineNumber++;
  130. _lineStartPos = pos;
  131. }
  132. private void ParseString(char quote, ReadType readType)
  133. {
  134. _charPos++;
  135. ShiftBufferIfNeeded();
  136. ReadStringIntoBuffer(quote);
  137. ParseReadString(quote, readType);
  138. }
  139. private void ParseReadString(char quote, ReadType readType)
  140. {
  141. SetPostValueState(true);
  142. switch (readType)
  143. {
  144. case ReadType.ReadAsBytes:
  145. Guid g;
  146. byte[] data;
  147. if (_stringReference.Length == 0)
  148. {
  149. data = CollectionUtils.ArrayEmpty<byte>();
  150. }
  151. else if (_stringReference.Length == 36 && ConvertUtils.TryConvertGuid(_stringReference.ToString(), out g))
  152. {
  153. data = g.ToByteArray();
  154. }
  155. else
  156. {
  157. data = Convert.FromBase64CharArray(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length);
  158. }
  159. SetToken(JsonToken.Bytes, data, false);
  160. break;
  161. case ReadType.ReadAsString:
  162. string text = _stringReference.ToString();
  163. SetToken(JsonToken.String, text, false);
  164. _quoteChar = quote;
  165. break;
  166. case ReadType.ReadAsInt32:
  167. case ReadType.ReadAsDecimal:
  168. case ReadType.ReadAsBoolean:
  169. // caller will convert result
  170. break;
  171. default:
  172. if (_dateParseHandling != DateParseHandling.None)
  173. {
  174. DateParseHandling dateParseHandling;
  175. if (readType == ReadType.ReadAsDateTime)
  176. {
  177. dateParseHandling = DateParseHandling.DateTime;
  178. }
  179. #if !NET20
  180. else if (readType == ReadType.ReadAsDateTimeOffset)
  181. {
  182. dateParseHandling = DateParseHandling.DateTimeOffset;
  183. }
  184. #endif
  185. else
  186. {
  187. dateParseHandling = _dateParseHandling;
  188. }
  189. if (dateParseHandling == DateParseHandling.DateTime)
  190. {
  191. if (DateTimeUtils.TryParseDateTime(_stringReference, DateTimeZoneHandling, DateFormatString, Culture, out DateTime dt))
  192. {
  193. SetToken(JsonToken.Date, dt, false);
  194. return;
  195. }
  196. }
  197. #if !NET20
  198. else
  199. {
  200. if (DateTimeUtils.TryParseDateTimeOffset(_stringReference, DateFormatString, Culture, out DateTimeOffset dt))
  201. {
  202. SetToken(JsonToken.Date, dt, false);
  203. return;
  204. }
  205. }
  206. #endif
  207. }
  208. SetToken(JsonToken.String, _stringReference.ToString(), false);
  209. _quoteChar = quote;
  210. break;
  211. }
  212. }
  213. private static void BlockCopyChars(char[] src, int srcOffset, char[] dst, int dstOffset, int count)
  214. {
  215. const int charByteCount = 2;
  216. Buffer.BlockCopy(src, srcOffset * charByteCount, dst, dstOffset * charByteCount, count * charByteCount);
  217. }
  218. private void ShiftBufferIfNeeded()
  219. {
  220. // once in the last 10% of the buffer, or buffer is already very large then
  221. // shift the remaining content to the start to avoid unnecessarily increasing
  222. // the buffer size when reading numbers/strings
  223. int length = _chars.Length;
  224. if (length - _charPos <= length * 0.1 || length >= LargeBufferLength)
  225. {
  226. int count = _charsUsed - _charPos;
  227. if (count > 0)
  228. {
  229. BlockCopyChars(_chars, _charPos, _chars, 0, count);
  230. }
  231. _lineStartPos -= _charPos;
  232. _charPos = 0;
  233. _charsUsed = count;
  234. _chars[_charsUsed] = '\0';
  235. }
  236. }
  237. private int ReadData(bool append)
  238. {
  239. return ReadData(append, 0);
  240. }
  241. private void PrepareBufferForReadData(bool append, int charsRequired)
  242. {
  243. // char buffer is full
  244. if (_charsUsed + charsRequired >= _chars.Length - 1)
  245. {
  246. if (append)
  247. {
  248. int doubledArrayLength = _chars.Length * 2;
  249. // copy to new array either double the size of the current or big enough to fit required content
  250. int newArrayLength = Math.Max(
  251. doubledArrayLength < 0 ? int.MaxValue : doubledArrayLength, // handle overflow
  252. _charsUsed + charsRequired + 1);
  253. // increase the size of the buffer
  254. char[] dst = BufferUtils.RentBuffer(_arrayPool, newArrayLength);
  255. BlockCopyChars(_chars, 0, dst, 0, _chars.Length);
  256. BufferUtils.ReturnBuffer(_arrayPool, _chars);
  257. _chars = dst;
  258. }
  259. else
  260. {
  261. int remainingCharCount = _charsUsed - _charPos;
  262. if (remainingCharCount + charsRequired + 1 >= _chars.Length)
  263. {
  264. // the remaining count plus the required is bigger than the current buffer size
  265. char[] dst = BufferUtils.RentBuffer(_arrayPool, remainingCharCount + charsRequired + 1);
  266. if (remainingCharCount > 0)
  267. {
  268. BlockCopyChars(_chars, _charPos, dst, 0, remainingCharCount);
  269. }
  270. BufferUtils.ReturnBuffer(_arrayPool, _chars);
  271. _chars = dst;
  272. }
  273. else
  274. {
  275. // copy any remaining data to the beginning of the buffer if needed and reset positions
  276. if (remainingCharCount > 0)
  277. {
  278. BlockCopyChars(_chars, _charPos, _chars, 0, remainingCharCount);
  279. }
  280. }
  281. _lineStartPos -= _charPos;
  282. _charPos = 0;
  283. _charsUsed = remainingCharCount;
  284. }
  285. }
  286. }
  287. private int ReadData(bool append, int charsRequired)
  288. {
  289. if (_isEndOfFile)
  290. {
  291. return 0;
  292. }
  293. PrepareBufferForReadData(append, charsRequired);
  294. int attemptCharReadCount = _chars.Length - _charsUsed - 1;
  295. int charsRead = _reader.Read(_chars, _charsUsed, attemptCharReadCount);
  296. _charsUsed += charsRead;
  297. if (charsRead == 0)
  298. {
  299. _isEndOfFile = true;
  300. }
  301. _chars[_charsUsed] = '\0';
  302. return charsRead;
  303. }
  304. private bool EnsureChars(int relativePosition, bool append)
  305. {
  306. if (_charPos + relativePosition >= _charsUsed)
  307. {
  308. return ReadChars(relativePosition, append);
  309. }
  310. return true;
  311. }
  312. private bool ReadChars(int relativePosition, bool append)
  313. {
  314. if (_isEndOfFile)
  315. {
  316. return false;
  317. }
  318. int charsRequired = _charPos + relativePosition - _charsUsed + 1;
  319. int totalCharsRead = 0;
  320. // it is possible that the TextReader doesn't return all data at once
  321. // repeat read until the required text is returned or the reader is out of content
  322. do
  323. {
  324. int charsRead = ReadData(append, charsRequired - totalCharsRead);
  325. // no more content
  326. if (charsRead == 0)
  327. {
  328. break;
  329. }
  330. totalCharsRead += charsRead;
  331. } while (totalCharsRead < charsRequired);
  332. if (totalCharsRead < charsRequired)
  333. {
  334. return false;
  335. }
  336. return true;
  337. }
  338. /// <summary>
  339. /// Reads the next JSON token from the underlying <see cref="TextReader"/>.
  340. /// </summary>
  341. /// <returns>
  342. /// <c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.
  343. /// </returns>
  344. public override bool Read()
  345. {
  346. EnsureBuffer();
  347. while (true)
  348. {
  349. switch (_currentState)
  350. {
  351. case State.Start:
  352. case State.Property:
  353. case State.Array:
  354. case State.ArrayStart:
  355. case State.Constructor:
  356. case State.ConstructorStart:
  357. return ParseValue();
  358. case State.Object:
  359. case State.ObjectStart:
  360. return ParseObject();
  361. case State.PostValue:
  362. // returns true if it hits
  363. // end of object or array
  364. if (ParsePostValue(false))
  365. {
  366. return true;
  367. }
  368. break;
  369. case State.Finished:
  370. if (EnsureChars(0, false))
  371. {
  372. EatWhitespace();
  373. if (_isEndOfFile)
  374. {
  375. SetToken(JsonToken.None);
  376. return false;
  377. }
  378. if (_chars[_charPos] == '/')
  379. {
  380. ParseComment(true);
  381. return true;
  382. }
  383. throw JsonReaderException.Create(this, "Additional text encountered after finished reading JSON content: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos]));
  384. }
  385. SetToken(JsonToken.None);
  386. return false;
  387. default:
  388. throw JsonReaderException.Create(this, "Unexpected state: {0}.".FormatWith(CultureInfo.InvariantCulture, CurrentState));
  389. }
  390. }
  391. }
  392. /// <summary>
  393. /// Reads the next JSON token from the underlying <see cref="TextReader"/> as a <see cref="Nullable{T}"/> of <see cref="Int32"/>.
  394. /// </summary>
  395. /// <returns>A <see cref="Nullable{T}"/> of <see cref="Int32"/>. This method will return <c>null</c> at the end of an array.</returns>
  396. public override int? ReadAsInt32()
  397. {
  398. return (int?)ReadNumberValue(ReadType.ReadAsInt32);
  399. }
  400. /// <summary>
  401. /// Reads the next JSON token from the underlying <see cref="TextReader"/> as a <see cref="Nullable{T}"/> of <see cref="DateTime"/>.
  402. /// </summary>
  403. /// <returns>A <see cref="Nullable{T}"/> of <see cref="DateTime"/>. This method will return <c>null</c> at the end of an array.</returns>
  404. public override DateTime? ReadAsDateTime()
  405. {
  406. return (DateTime?)ReadStringValue(ReadType.ReadAsDateTime);
  407. }
  408. /// <summary>
  409. /// Reads the next JSON token from the underlying <see cref="TextReader"/> as a <see cref="String"/>.
  410. /// </summary>
  411. /// <returns>A <see cref="String"/>. This method will return <c>null</c> at the end of an array.</returns>
  412. public override string ReadAsString()
  413. {
  414. return (string)ReadStringValue(ReadType.ReadAsString);
  415. }
  416. /// <summary>
  417. /// Reads the next JSON token from the underlying <see cref="TextReader"/> as a <see cref="Byte"/>[].
  418. /// </summary>
  419. /// <returns>A <see cref="Byte"/>[] or <c>null</c> if the next JSON token is null. This method will return <c>null</c> at the end of an array.</returns>
  420. public override byte[] ReadAsBytes()
  421. {
  422. EnsureBuffer();
  423. bool isWrapped = false;
  424. switch (_currentState)
  425. {
  426. case State.PostValue:
  427. if (ParsePostValue(true))
  428. {
  429. return null;
  430. }
  431. goto case State.Start;
  432. case State.Start:
  433. case State.Property:
  434. case State.Array:
  435. case State.ArrayStart:
  436. case State.Constructor:
  437. case State.ConstructorStart:
  438. while (true)
  439. {
  440. char currentChar = _chars[_charPos];
  441. switch (currentChar)
  442. {
  443. case '\0':
  444. if (ReadNullChar())
  445. {
  446. SetToken(JsonToken.None, null, false);
  447. return null;
  448. }
  449. break;
  450. case '"':
  451. case '\'':
  452. ParseString(currentChar, ReadType.ReadAsBytes);
  453. byte[] data = (byte[])Value;
  454. if (isWrapped)
  455. {
  456. ReaderReadAndAssert();
  457. if (TokenType != JsonToken.EndObject)
  458. {
  459. throw JsonReaderException.Create(this, "Error reading bytes. Unexpected token: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType));
  460. }
  461. SetToken(JsonToken.Bytes, data, false);
  462. }
  463. return data;
  464. case '{':
  465. _charPos++;
  466. SetToken(JsonToken.StartObject);
  467. ReadIntoWrappedTypeObject();
  468. isWrapped = true;
  469. break;
  470. case '[':
  471. _charPos++;
  472. SetToken(JsonToken.StartArray);
  473. return ReadArrayIntoByteArray();
  474. case 'n':
  475. HandleNull();
  476. return null;
  477. case '/':
  478. ParseComment(false);
  479. break;
  480. case ',':
  481. ProcessValueComma();
  482. break;
  483. case ']':
  484. _charPos++;
  485. if (_currentState == State.Array || _currentState == State.ArrayStart || _currentState == State.PostValue)
  486. {
  487. SetToken(JsonToken.EndArray);
  488. return null;
  489. }
  490. throw CreateUnexpectedCharacterException(currentChar);
  491. case StringUtils.CarriageReturn:
  492. ProcessCarriageReturn(false);
  493. break;
  494. case StringUtils.LineFeed:
  495. ProcessLineFeed();
  496. break;
  497. case ' ':
  498. case StringUtils.Tab:
  499. // eat
  500. _charPos++;
  501. break;
  502. default:
  503. _charPos++;
  504. if (!char.IsWhiteSpace(currentChar))
  505. {
  506. throw CreateUnexpectedCharacterException(currentChar);
  507. }
  508. // eat
  509. break;
  510. }
  511. }
  512. case State.Finished:
  513. ReadFinished();
  514. return null;
  515. default:
  516. throw JsonReaderException.Create(this, "Unexpected state: {0}.".FormatWith(CultureInfo.InvariantCulture, CurrentState));
  517. }
  518. }
  519. private object ReadStringValue(ReadType readType)
  520. {
  521. EnsureBuffer();
  522. switch (_currentState)
  523. {
  524. case State.PostValue:
  525. if (ParsePostValue(true))
  526. {
  527. return null;
  528. }
  529. goto case State.Start;
  530. case State.Start:
  531. case State.Property:
  532. case State.Array:
  533. case State.ArrayStart:
  534. case State.Constructor:
  535. case State.ConstructorStart:
  536. while (true)
  537. {
  538. char currentChar = _chars[_charPos];
  539. switch (currentChar)
  540. {
  541. case '\0':
  542. if (ReadNullChar())
  543. {
  544. SetToken(JsonToken.None, null, false);
  545. return null;
  546. }
  547. break;
  548. case '"':
  549. case '\'':
  550. ParseString(currentChar, readType);
  551. return FinishReadQuotedStringValue(readType);
  552. case '-':
  553. if (EnsureChars(1, true) && _chars[_charPos + 1] == 'I')
  554. {
  555. return ParseNumberNegativeInfinity(readType);
  556. }
  557. else
  558. {
  559. ParseNumber(readType);
  560. return Value;
  561. }
  562. case '.':
  563. case '0':
  564. case '1':
  565. case '2':
  566. case '3':
  567. case '4':
  568. case '5':
  569. case '6':
  570. case '7':
  571. case '8':
  572. case '9':
  573. if (readType != ReadType.ReadAsString)
  574. {
  575. _charPos++;
  576. throw CreateUnexpectedCharacterException(currentChar);
  577. }
  578. ParseNumber(ReadType.ReadAsString);
  579. return Value;
  580. case 't':
  581. case 'f':
  582. if (readType != ReadType.ReadAsString)
  583. {
  584. _charPos++;
  585. throw CreateUnexpectedCharacterException(currentChar);
  586. }
  587. string expected = currentChar == 't' ? JsonConvert.True : JsonConvert.False;
  588. if (!MatchValueWithTrailingSeparator(expected))
  589. {
  590. throw CreateUnexpectedCharacterException(_chars[_charPos]);
  591. }
  592. SetToken(JsonToken.String, expected);
  593. return expected;
  594. case 'I':
  595. return ParseNumberPositiveInfinity(readType);
  596. case 'N':
  597. return ParseNumberNaN(readType);
  598. case 'n':
  599. HandleNull();
  600. return null;
  601. case '/':
  602. ParseComment(false);
  603. break;
  604. case ',':
  605. ProcessValueComma();
  606. break;
  607. case ']':
  608. _charPos++;
  609. if (_currentState == State.Array || _currentState == State.ArrayStart || _currentState == State.PostValue)
  610. {
  611. SetToken(JsonToken.EndArray);
  612. return null;
  613. }
  614. throw CreateUnexpectedCharacterException(currentChar);
  615. case StringUtils.CarriageReturn:
  616. ProcessCarriageReturn(false);
  617. break;
  618. case StringUtils.LineFeed:
  619. ProcessLineFeed();
  620. break;
  621. case ' ':
  622. case StringUtils.Tab:
  623. // eat
  624. _charPos++;
  625. break;
  626. default:
  627. _charPos++;
  628. if (!char.IsWhiteSpace(currentChar))
  629. {
  630. throw CreateUnexpectedCharacterException(currentChar);
  631. }
  632. // eat
  633. break;
  634. }
  635. }
  636. case State.Finished:
  637. ReadFinished();
  638. return null;
  639. default:
  640. throw JsonReaderException.Create(this, "Unexpected state: {0}.".FormatWith(CultureInfo.InvariantCulture, CurrentState));
  641. }
  642. }
  643. private object FinishReadQuotedStringValue(ReadType readType)
  644. {
  645. switch (readType)
  646. {
  647. case ReadType.ReadAsBytes:
  648. case ReadType.ReadAsString:
  649. return Value;
  650. case ReadType.ReadAsDateTime:
  651. if (Value is DateTime time)
  652. {
  653. return time;
  654. }
  655. return ReadDateTimeString((string)Value);
  656. #if !NET20
  657. case ReadType.ReadAsDateTimeOffset:
  658. if (Value is DateTimeOffset offset)
  659. {
  660. return offset;
  661. }
  662. return ReadDateTimeOffsetString((string)Value);
  663. #endif
  664. default:
  665. throw new ArgumentOutOfRangeException(nameof(readType));
  666. }
  667. }
  668. private JsonReaderException CreateUnexpectedCharacterException(char c)
  669. {
  670. return JsonReaderException.Create(this, "Unexpected character encountered while parsing value: {0}.".FormatWith(CultureInfo.InvariantCulture, c));
  671. }
  672. /// <summary>
  673. /// Reads the next JSON token from the underlying <see cref="TextReader"/> as a <see cref="Nullable{T}"/> of <see cref="Boolean"/>.
  674. /// </summary>
  675. /// <returns>A <see cref="Nullable{T}"/> of <see cref="Boolean"/>. This method will return <c>null</c> at the end of an array.</returns>
  676. public override bool? ReadAsBoolean()
  677. {
  678. EnsureBuffer();
  679. switch (_currentState)
  680. {
  681. case State.PostValue:
  682. if (ParsePostValue(true))
  683. {
  684. return null;
  685. }
  686. goto case State.Start;
  687. case State.Start:
  688. case State.Property:
  689. case State.Array:
  690. case State.ArrayStart:
  691. case State.Constructor:
  692. case State.ConstructorStart:
  693. while (true)
  694. {
  695. char currentChar = _chars[_charPos];
  696. switch (currentChar)
  697. {
  698. case '\0':
  699. if (ReadNullChar())
  700. {
  701. SetToken(JsonToken.None, null, false);
  702. return null;
  703. }
  704. break;
  705. case '"':
  706. case '\'':
  707. ParseString(currentChar, ReadType.Read);
  708. return ReadBooleanString(_stringReference.ToString());
  709. case 'n':
  710. HandleNull();
  711. return null;
  712. case '-':
  713. case '.':
  714. case '0':
  715. case '1':
  716. case '2':
  717. case '3':
  718. case '4':
  719. case '5':
  720. case '6':
  721. case '7':
  722. case '8':
  723. case '9':
  724. ParseNumber(ReadType.Read);
  725. bool b;
  726. #if HAVE_BIG_INTEGER
  727. if (Value is BigInteger integer)
  728. {
  729. b = integer != 0;
  730. }
  731. else
  732. #endif
  733. {
  734. b = Convert.ToBoolean(Value, CultureInfo.InvariantCulture);
  735. }
  736. SetToken(JsonToken.Boolean, b, false);
  737. return b;
  738. case 't':
  739. case 'f':
  740. bool isTrue = currentChar == 't';
  741. string expected = isTrue ? JsonConvert.True : JsonConvert.False;
  742. if (!MatchValueWithTrailingSeparator(expected))
  743. {
  744. throw CreateUnexpectedCharacterException(_chars[_charPos]);
  745. }
  746. SetToken(JsonToken.Boolean, isTrue);
  747. return isTrue;
  748. case '/':
  749. ParseComment(false);
  750. break;
  751. case ',':
  752. ProcessValueComma();
  753. break;
  754. case ']':
  755. _charPos++;
  756. if (_currentState == State.Array || _currentState == State.ArrayStart || _currentState == State.PostValue)
  757. {
  758. SetToken(JsonToken.EndArray);
  759. return null;
  760. }
  761. throw CreateUnexpectedCharacterException(currentChar);
  762. case StringUtils.CarriageReturn:
  763. ProcessCarriageReturn(false);
  764. break;
  765. case StringUtils.LineFeed:
  766. ProcessLineFeed();
  767. break;
  768. case ' ':
  769. case StringUtils.Tab:
  770. // eat
  771. _charPos++;
  772. break;
  773. default:
  774. _charPos++;
  775. if (!char.IsWhiteSpace(currentChar))
  776. {
  777. throw CreateUnexpectedCharacterException(currentChar);
  778. }
  779. // eat
  780. break;
  781. }
  782. }
  783. case State.Finished:
  784. ReadFinished();
  785. return null;
  786. default:
  787. throw JsonReaderException.Create(this, "Unexpected state: {0}.".FormatWith(CultureInfo.InvariantCulture, CurrentState));
  788. }
  789. }
  790. private void ProcessValueComma()
  791. {
  792. _charPos++;
  793. if (_currentState != State.PostValue)
  794. {
  795. SetToken(JsonToken.Undefined);
  796. JsonReaderException ex = CreateUnexpectedCharacterException(',');
  797. // so the comma will be parsed again
  798. _charPos--;
  799. throw ex;
  800. }
  801. SetStateBasedOnCurrent();
  802. }
  803. private object ReadNumberValue(ReadType readType)
  804. {
  805. EnsureBuffer();
  806. switch (_currentState)
  807. {
  808. case State.PostValue:
  809. if (ParsePostValue(true))
  810. {
  811. return null;
  812. }
  813. goto case State.Start;
  814. case State.Start:
  815. case State.Property:
  816. case State.Array:
  817. case State.ArrayStart:
  818. case State.Constructor:
  819. case State.ConstructorStart:
  820. while (true)
  821. {
  822. char currentChar = _chars[_charPos];
  823. switch (currentChar)
  824. {
  825. case '\0':
  826. if (ReadNullChar())
  827. {
  828. SetToken(JsonToken.None, null, false);
  829. return null;
  830. }
  831. break;
  832. case '"':
  833. case '\'':
  834. ParseString(currentChar, readType);
  835. return FinishReadQuotedNumber(readType);
  836. case 'n':
  837. HandleNull();
  838. return null;
  839. case 'N':
  840. return ParseNumberNaN(readType);
  841. case 'I':
  842. return ParseNumberPositiveInfinity(readType);
  843. case '-':
  844. if (EnsureChars(1, true) && _chars[_charPos + 1] == 'I')
  845. {
  846. return ParseNumberNegativeInfinity(readType);
  847. }
  848. else
  849. {
  850. ParseNumber(readType);
  851. return Value;
  852. }
  853. case '.':
  854. case '0':
  855. case '1':
  856. case '2':
  857. case '3':
  858. case '4':
  859. case '5':
  860. case '6':
  861. case '7':
  862. case '8':
  863. case '9':
  864. ParseNumber(readType);
  865. return Value;
  866. case '/':
  867. ParseComment(false);
  868. break;
  869. case ',':
  870. ProcessValueComma();
  871. break;
  872. case ']':
  873. _charPos++;
  874. if (_currentState == State.Array || _currentState == State.ArrayStart || _currentState == State.PostValue)
  875. {
  876. SetToken(JsonToken.EndArray);
  877. return null;
  878. }
  879. throw CreateUnexpectedCharacterException(currentChar);
  880. case StringUtils.CarriageReturn:
  881. ProcessCarriageReturn(false);
  882. break;
  883. case StringUtils.LineFeed:
  884. ProcessLineFeed();
  885. break;
  886. case ' ':
  887. case StringUtils.Tab:
  888. // eat
  889. _charPos++;
  890. break;
  891. default:
  892. _charPos++;
  893. if (!char.IsWhiteSpace(currentChar))
  894. {
  895. throw CreateUnexpectedCharacterException(currentChar);
  896. }
  897. // eat
  898. break;
  899. }
  900. }
  901. case State.Finished:
  902. ReadFinished();
  903. return null;
  904. default:
  905. throw JsonReaderException.Create(this, "Unexpected state: {0}.".FormatWith(CultureInfo.InvariantCulture, CurrentState));
  906. }
  907. }
  908. private object FinishReadQuotedNumber(ReadType readType)
  909. {
  910. switch (readType)
  911. {
  912. case ReadType.ReadAsInt32:
  913. return ReadInt32String(_stringReference.ToString());
  914. case ReadType.ReadAsDecimal:
  915. return ReadDecimalString(_stringReference.ToString());
  916. case ReadType.ReadAsDouble:
  917. return ReadDoubleString(_stringReference.ToString());
  918. default:
  919. throw new ArgumentOutOfRangeException(nameof(readType));
  920. }
  921. }
  922. #if !NET20
  923. /// <summary>
  924. /// Reads the next JSON token from the underlying <see cref="TextReader"/> as a <see cref="Nullable{T}"/> of <see cref="DateTimeOffset"/>.
  925. /// </summary>
  926. /// <returns>A <see cref="Nullable{T}"/> of <see cref="DateTimeOffset"/>. This method will return <c>null</c> at the end of an array.</returns>
  927. public override DateTimeOffset? ReadAsDateTimeOffset()
  928. {
  929. return (DateTimeOffset?)ReadStringValue(ReadType.ReadAsDateTimeOffset);
  930. }
  931. #endif
  932. /// <summary>
  933. /// Reads the next JSON token from the underlying <see cref="TextReader"/> as a <see cref="Nullable{T}"/> of <see cref="Decimal"/>.
  934. /// </summary>
  935. /// <returns>A <see cref="Nullable{T}"/> of <see cref="Decimal"/>. This method will return <c>null</c> at the end of an array.</returns>
  936. public override decimal? ReadAsDecimal()
  937. {
  938. return (decimal?)ReadNumberValue(ReadType.ReadAsDecimal);
  939. }
  940. /// <summary>
  941. /// Reads the next JSON token from the underlying <see cref="TextReader"/> as a <see cref="Nullable{T}"/> of <see cref="Double"/>.
  942. /// </summary>
  943. /// <returns>A <see cref="Nullable{T}"/> of <see cref="Double"/>. This method will return <c>null</c> at the end of an array.</returns>
  944. public override double? ReadAsDouble()
  945. {
  946. return (double?)ReadNumberValue(ReadType.ReadAsDouble);
  947. }
  948. private void HandleNull()
  949. {
  950. if (EnsureChars(1, true))
  951. {
  952. char next = _chars[_charPos + 1];
  953. if (next == 'u')
  954. {
  955. ParseNull();
  956. return;
  957. }
  958. _charPos += 2;
  959. throw CreateUnexpectedCharacterException(_chars[_charPos - 1]);
  960. }
  961. _charPos = _charsUsed;
  962. throw CreateUnexpectedEndException();
  963. }
  964. private void ReadFinished()
  965. {
  966. if (EnsureChars(0, false))
  967. {
  968. EatWhitespace();
  969. if (_isEndOfFile)
  970. {
  971. return;
  972. }
  973. if (_chars[_charPos] == '/')
  974. {
  975. ParseComment(false);
  976. }
  977. else
  978. {
  979. throw JsonReaderException.Create(this, "Additional text encountered after finished reading JSON content: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos]));
  980. }
  981. }
  982. SetToken(JsonToken.None);
  983. }
  984. private bool ReadNullChar()
  985. {
  986. if (_charsUsed == _charPos)
  987. {
  988. if (ReadData(false) == 0)
  989. {
  990. _isEndOfFile = true;
  991. return true;
  992. }
  993. }
  994. else
  995. {
  996. _charPos++;
  997. }
  998. return false;
  999. }
  1000. private void EnsureBuffer()
  1001. {
  1002. if (_chars == null)
  1003. {
  1004. _chars = BufferUtils.RentBuffer(_arrayPool, 1024);
  1005. _chars[0] = '\0';
  1006. }
  1007. }
  1008. private void ReadStringIntoBuffer(char quote)
  1009. {
  1010. int charPos = _charPos;
  1011. int initialPosition = _charPos;
  1012. int lastWritePosition = _charPos;
  1013. _stringBuffer.Position = 0;
  1014. while (true)
  1015. {
  1016. switch (_chars[charPos++])
  1017. {
  1018. case '\0':
  1019. if (_charsUsed == charPos - 1)
  1020. {
  1021. charPos--;
  1022. if (ReadData(true) == 0)
  1023. {
  1024. _charPos = charPos;
  1025. throw JsonReaderException.Create(this, "Unterminated string. Expected delimiter: {0}.".FormatWith(CultureInfo.InvariantCulture, quote));
  1026. }
  1027. }
  1028. break;
  1029. case '\\':
  1030. _charPos = charPos;
  1031. if (!EnsureChars(0, true))
  1032. {
  1033. throw JsonReaderException.Create(this, "Unterminated string. Expected delimiter: {0}.".FormatWith(CultureInfo.InvariantCulture, quote));
  1034. }
  1035. // start of escape sequence
  1036. int escapeStartPos = charPos - 1;
  1037. char currentChar = _chars[charPos];
  1038. charPos++;
  1039. char writeChar;
  1040. switch (currentChar)
  1041. {
  1042. case 'b':
  1043. writeChar = '\b';
  1044. break;
  1045. case 't':
  1046. writeChar = '\t';
  1047. break;
  1048. case 'n':
  1049. writeChar = '\n';
  1050. break;
  1051. case 'f':
  1052. writeChar = '\f';
  1053. break;
  1054. case 'r':
  1055. writeChar = '\r';
  1056. break;
  1057. case '\\':
  1058. writeChar = '\\';
  1059. break;
  1060. case '"':
  1061. case '\'':
  1062. case '/':
  1063. writeChar = currentChar;
  1064. break;
  1065. case 'u':
  1066. _charPos = charPos;
  1067. writeChar = ParseUnicode();
  1068. if (StringUtils.IsLowSurrogate(writeChar))
  1069. {
  1070. // low surrogate with no preceding high surrogate; this char is replaced
  1071. writeChar = UnicodeReplacementChar;
  1072. }
  1073. else if (StringUtils.IsHighSurrogate(writeChar))
  1074. {
  1075. bool anotherHighSurrogate;
  1076. // loop for handling situations where there are multiple consecutive high surrogates
  1077. do
  1078. {
  1079. anotherHighSurrogate = false;
  1080. // potential start of a surrogate pair
  1081. if (EnsureChars(2, true) && _chars[_charPos] == '\\' && _chars[_charPos + 1] == 'u')
  1082. {
  1083. char highSurrogate = writeChar;
  1084. _charPos += 2;
  1085. writeChar = ParseUnicode();
  1086. if (StringUtils.IsLowSurrogate(writeChar))
  1087. {
  1088. // a valid surrogate pair!
  1089. }
  1090. else if (StringUtils.IsHighSurrogate(writeChar))
  1091. {
  1092. // another high surrogate; replace current and start check over
  1093. highSurrogate = UnicodeReplacementChar;
  1094. anotherHighSurrogate = true;
  1095. }
  1096. else
  1097. {
  1098. // high surrogate not followed by low surrogate; original char is replaced
  1099. highSurrogate = UnicodeReplacementChar;
  1100. }
  1101. EnsureBufferNotEmpty();
  1102. WriteCharToBuffer(highSurrogate, lastWritePosition, escapeStartPos);
  1103. lastWritePosition = _charPos;
  1104. }
  1105. else
  1106. {
  1107. // there are not enough remaining chars for the low surrogate or is not follow by unicode sequence
  1108. // replace high surrogate and continue on as usual
  1109. writeChar = UnicodeReplacementChar;
  1110. }
  1111. } while (anotherHighSurrogate);
  1112. }
  1113. charPos = _charPos;
  1114. break;
  1115. default:
  1116. _charPos = charPos;
  1117. throw JsonReaderException.Create(this, "Bad JSON escape sequence: {0}.".FormatWith(CultureInfo.InvariantCulture, @"\" + currentChar));
  1118. }
  1119. EnsureBufferNotEmpty();
  1120. WriteCharToBuffer(writeChar, lastWritePosition, escapeStartPos);
  1121. lastWritePosition = charPos;
  1122. break;
  1123. case StringUtils.CarriageReturn:
  1124. _charPos = charPos - 1;
  1125. ProcessCarriageReturn(true);
  1126. charPos = _charPos;
  1127. break;
  1128. case StringUtils.LineFeed:
  1129. _charPos = charPos - 1;
  1130. ProcessLineFeed();
  1131. charPos = _charPos;
  1132. break;
  1133. case '"':
  1134. case '\'':
  1135. if (_chars[charPos - 1] == quote)
  1136. {
  1137. FinishReadStringIntoBuffer(charPos - 1, initialPosition, lastWritePosition);
  1138. return;
  1139. }
  1140. break;
  1141. }
  1142. }
  1143. }
  1144. private void FinishReadStringIntoBuffer(int charPos, int initialPosition, int lastWritePosition)
  1145. {
  1146. if (initialPosition == lastWritePosition)
  1147. {
  1148. _stringReference = new StringReference(_chars, initialPosition, charPos - initialPosition);
  1149. }
  1150. else
  1151. {
  1152. EnsureBufferNotEmpty();
  1153. if (charPos > lastWritePosition)
  1154. {
  1155. _stringBuffer.Append(_arrayPool, _chars, lastWritePosition, charPos - lastWritePosition);
  1156. }
  1157. _stringReference = new StringReference(_stringBuffer.InternalBuffer, 0, _stringBuffer.Position);
  1158. }
  1159. _charPos = charPos + 1;
  1160. }
  1161. private void WriteCharToBuffer(char writeChar, int lastWritePosition, int writeToPosition)
  1162. {
  1163. if (writeToPosition > lastWritePosition)
  1164. {
  1165. _stringBuffer.Append(_arrayPool, _chars, lastWritePosition, writeToPosition - lastWritePosition);
  1166. }
  1167. _stringBuffer.Append(_arrayPool, writeChar);
  1168. }
  1169. private char ConvertUnicode(bool enoughChars)
  1170. {
  1171. if (enoughChars)
  1172. {
  1173. if (ConvertUtils.TryHexTextToInt(_chars, _charPos, _charPos + 4, out int value))
  1174. {
  1175. char hexChar = Convert.ToChar(value);
  1176. _charPos += 4;
  1177. return hexChar;
  1178. }
  1179. else
  1180. {
  1181. throw JsonReaderException.Create(this, @"Invalid Unicode escape sequence: \u{0}.".FormatWith(CultureInfo.InvariantCulture, new string(_chars, _charPos, 4)));
  1182. }
  1183. }
  1184. else
  1185. {
  1186. throw JsonReaderException.Create(this, "Unexpected end while parsing Unicode escape sequence.");
  1187. }
  1188. }
  1189. private char ParseUnicode()
  1190. {
  1191. return ConvertUnicode(EnsureChars(4, true));
  1192. }
  1193. private void ReadNumberIntoBuffer()
  1194. {
  1195. int charPos = _charPos;
  1196. while (true)
  1197. {
  1198. char currentChar = _chars[charPos];
  1199. if (currentChar == '\0')
  1200. {
  1201. _charPos = charPos;
  1202. if (_charsUsed == charPos)
  1203. {
  1204. if (ReadData(true) == 0)
  1205. {
  1206. return;
  1207. }
  1208. }
  1209. else
  1210. {
  1211. return;
  1212. }
  1213. }
  1214. else if (ReadNumberCharIntoBuffer(currentChar, charPos))
  1215. {
  1216. return;
  1217. }
  1218. else
  1219. {
  1220. charPos++;
  1221. }
  1222. }
  1223. }
  1224. private bool ReadNumberCharIntoBuffer(char currentChar, int charPos)
  1225. {
  1226. switch (currentChar)
  1227. {
  1228. case '-':
  1229. case '+':
  1230. case 'a':
  1231. case 'A':
  1232. case 'b':
  1233. case 'B':
  1234. case 'c':
  1235. case 'C':
  1236. case 'd':
  1237. case 'D':
  1238. case 'e':
  1239. case 'E':
  1240. case 'f':
  1241. case 'F':
  1242. case 'x':
  1243. case 'X':
  1244. case '.':
  1245. case '0':
  1246. case '1':
  1247. case '2':
  1248. case '3':
  1249. case '4':
  1250. case '5':
  1251. case '6':
  1252. case '7':
  1253. case '8':
  1254. case '9':
  1255. return false;
  1256. default:
  1257. _charPos = charPos;
  1258. if (char.IsWhiteSpace(currentChar) || currentChar == ',' || currentChar == '}' || currentChar == ']' || currentChar == ')' || currentChar == '/')
  1259. {
  1260. return true;
  1261. }
  1262. throw JsonReaderException.Create(this, "Unexpected character encountered while parsing number: {0}.".FormatWith(CultureInfo.InvariantCulture, currentChar));
  1263. }
  1264. }
  1265. private void ClearRecentString()
  1266. {
  1267. _stringBuffer.Position = 0;
  1268. _stringReference = new StringReference();
  1269. }
  1270. private bool ParsePostValue(bool ignoreComments)
  1271. {
  1272. while (true)
  1273. {
  1274. char currentChar = _chars[_charPos];
  1275. switch (currentChar)
  1276. {
  1277. case '\0':
  1278. if (_charsUsed == _charPos)
  1279. {
  1280. if (ReadData(false) == 0)
  1281. {
  1282. _currentState = State.Finished;
  1283. return false;
  1284. }
  1285. }
  1286. else
  1287. {
  1288. _charPos++;
  1289. }
  1290. break;
  1291. case '}':
  1292. _charPos++;
  1293. SetToken(JsonToken.EndObject);
  1294. return true;
  1295. case ']':
  1296. _charPos++;
  1297. SetToken(JsonToken.EndArray);
  1298. return true;
  1299. case ')':
  1300. _charPos++;
  1301. SetToken(JsonToken.EndConstructor);
  1302. return true;
  1303. case '/':
  1304. ParseComment(!ignoreComments);
  1305. if (!ignoreComments)
  1306. {
  1307. return true;
  1308. }
  1309. break;
  1310. case ',':
  1311. _charPos++;
  1312. // finished parsing
  1313. SetStateBasedOnCurrent();
  1314. return false;
  1315. case ' ':
  1316. case StringUtils.Tab:
  1317. // eat
  1318. _charPos++;
  1319. break;
  1320. case StringUtils.CarriageReturn:
  1321. ProcessCarriageReturn(false);
  1322. break;
  1323. case StringUtils.LineFeed:
  1324. ProcessLineFeed();
  1325. break;
  1326. default:
  1327. if (char.IsWhiteSpace(currentChar))
  1328. {
  1329. // eat
  1330. _charPos++;
  1331. }
  1332. else
  1333. {
  1334. // handle multiple content without comma delimiter
  1335. if (SupportMultipleContent && Depth == 0)
  1336. {
  1337. SetStateBasedOnCurrent();
  1338. return false;
  1339. }
  1340. throw JsonReaderException.Create(this, "After parsing a value an unexpected character was encountered: {0}.".FormatWith(CultureInfo.InvariantCulture, currentChar));
  1341. }
  1342. break;
  1343. }
  1344. }
  1345. }
  1346. private bool ParseObject()
  1347. {
  1348. while (true)
  1349. {
  1350. char currentChar = _chars[_charPos];
  1351. switch (currentChar)
  1352. {
  1353. case '\0':
  1354. if (_charsUsed == _charPos)
  1355. {
  1356. if (ReadData(false) == 0)
  1357. {
  1358. return false;
  1359. }
  1360. }
  1361. else
  1362. {
  1363. _charPos++;
  1364. }
  1365. break;
  1366. case '}':
  1367. SetToken(JsonToken.EndObject);
  1368. _charPos++;
  1369. return true;
  1370. case '/':
  1371. ParseComment(true);
  1372. return true;
  1373. case StringUtils.CarriageReturn:
  1374. ProcessCarriageReturn(false);
  1375. break;
  1376. case StringUtils.LineFeed:
  1377. ProcessLineFeed();
  1378. break;
  1379. case ' ':
  1380. case StringUtils.Tab:
  1381. // eat
  1382. _charPos++;
  1383. break;
  1384. default:
  1385. if (char.IsWhiteSpace(currentChar))
  1386. {
  1387. // eat
  1388. _charPos++;
  1389. }
  1390. else
  1391. {
  1392. return ParseProperty();
  1393. }
  1394. break;
  1395. }
  1396. }
  1397. }
  1398. private bool ParseProperty()
  1399. {
  1400. char firstChar = _chars[_charPos];
  1401. char quoteChar;
  1402. if (firstChar == '"' || firstChar == '\'')
  1403. {
  1404. _charPos++;
  1405. quoteChar = firstChar;
  1406. ShiftBufferIfNeeded();
  1407. ReadStringIntoBuffer(quoteChar);
  1408. }
  1409. else if (ValidIdentifierChar(firstChar))
  1410. {
  1411. quoteChar = '\0';
  1412. ShiftBufferIfNeeded();
  1413. ParseUnquotedProperty();
  1414. }
  1415. else
  1416. {
  1417. throw JsonReaderException.Create(this, "Invalid property identifier character: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos]));
  1418. }
  1419. string propertyName;
  1420. if (NameTable != null)
  1421. {
  1422. propertyName = NameTable.Get(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length);
  1423. // no match in name table
  1424. if (propertyName == null)
  1425. {
  1426. propertyName = _stringReference.ToString();
  1427. }
  1428. }
  1429. else
  1430. {
  1431. propertyName = _stringReference.ToString();
  1432. }
  1433. EatWhitespace();
  1434. if (_chars[_charPos] != ':')
  1435. {
  1436. throw JsonReaderException.Create(this, "Invalid character after parsing property name. Expected ':' but got: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos]));
  1437. }
  1438. _charPos++;
  1439. SetToken(JsonToken.PropertyName, propertyName);
  1440. _quoteChar = quoteChar;
  1441. ClearRecentString();
  1442. return true;
  1443. }
  1444. private bool ValidIdentifierChar(char value)
  1445. {
  1446. return (char.IsLetterOrDigit(value) || value == '_' || value == '$');
  1447. }
  1448. private void ParseUnquotedProperty()
  1449. {
  1450. int initialPosition = _charPos;
  1451. // parse unquoted property name until whitespace or colon
  1452. while (true)
  1453. {
  1454. char currentChar = _chars[_charPos];
  1455. if (currentChar == '\0')
  1456. {
  1457. if (_charsUsed == _charPos)
  1458. {
  1459. if (ReadData(true) == 0)
  1460. {
  1461. throw JsonReaderException.Create(this, "Unexpected end while parsing unquoted property name.");
  1462. }
  1463. continue;
  1464. }
  1465. _stringReference = new StringReference(_chars, initialPosition, _charPos - initialPosition);
  1466. return;
  1467. }
  1468. if (ReadUnquotedPropertyReportIfDone(currentChar, initialPosition))
  1469. {
  1470. return;
  1471. }
  1472. }
  1473. }
  1474. private bool ReadUnquotedPropertyReportIfDone(char currentChar, int initialPosition)
  1475. {
  1476. if (ValidIdentifierChar(currentChar))
  1477. {
  1478. _charPos++;
  1479. return false;
  1480. }
  1481. if (char.IsWhiteSpace(currentChar) || currentChar == ':')
  1482. {
  1483. _stringReference = new StringReference(_chars, initialPosition, _charPos - initialPosition);
  1484. return true;
  1485. }
  1486. throw JsonReaderException.Create(this, "Invalid JavaScript property identifier character: {0}.".FormatWith(CultureInfo.InvariantCulture, currentChar));
  1487. }
  1488. private bool ParseValue()
  1489. {
  1490. while (true)
  1491. {
  1492. char currentChar = _chars[_charPos];
  1493. switch (currentChar)
  1494. {
  1495. case '\0':
  1496. if (_charsUsed == _charPos)
  1497. {
  1498. if (ReadData(false) == 0)
  1499. {
  1500. return false;
  1501. }
  1502. }
  1503. else
  1504. {
  1505. _charPos++;
  1506. }
  1507. break;
  1508. case '"':
  1509. case '\'':
  1510. ParseString(currentChar, ReadType.Read);
  1511. return true;
  1512. case 't':
  1513. ParseTrue();
  1514. return true;
  1515. case 'f':
  1516. ParseFalse();
  1517. return true;
  1518. case 'n':
  1519. if (EnsureChars(1, true))
  1520. {
  1521. char next = _chars[_charPos + 1];
  1522. if (next == 'u')
  1523. {
  1524. ParseNull();
  1525. }
  1526. else if (next == 'e')
  1527. {
  1528. ParseConstructor();
  1529. }
  1530. else
  1531. {
  1532. throw CreateUnexpectedCharacterException(_chars[_charPos]);
  1533. }
  1534. }
  1535. else
  1536. {
  1537. _charPos++;
  1538. throw CreateUnexpectedEndException();
  1539. }
  1540. return true;
  1541. case 'N':
  1542. ParseNumberNaN(ReadType.Read);
  1543. return true;
  1544. case 'I':
  1545. ParseNumberPositiveInfinity(ReadType.Read);
  1546. return true;
  1547. case '-':
  1548. if (EnsureChars(1, true) && _chars[_charPos + 1] == 'I')
  1549. {
  1550. ParseNumberNegativeInfinity(ReadType.Read);
  1551. }
  1552. else
  1553. {
  1554. ParseNumber(ReadType.Read);
  1555. }
  1556. return true;
  1557. case '/':
  1558. ParseComment(true);
  1559. return true;
  1560. case 'u':
  1561. ParseUndefined();
  1562. return true;
  1563. case '{':
  1564. _charPos++;
  1565. SetToken(JsonToken.StartObject);
  1566. return true;
  1567. case '[':
  1568. _charPos++;
  1569. SetToken(JsonToken.StartArray);
  1570. return true;
  1571. case ']':
  1572. _charPos++;
  1573. SetToken(JsonToken.EndArray);
  1574. return true;
  1575. case ',':
  1576. // don't increment position, the next call to read will handle comma
  1577. // this is done to handle multiple empty comma values
  1578. SetToken(JsonToken.Undefined);
  1579. return true;
  1580. case ')':
  1581. _charPos++;
  1582. SetToken(JsonToken.EndConstructor);
  1583. return true;
  1584. case StringUtils.CarriageReturn:
  1585. ProcessCarriageReturn(false);
  1586. break;
  1587. case StringUtils.LineFeed:
  1588. ProcessLineFeed();
  1589. break;
  1590. case ' ':
  1591. case StringUtils.Tab:
  1592. // eat
  1593. _charPos++;
  1594. break;
  1595. default:
  1596. if (char.IsWhiteSpace(currentChar))
  1597. {
  1598. // eat
  1599. _charPos++;
  1600. break;
  1601. }
  1602. if (char.IsNumber(currentChar) || currentChar == '-' || currentChar == '.')
  1603. {
  1604. ParseNumber(ReadType.Read);
  1605. return true;
  1606. }
  1607. throw CreateUnexpectedCharacterException(currentChar);
  1608. }
  1609. }
  1610. }
  1611. private void ProcessLineFeed()
  1612. {
  1613. _charPos++;
  1614. OnNewLine(_charPos);
  1615. }
  1616. private void ProcessCarriageReturn(bool append)
  1617. {
  1618. _charPos++;
  1619. SetNewLine(EnsureChars(1, append));
  1620. }
  1621. private void EatWhitespace()
  1622. {
  1623. while (true)
  1624. {
  1625. char currentChar = _chars[_charPos];
  1626. switch (currentChar)
  1627. {
  1628. case '\0':
  1629. if (_charsUsed == _charPos)
  1630. {
  1631. if (ReadData(false) == 0)
  1632. {
  1633. return;
  1634. }
  1635. }
  1636. else
  1637. {
  1638. _charPos++;
  1639. }
  1640. break;
  1641. case StringUtils.CarriageReturn:
  1642. ProcessCarriageReturn(false);
  1643. break;
  1644. case StringUtils.LineFeed:
  1645. ProcessLineFeed();
  1646. break;
  1647. default:
  1648. if (currentChar == ' ' || char.IsWhiteSpace(currentChar))
  1649. {
  1650. _charPos++;
  1651. }
  1652. else
  1653. {
  1654. return;
  1655. }
  1656. break;
  1657. }
  1658. }
  1659. }
  1660. private void ParseConstructor()
  1661. {
  1662. if (MatchValueWithTrailingSeparator("new"))
  1663. {
  1664. EatWhitespace();
  1665. int initialPosition = _charPos;
  1666. int endPosition;
  1667. while (true)
  1668. {
  1669. char currentChar = _chars[_charPos];
  1670. if (currentChar == '\0')
  1671. {
  1672. if (_charsUsed == _charPos)
  1673. {
  1674. if (ReadData(true) == 0)
  1675. {
  1676. throw JsonReaderException.Create(this, "Unexpected end while parsing constructor.");
  1677. }
  1678. }
  1679. else
  1680. {
  1681. endPosition = _charPos;
  1682. _charPos++;
  1683. break;
  1684. }
  1685. }
  1686. else if (char.IsLetterOrDigit(currentChar))
  1687. {
  1688. _charPos++;
  1689. }
  1690. else if (currentChar == StringUtils.CarriageReturn)
  1691. {
  1692. endPosition = _charPos;
  1693. ProcessCarriageReturn(true);
  1694. break;
  1695. }
  1696. else if (currentChar == StringUtils.LineFeed)
  1697. {
  1698. endPosition = _charPos;
  1699. ProcessLineFeed();
  1700. break;
  1701. }
  1702. else if (char.IsWhiteSpace(currentChar))
  1703. {
  1704. endPosition = _charPos;
  1705. _charPos++;
  1706. break;
  1707. }
  1708. else if (currentChar == '(')
  1709. {
  1710. endPosition = _charPos;
  1711. break;
  1712. }
  1713. else
  1714. {
  1715. throw JsonReaderException.Create(this, "Unexpected character while parsing constructor: {0}.".FormatWith(CultureInfo.InvariantCulture, currentChar));
  1716. }
  1717. }
  1718. _stringReference = new StringReference(_chars, initialPosition, endPosition - initialPosition);
  1719. string constructorName = _stringReference.ToString();
  1720. EatWhitespace();
  1721. if (_chars[_charPos] != '(')
  1722. {
  1723. throw JsonReaderException.Create(this, "Unexpected character while parsing constructor: {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos]));
  1724. }
  1725. _charPos++;
  1726. ClearRecentString();
  1727. SetToken(JsonToken.StartConstructor, constructorName);
  1728. }
  1729. else
  1730. {
  1731. throw JsonReaderException.Create(this, "Unexpected content while parsing JSON.");
  1732. }
  1733. }
  1734. private void ParseNumber(ReadType readType)
  1735. {
  1736. ShiftBufferIfNeeded();
  1737. char firstChar = _chars[_charPos];
  1738. int initialPosition = _charPos;
  1739. ReadNumberIntoBuffer();
  1740. ParseReadNumber(readType, firstChar, initialPosition);
  1741. }
  1742. private void ParseReadNumber(ReadType readType, char firstChar, int initialPosition)
  1743. {
  1744. // set state to PostValue now so that if there is an error parsing the number then the reader can continue
  1745. SetPostValueState(true);
  1746. _stringReference = new StringReference(_chars, initialPosition, _charPos - initialPosition);
  1747. object numberValue;
  1748. JsonToken numberType;
  1749. bool singleDigit = (char.IsDigit(firstChar) && _stringReference.Length == 1);
  1750. bool nonBase10 = (firstChar == '0' && _stringReference.Length > 1 && _stringReference.Chars[_stringReference.StartIndex + 1] != '.' && _stringReference.Chars[_stringReference.StartIndex + 1] != 'e' && _stringReference.Chars[_stringReference.StartIndex + 1] != 'E');
  1751. if (readType == ReadType.ReadAsString)
  1752. {
  1753. string number = _stringReference.ToString();
  1754. // validate that the string is a valid number
  1755. if (nonBase10)
  1756. {
  1757. try
  1758. {
  1759. if (number.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
  1760. {
  1761. Convert.ToInt64(number, 16);
  1762. }
  1763. else
  1764. {
  1765. Convert.ToInt64(number, 8);
  1766. }
  1767. }
  1768. catch (Exception ex)
  1769. {
  1770. throw ThrowReaderError("Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, number), ex);
  1771. }
  1772. }
  1773. else
  1774. {
  1775. if (!double.TryParse(number, NumberStyles.Float, CultureInfo.InvariantCulture, out _))
  1776. {
  1777. throw ThrowReaderError("Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
  1778. }
  1779. }
  1780. numberType = JsonToken.String;
  1781. numberValue = number;
  1782. }
  1783. else if (readType == ReadType.ReadAsInt32)
  1784. {
  1785. if (singleDigit)
  1786. {
  1787. // digit char values start at 48
  1788. numberValue = firstChar - 48;
  1789. }
  1790. else if (nonBase10)
  1791. {
  1792. string number = _stringReference.ToString();
  1793. try
  1794. {
  1795. int integer = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? Convert.ToInt32(number, 16) : Convert.ToInt32(number, 8);
  1796. numberValue = integer;
  1797. }
  1798. catch (Exception ex)
  1799. {
  1800. throw ThrowReaderError("Input string '{0}' is not a valid integer.".FormatWith(CultureInfo.InvariantCulture, number), ex);
  1801. }
  1802. }
  1803. else
  1804. {
  1805. ParseResult parseResult = ConvertUtils.Int32TryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out int value);
  1806. if (parseResult == ParseResult.Success)
  1807. {
  1808. numberValue = value;
  1809. }
  1810. else if (parseResult == ParseResult.Overflow)
  1811. {
  1812. throw ThrowReaderError("JSON integer {0} is too large or small for an Int32.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
  1813. }
  1814. else
  1815. {
  1816. throw ThrowReaderError("Input string '{0}' is not a valid integer.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
  1817. }
  1818. }
  1819. numberType = JsonToken.Integer;
  1820. }
  1821. else if (readType == ReadType.ReadAsDecimal)
  1822. {
  1823. if (singleDigit)
  1824. {
  1825. // digit char values start at 48
  1826. numberValue = (decimal)firstChar - 48;
  1827. }
  1828. else if (nonBase10)
  1829. {
  1830. string number = _stringReference.ToString();
  1831. try
  1832. {
  1833. // decimal.Parse doesn't support parsing hexadecimal values
  1834. long integer = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? Convert.ToInt64(number, 16) : Convert.ToInt64(number, 8);
  1835. numberValue = Convert.ToDecimal(integer);
  1836. }
  1837. catch (Exception ex)
  1838. {
  1839. throw ThrowReaderError("Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, number), ex);
  1840. }
  1841. }
  1842. else
  1843. {
  1844. ParseResult parseResult = ConvertUtils.DecimalTryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out decimal value);
  1845. if (parseResult == ParseResult.Success)
  1846. {
  1847. numberValue = value;
  1848. }
  1849. else
  1850. {
  1851. throw ThrowReaderError("Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
  1852. }
  1853. }
  1854. numberType = JsonToken.Float;
  1855. }
  1856. else if (readType == ReadType.ReadAsDouble)
  1857. {
  1858. if (singleDigit)
  1859. {
  1860. // digit char values start at 48
  1861. numberValue = (double)firstChar - 48;
  1862. }
  1863. else if (nonBase10)
  1864. {
  1865. string number = _stringReference.ToString();
  1866. try
  1867. {
  1868. // double.Parse doesn't support parsing hexadecimal values
  1869. long integer = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? Convert.ToInt64(number, 16) : Convert.ToInt64(number, 8);
  1870. numberValue = Convert.ToDouble(integer);
  1871. }
  1872. catch (Exception ex)
  1873. {
  1874. throw ThrowReaderError("Input string '{0}' is not a valid double.".FormatWith(CultureInfo.InvariantCulture, number), ex);
  1875. }
  1876. }
  1877. else
  1878. {
  1879. string number = _stringReference.ToString();
  1880. if (double.TryParse(number, NumberStyles.Float, CultureInfo.InvariantCulture, out double value))
  1881. {
  1882. numberValue = value;
  1883. }
  1884. else
  1885. {
  1886. throw ThrowReaderError("Input string '{0}' is not a valid double.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
  1887. }
  1888. }
  1889. numberType = JsonToken.Float;
  1890. }
  1891. else
  1892. {
  1893. if (singleDigit)
  1894. {
  1895. // digit char values start at 48
  1896. numberValue = (long)firstChar - 48;
  1897. numberType = JsonToken.Integer;
  1898. }
  1899. else if (nonBase10)
  1900. {
  1901. string number = _stringReference.ToString();
  1902. try
  1903. {
  1904. numberValue = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? Convert.ToInt64(number, 16) : Convert.ToInt64(number, 8);
  1905. }
  1906. catch (Exception ex)
  1907. {
  1908. throw ThrowReaderError("Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, number), ex);
  1909. }
  1910. numberType = JsonToken.Integer;
  1911. }
  1912. else
  1913. {
  1914. ParseResult parseResult = ConvertUtils.Int64TryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out long value);
  1915. if (parseResult == ParseResult.Success)
  1916. {
  1917. numberValue = value;
  1918. numberType = JsonToken.Integer;
  1919. }
  1920. else if (parseResult == ParseResult.Overflow)
  1921. {
  1922. #if HAVE_BIG_INTEGER
  1923. string number = _stringReference.ToString();
  1924. if (number.Length > MaximumJavascriptIntegerCharacterLength)
  1925. {
  1926. throw ThrowReaderError("JSON integer {0} is too large to parse.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
  1927. }
  1928. numberValue = BigIntegerParse(number, CultureInfo.InvariantCulture);
  1929. numberType = JsonToken.Integer;
  1930. #else
  1931. throw ThrowReaderError("JSON integer {0} is too large or small for an Int64.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
  1932. #endif
  1933. }
  1934. else
  1935. {
  1936. if (_floatParseHandling == FloatParseHandling.Decimal)
  1937. {
  1938. parseResult = ConvertUtils.DecimalTryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out decimal d);
  1939. if (parseResult == ParseResult.Success)
  1940. {
  1941. numberValue = d;
  1942. }
  1943. else
  1944. {
  1945. throw ThrowReaderError("Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
  1946. }
  1947. }
  1948. else
  1949. {
  1950. string number = _stringReference.ToString();
  1951. if (double.TryParse(number, NumberStyles.Float, CultureInfo.InvariantCulture, out double d))
  1952. {
  1953. numberValue = d;
  1954. }
  1955. else
  1956. {
  1957. throw ThrowReaderError("Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString()));
  1958. }
  1959. }
  1960. numberType = JsonToken.Float;
  1961. }
  1962. }
  1963. }
  1964. ClearRecentString();
  1965. // index has already been updated
  1966. SetToken(numberType, numberValue, false);
  1967. }
  1968. private JsonReaderException ThrowReaderError(string message, Exception ex = null)
  1969. {
  1970. SetToken(JsonToken.Undefined, null, false);
  1971. return JsonReaderException.Create(this, message, ex);
  1972. }
  1973. #if HAVE_BIG_INTEGER
  1974. // By using the BigInteger type in a separate method,
  1975. // the runtime can execute the ParseNumber even if
  1976. // the System.Numerics.BigInteger.Parse method is
  1977. // missing, which happens in some versions of Mono
  1978. [MethodImpl(MethodImplOptions.NoInlining)]
  1979. private static object BigIntegerParse(string number, CultureInfo culture)
  1980. {
  1981. return System.Numerics.BigInteger.Parse(number, culture);
  1982. }
  1983. #endif
  1984. private void ParseComment(bool setToken)
  1985. {
  1986. // should have already parsed / character before reaching this method
  1987. _charPos++;
  1988. if (!EnsureChars(1, false))
  1989. {
  1990. throw JsonReaderException.Create(this, "Unexpected end while parsing comment.");
  1991. }
  1992. bool singlelineComment;
  1993. if (_chars[_charPos] == '*')
  1994. {
  1995. singlelineComment = false;
  1996. }
  1997. else if (_chars[_charPos] == '/')
  1998. {
  1999. singlelineComment = true;
  2000. }
  2001. else
  2002. {
  2003. throw JsonReaderException.Create(this, "Error parsing comment. Expected: *, got {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos]));
  2004. }
  2005. _charPos++;
  2006. int initialPosition = _charPos;
  2007. while (true)
  2008. {
  2009. switch (_chars[_charPos])
  2010. {
  2011. case '\0':
  2012. if (_charsUsed == _charPos)
  2013. {
  2014. if (ReadData(true) == 0)
  2015. {
  2016. if (!singlelineComment)
  2017. {
  2018. throw JsonReaderException.Create(this, "Unexpected end while parsing comment.");
  2019. }
  2020. EndComment(setToken, initialPosition, _charPos);
  2021. return;
  2022. }
  2023. }
  2024. else
  2025. {
  2026. _charPos++;
  2027. }
  2028. break;
  2029. case '*':
  2030. _charPos++;
  2031. if (!singlelineComment)
  2032. {
  2033. if (EnsureChars(0, true))
  2034. {
  2035. if (_chars[_charPos] == '/')
  2036. {
  2037. EndComment(setToken, initialPosition, _charPos - 1);
  2038. _charPos++;
  2039. return;
  2040. }
  2041. }
  2042. }
  2043. break;
  2044. case StringUtils.CarriageReturn:
  2045. if (singlelineComment)
  2046. {
  2047. EndComment(setToken, initialPosition, _charPos);
  2048. return;
  2049. }
  2050. ProcessCarriageReturn(true);
  2051. break;
  2052. case StringUtils.LineFeed:
  2053. if (singlelineComment)
  2054. {
  2055. EndComment(setToken, initialPosition, _charPos);
  2056. return;
  2057. }
  2058. ProcessLineFeed();
  2059. break;
  2060. default:
  2061. _charPos++;
  2062. break;
  2063. }
  2064. }
  2065. }
  2066. private void EndComment(bool setToken, int initialPosition, int endPosition)
  2067. {
  2068. if (setToken)
  2069. {
  2070. SetToken(JsonToken.Comment, new string(_chars, initialPosition, endPosition - initialPosition));
  2071. }
  2072. }
  2073. private bool MatchValue(string value)
  2074. {
  2075. return MatchValue(EnsureChars(value.Length - 1, true), value);
  2076. }
  2077. private bool MatchValue(bool enoughChars, string value)
  2078. {
  2079. if (!enoughChars)
  2080. {
  2081. _charPos = _charsUsed;
  2082. throw CreateUnexpectedEndException();
  2083. }
  2084. for (int i = 0; i < value.Length; i++)
  2085. {
  2086. if (_chars[_charPos + i] != value[i])
  2087. {
  2088. _charPos += i;
  2089. return false;
  2090. }
  2091. }
  2092. _charPos += value.Length;
  2093. return true;
  2094. }
  2095. private bool MatchValueWithTrailingSeparator(string value)
  2096. {
  2097. // will match value and then move to the next character, checking that it is a separator character
  2098. bool match = MatchValue(value);
  2099. if (!match)
  2100. {
  2101. return false;
  2102. }
  2103. if (!EnsureChars(0, false))
  2104. {
  2105. return true;
  2106. }
  2107. return IsSeparator(_chars[_charPos]) || _chars[_charPos] == '\0';
  2108. }
  2109. private bool IsSeparator(char c)
  2110. {
  2111. switch (c)
  2112. {
  2113. case '}':
  2114. case ']':
  2115. case ',':
  2116. return true;
  2117. case '/':
  2118. // check next character to see if start of a comment
  2119. if (!EnsureChars(1, false))
  2120. {
  2121. return false;
  2122. }
  2123. char nextChart = _chars[_charPos + 1];
  2124. return (nextChart == '*' || nextChart == '/');
  2125. case ')':
  2126. if (CurrentState == State.Constructor || CurrentState == State.ConstructorStart)
  2127. {
  2128. return true;
  2129. }
  2130. break;
  2131. case ' ':
  2132. case StringUtils.Tab:
  2133. case StringUtils.LineFeed:
  2134. case StringUtils.CarriageReturn:
  2135. return true;
  2136. default:
  2137. if (char.IsWhiteSpace(c))
  2138. {
  2139. return true;
  2140. }
  2141. break;
  2142. }
  2143. return false;
  2144. }
  2145. private void ParseTrue()
  2146. {
  2147. // check characters equal 'true'
  2148. // and that it is followed by either a separator character
  2149. // or the text ends
  2150. if (MatchValueWithTrailingSeparator(JsonConvert.True))
  2151. {
  2152. SetToken(JsonToken.Boolean, true);
  2153. }
  2154. else
  2155. {
  2156. throw JsonReaderException.Create(this, "Error parsing boolean value.");
  2157. }
  2158. }
  2159. private void ParseNull()
  2160. {
  2161. if (MatchValueWithTrailingSeparator(JsonConvert.Null))
  2162. {
  2163. SetToken(JsonToken.Null);
  2164. }
  2165. else
  2166. {
  2167. throw JsonReaderException.Create(this, "Error parsing null value.");
  2168. }
  2169. }
  2170. private void ParseUndefined()
  2171. {
  2172. if (MatchValueWithTrailingSeparator(JsonConvert.Undefined))
  2173. {
  2174. SetToken(JsonToken.Undefined);
  2175. }
  2176. else
  2177. {
  2178. throw JsonReaderException.Create(this, "Error parsing undefined value.");
  2179. }
  2180. }
  2181. private void ParseFalse()
  2182. {
  2183. if (MatchValueWithTrailingSeparator(JsonConvert.False))
  2184. {
  2185. SetToken(JsonToken.Boolean, false);
  2186. }
  2187. else
  2188. {
  2189. throw JsonReaderException.Create(this, "Error parsing boolean value.");
  2190. }
  2191. }
  2192. private object ParseNumberNegativeInfinity(ReadType readType)
  2193. {
  2194. return ParseNumberNegativeInfinity(readType, MatchValueWithTrailingSeparator(JsonConvert.NegativeInfinity));
  2195. }
  2196. private object ParseNumberNegativeInfinity(ReadType readType, bool matched)
  2197. {
  2198. if (matched)
  2199. {
  2200. switch (readType)
  2201. {
  2202. case ReadType.Read:
  2203. case ReadType.ReadAsDouble:
  2204. if (_floatParseHandling == FloatParseHandling.Double)
  2205. {
  2206. SetToken(JsonToken.Float, double.NegativeInfinity);
  2207. return double.NegativeInfinity;
  2208. }
  2209. break;
  2210. case ReadType.ReadAsString:
  2211. SetToken(JsonToken.String, JsonConvert.NegativeInfinity);
  2212. return JsonConvert.NegativeInfinity;
  2213. }
  2214. throw JsonReaderException.Create(this, "Cannot read -Infinity value.");
  2215. }
  2216. throw JsonReaderException.Create(this, "Error parsing -Infinity value.");
  2217. }
  2218. private object ParseNumberPositiveInfinity(ReadType readType)
  2219. {
  2220. return ParseNumberPositiveInfinity(readType, MatchValueWithTrailingSeparator(JsonConvert.PositiveInfinity));
  2221. }
  2222. private object ParseNumberPositiveInfinity(ReadType readType, bool matched)
  2223. {
  2224. if (matched)
  2225. {
  2226. switch (readType)
  2227. {
  2228. case ReadType.Read:
  2229. case ReadType.ReadAsDouble:
  2230. if (_floatParseHandling == FloatParseHandling.Double)
  2231. {
  2232. SetToken(JsonToken.Float, double.PositiveInfinity);
  2233. return double.PositiveInfinity;
  2234. }
  2235. break;
  2236. case ReadType.ReadAsString:
  2237. SetToken(JsonToken.String, JsonConvert.PositiveInfinity);
  2238. return JsonConvert.PositiveInfinity;
  2239. }
  2240. throw JsonReaderException.Create(this, "Cannot read Infinity value.");
  2241. }
  2242. throw JsonReaderException.Create(this, "Error parsing Infinity value.");
  2243. }
  2244. private object ParseNumberNaN(ReadType readType)
  2245. {
  2246. return ParseNumberNaN(readType, MatchValueWithTrailingSeparator(JsonConvert.NaN));
  2247. }
  2248. private object ParseNumberNaN(ReadType readType, bool matched)
  2249. {
  2250. if (matched)
  2251. {
  2252. switch (readType)
  2253. {
  2254. case ReadType.Read:
  2255. case ReadType.ReadAsDouble:
  2256. if (_floatParseHandling == FloatParseHandling.Double)
  2257. {
  2258. SetToken(JsonToken.Float, double.NaN);
  2259. return double.NaN;
  2260. }
  2261. break;
  2262. case ReadType.ReadAsString:
  2263. SetToken(JsonToken.String, JsonConvert.NaN);
  2264. return JsonConvert.NaN;
  2265. }
  2266. throw JsonReaderException.Create(this, "Cannot read NaN value.");
  2267. }
  2268. throw JsonReaderException.Create(this, "Error parsing NaN value.");
  2269. }
  2270. /// <summary>
  2271. /// Changes the reader's state to <see cref="JsonReader.State.Closed"/>.
  2272. /// If <see cref="JsonReader.CloseInput"/> is set to <c>true</c>, the underlying <see cref="TextReader"/> is also closed.
  2273. /// </summary>
  2274. public override void Close()
  2275. {
  2276. base.Close();
  2277. if (_chars != null)
  2278. {
  2279. BufferUtils.ReturnBuffer(_arrayPool, _chars);
  2280. _chars = null;
  2281. }
  2282. if (CloseInput)
  2283. {
  2284. _reader?.Close();
  2285. //_reader?.Dispose();
  2286. }
  2287. _stringBuffer.Clear(_arrayPool);
  2288. }
  2289. /// <summary>
  2290. /// Gets a value indicating whether the class can return line information.
  2291. /// </summary>
  2292. /// <returns>
  2293. /// <c>true</c> if <see cref="JsonTextReader.LineNumber"/> and <see cref="JsonTextReader.LinePosition"/> can be provided; otherwise, <c>false</c>.
  2294. /// </returns>
  2295. public bool HasLineInfo()
  2296. {
  2297. return true;
  2298. }
  2299. /// <summary>
  2300. /// Gets the current line number.
  2301. /// </summary>
  2302. /// <value>
  2303. /// The current line number or 0 if no line information is available (for example, <see cref="JsonTextReader.HasLineInfo"/> returns <c>false</c>).
  2304. /// </value>
  2305. public int LineNumber
  2306. {
  2307. get
  2308. {
  2309. if (CurrentState == State.Start && LinePosition == 0 && TokenType != JsonToken.Comment)
  2310. {
  2311. return 0;
  2312. }
  2313. return _lineNumber;
  2314. }
  2315. }
  2316. /// <summary>
  2317. /// Gets the current line position.
  2318. /// </summary>
  2319. /// <value>
  2320. /// The current line position or 0 if no line information is available (for example, <see cref="JsonTextReader.HasLineInfo"/> returns <c>false</c>).
  2321. /// </value>
  2322. public int LinePosition => _charPos - _lineStartPos;
  2323. }
  2324. }