BsonReader
Represents a reader that provides fast, non-cached, forward-only access to serialized JSON data.
using Newtonsoft.Json.Utilities;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
namespace Newtonsoft.Json.Bson
{
public class BsonReader : JsonReader
{
private enum BsonReaderState
{
Normal,
ReferenceStart,
ReferenceRef,
ReferenceId,
CodeWScopeStart,
CodeWScopeCode,
CodeWScopeScope,
CodeWScopeScopeObject,
CodeWScopeScopeEnd
}
private class ContainerContext
{
public readonly BsonType Type;
public int Length;
public int Position;
public ContainerContext(BsonType type)
{
Type = type;
}
}
private const int MaxCharBytesSize = 128;
private static readonly byte[] SeqRange1 = new byte[2] {
0,
127
};
private static readonly byte[] SeqRange2 = new byte[2] {
194,
223
};
private static readonly byte[] SeqRange3 = new byte[2] {
224,
239
};
private static readonly byte[] SeqRange4 = new byte[2] {
240,
244
};
private readonly BinaryReader _reader;
private readonly List<ContainerContext> _stack;
private byte[] _byteBuffer;
private char[] _charBuffer;
private BsonType _currentElementType;
private BsonReaderState _bsonReaderState;
private ContainerContext _currentContext;
private bool _readRootValueAsArray;
private bool _jsonNet35BinaryCompatibility;
private DateTimeKind _dateTimeKindHandling;
[Obsolete("JsonNet35BinaryCompatibility will be removed in a future version of Json.NET.")]
public bool JsonNet35BinaryCompatibility {
get {
return _jsonNet35BinaryCompatibility;
}
set {
_jsonNet35BinaryCompatibility = value;
}
}
public bool ReadRootValueAsArray {
get {
return _readRootValueAsArray;
}
set {
_readRootValueAsArray = value;
}
}
public DateTimeKind DateTimeKindHandling {
get {
return _dateTimeKindHandling;
}
set {
_dateTimeKindHandling = value;
}
}
public BsonReader(Stream stream)
: this(stream, false, DateTimeKind.Local)
{
}
public BsonReader(BinaryReader reader)
: this(reader, false, DateTimeKind.Local)
{
}
public BsonReader(Stream stream, bool readRootValueAsArray, DateTimeKind dateTimeKindHandling)
{
ValidationUtils.ArgumentNotNull(stream, "stream");
_reader = new BinaryReader(stream);
_stack = new List<ContainerContext>();
_readRootValueAsArray = readRootValueAsArray;
_dateTimeKindHandling = dateTimeKindHandling;
}
public BsonReader(BinaryReader reader, bool readRootValueAsArray, DateTimeKind dateTimeKindHandling)
{
ValidationUtils.ArgumentNotNull(reader, "reader");
_reader = reader;
_stack = new List<ContainerContext>();
_readRootValueAsArray = readRootValueAsArray;
_dateTimeKindHandling = dateTimeKindHandling;
}
private string ReadElement()
{
_currentElementType = ReadType();
return ReadString();
}
public override bool Read()
{
try {
bool flag;
switch (_bsonReaderState) {
case BsonReaderState.Normal:
flag = ReadNormal();
break;
case BsonReaderState.ReferenceStart:
case BsonReaderState.ReferenceRef:
case BsonReaderState.ReferenceId:
flag = ReadReference();
break;
case BsonReaderState.CodeWScopeStart:
case BsonReaderState.CodeWScopeCode:
case BsonReaderState.CodeWScopeScope:
case BsonReaderState.CodeWScopeScopeObject:
case BsonReaderState.CodeWScopeScopeEnd:
flag = ReadCodeWScope();
break;
default:
throw JsonReaderException.Create(this, "Unexpected state: {0}".FormatWith(CultureInfo.InvariantCulture, _bsonReaderState));
}
if (flag)
return true;
SetToken(JsonToken.None);
return false;
} catch (EndOfStreamException) {
SetToken(JsonToken.None);
return false;
}
}
public override void Close()
{
base.Close();
if (base.CloseInput && _reader != null)
_reader.Close();
}
private bool ReadCodeWScope()
{
switch (_bsonReaderState) {
case BsonReaderState.CodeWScopeStart:
SetToken(JsonToken.PropertyName, "$code");
_bsonReaderState = BsonReaderState.CodeWScopeCode;
return true;
case BsonReaderState.CodeWScopeCode:
ReadInt32();
SetToken(JsonToken.String, ReadLengthString());
_bsonReaderState = BsonReaderState.CodeWScopeScope;
return true;
case BsonReaderState.CodeWScopeScope: {
if (base.CurrentState == State.PostValue) {
SetToken(JsonToken.PropertyName, "$scope");
return true;
}
SetToken(JsonToken.StartObject);
_bsonReaderState = BsonReaderState.CodeWScopeScopeObject;
ContainerContext containerContext = new ContainerContext(BsonType.Object);
PushContext(containerContext);
containerContext.Length = ReadInt32();
return true;
}
case BsonReaderState.CodeWScopeScopeObject: {
bool num = ReadNormal();
if (num && TokenType == JsonToken.EndObject)
_bsonReaderState = BsonReaderState.CodeWScopeScopeEnd;
return num;
}
case BsonReaderState.CodeWScopeScopeEnd:
SetToken(JsonToken.EndObject);
_bsonReaderState = BsonReaderState.Normal;
return true;
default:
throw new ArgumentOutOfRangeException();
}
}
private bool ReadReference()
{
switch (base.CurrentState) {
case State.ObjectStart:
SetToken(JsonToken.PropertyName, "$ref");
_bsonReaderState = BsonReaderState.ReferenceRef;
return true;
case State.Property:
if (_bsonReaderState == BsonReaderState.ReferenceRef) {
SetToken(JsonToken.String, ReadLengthString());
return true;
}
if (_bsonReaderState == BsonReaderState.ReferenceId) {
SetToken(JsonToken.Bytes, ReadBytes(12));
return true;
}
throw JsonReaderException.Create(this, "Unexpected state when reading BSON reference: " + _bsonReaderState);
case State.PostValue:
if (_bsonReaderState == BsonReaderState.ReferenceRef) {
SetToken(JsonToken.PropertyName, "$id");
_bsonReaderState = BsonReaderState.ReferenceId;
return true;
}
if (_bsonReaderState == BsonReaderState.ReferenceId) {
SetToken(JsonToken.EndObject);
_bsonReaderState = BsonReaderState.Normal;
return true;
}
throw JsonReaderException.Create(this, "Unexpected state when reading BSON reference: " + _bsonReaderState);
default:
throw JsonReaderException.Create(this, "Unexpected state when reading BSON reference: " + base.CurrentState);
}
}
private bool ReadNormal()
{
switch (base.CurrentState) {
case State.Start: {
JsonToken token2 = (!_readRootValueAsArray) ? JsonToken.StartObject : JsonToken.StartArray;
int type = (!_readRootValueAsArray) ? 3 : 4;
SetToken(token2);
ContainerContext containerContext = new ContainerContext((BsonType)type);
PushContext(containerContext);
containerContext.Length = ReadInt32();
return true;
}
case State.Complete:
case State.Closed:
return false;
case State.Property:
ReadType(_currentElementType);
return true;
case State.ObjectStart:
case State.ArrayStart:
case State.PostValue: {
ContainerContext currentContext = _currentContext;
if (currentContext == null)
return false;
int num = currentContext.Length - 1;
if (currentContext.Position < num) {
if (currentContext.Type == BsonType.Array) {
ReadElement();
ReadType(_currentElementType);
return true;
}
SetToken(JsonToken.PropertyName, ReadElement());
return true;
}
if (currentContext.Position == num) {
if (ReadByte() != 0)
throw JsonReaderException.Create(this, "Unexpected end of object byte value.");
PopContext();
if (_currentContext != null)
MovePosition(currentContext.Length);
JsonToken token = (currentContext.Type == BsonType.Object) ? JsonToken.EndObject : JsonToken.EndArray;
SetToken(token);
return true;
}
throw JsonReaderException.Create(this, "Read past end of current container context.");
}
default:
throw new ArgumentOutOfRangeException();
case State.ConstructorStart:
case State.Constructor:
case State.Error:
case State.Finished:
return false;
}
}
private void PopContext()
{
_stack.RemoveAt(_stack.Count - 1);
if (_stack.Count == 0)
_currentContext = null;
else
_currentContext = _stack[_stack.Count - 1];
}
private void PushContext(ContainerContext newContext)
{
_stack.Add(newContext);
_currentContext = newContext;
}
private byte ReadByte()
{
MovePosition(1);
return _reader.ReadByte();
}
private void ReadType(BsonType type)
{
switch (type) {
case BsonType.Number: {
double num = ReadDouble();
if (_floatParseHandling == FloatParseHandling.Decimal)
SetToken(JsonToken.Float, Convert.ToDecimal(num, CultureInfo.InvariantCulture));
else
SetToken(JsonToken.Float, num);
break;
}
case BsonType.String:
case BsonType.Symbol:
SetToken(JsonToken.String, ReadLengthString());
break;
case BsonType.Object: {
SetToken(JsonToken.StartObject);
ContainerContext containerContext2 = new ContainerContext(BsonType.Object);
PushContext(containerContext2);
containerContext2.Length = ReadInt32();
break;
}
case BsonType.Array: {
SetToken(JsonToken.StartArray);
ContainerContext containerContext = new ContainerContext(BsonType.Array);
PushContext(containerContext);
containerContext.Length = ReadInt32();
break;
}
case BsonType.Binary: {
BsonBinaryType binaryType;
byte[] array = ReadBinary(out binaryType);
object value3 = (binaryType != BsonBinaryType.Uuid) ? array : ((object)new Guid(array));
SetToken(JsonToken.Bytes, value3);
break;
}
case BsonType.Undefined:
SetToken(JsonToken.Undefined);
break;
case BsonType.Oid: {
byte[] value2 = ReadBytes(12);
SetToken(JsonToken.Bytes, value2);
break;
}
case BsonType.Boolean: {
bool flag = Convert.ToBoolean(ReadByte());
SetToken(JsonToken.Boolean, flag);
break;
}
case BsonType.Date: {
DateTime dateTime = DateTimeUtils.ConvertJavaScriptTicksToDateTime(ReadInt64());
DateTime dateTime2;
switch (DateTimeKindHandling) {
case DateTimeKind.Unspecified:
dateTime2 = DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified);
break;
case DateTimeKind.Local:
dateTime2 = dateTime.ToLocalTime();
break;
default:
dateTime2 = dateTime;
break;
}
SetToken(JsonToken.Date, dateTime2);
break;
}
case BsonType.Null:
SetToken(JsonToken.Null);
break;
case BsonType.Regex: {
string str = ReadString();
string str2 = ReadString();
string value = "/" + str + "/" + str2;
SetToken(JsonToken.String, value);
break;
}
case BsonType.Reference:
SetToken(JsonToken.StartObject);
_bsonReaderState = BsonReaderState.ReferenceStart;
break;
case BsonType.Code:
SetToken(JsonToken.String, ReadLengthString());
break;
case BsonType.CodeWScope:
SetToken(JsonToken.StartObject);
_bsonReaderState = BsonReaderState.CodeWScopeStart;
break;
case BsonType.Integer:
SetToken(JsonToken.Integer, (long)ReadInt32());
break;
case BsonType.TimeStamp:
case BsonType.Long:
SetToken(JsonToken.Integer, ReadInt64());
break;
default:
throw new ArgumentOutOfRangeException("type", "Unexpected BsonType value: " + type);
}
}
private byte[] ReadBinary(out BsonBinaryType binaryType)
{
int count = ReadInt32();
binaryType = (BsonBinaryType)ReadByte();
if (binaryType == BsonBinaryType.BinaryOld && !_jsonNet35BinaryCompatibility)
count = ReadInt32();
return ReadBytes(count);
}
private string ReadString()
{
EnsureBuffers();
StringBuilder stringBuilder = null;
int num = 0;
int num2 = 0;
while (true) {
int num3 = num2;
byte b;
while (num3 < 128 && (b = _reader.ReadByte()) > 0) {
_byteBuffer[num3++] = b;
}
int num5 = num3 - num2;
num += num5;
if (num3 < 128 && stringBuilder == null) {
int chars = Encoding.UTF8.GetChars(_byteBuffer, 0, num5, _charBuffer, 0);
MovePosition(num + 1);
return new string(_charBuffer, 0, chars);
}
int lastFullCharStop = GetLastFullCharStop(num3 - 1);
int chars2 = Encoding.UTF8.GetChars(_byteBuffer, 0, lastFullCharStop + 1, _charBuffer, 0);
if (stringBuilder == null)
stringBuilder = new StringBuilder(256);
stringBuilder.Append(_charBuffer, 0, chars2);
if (lastFullCharStop < num5 - 1) {
num2 = num5 - lastFullCharStop - 1;
Array.Copy(_byteBuffer, lastFullCharStop + 1, _byteBuffer, 0, num2);
} else {
if (num3 < 128)
break;
num2 = 0;
}
}
MovePosition(num + 1);
return stringBuilder.ToString();
}
private string ReadLengthString()
{
int num = ReadInt32();
MovePosition(num);
string string = GetString(num - 1);
_reader.ReadByte();
return string;
}
private string GetString(int length)
{
if (length == 0)
return string.Empty;
EnsureBuffers();
StringBuilder stringBuilder = null;
int num = 0;
int num2 = 0;
do {
int count = (length - num > 128 - num2) ? (128 - num2) : (length - num);
int num3 = _reader.Read(_byteBuffer, num2, count);
if (num3 == 0)
throw new EndOfStreamException("Unable to read beyond the end of the stream.");
num += num3;
num3 += num2;
if (num3 == length) {
int chars = Encoding.UTF8.GetChars(_byteBuffer, 0, num3, _charBuffer, 0);
return new string(_charBuffer, 0, chars);
}
int lastFullCharStop = GetLastFullCharStop(num3 - 1);
if (stringBuilder == null)
stringBuilder = new StringBuilder(length);
int chars2 = Encoding.UTF8.GetChars(_byteBuffer, 0, lastFullCharStop + 1, _charBuffer, 0);
stringBuilder.Append(_charBuffer, 0, chars2);
if (lastFullCharStop < num3 - 1) {
num2 = num3 - lastFullCharStop - 1;
Array.Copy(_byteBuffer, lastFullCharStop + 1, _byteBuffer, 0, num2);
} else
num2 = 0;
} while (num < length);
return stringBuilder.ToString();
}
private int GetLastFullCharStop(int start)
{
int num = start;
int num2 = 0;
while (num >= 0) {
num2 = BytesInSequence(_byteBuffer[num]);
switch (num2) {
case 0:
break;
default:
num--;
goto end_IL_0028;
case 1:
goto end_IL_0028;
}
num--;
continue;
continue;
end_IL_0028:
break;
}
if (num2 == start - num)
return start;
return num;
}
private int BytesInSequence(byte b)
{
if (b <= SeqRange1[1])
return 1;
if (b >= SeqRange2[0] && b <= SeqRange2[1])
return 2;
if (b >= SeqRange3[0] && b <= SeqRange3[1])
return 3;
if (b >= SeqRange4[0] && b <= SeqRange4[1])
return 4;
return 0;
}
private void EnsureBuffers()
{
if (_byteBuffer == null)
_byteBuffer = new byte[128];
if (_charBuffer == null) {
int maxCharCount = Encoding.UTF8.GetMaxCharCount(128);
_charBuffer = new char[maxCharCount];
}
}
private double ReadDouble()
{
MovePosition(8);
return _reader.ReadDouble();
}
private int ReadInt32()
{
MovePosition(4);
return _reader.ReadInt32();
}
private long ReadInt64()
{
MovePosition(8);
return _reader.ReadInt64();
}
private BsonType ReadType()
{
MovePosition(1);
return (BsonType)_reader.ReadSByte();
}
private void MovePosition(int count)
{
_currentContext.Position += count;
}
private byte[] ReadBytes(int count)
{
MovePosition(count);
return _reader.ReadBytes(count);
}
}
}