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

  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. [CLSCompliant(false)]
  412. public override void WriteValue(uint value)
  413. {
  414. InternalWriteValue(JsonToken.Integer);
  415. WriteIntegerValue(value);
  416. }
  417. /// <summary>
  418. /// Writes a <see cref="Int64"/> value.
  419. /// </summary>
  420. /// <param name="value">The <see cref="Int64"/> value to write.</param>
  421. public override void WriteValue(long value)
  422. {
  423. InternalWriteValue(JsonToken.Integer);
  424. WriteIntegerValue(value);
  425. }
  426. /// <summary>
  427. /// Writes a <see cref="UInt64"/> value.
  428. /// </summary>
  429. /// <param name="value">The <see cref="UInt64"/> value to write.</param>
  430. [CLSCompliant(false)]
  431. public override void WriteValue(ulong value)
  432. {
  433. InternalWriteValue(JsonToken.Integer);
  434. WriteIntegerValue(value, false);
  435. }
  436. /// <summary>
  437. /// Writes a <see cref="Single"/> value.
  438. /// </summary>
  439. /// <param name="value">The <see cref="Single"/> value to write.</param>
  440. public override void WriteValue(float value)
  441. {
  442. InternalWriteValue(JsonToken.Float);
  443. WriteValueInternal(JsonConvert.ToString(value, FloatFormatHandling, QuoteChar, false), JsonToken.Float);
  444. }
  445. /// <summary>
  446. /// Writes a <see cref="Nullable{T}"/> of <see cref="Single"/> value.
  447. /// </summary>
  448. /// <param name="value">The <see cref="Nullable{T}"/> of <see cref="Single"/> value to write.</param>
  449. public override void WriteValue(float? value)
  450. {
  451. if (value == null)
  452. {
  453. WriteNull();
  454. }
  455. else
  456. {
  457. InternalWriteValue(JsonToken.Float);
  458. WriteValueInternal(JsonConvert.ToString(value.GetValueOrDefault(), FloatFormatHandling, QuoteChar, true), JsonToken.Float);
  459. }
  460. }
  461. /// <summary>
  462. /// Writes a <see cref="Double"/> value.
  463. /// </summary>
  464. /// <param name="value">The <see cref="Double"/> value to write.</param>
  465. public override void WriteValue(double value)
  466. {
  467. InternalWriteValue(JsonToken.Float);
  468. WriteValueInternal(JsonConvert.ToString(value, FloatFormatHandling, QuoteChar, false), JsonToken.Float);
  469. }
  470. /// <summary>
  471. /// Writes a <see cref="Nullable{T}"/> of <see cref="Double"/> value.
  472. /// </summary>
  473. /// <param name="value">The <see cref="Nullable{T}"/> of <see cref="Double"/> value to write.</param>
  474. public override void WriteValue(double? value)
  475. {
  476. if (value == null)
  477. {
  478. WriteNull();
  479. }
  480. else
  481. {
  482. InternalWriteValue(JsonToken.Float);
  483. WriteValueInternal(JsonConvert.ToString(value.GetValueOrDefault(), FloatFormatHandling, QuoteChar, true), JsonToken.Float);
  484. }
  485. }
  486. /// <summary>
  487. /// Writes a <see cref="Boolean"/> value.
  488. /// </summary>
  489. /// <param name="value">The <see cref="Boolean"/> value to write.</param>
  490. public override void WriteValue(bool value)
  491. {
  492. InternalWriteValue(JsonToken.Boolean);
  493. WriteValueInternal(JsonConvert.ToString(value), JsonToken.Boolean);
  494. }
  495. /// <summary>
  496. /// Writes a <see cref="Int16"/> value.
  497. /// </summary>
  498. /// <param name="value">The <see cref="Int16"/> value to write.</param>
  499. public override void WriteValue(short value)
  500. {
  501. InternalWriteValue(JsonToken.Integer);
  502. WriteIntegerValue(value);
  503. }
  504. /// <summary>
  505. /// Writes a <see cref="UInt16"/> value.
  506. /// </summary>
  507. /// <param name="value">The <see cref="UInt16"/> value to write.</param>
  508. [CLSCompliant(false)]
  509. public override void WriteValue(ushort value)
  510. {
  511. InternalWriteValue(JsonToken.Integer);
  512. WriteIntegerValue(value);
  513. }
  514. /// <summary>
  515. /// Writes a <see cref="Char"/> value.
  516. /// </summary>
  517. /// <param name="value">The <see cref="Char"/> value to write.</param>
  518. public override void WriteValue(char value)
  519. {
  520. InternalWriteValue(JsonToken.String);
  521. WriteValueInternal(JsonConvert.ToString(value), JsonToken.String);
  522. }
  523. /// <summary>
  524. /// Writes a <see cref="Byte"/> value.
  525. /// </summary>
  526. /// <param name="value">The <see cref="Byte"/> value to write.</param>
  527. public override void WriteValue(byte value)
  528. {
  529. InternalWriteValue(JsonToken.Integer);
  530. WriteIntegerValue(value);
  531. }
  532. /// <summary>
  533. /// Writes a <see cref="SByte"/> value.
  534. /// </summary>
  535. /// <param name="value">The <see cref="SByte"/> value to write.</param>
  536. [CLSCompliant(false)]
  537. public override void WriteValue(sbyte value)
  538. {
  539. InternalWriteValue(JsonToken.Integer);
  540. WriteIntegerValue(value);
  541. }
  542. /// <summary>
  543. /// Writes a <see cref="Decimal"/> value.
  544. /// </summary>
  545. /// <param name="value">The <see cref="Decimal"/> value to write.</param>
  546. public override void WriteValue(decimal value)
  547. {
  548. InternalWriteValue(JsonToken.Float);
  549. WriteValueInternal(JsonConvert.ToString(value), JsonToken.Float);
  550. }
  551. /// <summary>
  552. /// Writes a <see cref="DateTime"/> value.
  553. /// </summary>
  554. /// <param name="value">The <see cref="DateTime"/> value to write.</param>
  555. public override void WriteValue(DateTime value)
  556. {
  557. InternalWriteValue(JsonToken.Date);
  558. value = DateTimeUtils.EnsureDateTime(value, DateTimeZoneHandling);
  559. if (string.IsNullOrEmpty(DateFormatString))
  560. {
  561. int length = WriteValueToBuffer(value);
  562. _writer.Write(_writeBuffer, 0, length);
  563. }
  564. else
  565. {
  566. _writer.Write(_quoteChar);
  567. _writer.Write(value.ToString(DateFormatString, Culture));
  568. _writer.Write(_quoteChar);
  569. }
  570. }
  571. private int WriteValueToBuffer(DateTime value)
  572. {
  573. EnsureWriteBuffer();
  574. int pos = 0;
  575. _writeBuffer[pos++] = _quoteChar;
  576. pos = DateTimeUtils.WriteDateTimeString(_writeBuffer, pos, value, null, value.Kind, DateFormatHandling);
  577. _writeBuffer[pos++] = _quoteChar;
  578. return pos;
  579. }
  580. /// <summary>
  581. /// Writes a <see cref="Byte"/>[] value.
  582. /// </summary>
  583. /// <param name="value">The <see cref="Byte"/>[] value to write.</param>
  584. public override void WriteValue(byte[] value)
  585. {
  586. if (value == null)
  587. {
  588. WriteNull();
  589. }
  590. else
  591. {
  592. InternalWriteValue(JsonToken.Bytes);
  593. _writer.Write(_quoteChar);
  594. Base64Encoder.Encode(value, 0, value.Length);
  595. Base64Encoder.Flush();
  596. _writer.Write(_quoteChar);
  597. }
  598. }
  599. #if !NET20
  600. /// <summary>
  601. /// Writes a <see cref="DateTimeOffset"/> value.
  602. /// </summary>
  603. /// <param name="value">The <see cref="DateTimeOffset"/> value to write.</param>
  604. public override void WriteValue(DateTimeOffset value)
  605. {
  606. InternalWriteValue(JsonToken.Date);
  607. if (string.IsNullOrEmpty(DateFormatString))
  608. {
  609. int length = WriteValueToBuffer(value);
  610. _writer.Write(_writeBuffer, 0, length);
  611. }
  612. else
  613. {
  614. _writer.Write(_quoteChar);
  615. _writer.Write(value.ToString(DateFormatString, Culture));
  616. _writer.Write(_quoteChar);
  617. }
  618. }
  619. private int WriteValueToBuffer(DateTimeOffset value)
  620. {
  621. EnsureWriteBuffer();
  622. int pos = 0;
  623. _writeBuffer[pos++] = _quoteChar;
  624. pos = DateTimeUtils.WriteDateTimeString(_writeBuffer, pos, (DateFormatHandling == DateFormatHandling.IsoDateFormat) ? value.DateTime : value.UtcDateTime, value.Offset, DateTimeKind.Local, DateFormatHandling);
  625. _writeBuffer[pos++] = _quoteChar;
  626. return pos;
  627. }
  628. #endif
  629. /// <summary>
  630. /// Writes a <see cref="Guid"/> value.
  631. /// </summary>
  632. /// <param name="value">The <see cref="Guid"/> value to write.</param>
  633. public override void WriteValue(Guid value)
  634. {
  635. InternalWriteValue(JsonToken.String);
  636. string text = null;
  637. #if HAVE_CHAR_TO_STRING_WITH_CULTURE
  638. text = value.ToString("D", CultureInfo.InvariantCulture);
  639. #else
  640. text = value.ToString("D");
  641. #endif
  642. _writer.Write(_quoteChar);
  643. _writer.Write(text);
  644. _writer.Write(_quoteChar);
  645. }
  646. /// <summary>
  647. /// Writes a <see cref="TimeSpan"/> value.
  648. /// </summary>
  649. /// <param name="value">The <see cref="TimeSpan"/> value to write.</param>
  650. public override void WriteValue(TimeSpan value)
  651. {
  652. InternalWriteValue(JsonToken.String);
  653. string text;
  654. #if NET20
  655. text = value.ToString();
  656. #else
  657. text = value.ToString(null, CultureInfo.InvariantCulture);
  658. #endif
  659. _writer.Write(_quoteChar);
  660. _writer.Write(text);
  661. _writer.Write(_quoteChar);
  662. }
  663. /// <summary>
  664. /// Writes a <see cref="Uri"/> value.
  665. /// </summary>
  666. /// <param name="value">The <see cref="Uri"/> value to write.</param>
  667. public override void WriteValue(Uri value)
  668. {
  669. if (value == null)
  670. {
  671. WriteNull();
  672. }
  673. else
  674. {
  675. InternalWriteValue(JsonToken.String);
  676. WriteEscapedString(value.OriginalString, true);
  677. }
  678. }
  679. #endregion
  680. /// <summary>
  681. /// Writes a comment <c>/*...*/</c> containing the specified text.
  682. /// </summary>
  683. /// <param name="text">Text to place inside the comment.</param>
  684. public override void WriteComment(string text)
  685. {
  686. InternalWriteComment();
  687. _writer.Write("/*");
  688. _writer.Write(text);
  689. _writer.Write("*/");
  690. }
  691. /// <summary>
  692. /// Writes the given white space.
  693. /// </summary>
  694. /// <param name="ws">The string of white space characters.</param>
  695. public override void WriteWhitespace(string ws)
  696. {
  697. InternalWriteWhitespace(ws);
  698. _writer.Write(ws);
  699. }
  700. private void EnsureWriteBuffer()
  701. {
  702. if (_writeBuffer == null)
  703. {
  704. // maximum buffer sized used when writing iso date
  705. _writeBuffer = BufferUtils.RentBuffer(_arrayPool, 35);
  706. }
  707. }
  708. private void WriteIntegerValue(long value)
  709. {
  710. if (value >= 0 && value <= 9)
  711. {
  712. _writer.Write((char)('0' + value));
  713. }
  714. else
  715. {
  716. bool negative = value < 0;
  717. WriteIntegerValue(negative ? (ulong)-value : (ulong)value, negative);
  718. }
  719. }
  720. private void WriteIntegerValue(ulong value, bool negative)
  721. {
  722. if (!negative & value <= 9)
  723. {
  724. _writer.Write((char)('0' + value));
  725. }
  726. else
  727. {
  728. int length = WriteNumberToBuffer(value, negative);
  729. _writer.Write(_writeBuffer, 0, length);
  730. }
  731. }
  732. private int WriteNumberToBuffer(ulong value, bool negative)
  733. {
  734. if (value <= uint.MaxValue)
  735. {
  736. // avoid the 64 bit division if possible
  737. return WriteNumberToBuffer((uint)value, negative);
  738. }
  739. EnsureWriteBuffer();
  740. int totalLength = MathUtils.IntLength(value);
  741. if (negative)
  742. {
  743. totalLength++;
  744. _writeBuffer[0] = '-';
  745. }
  746. int index = totalLength;
  747. do
  748. {
  749. ulong quotient = value / 10;
  750. ulong digit = value - (quotient * 10);
  751. _writeBuffer[--index] = (char)('0' + digit);
  752. value = quotient;
  753. } while (value != 0);
  754. return totalLength;
  755. }
  756. private void WriteIntegerValue(int value)
  757. {
  758. if (value >= 0 && value <= 9)
  759. {
  760. _writer.Write((char)('0' + value));
  761. }
  762. else
  763. {
  764. bool negative = value < 0;
  765. WriteIntegerValue(negative ? (uint)-value : (uint)value, negative);
  766. }
  767. }
  768. private void WriteIntegerValue(uint value, bool negative)
  769. {
  770. if (!negative & value <= 9)
  771. {
  772. _writer.Write((char)('0' + value));
  773. }
  774. else
  775. {
  776. int length = WriteNumberToBuffer(value, negative);
  777. _writer.Write(_writeBuffer, 0, length);
  778. }
  779. }
  780. private int WriteNumberToBuffer(uint value, bool negative)
  781. {
  782. EnsureWriteBuffer();
  783. int totalLength = MathUtils.IntLength(value);
  784. if (negative)
  785. {
  786. totalLength++;
  787. _writeBuffer[0] = '-';
  788. }
  789. int index = totalLength;
  790. do
  791. {
  792. uint quotient = value / 10;
  793. uint digit = value - (quotient * 10);
  794. _writeBuffer[--index] = (char)('0' + digit);
  795. value = quotient;
  796. } while (value != 0);
  797. return totalLength;
  798. }
  799. }
  800. }