/src/LogentriesNLog/fastJSON/JsonParser.cs

https://github.com/NzbDrone/NzbDrone · C# · 409 lines · 357 code · 40 blank · 12 comment · 23 complexity · 19fc0a36daf474cc104f6e0266dbc52a MD5 · raw file

  1. //http://fastjson.codeplex.com/
  2. //http://fastjson.codeplex.com/license
  3. using System;
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using System.Text;
  7. namespace LogentriesNLog.fastJSON
  8. {
  9. /// <summary>
  10. /// This class encodes and decodes JSON strings.
  11. /// Spec. details, see http://www.json.org/
  12. ///
  13. /// JSON uses Arrays and Objects. These correspond here to the datatypes ArrayList and Hashtable.
  14. /// All numbers are parsed to doubles.
  15. /// </summary>
  16. internal class JsonParser
  17. {
  18. enum Token
  19. {
  20. None = -1, // Used to denote no Lookahead available
  21. Curly_Open,
  22. Curly_Close,
  23. Squared_Open,
  24. Squared_Close,
  25. Colon,
  26. Comma,
  27. String,
  28. Number,
  29. True,
  30. False,
  31. Null
  32. }
  33. readonly char[] json;
  34. readonly StringBuilder s = new StringBuilder();
  35. Token lookAheadToken = Token.None;
  36. int index;
  37. internal JsonParser(string json)
  38. {
  39. this.json = json.ToCharArray();
  40. }
  41. public object Decode()
  42. {
  43. return ParseValue();
  44. }
  45. private Dictionary<string, object> ParseObject()
  46. {
  47. var table = new Dictionary<string, object>();
  48. ConsumeToken(); // {
  49. while (true)
  50. {
  51. switch (LookAhead())
  52. {
  53. case Token.Comma:
  54. ConsumeToken();
  55. break;
  56. case Token.Curly_Close:
  57. ConsumeToken();
  58. return table;
  59. default:
  60. {
  61. // name
  62. string name = ParseString();
  63. // :
  64. if (NextToken() != Token.Colon)
  65. {
  66. throw new Exception("Expected colon at index " + index);
  67. }
  68. // value
  69. object value = ParseValue();
  70. table[name] = value;
  71. }
  72. break;
  73. }
  74. }
  75. }
  76. #if SILVERLIGHT
  77. private List<object> ParseArray()
  78. {
  79. List<object> array = new List<object>();
  80. #else
  81. private ArrayList ParseArray()
  82. {
  83. ArrayList array = new ArrayList();
  84. #endif
  85. ConsumeToken(); // [
  86. while (true)
  87. {
  88. switch (LookAhead())
  89. {
  90. case Token.Comma:
  91. ConsumeToken();
  92. break;
  93. case Token.Squared_Close:
  94. ConsumeToken();
  95. return array;
  96. default:
  97. {
  98. array.Add(ParseValue());
  99. }
  100. break;
  101. }
  102. }
  103. }
  104. private object ParseValue()
  105. {
  106. switch (LookAhead())
  107. {
  108. case Token.Number:
  109. return ParseNumber();
  110. case Token.String:
  111. return ParseString();
  112. case Token.Curly_Open:
  113. return ParseObject();
  114. case Token.Squared_Open:
  115. return ParseArray();
  116. case Token.True:
  117. ConsumeToken();
  118. return true;
  119. case Token.False:
  120. ConsumeToken();
  121. return false;
  122. case Token.Null:
  123. ConsumeToken();
  124. return null;
  125. }
  126. throw new Exception("Unrecognized token at index" + index);
  127. }
  128. private string ParseString()
  129. {
  130. ConsumeToken(); // "
  131. s.Length = 0;
  132. int runIndex = -1;
  133. while (index < json.Length)
  134. {
  135. var c = json[index++];
  136. if (c == '"')
  137. {
  138. if (runIndex != -1)
  139. {
  140. if (s.Length == 0)
  141. return new string(json, runIndex, index - runIndex - 1);
  142. s.Append(json, runIndex, index - runIndex - 1);
  143. }
  144. return s.ToString();
  145. }
  146. if (c != '\\')
  147. {
  148. if (runIndex == -1)
  149. runIndex = index - 1;
  150. continue;
  151. }
  152. if (index == json.Length) break;
  153. if (runIndex != -1)
  154. {
  155. s.Append(json, runIndex, index - runIndex - 1);
  156. runIndex = -1;
  157. }
  158. switch (json[index++])
  159. {
  160. case '"':
  161. s.Append('"');
  162. break;
  163. case '\\':
  164. s.Append('\\');
  165. break;
  166. case '/':
  167. s.Append('/');
  168. break;
  169. case 'b':
  170. s.Append('\b');
  171. break;
  172. case 'f':
  173. s.Append('\f');
  174. break;
  175. case 'n':
  176. s.Append('\n');
  177. break;
  178. case 'r':
  179. s.Append('\r');
  180. break;
  181. case 't':
  182. s.Append('\t');
  183. break;
  184. case 'u':
  185. {
  186. int remainingLength = json.Length - index;
  187. if (remainingLength < 4) break;
  188. // parse the 32 bit hex into an integer codepoint
  189. uint codePoint = ParseUnicode(json[index], json[index + 1], json[index + 2], json[index + 3]);
  190. s.Append((char)codePoint);
  191. // skip 4 chars
  192. index += 4;
  193. }
  194. break;
  195. }
  196. }
  197. throw new Exception("Unexpectedly reached end of string");
  198. }
  199. private uint ParseSingleChar(char c1, uint multipliyer)
  200. {
  201. uint p1 = 0;
  202. if (c1 >= '0' && c1 <= '9')
  203. p1 = (uint)(c1 - '0') * multipliyer;
  204. else if (c1 >= 'A' && c1 <= 'F')
  205. p1 = (uint)((c1 - 'A') + 10) * multipliyer;
  206. else if (c1 >= 'a' && c1 <= 'f')
  207. p1 = (uint)((c1 - 'a') + 10) * multipliyer;
  208. return p1;
  209. }
  210. private uint ParseUnicode(char c1, char c2, char c3, char c4)
  211. {
  212. uint p1 = ParseSingleChar(c1, 0x1000);
  213. uint p2 = ParseSingleChar(c2, 0x100);
  214. uint p3 = ParseSingleChar(c3, 0x10);
  215. uint p4 = ParseSingleChar(c4, 1);
  216. return p1 + p2 + p3 + p4;
  217. }
  218. private string ParseNumber()
  219. {
  220. ConsumeToken();
  221. // Need to start back one place because the first digit is also a token and would have been consumed
  222. var startIndex = index - 1;
  223. do
  224. {
  225. var c = json[index];
  226. if ((c >= '0' && c <= '9') || c == '.' || c == '-' || c == '+' || c == 'e' || c == 'E')
  227. {
  228. if (++index == json.Length) throw new Exception("Unexpected end of string whilst parsing number");
  229. continue;
  230. }
  231. break;
  232. } while (true);
  233. return new string(json, startIndex, index - startIndex);
  234. }
  235. private Token LookAhead()
  236. {
  237. if (lookAheadToken != Token.None) return lookAheadToken;
  238. return lookAheadToken = NextTokenCore();
  239. }
  240. private void ConsumeToken()
  241. {
  242. lookAheadToken = Token.None;
  243. }
  244. private Token NextToken()
  245. {
  246. var result = lookAheadToken != Token.None ? lookAheadToken : NextTokenCore();
  247. lookAheadToken = Token.None;
  248. return result;
  249. }
  250. private Token NextTokenCore()
  251. {
  252. char c;
  253. // Skip past whitespace
  254. do
  255. {
  256. c = json[index];
  257. if (c > ' ') break;
  258. if (c != ' ' && c != '\t' && c != '\n' && c != '\r') break;
  259. } while (++index < json.Length);
  260. if (index == json.Length)
  261. {
  262. throw new Exception("Reached end of string unexpectedly");
  263. }
  264. c = json[index];
  265. index++;
  266. //if (c >= '0' && c <= '9')
  267. // return Token.Number;
  268. switch (c)
  269. {
  270. case '{':
  271. return Token.Curly_Open;
  272. case '}':
  273. return Token.Curly_Close;
  274. case '[':
  275. return Token.Squared_Open;
  276. case ']':
  277. return Token.Squared_Close;
  278. case ',':
  279. return Token.Comma;
  280. case '"':
  281. return Token.String;
  282. case '0': case '1': case '2': case '3': case '4':
  283. case '5': case '6': case '7': case '8': case '9':
  284. case '-': case '+': case '.':
  285. return Token.Number;
  286. case ':':
  287. return Token.Colon;
  288. case 'f':
  289. if (json.Length - index >= 4 &&
  290. json[index + 0] == 'a' &&
  291. json[index + 1] == 'l' &&
  292. json[index + 2] == 's' &&
  293. json[index + 3] == 'e')
  294. {
  295. index += 4;
  296. return Token.False;
  297. }
  298. break;
  299. case 't':
  300. if (json.Length - index >= 3 &&
  301. json[index + 0] == 'r' &&
  302. json[index + 1] == 'u' &&
  303. json[index + 2] == 'e')
  304. {
  305. index += 3;
  306. return Token.True;
  307. }
  308. break;
  309. case 'n':
  310. if (json.Length - index >= 3 &&
  311. json[index + 0] == 'u' &&
  312. json[index + 1] == 'l' &&
  313. json[index + 2] == 'l')
  314. {
  315. index += 3;
  316. return Token.Null;
  317. }
  318. break;
  319. }
  320. throw new Exception("Could not find token at index " + --index);
  321. }
  322. }
  323. }