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.

911 lines
28 KiB

4 years ago
4 years ago
4 years ago
4 years ago
  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.Collections.Generic;
  27. using System.Globalization;
  28. #if HAVE_BIG_INTEGER
  29. using System.Numerics;
  30. #endif
  31. using System.Text;
  32. using System.IO;
  33. using System.Xml;
  34. using Newtonsoft.Json.Utilities;
  35. namespace Newtonsoft.Json
  36. {
  37. /// <summary>
  38. /// Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data.
  39. /// </summary>
  40. internal partial class JsonTextWriter : JsonWriter
  41. {
  42. private const int IndentCharBufferSize = 12;
  43. private readonly TextWriter _writer;
  44. private Base64Encoder _base64Encoder;
  45. private char _indentChar;
  46. private int _indentation;
  47. private char _quoteChar;
  48. private bool _quoteName;
  49. private bool[] _charEscapeFlags;
  50. private char[] _writeBuffer;
  51. private IArrayPool<char> _arrayPool;
  52. private char[] _indentChars;
  53. private Base64Encoder Base64Encoder
  54. {
  55. get
  56. {
  57. if (_base64Encoder == null)
  58. {
  59. _base64Encoder = new Base64Encoder(_writer);
  60. }
  61. return _base64Encoder;
  62. }
  63. }
  64. /// <summary>
  65. /// Gets or sets the writer's character array pool.
  66. /// </summary>
  67. public IArrayPool<char> ArrayPool
  68. {
  69. get => _arrayPool;
  70. set
  71. {
  72. if (value == null)
  73. {
  74. throw new ArgumentNullException(nameof(value));
  75. }
  76. _arrayPool = value;
  77. }
  78. }
  79. /// <summary>
  80. /// Gets or sets how many <see cref="JsonTextWriter.IndentChar"/>s to write for each level in the hierarchy when <see cref="JsonWriter.Formatting"/> is set to <see cref="Formatting.Indented"/>.
  81. /// </summary>
  82. public int Indentation
  83. {
  84. get => _indentation;
  85. set
  86. {
  87. if (value < 0)
  88. {
  89. throw new ArgumentException("Indentation value must be greater than 0.");
  90. }
  91. _indentation = value;
  92. }
  93. }
  94. /// <summary>
  95. /// Gets or sets which character to use to quote attribute values.
  96. /// </summary>
  97. public char QuoteChar
  98. {
  99. get => _quoteChar;
  100. set
  101. {
  102. if (value != '"' && value != '\'')
  103. {
  104. throw new ArgumentException(@"Invalid JavaScript string quote character. Valid quote characters are ' and "".");
  105. }
  106. _quoteChar = value;
  107. UpdateCharEscapeFlags();
  108. }
  109. }
  110. /// <summary>
  111. /// Gets or sets which character to use for indenting when <see cref="JsonWriter.Formatting"/> is set to <see cref="Formatting.Indented"/>.
  112. /// </summary>
  113. public char IndentChar
  114. {
  115. get => _indentChar;
  116. set
  117. {
  118. if (value != _indentChar)
  119. {
  120. _indentChar = value;
  121. _indentChars = null;
  122. }
  123. }
  124. }
  125. /// <summary>
  126. /// Gets or sets a value indicating whether object names will be surrounded with quotes.
  127. /// </summary>
  128. public bool QuoteName
  129. {
  130. get => _quoteName;
  131. set => _quoteName = value;
  132. }
  133. /// <summary>
  134. /// Initializes a new instance of the <see cref="JsonTextWriter"/> class using the specified <see cref="TextWriter"/>.
  135. /// </summary>
  136. /// <param name="textWriter">The <see cref="TextWriter"/> to write to.</param>
  137. public JsonTextWriter(TextWriter textWriter)
  138. {
  139. if (textWriter == null)
  140. {
  141. throw new ArgumentNullException(nameof(textWriter));
  142. }
  143. _writer = textWriter;
  144. _quoteChar = '"';
  145. _quoteName = true;
  146. _indentChar = ' ';
  147. _indentation = 2;
  148. UpdateCharEscapeFlags();
  149. #if HAVE_ASYNC
  150. _safeAsync = GetType() == typeof(JsonTextWriter);
  151. #endif
  152. }
  153. /// <summary>
  154. /// Flushes whatever is in the buffer to the underlying <see cref="TextWriter"/> and also flushes the underlying <see cref="TextWriter"/>.
  155. /// </summary>
  156. public override void Flush()
  157. {
  158. _writer.Flush();
  159. }
  160. /// <summary>
  161. /// Closes this writer.
  162. /// If <see cref="JsonWriter.CloseOutput"/> is set to <c>true</c>, the underlying <see cref="TextWriter"/> is also closed.
  163. /// If <see cref="JsonWriter.AutoCompleteOnClose"/> is set to <c>true</c>, the JSON is auto-completed.
  164. /// </summary>
  165. public override void Close()
  166. {
  167. base.Close();
  168. CloseBufferAndWriter();
  169. }
  170. private void CloseBufferAndWriter()
  171. {
  172. if (_writeBuffer != null)
  173. {
  174. BufferUtils.ReturnBuffer(_arrayPool, _writeBuffer);
  175. _writeBuffer = null;
  176. }
  177. if (CloseOutput)
  178. {
  179. _writer?.Close();
  180. //_writer?.Dispose();
  181. }
  182. }
  183. /// <summary>
  184. /// Writes the beginning of a JSON object.
  185. /// </summary>
  186. public override void WriteStartObject()
  187. {
  188. InternalWriteStart(JsonToken.StartObject, JsonContainerType.Object);
  189. _writer.Write('{');
  190. }
  191. /// <summary>
  192. /// Writes the beginning of a JSON array.
  193. /// </summary>
  194. public override void WriteStartArray()
  195. {
  196. InternalWriteStart(JsonToken.StartArray, JsonContainerType.Array);
  197. _writer.Write('[');
  198. }
  199. /// <summary>
  200. /// Writes the start of a constructor with the given name.
  201. /// </summary>
  202. /// <param name="name">The name of the constructor.</param>
  203. public override void WriteStartConstructor(string name)
  204. {
  205. InternalWriteStart(JsonToken.StartConstructor, JsonContainerType.Constructor);
  206. _writer.Write("new ");
  207. _writer.Write(name);
  208. _writer.Write('(');
  209. }
  210. /// <summary>
  211. /// Writes the specified end token.
  212. /// </summary>
  213. /// <param name="token">The end token to write.</param>
  214. protected override void WriteEnd(JsonToken token)
  215. {
  216. switch (token)
  217. {
  218. case JsonToken.EndObject:
  219. _writer.Write('}');
  220. break;
  221. case JsonToken.EndArray:
  222. _writer.Write(']');
  223. break;
  224. case JsonToken.EndConstructor:
  225. _writer.Write(')');
  226. break;
  227. default:
  228. throw JsonWriterException.Create(this, "Invalid JsonToken: " + token, null);
  229. }
  230. }
  231. /// <summary>
  232. /// Writes the property name of a name/value pair on a JSON object.
  233. /// </summary>
  234. /// <param name="name">The name of the property.</param>
  235. public override void WritePropertyName(string name)
  236. {
  237. InternalWritePropertyName(name);
  238. WriteEscapedString(name, _quoteName);
  239. _writer.Write(':');
  240. }
  241. /// <summary>
  242. /// Writes the property name of a name/value pair on a JSON object.
  243. /// </summary>
  244. /// <param name="name">The name of the property.</param>
  245. /// <param name="escape">A flag to indicate whether the text should be escaped when it is written as a JSON property name.</param>
  246. public override void WritePropertyName(string name, bool escape)
  247. {
  248. InternalWritePropertyName(name);
  249. if (escape)
  250. {
  251. WriteEscapedString(name, _quoteName);
  252. }
  253. else
  254. {
  255. if (_quoteName)
  256. {
  257. _writer.Write(_quoteChar);
  258. }
  259. _writer.Write(name);
  260. if (_quoteName)
  261. {
  262. _writer.Write(_quoteChar);
  263. }
  264. }
  265. _writer.Write(':');
  266. }
  267. internal override void OnStringEscapeHandlingChanged()
  268. {
  269. UpdateCharEscapeFlags();
  270. }
  271. private void UpdateCharEscapeFlags()
  272. {
  273. _charEscapeFlags = JavaScriptUtils.GetCharEscapeFlags(StringEscapeHandling, _quoteChar);
  274. }
  275. /// <summary>
  276. /// Writes indent characters.
  277. /// </summary>
  278. protected override void WriteIndent()
  279. {
  280. // levels of indentation multiplied by the indent count
  281. int currentIndentCount = Top * _indentation;
  282. int newLineLen = SetIndentChars();
  283. _writer.Write(_indentChars, 0, newLineLen + Math.Min(currentIndentCount, IndentCharBufferSize));
  284. while ((currentIndentCount -= IndentCharBufferSize) > 0)
  285. {
  286. _writer.Write(_indentChars, newLineLen, Math.Min(currentIndentCount, IndentCharBufferSize));
  287. }
  288. }
  289. private int SetIndentChars()
  290. {
  291. // Set _indentChars to be a newline followed by IndentCharBufferSize indent characters.
  292. string writerNewLine = _writer.NewLine;
  293. int newLineLen = writerNewLine.Length;
  294. bool match = _indentChars != null && _indentChars.Length == IndentCharBufferSize + newLineLen;
  295. if (match)
  296. {
  297. for (int i = 0; i != newLineLen; ++i)
  298. {
  299. if (writerNewLine[i] != _indentChars[i])
  300. {
  301. match = false;
  302. break;
  303. }
  304. }
  305. }
  306. if (!match)
  307. {
  308. // If we're here, either _indentChars hasn't been set yet, or _writer.NewLine
  309. // has been changed, or _indentChar has been changed.
  310. _indentChars = (writerNewLine + new string(_indentChar, IndentCharBufferSize)).ToCharArray();
  311. }
  312. return newLineLen;
  313. }
  314. /// <summary>
  315. /// Writes the JSON value delimiter.
  316. /// </summary>
  317. protected override void WriteValueDelimiter()
  318. {
  319. _writer.Write(',');
  320. }
  321. /// <summary>
  322. /// Writes an indent space.
  323. /// </summary>
  324. protected override void WriteIndentSpace()
  325. {
  326. _writer.Write(' ');
  327. }
  328. private void WriteValueInternal(string value, JsonToken token)
  329. {
  330. _writer.Write(value);
  331. }
  332. #region WriteValue methods
  333. /// <summary>
  334. /// Writes a <see cref="Object"/> value.
  335. /// An error will raised if the value cannot be written as a single JSON token.
  336. /// </summary>
  337. /// <param name="value">The <see cref="Object"/> value to write.</param>
  338. public override void WriteValue(object value)
  339. {
  340. #if HAVE_BIG_INTEGER
  341. if (value is BigInteger)
  342. {
  343. InternalWriteValue(JsonToken.Integer);
  344. WriteValueInternal(((BigInteger)value).ToString(CultureInfo.InvariantCulture), JsonToken.String);
  345. }
  346. else
  347. #endif
  348. {
  349. base.WriteValue(value);
  350. }
  351. }
  352. /// <summary>
  353. /// Writes a null value.
  354. /// </summary>
  355. public override void WriteNull()
  356. {
  357. InternalWriteValue(JsonToken.Null);
  358. WriteValueInternal(JsonConvert.Null, JsonToken.Null);
  359. }
  360. /// <summary>
  361. /// Writes an undefined value.
  362. /// </summary>
  363. public override void WriteUndefined()
  364. {
  365. InternalWriteValue(JsonToken.Undefined);
  366. WriteValueInternal(JsonConvert.Undefined, JsonToken.Undefined);
  367. }
  368. /// <summary>
  369. /// Writes raw JSON.
  370. /// </summary>
  371. /// <param name="json">The raw JSON to write.</param>
  372. public override void WriteRaw(string json)
  373. {
  374. InternalWriteRaw();
  375. _writer.Write(json);
  376. }
  377. /// <summary>
  378. /// Writes a <see cref="String"/> value.
  379. /// </summary>
  380. /// <param name="value">The <see cref="String"/> value to write.</param>
  381. public override void WriteValue(string value)
  382. {
  383. InternalWriteValue(JsonToken.String);
  384. if (value == null)
  385. {
  386. WriteValueInternal(JsonConvert.Null, JsonToken.Null);
  387. }
  388. else
  389. {
  390. WriteEscapedString(value, true);
  391. }
  392. }
  393. private void WriteEscapedString(string value, bool quote)
  394. {
  395. EnsureWriteBuffer();
  396. JavaScriptUtils.WriteEscapedJavaScriptString(_writer, value, _quoteChar, quote, _charEscapeFlags, StringEscapeHandling, _arrayPool, ref _writeBuffer);
  397. }
  398. /// <summary>
  399. /// Writes a <see cref="Int32"/> value.
  400. /// </summary>
  401. /// <param name="value">The <see cref="Int32"/> value to write.</param>
  402. public override void WriteValue(int value)
  403. {
  404. InternalWriteValue(JsonToken.Integer);
  405. WriteIntegerValue(value);
  406. }
  407. /// <summary>
  408. /// Writes a <see cref="UInt32"/> value.
  409. /// </summary>
  410. /// <param name="value">The <see cref="UInt32"/> value to write.</param>
  411. public override void WriteValue(uint value)
  412. {
  413. InternalWriteValue(JsonToken.Integer);
  414. WriteIntegerValue(value);
  415. }
  416. /// <summary>
  417. /// Writes a <see cref="Int64"/> value.
  418. /// </summary>
  419. /// <param name="value">The <see cref="Int64"/> value to write.</param>
  420. public override void WriteValue(long value)
  421. {
  422. InternalWriteValue(JsonToken.Integer);
  423. WriteIntegerValue(value);
  424. }
  425. /// <summary>
  426. /// Writes a <see cref="UInt64"/> value.
  427. /// </summary>
  428. /// <param name="value">The <see cref="UInt64"/> value to write.</param>
  429. public override void WriteValue(ulong value)
  430. {
  431. InternalWriteValue(JsonToken.Integer);
  432. WriteIntegerValue(value, false);
  433. }
  434. /// <summary>
  435. /// Writes a <see cref="Single"/> value.
  436. /// </summary>
  437. /// <param name="value">The <see cref="Single"/> value to write.</param>
  438. public override void WriteValue(float value)
  439. {
  440. InternalWriteValue(JsonToken.Float);
  441. WriteValueInternal(JsonConvert.ToString(value, FloatFormatHandling, QuoteChar, false), JsonToken.Float);
  442. }
  443. /// <summary>
  444. /// Writes a <see cref="Nullable{T}"/> of <see cref="Single"/> value.
  445. /// </summary>
  446. /// <param name="value">The <see cref="Nullable{T}"/> of <see cref="Single"/> value to write.</param>
  447. public override void WriteValue(float? value)
  448. {
  449. if (value == null)
  450. {
  451. WriteNull();
  452. }
  453. else
  454. {
  455. InternalWriteValue(JsonToken.Float);
  456. WriteValueInternal(JsonConvert.ToString(value.GetValueOrDefault(), FloatFormatHandling, QuoteChar, true), JsonToken.Float);
  457. }
  458. }
  459. /// <summary>
  460. /// Writes a <see cref="Double"/> value.
  461. /// </summary>
  462. /// <param name="value">The <see cref="Double"/> value to write.</param>
  463. public override void WriteValue(double value)
  464. {
  465. InternalWriteValue(JsonToken.Float);
  466. WriteValueInternal(JsonConvert.ToString(value, FloatFormatHandling, QuoteChar, false), JsonToken.Float);
  467. }
  468. /// <summary>
  469. /// Writes a <see cref="Nullable{T}"/> of <see cref="Double"/> value.
  470. /// </summary>
  471. /// <param name="value">The <see cref="Nullable{T}"/> of <see cref="Double"/> value to write.</param>
  472. public override void WriteValue(double? value)
  473. {
  474. if (value == null)
  475. {
  476. WriteNull();
  477. }
  478. else
  479. {
  480. InternalWriteValue(JsonToken.Float);
  481. WriteValueInternal(JsonConvert.ToString(value.GetValueOrDefault(), FloatFormatHandling, QuoteChar, true), JsonToken.Float);
  482. }
  483. }
  484. /// <summary>
  485. /// Writes a <see cref="Boolean"/> value.
  486. /// </summary>
  487. /// <param name="value">The <see cref="Boolean"/> value to write.</param>
  488. public override void WriteValue(bool value)
  489. {
  490. InternalWriteValue(JsonToken.Boolean);
  491. WriteValueInternal(JsonConvert.ToString(value), JsonToken.Boolean);
  492. }
  493. /// <summary>
  494. /// Writes a <see cref="Int16"/> value.
  495. /// </summary>
  496. /// <param name="value">The <see cref="Int16"/> value to write.</param>
  497. public override void WriteValue(short value)
  498. {
  499. InternalWriteValue(JsonToken.Integer);
  500. WriteIntegerValue(value);
  501. }
  502. /// <summary>
  503. /// Writes a <see cref="UInt16"/> value.
  504. /// </summary>
  505. /// <param name="value">The <see cref="UInt16"/> value to write.</param>
  506. public override void WriteValue(ushort value)
  507. {
  508. InternalWriteValue(JsonToken.Integer);
  509. WriteIntegerValue(value);
  510. }
  511. /// <summary>
  512. /// Writes a <see cref="Char"/> value.
  513. /// </summary>
  514. /// <param name="value">The <see cref="Char"/> value to write.</param>
  515. public override void WriteValue(char value)
  516. {
  517. InternalWriteValue(JsonToken.String);
  518. WriteValueInternal(JsonConvert.ToString(value), JsonToken.String);
  519. }
  520. /// <summary>
  521. /// Writes a <see cref="Byte"/> value.
  522. /// </summary>
  523. /// <param name="value">The <see cref="Byte"/> value to write.</param>
  524. public override void WriteValue(byte value)
  525. {
  526. InternalWriteValue(JsonToken.Integer);
  527. WriteIntegerValue(value);
  528. }
  529. /// <summary>
  530. /// Writes a <see cref="SByte"/> value.
  531. /// </summary>
  532. /// <param name="value">The <see cref="SByte"/> value to write.</param>
  533. public override void WriteValue(sbyte value)
  534. {
  535. InternalWriteValue(JsonToken.Integer);
  536. WriteIntegerValue(value);
  537. }
  538. /// <summary>
  539. /// Writes a <see cref="Decimal"/> value.
  540. /// </summary>
  541. /// <param name="value">The <see cref="Decimal"/> value to write.</param>
  542. public override void WriteValue(decimal value)
  543. {
  544. InternalWriteValue(JsonToken.Float);
  545. WriteValueInternal(JsonConvert.ToString(value), JsonToken.Float);
  546. }
  547. /// <summary>
  548. /// Writes a <see cref="DateTime"/> value.
  549. /// </summary>
  550. /// <param name="value">The <see cref="DateTime"/> value to write.</param>
  551. public override void WriteValue(DateTime value)
  552. {
  553. InternalWriteValue(JsonToken.Date);
  554. value = DateTimeUtils.EnsureDateTime(value, DateTimeZoneHandling);
  555. if (string.IsNullOrEmpty(DateFormatString))
  556. {
  557. int length = WriteValueToBuffer(value);
  558. _writer.Write(_writeBuffer, 0, length);
  559. }
  560. else
  561. {
  562. _writer.Write(_quoteChar);
  563. _writer.Write(value.ToString(DateFormatString, Culture));
  564. _writer.Write(_quoteChar);
  565. }
  566. }
  567. private int WriteValueToBuffer(DateTime value)
  568. {
  569. EnsureWriteBuffer();
  570. int pos = 0;
  571. _writeBuffer[pos++] = _quoteChar;
  572. pos = DateTimeUtils.WriteDateTimeString(_writeBuffer, pos, value, null, value.Kind, DateFormatHandling);
  573. _writeBuffer[pos++] = _quoteChar;
  574. return pos;
  575. }
  576. /// <summary>
  577. /// Writes a <see cref="Byte"/>[] value.
  578. /// </summary>
  579. /// <param name="value">The <see cref="Byte"/>[] value to write.</param>
  580. public override void WriteValue(byte[] value)
  581. {
  582. if (value == null)
  583. {
  584. WriteNull();
  585. }
  586. else
  587. {
  588. InternalWriteValue(JsonToken.Bytes);
  589. _writer.Write(_quoteChar);
  590. Base64Encoder.Encode(value, 0, value.Length);
  591. Base64Encoder.Flush();
  592. _writer.Write(_quoteChar);
  593. }
  594. }
  595. #if !NET20
  596. /// <summary>
  597. /// Writes a <see cref="DateTimeOffset"/> value.
  598. /// </summary>
  599. /// <param name="value">The <see cref="DateTimeOffset"/> value to write.</param>
  600. public override void WriteValue(DateTimeOffset value)
  601. {
  602. InternalWriteValue(JsonToken.Date);
  603. if (string.IsNullOrEmpty(DateFormatString))
  604. {
  605. int length = WriteValueToBuffer(value);
  606. _writer.Write(_writeBuffer, 0, length);
  607. }
  608. else
  609. {
  610. _writer.Write(_quoteChar);
  611. _writer.Write(value.ToString(DateFormatString, Culture));
  612. _writer.Write(_quoteChar);
  613. }
  614. }
  615. private int WriteValueToBuffer(DateTimeOffset value)
  616. {
  617. EnsureWriteBuffer();
  618. int pos = 0;
  619. _writeBuffer[pos++] = _quoteChar;
  620. pos = DateTimeUtils.WriteDateTimeString(_writeBuffer, pos, (DateFormatHandling == DateFormatHandling.IsoDateFormat) ? value.DateTime : value.UtcDateTime, value.Offset, DateTimeKind.Local, DateFormatHandling);
  621. _writeBuffer[pos++] = _quoteChar;
  622. return pos;
  623. }
  624. #endif
  625. /// <summary>
  626. /// Writes a <see cref="Guid"/> value.
  627. /// </summary>
  628. /// <param name="value">The <see cref="Guid"/> value to write.</param>
  629. public override void WriteValue(Guid value)
  630. {
  631. InternalWriteValue(JsonToken.String);
  632. string text = null;
  633. #if HAVE_CHAR_TO_STRING_WITH_CULTURE
  634. text = value.ToString("D", CultureInfo.InvariantCulture);
  635. #else
  636. text = value.ToString("D");
  637. #endif
  638. _writer.Write(_quoteChar);
  639. _writer.Write(text);
  640. _writer.Write(_quoteChar);
  641. }
  642. /// <summary>
  643. /// Writes a <see cref="TimeSpan"/> value.
  644. /// </summary>
  645. /// <param name="value">The <see cref="TimeSpan"/> value to write.</param>
  646. public override void WriteValue(TimeSpan value)
  647. {
  648. InternalWriteValue(JsonToken.String);
  649. string text;
  650. #if NET20
  651. text = value.ToString();
  652. #else
  653. text = value.ToString(null, CultureInfo.InvariantCulture);
  654. #endif
  655. _writer.Write(_quoteChar);
  656. _writer.Write(text);
  657. _writer.Write(_quoteChar);
  658. }
  659. /// <summary>
  660. /// Writes a <see cref="Uri"/> value.
  661. /// </summary>
  662. /// <param name="value">The <see cref="Uri"/> value to write.</param>
  663. public override void WriteValue(Uri value)
  664. {
  665. if (value == null)
  666. {
  667. WriteNull();
  668. }
  669. else
  670. {
  671. InternalWriteValue(JsonToken.String);
  672. WriteEscapedString(value.OriginalString, true);
  673. }
  674. }
  675. #endregion
  676. /// <summary>
  677. /// Writes a comment <c>/*...*/</c> containing the specified text.
  678. /// </summary>
  679. /// <param name="text">Text to place inside the comment.</param>
  680. public override void WriteComment(string text)
  681. {
  682. InternalWriteComment();
  683. _writer.Write("/*");
  684. _writer.Write(text);
  685. _writer.Write("*/");
  686. }
  687. /// <summary>
  688. /// Writes the given white space.
  689. /// </summary>
  690. /// <param name="ws">The string of white space characters.</param>
  691. public override void WriteWhitespace(string ws)
  692. {
  693. InternalWriteWhitespace(ws);
  694. _writer.Write(ws);
  695. }
  696. private void EnsureWriteBuffer()
  697. {
  698. if (_writeBuffer == null)
  699. {
  700. // maximum buffer sized used when writing iso date
  701. _writeBuffer = BufferUtils.RentBuffer(_arrayPool, 35);
  702. }
  703. }
  704. private void WriteIntegerValue(long value)
  705. {
  706. if (value >= 0 && value <= 9)
  707. {
  708. _writer.Write((char)('0' + value));
  709. }
  710. else
  711. {
  712. bool negative = value < 0;
  713. WriteIntegerValue(negative ? (ulong)-value : (ulong)value, negative);
  714. }
  715. }
  716. private void WriteIntegerValue(ulong value, bool negative)
  717. {
  718. if (!negative & value <= 9)
  719. {
  720. _writer.Write((char)('0' + value));
  721. }
  722. else
  723. {
  724. int length = WriteNumberToBuffer(value, negative);
  725. _writer.Write(_writeBuffer, 0, length);
  726. }
  727. }
  728. private int WriteNumberToBuffer(ulong value, bool negative)
  729. {
  730. if (value <= uint.MaxValue)
  731. {
  732. // avoid the 64 bit division if possible
  733. return WriteNumberToBuffer((uint)value, negative);
  734. }
  735. EnsureWriteBuffer();
  736. int totalLength = MathUtils.IntLength(value);
  737. if (negative)
  738. {
  739. totalLength++;
  740. _writeBuffer[0] = '-';
  741. }
  742. int index = totalLength;
  743. do
  744. {
  745. ulong quotient = value / 10;
  746. ulong digit = value - (quotient * 10);
  747. _writeBuffer[--index] = (char)('0' + digit);
  748. value = quotient;
  749. } while (value != 0);
  750. return totalLength;
  751. }
  752. private void WriteIntegerValue(int value)
  753. {
  754. if (value >= 0 && value <= 9)
  755. {
  756. _writer.Write((char)('0' + value));
  757. }
  758. else
  759. {
  760. bool negative = value < 0;
  761. WriteIntegerValue(negative ? (uint)-value : (uint)value, negative);
  762. }
  763. }
  764. private void WriteIntegerValue(uint value, bool negative)
  765. {
  766. if (!negative & value <= 9)
  767. {
  768. _writer.Write((char)('0' + value));
  769. }
  770. else
  771. {
  772. int length = WriteNumberToBuffer(value, negative);
  773. _writer.Write(_writeBuffer, 0, length);
  774. }
  775. }
  776. private int WriteNumberToBuffer(uint value, bool negative)
  777. {
  778. EnsureWriteBuffer();
  779. int totalLength = MathUtils.IntLength(value);
  780. if (negative)
  781. {
  782. totalLength++;
  783. _writeBuffer[0] = '-';
  784. }
  785. int index = totalLength;
  786. do
  787. {
  788. uint quotient = value / 10;
  789. uint digit = value - (quotient * 10);
  790. _writeBuffer[--index] = (char)('0' + digit);
  791. value = quotient;
  792. } while (value != 0);
  793. return totalLength;
  794. }
  795. }
  796. }