JSONStringBuilder.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "RuntimeLibraryPch.h"
  6. namespace Js
  7. {
  8. void
  9. JSONStringBuilder::AppendCharacter(char16 character)
  10. {
  11. AssertOrFailFast(this->currentLocation < endLocation);
  12. *this->currentLocation = character;
  13. ++this->currentLocation;
  14. }
  15. void
  16. JSONStringBuilder::AppendBuffer(_In_ const char16* buffer, charcount_t length)
  17. {
  18. AssertOrFailFast(this->currentLocation + length <= endLocation);
  19. wmemcpy_s(this->currentLocation, length, buffer, length);
  20. this->currentLocation += length;
  21. }
  22. void
  23. JSONStringBuilder::AppendString(_In_ JavascriptString* str)
  24. {
  25. AppendBuffer(str->GetString(), str->GetLength());
  26. }
  27. void
  28. JSONStringBuilder::EscapeAndAppendString(_In_ JavascriptString* str)
  29. {
  30. const charcount_t strLength = str->GetLength();
  31. // Strings should be surrounded by double quotes
  32. this->AppendCharacter(_u('"'));
  33. const char16* bufferStart = str->GetString();
  34. for (const char16* index = bufferStart; index < bufferStart + strLength; ++index)
  35. {
  36. char16 currentCharacter = *index;
  37. switch (currentCharacter)
  38. {
  39. case _u('"'):
  40. case _u('\\'):
  41. // Special characters are escaped with a backslash
  42. this->AppendCharacter(_u('\\'));
  43. this->AppendCharacter(currentCharacter);
  44. break;
  45. case _u('\b'):
  46. this->AppendCharacter(_u('\\'));
  47. this->AppendCharacter(_u('b'));
  48. break;
  49. case _u('\f'):
  50. this->AppendCharacter(_u('\\'));
  51. this->AppendCharacter(_u('f'));
  52. break;
  53. case _u('\n'):
  54. this->AppendCharacter(_u('\\'));
  55. this->AppendCharacter(_u('n'));
  56. break;
  57. case _u('\r'):
  58. this->AppendCharacter(_u('\\'));
  59. this->AppendCharacter(_u('r'));
  60. break;
  61. case _u('\t'):
  62. this->AppendCharacter(_u('\\'));
  63. this->AppendCharacter(_u('t'));
  64. break;
  65. default:
  66. if (currentCharacter < _u(' '))
  67. {
  68. // If character is less than SPACE, it is converted into a 4 digit hex code (e.g. \u0010)
  69. this->AppendCharacter(_u('\\'));
  70. this->AppendCharacter(_u('u'));
  71. {
  72. char16 buf[5];
  73. // Get hex value
  74. _ltow_s(currentCharacter, buf, _countof(buf), 16);
  75. // Append leading zeros if necessary before the hex value
  76. charcount_t count = static_cast<charcount_t>(wcslen(buf));
  77. switch (count)
  78. {
  79. case 1:
  80. this->AppendCharacter(_u('0'));
  81. case 2:
  82. this->AppendCharacter(_u('0'));
  83. case 3:
  84. this->AppendCharacter(_u('0'));
  85. default:
  86. this->AppendBuffer(buf, count);
  87. break;
  88. }
  89. }
  90. }
  91. else
  92. {
  93. this->AppendCharacter(currentCharacter);
  94. }
  95. break;
  96. }
  97. }
  98. this->AppendCharacter(_u('"'));
  99. }
  100. void
  101. JSONStringBuilder::AppendGap(uint32 count)
  102. {
  103. for (uint i = 0; i < count; ++i)
  104. {
  105. this->AppendBuffer(this->gap, this->gapLength);
  106. }
  107. }
  108. void
  109. JSONStringBuilder::AppendObjectString(_In_ JSONObject* valueList)
  110. {
  111. const uint elementCount = valueList->Count();
  112. if (elementCount == 0)
  113. {
  114. this->AppendCharacter(_u('{'));
  115. this->AppendCharacter(_u('}'));
  116. return;
  117. }
  118. const uint32 stepbackLevel = this->indentLevel;
  119. ++this->indentLevel;
  120. this->AppendCharacter(_u('{'));
  121. if (this->gap != nullptr)
  122. {
  123. this->AppendCharacter(_u('\n'));
  124. this->AppendGap(indentLevel);
  125. }
  126. bool isFirstMember = true;
  127. FOREACH_SLISTCOUNTED_ENTRY(JSONObjectProperty, entry, valueList)
  128. {
  129. if (!isFirstMember)
  130. {
  131. if (this->gap == nullptr)
  132. {
  133. this->AppendCharacter(_u(','));
  134. }
  135. else
  136. {
  137. this->AppendCharacter(_u(','));
  138. this->AppendCharacter(_u('\n'));
  139. this->AppendGap(indentLevel);
  140. }
  141. }
  142. this->EscapeAndAppendString(entry.propertyName);
  143. this->AppendCharacter(_u(':'));
  144. if (this->gap != nullptr)
  145. {
  146. this->AppendCharacter(_u(' '));
  147. }
  148. this->AppendJSONPropertyString(&entry.propertyValue);
  149. isFirstMember = false;
  150. }
  151. NEXT_SLISTCOUNTED_ENTRY;
  152. if (this->gap != nullptr)
  153. {
  154. this->AppendCharacter(_u('\n'));
  155. this->AppendGap(stepbackLevel);
  156. }
  157. this->AppendCharacter(_u('}'));
  158. this->indentLevel = stepbackLevel;
  159. }
  160. void
  161. JSONStringBuilder::AppendArrayString(_In_ JSONArray* valueArray)
  162. {
  163. uint32 length = valueArray->length;
  164. if (length == 0)
  165. {
  166. this->AppendCharacter(_u('['));
  167. this->AppendCharacter(_u(']'));
  168. return;
  169. }
  170. const uint32 stepbackLevel = this->indentLevel;
  171. ++this->indentLevel;
  172. JSONProperty* arr = valueArray->arr;
  173. this->AppendCharacter(_u('['));
  174. if (this->gap != nullptr)
  175. {
  176. this->AppendCharacter(_u('\n'));
  177. this->AppendGap(indentLevel);
  178. }
  179. this->AppendJSONPropertyString(&arr[0]);
  180. for (uint32 i = 1; i < length; ++i)
  181. {
  182. if (this->gap == nullptr)
  183. {
  184. this->AppendCharacter(_u(','));
  185. }
  186. else
  187. {
  188. this->AppendCharacter(_u(','));
  189. this->AppendCharacter(_u('\n'));
  190. this->AppendGap(indentLevel);
  191. }
  192. AppendJSONPropertyString(&arr[i]);
  193. }
  194. if (this->gap != nullptr)
  195. {
  196. this->AppendCharacter(_u('\n'));
  197. this->AppendGap(stepbackLevel);
  198. }
  199. this->AppendCharacter(_u(']'));
  200. this->indentLevel = stepbackLevel;
  201. }
  202. void
  203. JSONStringBuilder::AppendJSONPropertyString(_In_ JSONProperty* prop)
  204. {
  205. switch (prop->type)
  206. {
  207. case JSONContentType::False:
  208. this->AppendString(this->scriptContext->GetLibrary()->GetFalseDisplayString());
  209. return;
  210. case JSONContentType::True:
  211. this->AppendString(this->scriptContext->GetLibrary()->GetTrueDisplayString());
  212. return;
  213. case JSONContentType::Null:
  214. this->AppendString(this->scriptContext->GetLibrary()->GetNullDisplayString());
  215. return;
  216. case JSONContentType::Number:
  217. this->AppendString(prop->numericValue.string);
  218. return;
  219. case JSONContentType::Object:
  220. this->AppendObjectString(prop->obj);
  221. return;
  222. case JSONContentType::Array:
  223. this->AppendArrayString(prop->arr);
  224. return;
  225. case JSONContentType::String:
  226. this->EscapeAndAppendString(prop->stringValue);
  227. return;
  228. default:
  229. Assume(UNREACHED);
  230. }
  231. }
  232. void
  233. JSONStringBuilder::Build()
  234. {
  235. this->AppendJSONPropertyString(this->jsonContent);
  236. // Null terminate the string
  237. AssertOrFailFast(this->currentLocation == endLocation);
  238. *this->currentLocation = _u('\0');
  239. }
  240. JSONStringBuilder::JSONStringBuilder(
  241. _In_ ScriptContext* scriptContext,
  242. _In_ JSONProperty* jsonContent,
  243. _In_ char16* buffer,
  244. charcount_t bufferLength,
  245. _In_opt_ const char16* gap,
  246. charcount_t gapLength) :
  247. scriptContext(scriptContext),
  248. endLocation(buffer + bufferLength - 1),
  249. currentLocation(buffer),
  250. jsonContent(jsonContent),
  251. gap(gap),
  252. gapLength(gapLength),
  253. indentLevel(0)
  254. {
  255. }
  256. } //namespace Js