326 lines
						
					
					
						
							11 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							326 lines
						
					
					
						
							11 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.Globalization;
							 | 
						|
								using System.IO;
							 | 
						|
								using System.Text;
							 | 
						|
								using Newtonsoft.Json.Utilities;
							 | 
						|
								
							 | 
						|
								namespace Newtonsoft.Json.Bson
							 | 
						|
								{
							 | 
						|
								    internal class BsonBinaryWriter
							 | 
						|
								    {
							 | 
						|
								        private static readonly Encoding Encoding = new UTF8Encoding(false);
							 | 
						|
								
							 | 
						|
								        private readonly BinaryWriter _writer;
							 | 
						|
								
							 | 
						|
								        private byte[] _largeByteBuffer;
							 | 
						|
								
							 | 
						|
								        public DateTimeKind DateTimeKindHandling { get; set; }
							 | 
						|
								
							 | 
						|
								        public BsonBinaryWriter(BinaryWriter writer)
							 | 
						|
								        {
							 | 
						|
								            DateTimeKindHandling = DateTimeKind.Utc;
							 | 
						|
								            _writer = writer;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        public void Flush()
							 | 
						|
								        {
							 | 
						|
								            _writer.Flush();
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        public void Close()
							 | 
						|
								        {
							 | 
						|
								            _writer.Close();
							 | 
						|
								            //_writer.Dispose();
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        public void WriteToken(BsonToken t)
							 | 
						|
								        {
							 | 
						|
								            CalculateSize(t);
							 | 
						|
								            WriteTokenInternal(t);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        private void WriteTokenInternal(BsonToken t)
							 | 
						|
								        {
							 | 
						|
								            switch (t.Type)
							 | 
						|
								            {
							 | 
						|
								                case BsonType.Object:
							 | 
						|
								                {
							 | 
						|
								                    BsonObject value = (BsonObject)t;
							 | 
						|
								                    _writer.Write(value.CalculatedSize);
							 | 
						|
								                    foreach (BsonProperty property in value)
							 | 
						|
								                    {
							 | 
						|
								                        _writer.Write((sbyte)property.Value.Type);
							 | 
						|
								                        WriteString((string)property.Name.Value, property.Name.ByteCount, null);
							 | 
						|
								                        WriteTokenInternal(property.Value);
							 | 
						|
								                    }
							 | 
						|
								                    _writer.Write((byte)0);
							 | 
						|
								                }
							 | 
						|
								                    break;
							 | 
						|
								                case BsonType.Array:
							 | 
						|
								                {
							 | 
						|
								                    BsonArray value = (BsonArray)t;
							 | 
						|
								                    _writer.Write(value.CalculatedSize);
							 | 
						|
								                    ulong index = 0;
							 | 
						|
								                    foreach (BsonToken c in value)
							 | 
						|
								                    {
							 | 
						|
								                        _writer.Write((sbyte)c.Type);
							 | 
						|
								                        WriteString(index.ToString(CultureInfo.InvariantCulture), MathUtils.IntLength(index), null);
							 | 
						|
								                        WriteTokenInternal(c);
							 | 
						|
								                        index++;
							 | 
						|
								                    }
							 | 
						|
								                    _writer.Write((byte)0);
							 | 
						|
								                }
							 | 
						|
								                    break;
							 | 
						|
								                case BsonType.Integer:
							 | 
						|
								                {
							 | 
						|
								                    BsonValue value = (BsonValue)t;
							 | 
						|
								                    _writer.Write(Convert.ToInt32(value.Value, CultureInfo.InvariantCulture));
							 | 
						|
								                }
							 | 
						|
								                    break;
							 | 
						|
								                case BsonType.Long:
							 | 
						|
								                {
							 | 
						|
								                    BsonValue value = (BsonValue)t;
							 | 
						|
								                    _writer.Write(Convert.ToInt64(value.Value, CultureInfo.InvariantCulture));
							 | 
						|
								                }
							 | 
						|
								                    break;
							 | 
						|
								                case BsonType.Number:
							 | 
						|
								                {
							 | 
						|
								                    BsonValue value = (BsonValue)t;
							 | 
						|
								                    _writer.Write(Convert.ToDouble(value.Value, CultureInfo.InvariantCulture));
							 | 
						|
								                }
							 | 
						|
								                    break;
							 | 
						|
								                case BsonType.String:
							 | 
						|
								                {
							 | 
						|
								                    BsonString value = (BsonString)t;
							 | 
						|
								                    WriteString((string)value.Value, value.ByteCount, value.CalculatedSize - 4);
							 | 
						|
								                }
							 | 
						|
								                    break;
							 | 
						|
								                case BsonType.Boolean:
							 | 
						|
								                    _writer.Write(t == BsonBoolean.True);
							 | 
						|
								                    break;
							 | 
						|
								                case BsonType.Null:
							 | 
						|
								                case BsonType.Undefined:
							 | 
						|
								                    break;
							 | 
						|
								                case BsonType.Date:
							 | 
						|
								                {
							 | 
						|
								                    BsonValue value = (BsonValue)t;
							 | 
						|
								
							 | 
						|
								                    long ticks = 0;
							 | 
						|
								
							 | 
						|
								                    if (value.Value is DateTime)
							 | 
						|
								                    {
							 | 
						|
								                        DateTime dateTime = (DateTime)value.Value;
							 | 
						|
								                        if (DateTimeKindHandling == DateTimeKind.Utc)
							 | 
						|
								                        {
							 | 
						|
								                            dateTime = dateTime.ToUniversalTime();
							 | 
						|
								                        }
							 | 
						|
								                        else if (DateTimeKindHandling == DateTimeKind.Local)
							 | 
						|
								                        {
							 | 
						|
								                            dateTime = dateTime.ToLocalTime();
							 | 
						|
								                        }
							 | 
						|
								
							 | 
						|
								                        ticks = DateTimeUtils.ConvertDateTimeToJavaScriptTicks(dateTime, false);
							 | 
						|
								                    }
							 | 
						|
								#if !NET20
							 | 
						|
								                    else
							 | 
						|
								                    {
							 | 
						|
								                        DateTimeOffset dateTimeOffset = (DateTimeOffset)value.Value;
							 | 
						|
								                        ticks = DateTimeUtils.ConvertDateTimeToJavaScriptTicks(dateTimeOffset.UtcDateTime, dateTimeOffset.Offset);
							 | 
						|
								                    }
							 | 
						|
								#endif
							 | 
						|
								
							 | 
						|
								                    _writer.Write(ticks);
							 | 
						|
								                }
							 | 
						|
								                    break;
							 | 
						|
								                case BsonType.Binary:
							 | 
						|
								                {
							 | 
						|
								                    BsonBinary value = (BsonBinary)t;
							 | 
						|
								
							 | 
						|
								                    byte[] data = (byte[])value.Value;
							 | 
						|
								                    _writer.Write(data.Length);
							 | 
						|
								                    _writer.Write((byte)value.BinaryType);
							 | 
						|
								                    _writer.Write(data);
							 | 
						|
								                }
							 | 
						|
								                    break;
							 | 
						|
								                case BsonType.Oid:
							 | 
						|
								                {
							 | 
						|
								                    BsonValue value = (BsonValue)t;
							 | 
						|
								
							 | 
						|
								                    byte[] data = (byte[])value.Value;
							 | 
						|
								                    _writer.Write(data);
							 | 
						|
								                }
							 | 
						|
								                    break;
							 | 
						|
								                case BsonType.Regex:
							 | 
						|
								                {
							 | 
						|
								                    BsonRegex value = (BsonRegex)t;
							 | 
						|
								
							 | 
						|
								                    WriteString((string)value.Pattern.Value, value.Pattern.ByteCount, null);
							 | 
						|
								                    WriteString((string)value.Options.Value, value.Options.ByteCount, null);
							 | 
						|
								                }
							 | 
						|
								                    break;
							 | 
						|
								                default:
							 | 
						|
								                    throw new ArgumentOutOfRangeException(nameof(t), "Unexpected token when writing BSON: {0}".FormatWith(CultureInfo.InvariantCulture, t.Type));
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        private void WriteString(string s, int byteCount, int? calculatedlengthPrefix)
							 | 
						|
								        {
							 | 
						|
								            if (calculatedlengthPrefix != null)
							 | 
						|
								            {
							 | 
						|
								                _writer.Write(calculatedlengthPrefix.GetValueOrDefault());
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            WriteUtf8Bytes(s, byteCount);
							 | 
						|
								
							 | 
						|
								            _writer.Write((byte)0);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        public void WriteUtf8Bytes(string s, int byteCount)
							 | 
						|
								        {
							 | 
						|
								            if (s != null)
							 | 
						|
								            {
							 | 
						|
								                if (byteCount <= 256)
							 | 
						|
								                {
							 | 
						|
								                    if (_largeByteBuffer == null)
							 | 
						|
								                    {
							 | 
						|
								                        _largeByteBuffer = new byte[256];
							 | 
						|
								                    }
							 | 
						|
								
							 | 
						|
								                    Encoding.GetBytes(s, 0, s.Length, _largeByteBuffer, 0);
							 | 
						|
								                    _writer.Write(_largeByteBuffer, 0, byteCount);
							 | 
						|
								                }
							 | 
						|
								                else
							 | 
						|
								                {
							 | 
						|
								                    byte[] bytes = Encoding.GetBytes(s);
							 | 
						|
								                    _writer.Write(bytes);
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        private int CalculateSize(int stringByteCount)
							 | 
						|
								        {
							 | 
						|
								            return stringByteCount + 1;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        private int CalculateSizeWithLength(int stringByteCount, bool includeSize)
							 | 
						|
								        {
							 | 
						|
								            int baseSize = (includeSize)
							 | 
						|
								                ? 5 // size bytes + terminator
							 | 
						|
								                : 1; // terminator
							 | 
						|
								
							 | 
						|
								            return baseSize + stringByteCount;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        private int CalculateSize(BsonToken t)
							 | 
						|
								        {
							 | 
						|
								            switch (t.Type)
							 | 
						|
								            {
							 | 
						|
								                case BsonType.Object:
							 | 
						|
								                {
							 | 
						|
								                    BsonObject value = (BsonObject)t;
							 | 
						|
								
							 | 
						|
								                    int bases = 4;
							 | 
						|
								                    foreach (BsonProperty p in value)
							 | 
						|
								                    {
							 | 
						|
								                        int size = 1;
							 | 
						|
								                        size += CalculateSize(p.Name);
							 | 
						|
								                        size += CalculateSize(p.Value);
							 | 
						|
								
							 | 
						|
								                        bases += size;
							 | 
						|
								                    }
							 | 
						|
								                    bases += 1;
							 | 
						|
								                    value.CalculatedSize = bases;
							 | 
						|
								                    return bases;
							 | 
						|
								                }
							 | 
						|
								                case BsonType.Array:
							 | 
						|
								                {
							 | 
						|
								                    BsonArray value = (BsonArray)t;
							 | 
						|
								
							 | 
						|
								                    int size = 4;
							 | 
						|
								                    ulong index = 0;
							 | 
						|
								                    foreach (BsonToken c in value)
							 | 
						|
								                    {
							 | 
						|
								                        size += 1;
							 | 
						|
								                        size += CalculateSize(MathUtils.IntLength(index));
							 | 
						|
								                        size += CalculateSize(c);
							 | 
						|
								                        index++;
							 | 
						|
								                    }
							 | 
						|
								                    size += 1;
							 | 
						|
								                    value.CalculatedSize = size;
							 | 
						|
								
							 | 
						|
								                    return value.CalculatedSize;
							 | 
						|
								                }
							 | 
						|
								                case BsonType.Integer:
							 | 
						|
								                    return 4;
							 | 
						|
								                case BsonType.Long:
							 | 
						|
								                    return 8;
							 | 
						|
								                case BsonType.Number:
							 | 
						|
								                    return 8;
							 | 
						|
								                case BsonType.String:
							 | 
						|
								                {
							 | 
						|
								                    BsonString value = (BsonString)t;
							 | 
						|
								                    string s = (string)value.Value;
							 | 
						|
								                    value.ByteCount = (s != null) ? Encoding.GetByteCount(s) : 0;
							 | 
						|
								                    value.CalculatedSize = CalculateSizeWithLength(value.ByteCount, value.IncludeLength);
							 | 
						|
								
							 | 
						|
								                    return value.CalculatedSize;
							 | 
						|
								                }
							 | 
						|
								                case BsonType.Boolean:
							 | 
						|
								                    return 1;
							 | 
						|
								                case BsonType.Null:
							 | 
						|
								                case BsonType.Undefined:
							 | 
						|
								                    return 0;
							 | 
						|
								                case BsonType.Date:
							 | 
						|
								                    return 8;
							 | 
						|
								                case BsonType.Binary:
							 | 
						|
								                {
							 | 
						|
								                    BsonBinary value = (BsonBinary)t;
							 | 
						|
								
							 | 
						|
								                    byte[] data = (byte[])value.Value;
							 | 
						|
								                    value.CalculatedSize = 4 + 1 + data.Length;
							 | 
						|
								
							 | 
						|
								                    return value.CalculatedSize;
							 | 
						|
								                }
							 | 
						|
								                case BsonType.Oid:
							 | 
						|
								                    return 12;
							 | 
						|
								                case BsonType.Regex:
							 | 
						|
								                {
							 | 
						|
								                    BsonRegex value = (BsonRegex)t;
							 | 
						|
								                    int size = 0;
							 | 
						|
								                    size += CalculateSize(value.Pattern);
							 | 
						|
								                    size += CalculateSize(value.Options);
							 | 
						|
								                    value.CalculatedSize = size;
							 | 
						|
								
							 | 
						|
								                    return value.CalculatedSize;
							 | 
						|
								                }
							 | 
						|
								                default:
							 | 
						|
								                    throw new ArgumentOutOfRangeException(nameof(t), "Unexpected token when writing BSON: {0}".FormatWith(CultureInfo.InvariantCulture, t.Type));
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								}
							 |