/src/ProtocolBuffers.Serialization/JsonTextCursor.cs

https://code.google.com/p/protobuf-csharp-port/ · C# · 442 lines · 400 code · 37 blank · 5 comment · 65 complexity · 34860cd55a323cd7aa27ef57b64acf4f MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Globalization;
  5. using System.IO;
  6. namespace Google.ProtocolBuffers.Serialization
  7. {
  8. /// <summary>
  9. /// JSon Tokenizer used by JsonFormatReader
  10. /// </summary>
  11. internal abstract class JsonCursor
  12. {
  13. public enum JsType
  14. {
  15. String,
  16. Number,
  17. Object,
  18. Array,
  19. True,
  20. False,
  21. Null
  22. }
  23. #region Buffering implementations
  24. private class JsonStreamCursor : JsonCursor
  25. {
  26. private readonly byte[] _buffer;
  27. private int _bufferPos;
  28. private readonly Stream _input;
  29. public JsonStreamCursor(Stream input)
  30. {
  31. _input = input;
  32. _next = _input.ReadByte();
  33. }
  34. public JsonStreamCursor(byte[] input)
  35. {
  36. _input = null;
  37. _buffer = input;
  38. _next = _buffer[_bufferPos];
  39. }
  40. protected override int Peek()
  41. {
  42. if (_input != null)
  43. {
  44. return _next;
  45. }
  46. else if (_bufferPos < _buffer.Length)
  47. {
  48. return _buffer[_bufferPos];
  49. }
  50. else
  51. {
  52. return -1;
  53. }
  54. }
  55. protected override int Read()
  56. {
  57. if (_input != null)
  58. {
  59. int result = _next;
  60. _next = _input.ReadByte();
  61. return result;
  62. }
  63. else if (_bufferPos < _buffer.Length)
  64. {
  65. return _buffer[_bufferPos++];
  66. }
  67. else
  68. {
  69. return -1;
  70. }
  71. }
  72. }
  73. private class JsonTextCursor : JsonCursor
  74. {
  75. private readonly char[] _buffer;
  76. private int _bufferPos;
  77. private readonly TextReader _input;
  78. public JsonTextCursor(char[] input)
  79. {
  80. _input = null;
  81. _buffer = input;
  82. _bufferPos = 0;
  83. _next = Peek();
  84. }
  85. public JsonTextCursor(TextReader input)
  86. {
  87. _input = input;
  88. _next = Peek();
  89. }
  90. protected override int Peek()
  91. {
  92. if (_input != null)
  93. {
  94. return _input.Peek();
  95. }
  96. else if (_bufferPos < _buffer.Length)
  97. {
  98. return _buffer[_bufferPos];
  99. }
  100. else
  101. {
  102. return -1;
  103. }
  104. }
  105. protected override int Read()
  106. {
  107. if (_input != null)
  108. {
  109. return _input.Read();
  110. }
  111. else if (_bufferPos < _buffer.Length)
  112. {
  113. return _buffer[_bufferPos++];
  114. }
  115. else
  116. {
  117. return -1;
  118. }
  119. }
  120. }
  121. #endregion
  122. protected int _next;
  123. private int _lineNo, _linePos;
  124. public static JsonCursor CreateInstance(byte[] input)
  125. {
  126. return new JsonStreamCursor(input);
  127. }
  128. public static JsonCursor CreateInstance(Stream input)
  129. {
  130. return new JsonStreamCursor(input);
  131. }
  132. public static JsonCursor CreateInstance(string input)
  133. {
  134. return new JsonTextCursor(input.ToCharArray());
  135. }
  136. public static JsonCursor CreateInstance(TextReader input)
  137. {
  138. return new JsonTextCursor(input);
  139. }
  140. protected JsonCursor()
  141. {
  142. _lineNo = 1;
  143. _linePos = 0;
  144. }
  145. /// <summary>Returns the next character without actually 'reading' it</summary>
  146. protected abstract int Peek();
  147. /// <summary>Reads the next character in the input</summary>
  148. protected abstract int Read();
  149. public Char NextChar
  150. {
  151. get
  152. {
  153. SkipWhitespace();
  154. return (char) _next;
  155. }
  156. }
  157. #region Assert(...)
  158. [DebuggerNonUserCode]
  159. private string CharDisplay(int ch)
  160. {
  161. return ch == -1
  162. ? "EOF"
  163. : (ch > 32 && ch < 127)
  164. ? String.Format("'{0}'", (char) ch)
  165. : String.Format("'\\u{0:x4}'", ch);
  166. }
  167. [DebuggerNonUserCode]
  168. private void Assert(bool cond, char expected)
  169. {
  170. if (!cond)
  171. {
  172. throw new FormatException(
  173. String.Format(CultureInfo.InvariantCulture,
  174. "({0}:{1}) error: Unexpected token {2}, expected: {3}.",
  175. _lineNo, _linePos,
  176. CharDisplay(_next),
  177. CharDisplay(expected)
  178. ));
  179. }
  180. }
  181. [DebuggerNonUserCode]
  182. public void Assert(bool cond, string message)
  183. {
  184. if (!cond)
  185. {
  186. throw new FormatException(
  187. String.Format(CultureInfo.InvariantCulture,
  188. "({0},{1}) error: {2}", _lineNo, _linePos, message));
  189. }
  190. }
  191. [DebuggerNonUserCode]
  192. public void Assert(bool cond, string format, params object[] args)
  193. {
  194. if (!cond)
  195. {
  196. if (args != null && args.Length > 0)
  197. {
  198. format = String.Format(format, args);
  199. }
  200. throw new FormatException(
  201. String.Format(CultureInfo.InvariantCulture,
  202. "({0},{1}) error: {2}", _lineNo, _linePos, format));
  203. }
  204. }
  205. #endregion
  206. private char ReadChar()
  207. {
  208. int ch = Read();
  209. Assert(ch != -1, "Unexpected end of file.");
  210. if (ch == '\n')
  211. {
  212. _lineNo++;
  213. _linePos = 0;
  214. }
  215. else if (ch != '\r')
  216. {
  217. _linePos++;
  218. }
  219. _next = Peek();
  220. return (char) ch;
  221. }
  222. public void Consume(char ch)
  223. {
  224. Assert(TryConsume(ch), ch);
  225. }
  226. public bool TryConsume(char ch)
  227. {
  228. SkipWhitespace();
  229. if (_next == ch)
  230. {
  231. ReadChar();
  232. return true;
  233. }
  234. return false;
  235. }
  236. public void Consume(string sequence)
  237. {
  238. SkipWhitespace();
  239. foreach (char ch in sequence)
  240. {
  241. Assert(ch == ReadChar(), "Expected token '{0}'.", sequence);
  242. }
  243. }
  244. public void SkipWhitespace()
  245. {
  246. int chnext = _next;
  247. while (chnext != -1)
  248. {
  249. if (!Char.IsWhiteSpace((char) chnext))
  250. {
  251. break;
  252. }
  253. ReadChar();
  254. chnext = _next;
  255. }
  256. }
  257. public string ReadString()
  258. {
  259. SkipWhitespace();
  260. Consume('"');
  261. List<Char> sb = new List<char>(100);
  262. while (_next != '"')
  263. {
  264. if (_next == '\\')
  265. {
  266. Consume('\\'); //skip the escape
  267. char ch = ReadChar();
  268. switch (ch)
  269. {
  270. case 'b':
  271. sb.Add('\b');
  272. break;
  273. case 'f':
  274. sb.Add('\f');
  275. break;
  276. case 'n':
  277. sb.Add('\n');
  278. break;
  279. case 'r':
  280. sb.Add('\r');
  281. break;
  282. case 't':
  283. sb.Add('\t');
  284. break;
  285. case 'u':
  286. {
  287. string hex = new string(new char[] {ReadChar(), ReadChar(), ReadChar(), ReadChar()});
  288. int result;
  289. Assert(
  290. int.TryParse(hex, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture,
  291. out result),
  292. "Expected a 4-character hex specifier.");
  293. sb.Add((char) result);
  294. break;
  295. }
  296. default:
  297. sb.Add(ch);
  298. break;
  299. }
  300. }
  301. else
  302. {
  303. Assert(_next != '\n' && _next != '\r' && _next != '\f' && _next != -1, '"');
  304. sb.Add(ReadChar());
  305. }
  306. }
  307. Consume('"');
  308. return new String(sb.ToArray());
  309. }
  310. public string ReadNumber()
  311. {
  312. SkipWhitespace();
  313. List<Char> sb = new List<char>(24);
  314. if (_next == '-')
  315. {
  316. sb.Add(ReadChar());
  317. }
  318. Assert(_next >= '0' && _next <= '9', "Expected a numeric type.");
  319. while ((_next >= '0' && _next <= '9') || _next == '.')
  320. {
  321. sb.Add(ReadChar());
  322. }
  323. if (_next == 'e' || _next == 'E')
  324. {
  325. sb.Add(ReadChar());
  326. if (_next == '-' || _next == '+')
  327. {
  328. sb.Add(ReadChar());
  329. }
  330. Assert(_next >= '0' && _next <= '9', "Expected a numeric type.");
  331. while (_next >= '0' && _next <= '9')
  332. {
  333. sb.Add(ReadChar());
  334. }
  335. }
  336. return new String(sb.ToArray());
  337. }
  338. public JsType ReadVariant(out object value)
  339. {
  340. SkipWhitespace();
  341. switch (_next)
  342. {
  343. case 'n':
  344. Consume("null");
  345. value = null;
  346. return JsType.Null;
  347. case 't':
  348. Consume("true");
  349. value = true;
  350. return JsType.True;
  351. case 'f':
  352. Consume("false");
  353. value = false;
  354. return JsType.False;
  355. case '"':
  356. value = ReadString();
  357. return JsType.String;
  358. case '{':
  359. {
  360. Consume('{');
  361. while (NextChar != '}')
  362. {
  363. ReadString();
  364. Consume(':');
  365. object tmp;
  366. ReadVariant(out tmp);
  367. if (!TryConsume(','))
  368. {
  369. break;
  370. }
  371. }
  372. Consume('}');
  373. value = null;
  374. return JsType.Object;
  375. }
  376. case '[':
  377. {
  378. Consume('[');
  379. List<object> values = new List<object>();
  380. while (NextChar != ']')
  381. {
  382. object tmp;
  383. ReadVariant(out tmp);
  384. values.Add(tmp);
  385. if (!TryConsume(','))
  386. {
  387. break;
  388. }
  389. }
  390. Consume(']');
  391. value = values.ToArray();
  392. return JsType.Array;
  393. }
  394. default:
  395. if ((_next >= '0' && _next <= '9') || _next == '-')
  396. {
  397. value = ReadNumber();
  398. return JsType.Number;
  399. }
  400. Assert(false, "Expected a value.");
  401. throw new FormatException();
  402. }
  403. }
  404. }
  405. }