海淀天下城电子沙盘单机版
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.

355 lines
12 KiB

4 years ago
using System;
using System.Globalization;
using System.IO;
using System.Text;
namespace MessagePack
{
// simple, tiny JSON reader for MessagePackSerializer.FromJson.
// this is simple, compact and enough fast but not optimized extremely.
internal enum TinyJsonToken
{
None,
StartObject, // {
EndObject, // }
StartArray, // [
EndArray, // ]
Number, // -0~9
String, // "___"
True, // true
False, // false
Null, // null
}
internal enum ValueType : byte
{
Null,
True,
False,
Double,
Long,
ULong,
Decimal,
String
}
internal class TinyJsonException : Exception
{
public TinyJsonException(string message) : base(message)
{
}
}
internal class TinyJsonReader : IDisposable
{
readonly TextReader reader;
readonly bool disposeInnerReader;
StringBuilder reusableBuilder;
public TinyJsonToken TokenType { get; private set; }
public ValueType ValueType { get; private set; }
public double DoubleValue { get; private set; }
public long LongValue { get; private set; }
public ulong ULongValue { get; private set; }
public decimal DecimalValue { get; private set; }
public string StringValue { get; private set; }
public TinyJsonReader(TextReader reader, bool disposeInnerReader = true)
{
this.reader = reader;
this.disposeInnerReader = disposeInnerReader;
}
public bool Read()
{
ReadNextToken();
ReadValue();
return TokenType != TinyJsonToken.None;
}
public void Dispose()
{
if (reader != null && disposeInnerReader)
{
reader.Dispose();
}
TokenType = TinyJsonToken.None;
ValueType = ValueType.Null;
}
void SkipWhiteSpace()
{
var c = reader.Peek();
while (c != -1 && Char.IsWhiteSpace((char)c))
{
reader.Read();
c = reader.Peek();
}
}
char ReadChar()
{
return (char)reader.Read();
}
static bool IsWordBreak(char c)
{
switch (c)
{
case ' ':
case '{':
case '}':
case '[':
case ']':
case ',':
case ':':
case '\"':
return true;
default:
return false;
}
}
void ReadNextToken()
{
SkipWhiteSpace();
var intChar = reader.Peek();
if (intChar == -1)
{
TokenType = TinyJsonToken.None;
return;
}
var c = (char)intChar;
switch (c)
{
case '{':
TokenType = TinyJsonToken.StartObject;
return;
case '}':
TokenType = TinyJsonToken.EndObject;
return;
case '[':
TokenType = TinyJsonToken.StartArray;
return;
case ']':
TokenType = TinyJsonToken.EndArray;
return;
case '"':
TokenType = TinyJsonToken.String;
return;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
TokenType = TinyJsonToken.Number;
return;
case 't':
TokenType = TinyJsonToken.True;
return;
case 'f':
TokenType = TinyJsonToken.False;
return;
case 'n':
TokenType = TinyJsonToken.Null;
return;
case ',':
case ':':
reader.Read();
ReadNextToken();
return;
default:
throw new TinyJsonException("Invalid String:" + c);
}
}
void ReadValue()
{
ValueType = ValueType.Null;
switch (TokenType)
{
case TinyJsonToken.None:
break;
case TinyJsonToken.StartObject:
case TinyJsonToken.EndObject:
case TinyJsonToken.StartArray:
case TinyJsonToken.EndArray:
reader.Read();
break;
case TinyJsonToken.Number:
ReadNumber();
break;
case TinyJsonToken.String:
ReadString();
break;
case TinyJsonToken.True:
if (ReadChar() != 't') throw new TinyJsonException("Invalid Token");
if (ReadChar() != 'r') throw new TinyJsonException("Invalid Token");
if (ReadChar() != 'u') throw new TinyJsonException("Invalid Token");
if (ReadChar() != 'e') throw new TinyJsonException("Invalid Token");
ValueType = ValueType.True;
break;
case TinyJsonToken.False:
if (ReadChar() != 'f') throw new TinyJsonException("Invalid Token");
if (ReadChar() != 'a') throw new TinyJsonException("Invalid Token");
if (ReadChar() != 'l') throw new TinyJsonException("Invalid Token");
if (ReadChar() != 's') throw new TinyJsonException("Invalid Token");
if (ReadChar() != 'e') throw new TinyJsonException("Invalid Token");
ValueType = ValueType.False;
break;
case TinyJsonToken.Null:
if (ReadChar() != 'n') throw new TinyJsonException("Invalid Token");
if (ReadChar() != 'u') throw new TinyJsonException("Invalid Token");
if (ReadChar() != 'l') throw new TinyJsonException("Invalid Token");
if (ReadChar() != 'l') throw new TinyJsonException("Invalid Token");
ValueType = ValueType.Null;
break;
default:
throw new ArgumentException("InvalidTokenState:" + TokenType);
}
}
void ReadNumber()
{
StringBuilder numberWord;
if (reusableBuilder == null)
{
reusableBuilder = new StringBuilder();
numberWord = reusableBuilder;
}
else
{
numberWord = reusableBuilder;
numberWord.Length = 0; // Clear
}
var isDouble = false;
var intChar = reader.Peek();
while (intChar != -1 && !IsWordBreak((char)intChar))
{
var c = ReadChar();
numberWord.Append(c);
if (c == '.' || c == 'e' || c == 'E') isDouble = true;
intChar = reader.Peek();
}
var number = numberWord.ToString();
if (isDouble)
{
double parsedDouble;
Double.TryParse(number, NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite | NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands | NumberStyles.AllowExponent, System.Globalization.CultureInfo.InvariantCulture, out parsedDouble);
ValueType = ValueType.Double;
DoubleValue = parsedDouble;
}
else
{
long parsedInt;
if (Int64.TryParse(number, NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out parsedInt))
{
ValueType = ValueType.Long;
LongValue = parsedInt;
return;
}
ulong parsedULong;
if (ulong.TryParse(number, NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out parsedULong))
{
ValueType = ValueType.ULong;
ULongValue = parsedULong;
return;
}
Decimal parsedDecimal;
if (decimal.TryParse(number, NumberStyles.Number, System.Globalization.CultureInfo.InvariantCulture, out parsedDecimal))
{
ValueType = ValueType.Decimal;
DecimalValue = parsedDecimal;
return;
}
}
}
void ReadString()
{
reader.Read(); // skip ["]
StringBuilder sb;
if (reusableBuilder == null)
{
reusableBuilder = new StringBuilder();
sb = reusableBuilder;
}
else
{
sb = reusableBuilder;
sb.Length = 0; // Clear
}
while (true)
{
if (reader.Peek() == -1) throw new TinyJsonException("Invalid Json String");
var c = ReadChar();
switch (c)
{
case '"': // endtoken
goto END;
case '\\': // escape character
if (reader.Peek() == -1) throw new TinyJsonException("Invalid Json String");
c = ReadChar();
switch (c)
{
case '"':
case '\\':
case '/':
sb.Append(c);
break;
case 'b':
sb.Append('\b');
break;
case 'f':
sb.Append('\f');
break;
case 'n':
sb.Append('\n');
break;
case 'r':
sb.Append('\r');
break;
case 't':
sb.Append('\t');
break;
case 'u':
var hex = new char[4];
hex[0] = ReadChar();
hex[1] = ReadChar();
hex[2] = ReadChar();
hex[3] = ReadChar();
sb.Append((char)Convert.ToInt32(new string(hex), 16));
break;
}
break;
default: // string
sb.Append(c);
break;
}
}
END:
ValueType = ValueType.String;
StringValue = sb.ToString();
}
}
}