826 lines
						
					
					
						
							30 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							826 lines
						
					
					
						
							30 KiB
						
					
					
				
								#region License
							 | 
						|
								// Copyright (c) 2007 James Newton-King
							 | 
						|
								//
							 | 
						|
								// Permission is hereby granted, free of charge, to any person
							 | 
						|
								// obtaining a copy of this software and associated documentation
							 | 
						|
								// files (the "Software"), to deal in the Software without
							 | 
						|
								// restriction, including without limitation the rights to use,
							 | 
						|
								// copy, modify, merge, publish, distribute, sublicense, and/or sell
							 | 
						|
								// copies of the Software, and to permit persons to whom the
							 | 
						|
								// Software is furnished to do so, subject to the following
							 | 
						|
								// conditions:
							 | 
						|
								//
							 | 
						|
								// The above copyright notice and this permission notice shall be
							 | 
						|
								// included in all copies or substantial portions of the Software.
							 | 
						|
								//
							 | 
						|
								// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
							 | 
						|
								// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
							 | 
						|
								// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
							 | 
						|
								// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
							 | 
						|
								// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
							 | 
						|
								// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
							 | 
						|
								// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
							 | 
						|
								// OTHER DEALINGS IN THE SOFTWARE.
							 | 
						|
								#endregion
							 | 
						|
								
							 | 
						|
								using System;
							 | 
						|
								using System.Collections.Generic;
							 | 
						|
								using System.Globalization;
							 | 
						|
								using System.Text;
							 | 
						|
								using System.IO;
							 | 
						|
								using Newtonsoft.Json.Serialization;
							 | 
						|
								using Newtonsoft.Json.Utilities;
							 | 
						|
								using Newtonsoft.Json.Linq;
							 | 
						|
								
							 | 
						|
								namespace Newtonsoft.Json.Bson
							 | 
						|
								{
							 | 
						|
								    /// <summary>
							 | 
						|
								    /// Represents a reader that provides fast, non-cached, forward-only access to serialized BSON data.
							 | 
						|
								    /// </summary>
							 | 
						|
								    [Obsolete("BSON reading and writing has been moved to its own package. See https://www.nuget.org/packages/Newtonsoft.Json.Bson for more details.")]
							 | 
						|
								    internal class BsonReader : JsonReader
							 | 
						|
								    {
							 | 
						|
								        private const int MaxCharBytesSize = 128;
							 | 
						|
								        private static readonly byte[] SeqRange1 = new byte[] { 0, 127 }; // range of 1-byte sequence
							 | 
						|
								        private static readonly byte[] SeqRange2 = new byte[] { 194, 223 }; // range of 2-byte sequence
							 | 
						|
								        private static readonly byte[] SeqRange3 = new byte[] { 224, 239 }; // range of 3-byte sequence
							 | 
						|
								        private static readonly byte[] SeqRange4 = new byte[] { 240, 244 }; // range of 4-byte sequence
							 | 
						|
								
							 | 
						|
								        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;
							 | 
						|
								
							 | 
						|
								        private enum BsonReaderState
							 | 
						|
								        {
							 | 
						|
								            Normal = 0,
							 | 
						|
								            ReferenceStart = 1,
							 | 
						|
								            ReferenceRef = 2,
							 | 
						|
								            ReferenceId = 3,
							 | 
						|
								            CodeWScopeStart = 4,
							 | 
						|
								            CodeWScopeCode = 5,
							 | 
						|
								            CodeWScopeScope = 6,
							 | 
						|
								            CodeWScopeScopeObject = 7,
							 | 
						|
								            CodeWScopeScopeEnd = 8
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        private class ContainerContext
							 | 
						|
								        {
							 | 
						|
								            public readonly BsonType Type;
							 | 
						|
								            public int Length;
							 | 
						|
								            public int Position;
							 | 
						|
								
							 | 
						|
								            public ContainerContext(BsonType type)
							 | 
						|
								            {
							 | 
						|
								                Type = type;
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>
							 | 
						|
								        /// Gets or sets a value indicating whether binary data reading should be compatible with incorrect Json.NET 3.5 written binary.
							 | 
						|
								        /// </summary>
							 | 
						|
								        /// <value>
							 | 
						|
								        /// 	<c>true</c> if binary data reading will be compatible with incorrect Json.NET 3.5 written binary; otherwise, <c>false</c>.
							 | 
						|
								        /// </value>
							 | 
						|
								        [Obsolete("JsonNet35BinaryCompatibility will be removed in a future version of Json.NET.")]
							 | 
						|
								        public bool JsonNet35BinaryCompatibility
							 | 
						|
								        {
							 | 
						|
								            get => _jsonNet35BinaryCompatibility;
							 | 
						|
								            set => _jsonNet35BinaryCompatibility = value;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>
							 | 
						|
								        /// Gets or sets a value indicating whether the root object will be read as a JSON array.
							 | 
						|
								        /// </summary>
							 | 
						|
								        /// <value>
							 | 
						|
								        /// 	<c>true</c> if the root object will be read as a JSON array; otherwise, <c>false</c>.
							 | 
						|
								        /// </value>
							 | 
						|
								        public bool ReadRootValueAsArray
							 | 
						|
								        {
							 | 
						|
								            get => _readRootValueAsArray;
							 | 
						|
								            set => _readRootValueAsArray = value;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>
							 | 
						|
								        /// Gets or sets the <see cref="DateTimeKind" /> used when reading <see cref="DateTime"/> values from BSON.
							 | 
						|
								        /// </summary>
							 | 
						|
								        /// <value>The <see cref="DateTimeKind" /> used when reading <see cref="DateTime"/> values from BSON.</value>
							 | 
						|
								        public DateTimeKind DateTimeKindHandling
							 | 
						|
								        {
							 | 
						|
								            get => _dateTimeKindHandling;
							 | 
						|
								            set => _dateTimeKindHandling = value;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>
							 | 
						|
								        /// Initializes a new instance of the <see cref="BsonReader"/> class.
							 | 
						|
								        /// </summary>
							 | 
						|
								        /// <param name="stream">The <see cref="Stream"/> containing the BSON data to read.</param>
							 | 
						|
								        public BsonReader(Stream stream)
							 | 
						|
								            : this(stream, false, DateTimeKind.Local)
							 | 
						|
								        {
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>
							 | 
						|
								        /// Initializes a new instance of the <see cref="BsonReader"/> class.
							 | 
						|
								        /// </summary>
							 | 
						|
								        /// <param name="reader">The <see cref="BinaryReader"/> containing the BSON data to read.</param>
							 | 
						|
								        public BsonReader(BinaryReader reader)
							 | 
						|
								            : this(reader, false, DateTimeKind.Local)
							 | 
						|
								        {
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>
							 | 
						|
								        /// Initializes a new instance of the <see cref="BsonReader"/> class.
							 | 
						|
								        /// </summary>
							 | 
						|
								        /// <param name="stream">The <see cref="Stream"/> containing the BSON data to read.</param>
							 | 
						|
								        /// <param name="readRootValueAsArray">if set to <c>true</c> the root object will be read as a JSON array.</param>
							 | 
						|
								        /// <param name="dateTimeKindHandling">The <see cref="DateTimeKind" /> used when reading <see cref="DateTime"/> values from BSON.</param>
							 | 
						|
								        public BsonReader(Stream stream, bool readRootValueAsArray, DateTimeKind dateTimeKindHandling)
							 | 
						|
								        {
							 | 
						|
								            ValidationUtils.ArgumentNotNull(stream, nameof(stream));
							 | 
						|
								            _reader = new BinaryReader(stream);
							 | 
						|
								            _stack = new List<ContainerContext>();
							 | 
						|
								            _readRootValueAsArray = readRootValueAsArray;
							 | 
						|
								            _dateTimeKindHandling = dateTimeKindHandling;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>
							 | 
						|
								        /// Initializes a new instance of the <see cref="BsonReader"/> class.
							 | 
						|
								        /// </summary>
							 | 
						|
								        /// <param name="reader">The <see cref="BinaryReader"/> containing the BSON data to read.</param>
							 | 
						|
								        /// <param name="readRootValueAsArray">if set to <c>true</c> the root object will be read as a JSON array.</param>
							 | 
						|
								        /// <param name="dateTimeKindHandling">The <see cref="DateTimeKind" /> used when reading <see cref="DateTime"/> values from BSON.</param>
							 | 
						|
								        public BsonReader(BinaryReader reader, bool readRootValueAsArray, DateTimeKind dateTimeKindHandling)
							 | 
						|
								        {
							 | 
						|
								            ValidationUtils.ArgumentNotNull(reader, nameof(reader));
							 | 
						|
								            _reader = reader;
							 | 
						|
								            _stack = new List<ContainerContext>();
							 | 
						|
								            _readRootValueAsArray = readRootValueAsArray;
							 | 
						|
								            _dateTimeKindHandling = dateTimeKindHandling;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        private string ReadElement()
							 | 
						|
								        {
							 | 
						|
								            _currentElementType = ReadType();
							 | 
						|
								            string elementName = ReadString();
							 | 
						|
								            return elementName;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>
							 | 
						|
								        /// Reads the next JSON token from the underlying <see cref="Stream"/>.
							 | 
						|
								        /// </summary>
							 | 
						|
								        /// <returns>
							 | 
						|
								        /// <c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.
							 | 
						|
								        /// </returns>
							 | 
						|
								        public override bool Read()
							 | 
						|
								        {
							 | 
						|
								            try
							 | 
						|
								            {
							 | 
						|
								                bool success;
							 | 
						|
								
							 | 
						|
								                switch (_bsonReaderState)
							 | 
						|
								                {
							 | 
						|
								                    case BsonReaderState.Normal:
							 | 
						|
								                        success = ReadNormal();
							 | 
						|
								                        break;
							 | 
						|
								                    case BsonReaderState.ReferenceStart:
							 | 
						|
								                    case BsonReaderState.ReferenceRef:
							 | 
						|
								                    case BsonReaderState.ReferenceId:
							 | 
						|
								                        success = ReadReference();
							 | 
						|
								                        break;
							 | 
						|
								                    case BsonReaderState.CodeWScopeStart:
							 | 
						|
								                    case BsonReaderState.CodeWScopeCode:
							 | 
						|
								                    case BsonReaderState.CodeWScopeScope:
							 | 
						|
								                    case BsonReaderState.CodeWScopeScopeObject:
							 | 
						|
								                    case BsonReaderState.CodeWScopeScopeEnd:
							 | 
						|
								                        success = ReadCodeWScope();
							 | 
						|
								                        break;
							 | 
						|
								                    default:
							 | 
						|
								                        throw JsonReaderException.Create(this, "Unexpected state: {0}".FormatWith(CultureInfo.InvariantCulture, _bsonReaderState));
							 | 
						|
								                }
							 | 
						|
								
							 | 
						|
								                if (!success)
							 | 
						|
								                {
							 | 
						|
								                    SetToken(JsonToken.None);
							 | 
						|
								                    return false;
							 | 
						|
								                }
							 | 
						|
								
							 | 
						|
								                return true;
							 | 
						|
								            }
							 | 
						|
								            catch (EndOfStreamException)
							 | 
						|
								            {
							 | 
						|
								                SetToken(JsonToken.None);
							 | 
						|
								                return false;
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /// <summary>
							 | 
						|
								        /// Changes the reader's state to <see cref="JsonReader.State.Closed"/>.
							 | 
						|
								        /// If <see cref="JsonReader.CloseInput"/> is set to <c>true</c>, the underlying <see cref="Stream"/> is also closed.
							 | 
						|
								        /// </summary>
							 | 
						|
								        public override void Close()
							 | 
						|
								        {
							 | 
						|
								            base.Close();
							 | 
						|
								
							 | 
						|
								            if (CloseInput)
							 | 
						|
								            {
							 | 
						|
								                _reader?.Close();
							 | 
						|
								                //_reader?.Dispose();
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        private bool ReadCodeWScope()
							 | 
						|
								        {
							 | 
						|
								            switch (_bsonReaderState)
							 | 
						|
								            {
							 | 
						|
								                case BsonReaderState.CodeWScopeStart:
							 | 
						|
								                    SetToken(JsonToken.PropertyName, "$code");
							 | 
						|
								                    _bsonReaderState = BsonReaderState.CodeWScopeCode;
							 | 
						|
								                    return true;
							 | 
						|
								                case BsonReaderState.CodeWScopeCode:
							 | 
						|
								                    // total CodeWScope size - not used
							 | 
						|
								                    ReadInt32();
							 | 
						|
								
							 | 
						|
								                    SetToken(JsonToken.String, ReadLengthString());
							 | 
						|
								                    _bsonReaderState = BsonReaderState.CodeWScopeScope;
							 | 
						|
								                    return true;
							 | 
						|
								                case BsonReaderState.CodeWScopeScope:
							 | 
						|
								                    if (CurrentState == State.PostValue)
							 | 
						|
								                    {
							 | 
						|
								                        SetToken(JsonToken.PropertyName, "$scope");
							 | 
						|
								                        return true;
							 | 
						|
								                    }
							 | 
						|
								                    else
							 | 
						|
								                    {
							 | 
						|
								                        SetToken(JsonToken.StartObject);
							 | 
						|
								                        _bsonReaderState = BsonReaderState.CodeWScopeScopeObject;
							 | 
						|
								
							 | 
						|
								                        ContainerContext newContext = new ContainerContext(BsonType.Object);
							 | 
						|
								                        PushContext(newContext);
							 | 
						|
								                        newContext.Length = ReadInt32();
							 | 
						|
								
							 | 
						|
								                        return true;
							 | 
						|
								                    }
							 | 
						|
								                case BsonReaderState.CodeWScopeScopeObject:
							 | 
						|
								                    bool result = ReadNormal();
							 | 
						|
								                    if (result && TokenType == JsonToken.EndObject)
							 | 
						|
								                    {
							 | 
						|
								                        _bsonReaderState = BsonReaderState.CodeWScopeScopeEnd;
							 | 
						|
								                    }
							 | 
						|
								
							 | 
						|
								                    return result;
							 | 
						|
								                case BsonReaderState.CodeWScopeScopeEnd:
							 | 
						|
								                    SetToken(JsonToken.EndObject);
							 | 
						|
								                    _bsonReaderState = BsonReaderState.Normal;
							 | 
						|
								                    return true;
							 | 
						|
								                default:
							 | 
						|
								                    throw new ArgumentOutOfRangeException();
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        private bool ReadReference()
							 | 
						|
								        {
							 | 
						|
								            switch (CurrentState)
							 | 
						|
								            {
							 | 
						|
								                case State.ObjectStart:
							 | 
						|
								                {
							 | 
						|
								                    SetToken(JsonToken.PropertyName, JsonTypeReflector.RefPropertyName);
							 | 
						|
								                    _bsonReaderState = BsonReaderState.ReferenceRef;
							 | 
						|
								                    return true;
							 | 
						|
								                }
							 | 
						|
								                case State.Property:
							 | 
						|
								                {
							 | 
						|
								                    if (_bsonReaderState == BsonReaderState.ReferenceRef)
							 | 
						|
								                    {
							 | 
						|
								                        SetToken(JsonToken.String, ReadLengthString());
							 | 
						|
								                        return true;
							 | 
						|
								                    }
							 | 
						|
								                    else if (_bsonReaderState == BsonReaderState.ReferenceId)
							 | 
						|
								                    {
							 | 
						|
								                        SetToken(JsonToken.Bytes, ReadBytes(12));
							 | 
						|
								                        return true;
							 | 
						|
								                    }
							 | 
						|
								                    else
							 | 
						|
								                    {
							 | 
						|
								                        throw JsonReaderException.Create(this, "Unexpected state when reading BSON reference: " + _bsonReaderState);
							 | 
						|
								                    }
							 | 
						|
								                }
							 | 
						|
								                case State.PostValue:
							 | 
						|
								                {
							 | 
						|
								                    if (_bsonReaderState == BsonReaderState.ReferenceRef)
							 | 
						|
								                    {
							 | 
						|
								                        SetToken(JsonToken.PropertyName, JsonTypeReflector.IdPropertyName);
							 | 
						|
								                        _bsonReaderState = BsonReaderState.ReferenceId;
							 | 
						|
								                        return true;
							 | 
						|
								                    }
							 | 
						|
								                    else if (_bsonReaderState == BsonReaderState.ReferenceId)
							 | 
						|
								                    {
							 | 
						|
								                        SetToken(JsonToken.EndObject);
							 | 
						|
								                        _bsonReaderState = BsonReaderState.Normal;
							 | 
						|
								                        return true;
							 | 
						|
								                    }
							 | 
						|
								                    else
							 | 
						|
								                    {
							 | 
						|
								                        throw JsonReaderException.Create(this, "Unexpected state when reading BSON reference: " + _bsonReaderState);
							 | 
						|
								                    }
							 | 
						|
								                }
							 | 
						|
								                default:
							 | 
						|
								                    throw JsonReaderException.Create(this, "Unexpected state when reading BSON reference: " + CurrentState);
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        private bool ReadNormal()
							 | 
						|
								        {
							 | 
						|
								            switch (CurrentState)
							 | 
						|
								            {
							 | 
						|
								                case State.Start:
							 | 
						|
								                {
							 | 
						|
								                    JsonToken token = (!_readRootValueAsArray) ? JsonToken.StartObject : JsonToken.StartArray;
							 | 
						|
								                    BsonType type = (!_readRootValueAsArray) ? BsonType.Object : BsonType.Array;
							 | 
						|
								
							 | 
						|
								                    SetToken(token);
							 | 
						|
								                    ContainerContext newContext = new ContainerContext(type);
							 | 
						|
								                    PushContext(newContext);
							 | 
						|
								                    newContext.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 context = _currentContext;
							 | 
						|
								                    if (context == null)
							 | 
						|
								                    {
							 | 
						|
								                        return false;
							 | 
						|
								                    }
							 | 
						|
								
							 | 
						|
								                    int lengthMinusEnd = context.Length - 1;
							 | 
						|
								
							 | 
						|
								                    if (context.Position < lengthMinusEnd)
							 | 
						|
								                    {
							 | 
						|
								                        if (context.Type == BsonType.Array)
							 | 
						|
								                        {
							 | 
						|
								                            ReadElement();
							 | 
						|
								                            ReadType(_currentElementType);
							 | 
						|
								                            return true;
							 | 
						|
								                        }
							 | 
						|
								                        else
							 | 
						|
								                        {
							 | 
						|
								                            SetToken(JsonToken.PropertyName, ReadElement());
							 | 
						|
								                            return true;
							 | 
						|
								                        }
							 | 
						|
								                    }
							 | 
						|
								                    else if (context.Position == lengthMinusEnd)
							 | 
						|
								                    {
							 | 
						|
								                        if (ReadByte() != 0)
							 | 
						|
								                        {
							 | 
						|
								                            throw JsonReaderException.Create(this, "Unexpected end of object byte value.");
							 | 
						|
								                        }
							 | 
						|
								
							 | 
						|
								                        PopContext();
							 | 
						|
								                        if (_currentContext != null)
							 | 
						|
								                        {
							 | 
						|
								                            MovePosition(context.Length);
							 | 
						|
								                        }
							 | 
						|
								
							 | 
						|
								                        JsonToken endToken = (context.Type == BsonType.Object) ? JsonToken.EndObject : JsonToken.EndArray;
							 | 
						|
								                        SetToken(endToken);
							 | 
						|
								                        return true;
							 | 
						|
								                    }
							 | 
						|
								                    else
							 | 
						|
								                    {
							 | 
						|
								                        throw JsonReaderException.Create(this, "Read past end of current container context.");
							 | 
						|
								                    }
							 | 
						|
								                case State.ConstructorStart:
							 | 
						|
								                    break;
							 | 
						|
								                case State.Constructor:
							 | 
						|
								                    break;
							 | 
						|
								                case State.Error:
							 | 
						|
								                    break;
							 | 
						|
								                case State.Finished:
							 | 
						|
								                    break;
							 | 
						|
								                default:
							 | 
						|
								                    throw new ArgumentOutOfRangeException();
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            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 d = ReadDouble();
							 | 
						|
								
							 | 
						|
								                    if (_floatParseHandling == FloatParseHandling.Decimal)
							 | 
						|
								                    {
							 | 
						|
								                        SetToken(JsonToken.Float, Convert.ToDecimal(d, CultureInfo.InvariantCulture));
							 | 
						|
								                    }
							 | 
						|
								                    else
							 | 
						|
								                    {
							 | 
						|
								                        SetToken(JsonToken.Float, d);
							 | 
						|
								                    }
							 | 
						|
								                    break;
							 | 
						|
								                case BsonType.String:
							 | 
						|
								                case BsonType.Symbol:
							 | 
						|
								                    SetToken(JsonToken.String, ReadLengthString());
							 | 
						|
								                    break;
							 | 
						|
								                case BsonType.Object:
							 | 
						|
								                {
							 | 
						|
								                    SetToken(JsonToken.StartObject);
							 | 
						|
								
							 | 
						|
								                    ContainerContext newContext = new ContainerContext(BsonType.Object);
							 | 
						|
								                    PushContext(newContext);
							 | 
						|
								                    newContext.Length = ReadInt32();
							 | 
						|
								                    break;
							 | 
						|
								                }
							 | 
						|
								                case BsonType.Array:
							 | 
						|
								                {
							 | 
						|
								                    SetToken(JsonToken.StartArray);
							 | 
						|
								
							 | 
						|
								                    ContainerContext newContext = new ContainerContext(BsonType.Array);
							 | 
						|
								                    PushContext(newContext);
							 | 
						|
								                    newContext.Length = ReadInt32();
							 | 
						|
								                    break;
							 | 
						|
								                }
							 | 
						|
								                case BsonType.Binary:
							 | 
						|
								                    BsonBinaryType binaryType;
							 | 
						|
								                    byte[] data = ReadBinary(out binaryType);
							 | 
						|
								
							 | 
						|
								                    object value = (binaryType != BsonBinaryType.Uuid)
							 | 
						|
								                        ? data
							 | 
						|
								                        : (object)new Guid(data);
							 | 
						|
								
							 | 
						|
								                    SetToken(JsonToken.Bytes, value);
							 | 
						|
								                    break;
							 | 
						|
								                case BsonType.Undefined:
							 | 
						|
								                    SetToken(JsonToken.Undefined);
							 | 
						|
								                    break;
							 | 
						|
								                case BsonType.Oid:
							 | 
						|
								                    byte[] oid = ReadBytes(12);
							 | 
						|
								                    SetToken(JsonToken.Bytes, oid);
							 | 
						|
								                    break;
							 | 
						|
								                case BsonType.Boolean:
							 | 
						|
								                    bool b = Convert.ToBoolean(ReadByte());
							 | 
						|
								                    SetToken(JsonToken.Boolean, b);
							 | 
						|
								                    break;
							 | 
						|
								                case BsonType.Date:
							 | 
						|
								                    long ticks = ReadInt64();
							 | 
						|
								                    DateTime utcDateTime = DateTimeUtils.ConvertJavaScriptTicksToDateTime(ticks);
							 | 
						|
								
							 | 
						|
								                    DateTime dateTime;
							 | 
						|
								                    switch (DateTimeKindHandling)
							 | 
						|
								                    {
							 | 
						|
								                        case DateTimeKind.Unspecified:
							 | 
						|
								                            dateTime = DateTime.SpecifyKind(utcDateTime, DateTimeKind.Unspecified);
							 | 
						|
								                            break;
							 | 
						|
								                        case DateTimeKind.Local:
							 | 
						|
								                            dateTime = utcDateTime.ToLocalTime();
							 | 
						|
								                            break;
							 | 
						|
								                        default:
							 | 
						|
								                            dateTime = utcDateTime;
							 | 
						|
								                            break;
							 | 
						|
								                    }
							 | 
						|
								
							 | 
						|
								                    SetToken(JsonToken.Date, dateTime);
							 | 
						|
								                    break;
							 | 
						|
								                case BsonType.Null:
							 | 
						|
								                    SetToken(JsonToken.Null);
							 | 
						|
								                    break;
							 | 
						|
								                case BsonType.Regex:
							 | 
						|
								                    string expression = ReadString();
							 | 
						|
								                    string modifiers = ReadString();
							 | 
						|
								
							 | 
						|
								                    string regex = @"/" + expression + @"/" + modifiers;
							 | 
						|
								                    SetToken(JsonToken.String, regex);
							 | 
						|
								                    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(nameof(type), "Unexpected BsonType value: " + type);
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        private byte[] ReadBinary(out BsonBinaryType binaryType)
							 | 
						|
								        {
							 | 
						|
								            int dataLength = ReadInt32();
							 | 
						|
								
							 | 
						|
								            binaryType = (BsonBinaryType)ReadByte();
							 | 
						|
								
							 | 
						|
								#pragma warning disable 612,618
							 | 
						|
								            // the old binary type has the data length repeated in the data for some reason
							 | 
						|
								            if (binaryType == BsonBinaryType.BinaryOld && !_jsonNet35BinaryCompatibility)
							 | 
						|
								            {
							 | 
						|
								                dataLength = ReadInt32();
							 | 
						|
								            }
							 | 
						|
								#pragma warning restore 612,618
							 | 
						|
								
							 | 
						|
								            return ReadBytes(dataLength);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        private string ReadString()
							 | 
						|
								        {
							 | 
						|
								            EnsureBuffers();
							 | 
						|
								
							 | 
						|
								            StringBuilder builder = null;
							 | 
						|
								
							 | 
						|
								            int totalBytesRead = 0;
							 | 
						|
								            // used in case of left over multibyte characters in the buffer
							 | 
						|
								            int offset = 0;
							 | 
						|
								            while (true)
							 | 
						|
								            {
							 | 
						|
								                int count = offset;
							 | 
						|
								                byte b;
							 | 
						|
								                while (count < MaxCharBytesSize && (b = _reader.ReadByte()) > 0)
							 | 
						|
								                {
							 | 
						|
								                    _byteBuffer[count++] = b;
							 | 
						|
								                }
							 | 
						|
								                int byteCount = count - offset;
							 | 
						|
								                totalBytesRead += byteCount;
							 | 
						|
								
							 | 
						|
								                if (count < MaxCharBytesSize && builder == null)
							 | 
						|
								                {
							 | 
						|
								                    // pref optimization to avoid reading into a string builder
							 | 
						|
								                    // if string is smaller than the buffer then return it directly
							 | 
						|
								                    int length = Encoding.UTF8.GetChars(_byteBuffer, 0, byteCount, _charBuffer, 0);
							 | 
						|
								
							 | 
						|
								                    MovePosition(totalBytesRead + 1);
							 | 
						|
								                    return new string(_charBuffer, 0, length);
							 | 
						|
								                }
							 | 
						|
								                else
							 | 
						|
								                {
							 | 
						|
								                    // calculate the index of the end of the last full character in the buffer
							 | 
						|
								                    int lastFullCharStop = GetLastFullCharStop(count - 1);
							 | 
						|
								
							 | 
						|
								                    int charCount = Encoding.UTF8.GetChars(_byteBuffer, 0, lastFullCharStop + 1, _charBuffer, 0);
							 | 
						|
								
							 | 
						|
								                    if (builder == null)
							 | 
						|
								                    {
							 | 
						|
								                        builder = new StringBuilder(MaxCharBytesSize * 2);
							 | 
						|
								                    }
							 | 
						|
								
							 | 
						|
								                    builder.Append(_charBuffer, 0, charCount);
							 | 
						|
								
							 | 
						|
								                    if (lastFullCharStop < byteCount - 1)
							 | 
						|
								                    {
							 | 
						|
								                        offset = byteCount - lastFullCharStop - 1;
							 | 
						|
								                        // copy left over multi byte characters to beginning of buffer for next iteration
							 | 
						|
								                        Array.Copy(_byteBuffer, lastFullCharStop + 1, _byteBuffer, 0, offset);
							 | 
						|
								                    }
							 | 
						|
								                    else
							 | 
						|
								                    {
							 | 
						|
								                        // reached end of string
							 | 
						|
								                        if (count < MaxCharBytesSize)
							 | 
						|
								                        {
							 | 
						|
								                            MovePosition(totalBytesRead + 1);
							 | 
						|
								                            return builder.ToString();
							 | 
						|
								                        }
							 | 
						|
								
							 | 
						|
								                        offset = 0;
							 | 
						|
								                    }
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        private string ReadLengthString()
							 | 
						|
								        {
							 | 
						|
								            int length = ReadInt32();
							 | 
						|
								
							 | 
						|
								            MovePosition(length);
							 | 
						|
								
							 | 
						|
								            string s = GetString(length - 1);
							 | 
						|
								            _reader.ReadByte();
							 | 
						|
								
							 | 
						|
								            return s;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        private string GetString(int length)
							 | 
						|
								        {
							 | 
						|
								            if (length == 0)
							 | 
						|
								            {
							 | 
						|
								                return string.Empty;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            EnsureBuffers();
							 | 
						|
								
							 | 
						|
								            StringBuilder builder = null;
							 | 
						|
								
							 | 
						|
								            int totalBytesRead = 0;
							 | 
						|
								
							 | 
						|
								            // used in case of left over multibyte characters in the buffer
							 | 
						|
								            int offset = 0;
							 | 
						|
								            do
							 | 
						|
								            {
							 | 
						|
								                int count = ((length - totalBytesRead) > MaxCharBytesSize - offset)
							 | 
						|
								                    ? MaxCharBytesSize - offset
							 | 
						|
								                    : length - totalBytesRead;
							 | 
						|
								
							 | 
						|
								                int byteCount = _reader.Read(_byteBuffer, offset, count);
							 | 
						|
								
							 | 
						|
								                if (byteCount == 0)
							 | 
						|
								                {
							 | 
						|
								                    throw new EndOfStreamException("Unable to read beyond the end of the stream.");
							 | 
						|
								                }
							 | 
						|
								
							 | 
						|
								                totalBytesRead += byteCount;
							 | 
						|
								
							 | 
						|
								                // Above, byteCount is how many bytes we read this time.
							 | 
						|
								                // Below, byteCount is how many bytes are in the _byteBuffer.
							 | 
						|
								                byteCount += offset;
							 | 
						|
								
							 | 
						|
								                if (byteCount == length)
							 | 
						|
								                {
							 | 
						|
								                    // pref optimization to avoid reading into a string builder
							 | 
						|
								                    // first iteration and all bytes read then return string directly
							 | 
						|
								                    int charCount = Encoding.UTF8.GetChars(_byteBuffer, 0, byteCount, _charBuffer, 0);
							 | 
						|
								                    return new string(_charBuffer, 0, charCount);
							 | 
						|
								                }
							 | 
						|
								                else
							 | 
						|
								                {
							 | 
						|
								                    int lastFullCharStop = GetLastFullCharStop(byteCount - 1);
							 | 
						|
								
							 | 
						|
								                    if (builder == null)
							 | 
						|
								                    {
							 | 
						|
								                        builder = new StringBuilder(length);
							 | 
						|
								                    }
							 | 
						|
								
							 | 
						|
								                    int charCount = Encoding.UTF8.GetChars(_byteBuffer, 0, lastFullCharStop + 1, _charBuffer, 0);
							 | 
						|
								                    builder.Append(_charBuffer, 0, charCount);
							 | 
						|
								
							 | 
						|
								                    if (lastFullCharStop < byteCount - 1)
							 | 
						|
								                    {
							 | 
						|
								                        offset = byteCount - lastFullCharStop - 1;
							 | 
						|
								                        // copy left over multi byte characters to beginning of buffer for next iteration
							 | 
						|
								                        Array.Copy(_byteBuffer, lastFullCharStop + 1, _byteBuffer, 0, offset);
							 | 
						|
								                    }
							 | 
						|
								                    else
							 | 
						|
								                    {
							 | 
						|
								                        offset = 0;
							 | 
						|
								                    }
							 | 
						|
								                }
							 | 
						|
								            } while (totalBytesRead < length);
							 | 
						|
								
							 | 
						|
								            return builder.ToString();
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        private int GetLastFullCharStop(int start)
							 | 
						|
								        {
							 | 
						|
								            int lookbackPos = start;
							 | 
						|
								            int bis = 0;
							 | 
						|
								            while (lookbackPos >= 0)
							 | 
						|
								            {
							 | 
						|
								                bis = BytesInSequence(_byteBuffer[lookbackPos]);
							 | 
						|
								                if (bis == 0)
							 | 
						|
								                {
							 | 
						|
								                    lookbackPos--;
							 | 
						|
								                    continue;
							 | 
						|
								                }
							 | 
						|
								                else if (bis == 1)
							 | 
						|
								                {
							 | 
						|
								                    break;
							 | 
						|
								                }
							 | 
						|
								                else
							 | 
						|
								                {
							 | 
						|
								                    lookbackPos--;
							 | 
						|
								                    break;
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								            if (bis == start - lookbackPos)
							 | 
						|
								            {
							 | 
						|
								                //Full character.
							 | 
						|
								                return start;
							 | 
						|
								            }
							 | 
						|
								            else
							 | 
						|
								            {
							 | 
						|
								                return lookbackPos;
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        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[MaxCharBytesSize];
							 | 
						|
								            }
							 | 
						|
								            if (_charBuffer == null)
							 | 
						|
								            {
							 | 
						|
								                int charBufferSize = Encoding.UTF8.GetMaxCharCount(MaxCharBytesSize);
							 | 
						|
								                _charBuffer = new char[charBufferSize];
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        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);
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								}
							 |