aboutsummaryrefslogtreecommitdiffhomepage
path: root/csharp/src/ProtocolBuffers.Serialization/JsonTextCursor.cs
diff options
context:
space:
mode:
Diffstat (limited to 'csharp/src/ProtocolBuffers.Serialization/JsonTextCursor.cs')
-rw-r--r--csharp/src/ProtocolBuffers.Serialization/JsonTextCursor.cs442
1 files changed, 442 insertions, 0 deletions
diff --git a/csharp/src/ProtocolBuffers.Serialization/JsonTextCursor.cs b/csharp/src/ProtocolBuffers.Serialization/JsonTextCursor.cs
new file mode 100644
index 00000000..19c45af7
--- /dev/null
+++ b/csharp/src/ProtocolBuffers.Serialization/JsonTextCursor.cs
@@ -0,0 +1,442 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+
+namespace Google.ProtocolBuffers.Serialization
+{
+ /// <summary>
+ /// JSon Tokenizer used by JsonFormatReader
+ /// </summary>
+ internal abstract class JsonCursor
+ {
+ public enum JsType
+ {
+ String,
+ Number,
+ Object,
+ Array,
+ True,
+ False,
+ Null
+ }
+
+ #region Buffering implementations
+
+ private class JsonStreamCursor : JsonCursor
+ {
+ private readonly byte[] _buffer;
+ private int _bufferPos;
+ private readonly Stream _input;
+
+ public JsonStreamCursor(Stream input)
+ {
+ _input = input;
+ _next = _input.ReadByte();
+ }
+
+ public JsonStreamCursor(byte[] input)
+ {
+ _input = null;
+ _buffer = input;
+ _next = _buffer[_bufferPos];
+ }
+
+ protected override int Peek()
+ {
+ if (_input != null)
+ {
+ return _next;
+ }
+ else if (_bufferPos < _buffer.Length)
+ {
+ return _buffer[_bufferPos];
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ protected override int Read()
+ {
+ if (_input != null)
+ {
+ int result = _next;
+ _next = _input.ReadByte();
+ return result;
+ }
+ else if (_bufferPos < _buffer.Length)
+ {
+ return _buffer[_bufferPos++];
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ }
+
+ private class JsonTextCursor : JsonCursor
+ {
+ private readonly char[] _buffer;
+ private int _bufferPos;
+ private readonly TextReader _input;
+
+ public JsonTextCursor(char[] input)
+ {
+ _input = null;
+ _buffer = input;
+ _bufferPos = 0;
+ _next = Peek();
+ }
+
+ public JsonTextCursor(TextReader input)
+ {
+ _input = input;
+ _next = Peek();
+ }
+
+ protected override int Peek()
+ {
+ if (_input != null)
+ {
+ return _input.Peek();
+ }
+ else if (_bufferPos < _buffer.Length)
+ {
+ return _buffer[_bufferPos];
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ protected override int Read()
+ {
+ if (_input != null)
+ {
+ return _input.Read();
+ }
+ else if (_bufferPos < _buffer.Length)
+ {
+ return _buffer[_bufferPos++];
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ }
+
+ #endregion
+
+ protected int _next;
+ private int _lineNo, _linePos;
+
+ public static JsonCursor CreateInstance(byte[] input)
+ {
+ return new JsonStreamCursor(input);
+ }
+
+ public static JsonCursor CreateInstance(Stream input)
+ {
+ return new JsonStreamCursor(input);
+ }
+
+ public static JsonCursor CreateInstance(string input)
+ {
+ return new JsonTextCursor(input.ToCharArray());
+ }
+
+ public static JsonCursor CreateInstance(TextReader input)
+ {
+ return new JsonTextCursor(input);
+ }
+
+ protected JsonCursor()
+ {
+ _lineNo = 1;
+ _linePos = 0;
+ }
+
+ /// <summary>Returns the next character without actually 'reading' it</summary>
+ protected abstract int Peek();
+
+ /// <summary>Reads the next character in the input</summary>
+ protected abstract int Read();
+
+ public Char NextChar
+ {
+ get
+ {
+ SkipWhitespace();
+ return (char) _next;
+ }
+ }
+
+ #region Assert(...)
+
+ [DebuggerNonUserCode]
+ private string CharDisplay(int ch)
+ {
+ return ch == -1
+ ? "EOF"
+ : (ch > 32 && ch < 127)
+ ? String.Format("'{0}'", (char) ch)
+ : String.Format("'\\u{0:x4}'", ch);
+ }
+
+ [DebuggerNonUserCode]
+ private void Assert(bool cond, char expected)
+ {
+ if (!cond)
+ {
+ throw new FormatException(
+ String.Format(FrameworkPortability.InvariantCulture,
+ "({0}:{1}) error: Unexpected token {2}, expected: {3}.",
+ _lineNo, _linePos,
+ CharDisplay(_next),
+ CharDisplay(expected)
+ ));
+ }
+ }
+
+ [DebuggerNonUserCode]
+ public void Assert(bool cond, string message)
+ {
+ if (!cond)
+ {
+ throw new FormatException(
+ String.Format(FrameworkPortability.InvariantCulture,
+ "({0},{1}) error: {2}", _lineNo, _linePos, message));
+ }
+ }
+
+ [DebuggerNonUserCode]
+ public void Assert(bool cond, string format, params object[] args)
+ {
+ if (!cond)
+ {
+ if (args != null && args.Length > 0)
+ {
+ format = String.Format(format, args);
+ }
+ throw new FormatException(
+ String.Format(FrameworkPortability.InvariantCulture,
+ "({0},{1}) error: {2}", _lineNo, _linePos, format));
+ }
+ }
+
+ #endregion
+
+ private char ReadChar()
+ {
+ int ch = Read();
+ Assert(ch != -1, "Unexpected end of file.");
+ if (ch == '\n')
+ {
+ _lineNo++;
+ _linePos = 0;
+ }
+ else if (ch != '\r')
+ {
+ _linePos++;
+ }
+ _next = Peek();
+ return (char) ch;
+ }
+
+ public void Consume(char ch)
+ {
+ Assert(TryConsume(ch), ch);
+ }
+
+ public bool TryConsume(char ch)
+ {
+ SkipWhitespace();
+ if (_next == ch)
+ {
+ ReadChar();
+ return true;
+ }
+ return false;
+ }
+
+ public void Consume(string sequence)
+ {
+ SkipWhitespace();
+
+ foreach (char ch in sequence)
+ {
+ Assert(ch == ReadChar(), "Expected token '{0}'.", sequence);
+ }
+ }
+
+ public void SkipWhitespace()
+ {
+ int chnext = _next;
+ while (chnext != -1)
+ {
+ if (!Char.IsWhiteSpace((char) chnext))
+ {
+ break;
+ }
+ ReadChar();
+ chnext = _next;
+ }
+ }
+
+ public string ReadString()
+ {
+ SkipWhitespace();
+ Consume('"');
+ List<Char> sb = new List<char>(100);
+ while (_next != '"')
+ {
+ if (_next == '\\')
+ {
+ Consume('\\'); //skip the escape
+ char ch = ReadChar();
+ switch (ch)
+ {
+ case 'b':
+ sb.Add('\b');
+ break;
+ case 'f':
+ sb.Add('\f');
+ break;
+ case 'n':
+ sb.Add('\n');
+ break;
+ case 'r':
+ sb.Add('\r');
+ break;
+ case 't':
+ sb.Add('\t');
+ break;
+ case 'u':
+ {
+ string hex = new string(new char[] {ReadChar(), ReadChar(), ReadChar(), ReadChar()});
+ int result;
+ Assert(
+ FrameworkPortability.TryParseInt32(hex, NumberStyles.AllowHexSpecifier, FrameworkPortability.InvariantCulture,
+ out result),
+ "Expected a 4-character hex specifier.");
+ sb.Add((char) result);
+ break;
+ }
+ default:
+ sb.Add(ch);
+ break;
+ }
+ }
+ else
+ {
+ Assert(_next != '\n' && _next != '\r' && _next != '\f' && _next != -1, '"');
+ sb.Add(ReadChar());
+ }
+ }
+ Consume('"');
+ return new String(sb.ToArray());
+ }
+
+ public string ReadNumber()
+ {
+ SkipWhitespace();
+ List<Char> sb = new List<char>(24);
+ if (_next == '-')
+ {
+ sb.Add(ReadChar());
+ }
+ Assert(_next >= '0' && _next <= '9', "Expected a numeric type.");
+ while ((_next >= '0' && _next <= '9') || _next == '.')
+ {
+ sb.Add(ReadChar());
+ }
+ if (_next == 'e' || _next == 'E')
+ {
+ sb.Add(ReadChar());
+ if (_next == '-' || _next == '+')
+ {
+ sb.Add(ReadChar());
+ }
+ Assert(_next >= '0' && _next <= '9', "Expected a numeric type.");
+ while (_next >= '0' && _next <= '9')
+ {
+ sb.Add(ReadChar());
+ }
+ }
+ return new String(sb.ToArray());
+ }
+
+ public JsType ReadVariant(out object value)
+ {
+ SkipWhitespace();
+ switch (_next)
+ {
+ case 'n':
+ Consume("null");
+ value = null;
+ return JsType.Null;
+ case 't':
+ Consume("true");
+ value = true;
+ return JsType.True;
+ case 'f':
+ Consume("false");
+ value = false;
+ return JsType.False;
+ case '"':
+ value = ReadString();
+ return JsType.String;
+ case '{':
+ {
+ Consume('{');
+ while (NextChar != '}')
+ {
+ ReadString();
+ Consume(':');
+ object tmp;
+ ReadVariant(out tmp);
+ if (!TryConsume(','))
+ {
+ break;
+ }
+ }
+ Consume('}');
+ value = null;
+ return JsType.Object;
+ }
+ case '[':
+ {
+ Consume('[');
+ List<object> values = new List<object>();
+ while (NextChar != ']')
+ {
+ object tmp;
+ ReadVariant(out tmp);
+ values.Add(tmp);
+ if (!TryConsume(','))
+ {
+ break;
+ }
+ }
+ Consume(']');
+ value = values.ToArray();
+ return JsType.Array;
+ }
+ default:
+ if ((_next >= '0' && _next <= '9') || _next == '-')
+ {
+ value = ReadNumber();
+ return JsType.Number;
+ }
+ Assert(false, "Expected a value.");
+ throw new FormatException();
+ }
+ }
+ }
+} \ No newline at end of file