-New improved BSON writer
-Performance improvement for reference loop checking
This commit is contained in:
@@ -67,6 +67,95 @@ namespace Newtonsoft.Json.Tests.Bson
|
||||
Assert.IsFalse(reader.Read());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WriteValues()
|
||||
{
|
||||
byte[] data = MiscellaneousUtils.HexToBytes("8C-00-00-00-12-30-00-FF-FF-FF-FF-FF-FF-FF-7F-12-31-00-FF-FF-FF-FF-FF-FF-FF-7F-10-32-00-FF-FF-FF-7F-10-33-00-FF-FF-FF-7F-10-34-00-FF-00-00-00-10-35-00-7F-00-00-00-02-36-00-02-00-00-00-61-00-01-37-00-00-00-00-00-00-00-F0-45-01-38-00-FF-FF-FF-FF-FF-FF-EF-7F-01-39-00-00-00-00-E0-FF-FF-EF-47-08-31-30-00-01-05-31-31-00-05-00-00-00-02-00-01-02-03-04-09-31-32-00-40-C5-E2-BA-E3-00-00-00-09-31-33-00-40-C5-E2-BA-E3-00-00-00-00");
|
||||
MemoryStream ms = new MemoryStream(data);
|
||||
BsonReader reader = new BsonReader(ms);
|
||||
reader.ReadRootValueAsArray = true;
|
||||
reader.DateTimeKindHandling = DateTimeKind.Utc;
|
||||
|
||||
Assert.IsTrue(reader.Read());
|
||||
Assert.AreEqual(JsonToken.StartArray, reader.TokenType);
|
||||
|
||||
Assert.IsTrue(reader.Read());
|
||||
Assert.AreEqual(JsonToken.Integer, reader.TokenType);
|
||||
Assert.AreEqual(long.MaxValue, reader.Value);
|
||||
Assert.AreEqual(typeof(long), reader.ValueType);
|
||||
|
||||
Assert.IsTrue(reader.Read());
|
||||
Assert.AreEqual(JsonToken.Integer, reader.TokenType);
|
||||
Assert.AreEqual(long.MaxValue, reader.Value);
|
||||
Assert.AreEqual(typeof(long), reader.ValueType);
|
||||
|
||||
Assert.IsTrue(reader.Read());
|
||||
Assert.AreEqual(JsonToken.Integer, reader.TokenType);
|
||||
Assert.AreEqual(int.MaxValue, reader.Value);
|
||||
Assert.AreEqual(typeof(long), reader.ValueType);
|
||||
|
||||
Assert.IsTrue(reader.Read());
|
||||
Assert.AreEqual(JsonToken.Integer, reader.TokenType);
|
||||
Assert.AreEqual(int.MaxValue, reader.Value);
|
||||
Assert.AreEqual(typeof(long), reader.ValueType);
|
||||
|
||||
Assert.IsTrue(reader.Read());
|
||||
Assert.AreEqual(JsonToken.Integer, reader.TokenType);
|
||||
Assert.AreEqual(byte.MaxValue, reader.Value);
|
||||
Assert.AreEqual(typeof(long), reader.ValueType);
|
||||
|
||||
Assert.IsTrue(reader.Read());
|
||||
Assert.AreEqual(JsonToken.Integer, reader.TokenType);
|
||||
Assert.AreEqual(sbyte.MaxValue, reader.Value);
|
||||
Assert.AreEqual(typeof(long), reader.ValueType);
|
||||
|
||||
Assert.IsTrue(reader.Read());
|
||||
Assert.AreEqual(JsonToken.String, reader.TokenType);
|
||||
Assert.AreEqual("a", reader.Value);
|
||||
Assert.AreEqual(typeof(string), reader.ValueType);
|
||||
|
||||
Assert.IsTrue(reader.Read());
|
||||
Assert.AreEqual(JsonToken.Float, reader.TokenType);
|
||||
Assert.AreEqual(decimal.MaxValue, reader.Value);
|
||||
Assert.AreEqual(typeof(double), reader.ValueType);
|
||||
|
||||
Assert.IsTrue(reader.Read());
|
||||
Assert.AreEqual(JsonToken.Float, reader.TokenType);
|
||||
Assert.AreEqual(double.MaxValue, reader.Value);
|
||||
Assert.AreEqual(typeof(double), reader.ValueType);
|
||||
|
||||
Assert.IsTrue(reader.Read());
|
||||
Assert.AreEqual(JsonToken.Float, reader.TokenType);
|
||||
Assert.AreEqual(float.MaxValue, reader.Value);
|
||||
Assert.AreEqual(typeof(double), reader.ValueType);
|
||||
|
||||
Assert.IsTrue(reader.Read());
|
||||
Assert.AreEqual(JsonToken.Boolean, reader.TokenType);
|
||||
Assert.AreEqual(true, reader.Value);
|
||||
Assert.AreEqual(typeof(bool), reader.ValueType);
|
||||
|
||||
Assert.IsTrue(reader.Read());
|
||||
Assert.AreEqual(JsonToken.Bytes, reader.TokenType);
|
||||
Assert.AreEqual(new byte[] { 0, 1, 2, 3, 4 }, reader.Value);
|
||||
Assert.AreEqual(typeof(byte[]), reader.ValueType);
|
||||
|
||||
Assert.IsTrue(reader.Read());
|
||||
Assert.AreEqual(JsonToken.Date, reader.TokenType);
|
||||
Assert.AreEqual(new DateTime(2000, 12, 29, 12, 30, 0, DateTimeKind.Utc), reader.Value);
|
||||
Assert.AreEqual(typeof(DateTime), reader.ValueType);
|
||||
|
||||
Assert.IsTrue(reader.Read());
|
||||
Assert.AreEqual(JsonToken.Date, reader.TokenType);
|
||||
Assert.AreEqual(new DateTime(2000, 12, 29, 12, 30, 0, DateTimeKind.Utc), reader.Value);
|
||||
Assert.AreEqual(typeof(DateTime), reader.ValueType);
|
||||
|
||||
Assert.IsTrue(reader.Read());
|
||||
Assert.AreEqual(JsonToken.EndArray, reader.TokenType);
|
||||
|
||||
Assert.IsFalse(reader.Read());
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void ReadObjectBsonFromSite()
|
||||
{
|
||||
|
||||
@@ -48,11 +48,40 @@ namespace Newtonsoft.Json.Tests.Bson
|
||||
writer.WritePropertyName("Blah");
|
||||
writer.WriteValue(1);
|
||||
writer.WriteEndObject();
|
||||
|
||||
|
||||
string bson = MiscellaneousUtils.BytesToHex(ms.ToArray());
|
||||
Assert.AreEqual("0F-00-00-00-10-42-6C-61-68-00-01-00-00-00-00", bson);
|
||||
}
|
||||
|
||||
#if !PocketPC && !NET20
|
||||
[Test]
|
||||
public void WriteValues()
|
||||
{
|
||||
MemoryStream ms = new MemoryStream();
|
||||
BsonWriter writer = new BsonWriter(ms);
|
||||
|
||||
writer.WriteStartArray();
|
||||
writer.WriteValue(long.MaxValue);
|
||||
writer.WriteValue((ulong)long.MaxValue);
|
||||
writer.WriteValue(int.MaxValue);
|
||||
writer.WriteValue((uint)int.MaxValue);
|
||||
writer.WriteValue(byte.MaxValue);
|
||||
writer.WriteValue(sbyte.MaxValue);
|
||||
writer.WriteValue('a');
|
||||
writer.WriteValue(decimal.MaxValue);
|
||||
writer.WriteValue(double.MaxValue);
|
||||
writer.WriteValue(float.MaxValue);
|
||||
writer.WriteValue(true);
|
||||
writer.WriteValue(new byte[] { 0, 1, 2, 3, 4 });
|
||||
writer.WriteValue(new DateTimeOffset(2000, 12, 29, 12, 30, 0, TimeSpan.Zero));
|
||||
writer.WriteValue(new DateTime(2000, 12, 29, 12, 30, 0, DateTimeKind.Utc));
|
||||
writer.WriteEnd();
|
||||
|
||||
string bson = MiscellaneousUtils.BytesToHex(ms.ToArray());
|
||||
Assert.AreEqual("8C-00-00-00-12-30-00-FF-FF-FF-FF-FF-FF-FF-7F-12-31-00-FF-FF-FF-FF-FF-FF-FF-7F-10-32-00-FF-FF-FF-7F-10-33-00-FF-FF-FF-7F-10-34-00-FF-00-00-00-10-35-00-7F-00-00-00-02-36-00-02-00-00-00-61-00-01-37-00-00-00-00-00-00-00-F0-45-01-38-00-FF-FF-FF-FF-FF-FF-EF-7F-01-39-00-00-00-00-E0-FF-FF-EF-47-08-31-30-00-01-05-31-31-00-05-00-00-00-02-00-01-02-03-04-09-31-32-00-40-C5-E2-BA-E3-00-00-00-09-31-33-00-40-C5-E2-BA-E3-00-00-00-00", bson);
|
||||
}
|
||||
#endif
|
||||
|
||||
[Test]
|
||||
public void WriteArrayBsonFromSite()
|
||||
{
|
||||
|
||||
@@ -122,10 +122,13 @@ namespace Newtonsoft.Json.Tests
|
||||
|
||||
private void SerializeSize(object value)
|
||||
{
|
||||
// this is extremely slow with 5000 interations
|
||||
int interations = 100;
|
||||
|
||||
byte[] jsonBytes = TimeOperation(() =>
|
||||
{
|
||||
string json = null;
|
||||
for (int i = 0; i < Iterations; i++)
|
||||
for (int i = 0; i < interations; i++)
|
||||
{
|
||||
json = JsonConvert.SerializeObject(value, Formatting.None);
|
||||
}
|
||||
@@ -136,7 +139,7 @@ namespace Newtonsoft.Json.Tests
|
||||
byte[] bsonBytes = TimeOperation(() =>
|
||||
{
|
||||
MemoryStream ms = null;
|
||||
for (int i = 0; i < Iterations; i++)
|
||||
for (int i = 0; i < interations; i++)
|
||||
{
|
||||
ms = new MemoryStream();
|
||||
JsonSerializer serializer = new JsonSerializer();
|
||||
@@ -152,7 +155,7 @@ namespace Newtonsoft.Json.Tests
|
||||
byte[] xmlBytes = TimeOperation(() =>
|
||||
{
|
||||
MemoryStream ms = null;
|
||||
for (int i = 0; i < Iterations; i++)
|
||||
for (int i = 0; i < interations; i++)
|
||||
{
|
||||
ms = new MemoryStream();
|
||||
DataContractSerializer dataContractSerializer = new DataContractSerializer(value.GetType());
|
||||
@@ -165,7 +168,7 @@ namespace Newtonsoft.Json.Tests
|
||||
byte[] wcfJsonBytes = TimeOperation(() =>
|
||||
{
|
||||
MemoryStream ms = null;
|
||||
for (int i = 0; i < Iterations; i++)
|
||||
for (int i = 0; i < interations; i++)
|
||||
{
|
||||
ms = new MemoryStream();
|
||||
DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(value.GetType());
|
||||
@@ -178,7 +181,7 @@ namespace Newtonsoft.Json.Tests
|
||||
byte[] binaryFormatterBytes = TimeOperation(() =>
|
||||
{
|
||||
MemoryStream ms = null;
|
||||
for (int i = 0; i < Iterations; i++)
|
||||
for (int i = 0; i < interations; i++)
|
||||
{
|
||||
ms = new MemoryStream();
|
||||
BinaryFormatter formatter = new BinaryFormatter();
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json.Utilities;
|
||||
|
||||
namespace Newtonsoft.Json.Bson
|
||||
{
|
||||
internal class BsonBinaryWriter
|
||||
{
|
||||
private static readonly Encoding Encoding = Encoding.UTF8;
|
||||
|
||||
private readonly BinaryWriter _writer;
|
||||
|
||||
private byte[] _largeByteBuffer;
|
||||
private int _maxChars;
|
||||
|
||||
public BsonBinaryWriter(Stream stream)
|
||||
{
|
||||
_writer = new BinaryWriter(stream);
|
||||
}
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
_writer.Flush();
|
||||
}
|
||||
|
||||
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);
|
||||
int index = 0;
|
||||
foreach (BsonToken c in value)
|
||||
{
|
||||
_writer.Write((sbyte)c.Type);
|
||||
WriteString(index.ToString(CultureInfo.InvariantCulture), Encoding.GetByteCount(index.ToString(CultureInfo.InvariantCulture)), 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:
|
||||
{
|
||||
BsonValue value = (BsonValue)t;
|
||||
_writer.Write((bool)value.Value);
|
||||
}
|
||||
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;
|
||||
dateTime = dateTime.ToUniversalTime();
|
||||
ticks = JsonConvert.ConvertDateTimeToJavaScriptTicks(dateTime);
|
||||
}
|
||||
#if !PocketPC && !NET20
|
||||
else
|
||||
{
|
||||
DateTimeOffset dateTimeOffset = (DateTimeOffset) value.Value;
|
||||
ticks = JsonConvert.ConvertDateTimeToJavaScriptTicks(dateTimeOffset.UtcDateTime, dateTimeOffset.Offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
_writer.Write(ticks);
|
||||
}
|
||||
break;
|
||||
case BsonType.Binary:
|
||||
{
|
||||
BsonValue value = (BsonValue)t;
|
||||
|
||||
byte[] data = (byte[])value.Value;
|
||||
_writer.Write(data.Length);
|
||||
_writer.Write((byte)BsonBinaryType.Data);
|
||||
_writer.Write(data);
|
||||
}
|
||||
break;
|
||||
case BsonType.Oid:
|
||||
{
|
||||
BsonValue value = (BsonValue)t;
|
||||
|
||||
byte[] data = (byte[])value.Value;
|
||||
_writer.Write(data);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("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.Value);
|
||||
|
||||
if (_largeByteBuffer == null)
|
||||
{
|
||||
_largeByteBuffer = new byte[256];
|
||||
_maxChars = 256 / Encoding.GetMaxByteCount(1);
|
||||
}
|
||||
if (byteCount <= 256)
|
||||
{
|
||||
Encoding.GetBytes(s, 0, s.Length, _largeByteBuffer, 0);
|
||||
_writer.Write(_largeByteBuffer, 0, byteCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
int charCount;
|
||||
int totalCharsWritten = 0;
|
||||
for (int i = s.Length; i > 0; i -= charCount)
|
||||
{
|
||||
charCount = (i > _maxChars) ? _maxChars : i;
|
||||
int count = Encoding.GetBytes(s, totalCharsWritten, charCount, _largeByteBuffer, 0);
|
||||
_writer.Write(_largeByteBuffer, 0, count);
|
||||
totalCharsWritten += charCount;
|
||||
}
|
||||
}
|
||||
|
||||
_writer.Write((byte)0);
|
||||
}
|
||||
|
||||
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;
|
||||
int 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 = Encoding.GetByteCount(s);
|
||||
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:
|
||||
{
|
||||
BsonValue value = (BsonValue) t;
|
||||
|
||||
byte[] data = (byte[])value.Value;
|
||||
value.CalculatedSize = 4 + 1 + data.Length;
|
||||
|
||||
return value.CalculatedSize;
|
||||
}
|
||||
case BsonType.Oid:
|
||||
return 12;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("t", "Unexpected token when writing BSON: {0}".FormatWith(CultureInfo.InvariantCulture, t.Type));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,11 +72,11 @@ namespace Newtonsoft.Json.Bson
|
||||
|
||||
private class ContainerContext
|
||||
{
|
||||
public readonly JTokenType Type;
|
||||
public readonly BsonType Type;
|
||||
public int Length;
|
||||
public int Position;
|
||||
|
||||
public ContainerContext(JTokenType type)
|
||||
public ContainerContext(BsonType type)
|
||||
{
|
||||
Type = type;
|
||||
}
|
||||
@@ -210,7 +210,7 @@ namespace Newtonsoft.Json.Bson
|
||||
SetToken(JsonToken.StartObject);
|
||||
_bsonReaderState = BsonReaderState.CodeWScopeScopeObject;
|
||||
|
||||
ContainerContext newContext = new ContainerContext(JTokenType.Object);
|
||||
ContainerContext newContext = new ContainerContext(BsonType.Object);
|
||||
PushContext(newContext);
|
||||
newContext.Length = ReadInt32();
|
||||
|
||||
@@ -289,7 +289,7 @@ namespace Newtonsoft.Json.Bson
|
||||
case State.Start:
|
||||
{
|
||||
JsonToken token = (!_readRootValueAsArray) ? JsonToken.StartObject : JsonToken.StartArray;
|
||||
JTokenType type = (!_readRootValueAsArray) ? JTokenType.Object : JTokenType.Array;
|
||||
BsonType type = (!_readRootValueAsArray) ? BsonType.Object : BsonType.Array;
|
||||
|
||||
SetToken(token);
|
||||
ContainerContext newContext = new ContainerContext(type);
|
||||
@@ -316,7 +316,7 @@ namespace Newtonsoft.Json.Bson
|
||||
|
||||
if (context.Position < lengthMinusEnd)
|
||||
{
|
||||
if (context.Type == JTokenType.Array)
|
||||
if (context.Type == BsonType.Array)
|
||||
{
|
||||
ReadElement();
|
||||
ReadType(_currentElementType);
|
||||
@@ -337,7 +337,7 @@ namespace Newtonsoft.Json.Bson
|
||||
if (_currentContext != null)
|
||||
MovePosition(context.Length);
|
||||
|
||||
JsonToken endToken = (context.Type == JTokenType.Object) ? JsonToken.EndObject : JsonToken.EndArray;
|
||||
JsonToken endToken = (context.Type == BsonType.Object) ? JsonToken.EndObject : JsonToken.EndArray;
|
||||
SetToken(endToken);
|
||||
return true;
|
||||
}
|
||||
@@ -396,7 +396,7 @@ namespace Newtonsoft.Json.Bson
|
||||
{
|
||||
SetToken(JsonToken.StartObject);
|
||||
|
||||
ContainerContext newContext = new ContainerContext(JTokenType.Object);
|
||||
ContainerContext newContext = new ContainerContext(BsonType.Object);
|
||||
PushContext(newContext);
|
||||
newContext.Length = ReadInt32();
|
||||
break;
|
||||
@@ -405,7 +405,7 @@ namespace Newtonsoft.Json.Bson
|
||||
{
|
||||
SetToken(JsonToken.StartArray);
|
||||
|
||||
ContainerContext newContext = new ContainerContext(JTokenType.Array);
|
||||
ContainerContext newContext = new ContainerContext(BsonType.Array);
|
||||
PushContext(newContext);
|
||||
newContext.Length = ReadInt32();
|
||||
break;
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json.Utilities;
|
||||
@@ -32,17 +34,115 @@ using System.Globalization;
|
||||
|
||||
namespace Newtonsoft.Json.Bson
|
||||
{
|
||||
internal abstract class BsonToken
|
||||
{
|
||||
public abstract BsonType Type { get; }
|
||||
public BsonToken Parent { get; set; }
|
||||
public int CalculatedSize { get; set; }
|
||||
}
|
||||
|
||||
internal class BsonObject : BsonToken, IEnumerable<BsonProperty>
|
||||
{
|
||||
private readonly List<BsonProperty> _children = new List<BsonProperty>();
|
||||
|
||||
public void Add(string name, BsonToken token)
|
||||
{
|
||||
_children.Add(new BsonProperty { Name = new BsonString(name, false), Value = token });
|
||||
token.Parent = this;
|
||||
}
|
||||
|
||||
public override BsonType Type
|
||||
{
|
||||
get { return BsonType.Object; }
|
||||
}
|
||||
|
||||
public IEnumerator<BsonProperty> GetEnumerator()
|
||||
{
|
||||
return _children.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
|
||||
internal class BsonArray : BsonToken, IEnumerable<BsonToken>
|
||||
{
|
||||
private readonly List<BsonToken> _children = new List<BsonToken>();
|
||||
|
||||
public void Add(BsonToken token)
|
||||
{
|
||||
_children.Add(token);
|
||||
token.Parent = this;
|
||||
}
|
||||
|
||||
public override BsonType Type
|
||||
{
|
||||
get { return BsonType.Array; }
|
||||
}
|
||||
|
||||
public IEnumerator<BsonToken> GetEnumerator()
|
||||
{
|
||||
return _children.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
|
||||
internal class BsonValue : BsonToken
|
||||
{
|
||||
private object _value;
|
||||
private BsonType _type;
|
||||
|
||||
public BsonValue(object value, BsonType type)
|
||||
{
|
||||
_value = value;
|
||||
_type = type;
|
||||
}
|
||||
|
||||
public object Value
|
||||
{
|
||||
get { return _value; }
|
||||
}
|
||||
|
||||
public override BsonType Type
|
||||
{
|
||||
get { return _type; }
|
||||
}
|
||||
}
|
||||
|
||||
internal class BsonString : BsonValue
|
||||
{
|
||||
public int ByteCount { get; set; }
|
||||
public bool IncludeLength { get; set; }
|
||||
|
||||
public BsonString(object value, bool includeLength)
|
||||
: base(value, BsonType.String)
|
||||
{
|
||||
IncludeLength = includeLength;
|
||||
}
|
||||
}
|
||||
|
||||
internal class BsonProperty
|
||||
{
|
||||
public BsonString Name { get; set; }
|
||||
public BsonToken Value { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a writer that provides a fast, non-cached, forward-only way of generating Json data.
|
||||
/// </summary>
|
||||
public class BsonWriter : JTokenWriter
|
||||
public class BsonWriter : JsonWriter
|
||||
{
|
||||
private static readonly Encoding Encoding = Encoding.UTF8;
|
||||
private readonly BsonBinaryWriter _writer;
|
||||
|
||||
private readonly BinaryWriter _writer;
|
||||
|
||||
private byte[] _largeByteBuffer;
|
||||
private int _maxChars;
|
||||
private BsonToken _root;
|
||||
private BsonToken _parent;
|
||||
private string _propertyName;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BsonWriter"/> class.
|
||||
@@ -51,7 +151,7 @@ namespace Newtonsoft.Json.Bson
|
||||
public BsonWriter(Stream stream)
|
||||
{
|
||||
ValidationUtils.ArgumentNotNull(stream, "stream");
|
||||
_writer = new BinaryWriter(stream);
|
||||
_writer = new BsonBinaryWriter(stream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -62,218 +162,6 @@ namespace Newtonsoft.Json.Bson
|
||||
_writer.Flush();
|
||||
}
|
||||
|
||||
private void WriteToken(JToken t)
|
||||
{
|
||||
switch (t.Type)
|
||||
{
|
||||
case JTokenType.Object:
|
||||
{
|
||||
int size = CalculateSize(t);
|
||||
_writer.Write(size);
|
||||
foreach (JProperty property in t)
|
||||
{
|
||||
_writer.Write((sbyte)GetTypeNumber(property.Value));
|
||||
WriteString(property.Name, false);
|
||||
WriteToken(property.Value);
|
||||
}
|
||||
_writer.Write((byte)0);
|
||||
}
|
||||
break;
|
||||
case JTokenType.Array:
|
||||
{
|
||||
int size = CalculateSize(t);
|
||||
_writer.Write(size);
|
||||
int index = 0;
|
||||
foreach (JToken c in t)
|
||||
{
|
||||
_writer.Write((sbyte)GetTypeNumber(c));
|
||||
WriteString(index.ToString(CultureInfo.InvariantCulture), false);
|
||||
WriteToken(c);
|
||||
index++;
|
||||
}
|
||||
_writer.Write((byte)0);
|
||||
}
|
||||
break;
|
||||
case JTokenType.Integer:
|
||||
_writer.Write(Convert.ToInt32(((JValue)t).Value, CultureInfo.InvariantCulture));
|
||||
break;
|
||||
case JTokenType.Float:
|
||||
_writer.Write(Convert.ToDouble(((JValue)t).Value, CultureInfo.InvariantCulture));
|
||||
break;
|
||||
case JTokenType.String:
|
||||
WriteString((string)t, true);
|
||||
break;
|
||||
case JTokenType.Boolean:
|
||||
_writer.Write((bool)t);
|
||||
break;
|
||||
case JTokenType.Null:
|
||||
case JTokenType.Undefined:
|
||||
break;
|
||||
case JTokenType.Date:
|
||||
DateTime dateTime = (DateTime)t;
|
||||
dateTime = dateTime.ToUniversalTime();
|
||||
long ticks = JsonConvert.ConvertDateTimeToJavaScriptTicks(dateTime);
|
||||
_writer.Write(ticks);
|
||||
break;
|
||||
case JTokenType.Bytes:
|
||||
byte[] data = (byte[])t;
|
||||
_writer.Write(data.Length);
|
||||
_writer.Write((byte)BsonBinaryType.Data);
|
||||
_writer.Write(data);
|
||||
break;
|
||||
case JTokenType.Raw:
|
||||
// hacky
|
||||
JRaw r = (JRaw)t;
|
||||
_writer.Write((byte[])r.Value);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("t", "Unexpected token when writing BSON: {0}".FormatWith(CultureInfo.InvariantCulture, t.Type));
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteString(string s, bool includeLength)
|
||||
{
|
||||
int byteCount = Encoding.GetByteCount(s);
|
||||
|
||||
if (includeLength)
|
||||
_writer.Write(CalculateSizeWithLength(byteCount, false));
|
||||
|
||||
if (_largeByteBuffer == null)
|
||||
{
|
||||
_largeByteBuffer = new byte[256];
|
||||
_maxChars = 256 / Encoding.GetMaxByteCount(1);
|
||||
}
|
||||
if (byteCount <= 256)
|
||||
{
|
||||
Encoding.GetBytes(s, 0, s.Length, _largeByteBuffer, 0);
|
||||
_writer.Write(_largeByteBuffer, 0, byteCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
int charCount;
|
||||
int totalCharsWritten = 0;
|
||||
for (int i = s.Length; i > 0; i -= charCount)
|
||||
{
|
||||
charCount = (i > _maxChars) ? _maxChars : i;
|
||||
int count = Encoding.GetBytes(s, totalCharsWritten, charCount, _largeByteBuffer, 0);
|
||||
_writer.Write(_largeByteBuffer, 0, count);
|
||||
totalCharsWritten += charCount;
|
||||
}
|
||||
}
|
||||
|
||||
_writer.Write((byte)0);
|
||||
}
|
||||
|
||||
private BsonType GetTypeNumber(JToken t)
|
||||
{
|
||||
switch (t.Type)
|
||||
{
|
||||
case JTokenType.Object:
|
||||
return BsonType.Object;
|
||||
case JTokenType.Array:
|
||||
return BsonType.Array;
|
||||
case JTokenType.Integer:
|
||||
return BsonType.Integer;
|
||||
case JTokenType.Float:
|
||||
return BsonType.Number;
|
||||
case JTokenType.String:
|
||||
return BsonType.String;
|
||||
case JTokenType.Boolean:
|
||||
return BsonType.Boolean;
|
||||
case JTokenType.Null:
|
||||
case JTokenType.Undefined:
|
||||
return BsonType.Null;
|
||||
case JTokenType.Date:
|
||||
return BsonType.Date;
|
||||
case JTokenType.Bytes:
|
||||
return BsonType.Binary;
|
||||
case JTokenType.Raw:
|
||||
// hacky
|
||||
return BsonType.Oid;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("t", "Unexpected token when resolving JSON type to BSON type: {0}".FormatWith(CultureInfo.InvariantCulture, t.Type));
|
||||
}
|
||||
}
|
||||
|
||||
private int CalculateSize(string s)
|
||||
{
|
||||
return Encoding.GetByteCount(s) + 1;
|
||||
}
|
||||
|
||||
private int CalculateSizeWithLength(string s, bool includeSize)
|
||||
{
|
||||
return CalculateSizeWithLength(Encoding.GetByteCount(s), includeSize);
|
||||
}
|
||||
|
||||
private int CalculateSizeWithLength(int stringByteCount, bool includeSize)
|
||||
{
|
||||
int baseSize = (includeSize)
|
||||
? 5 // size bytes + terminator
|
||||
: 1; // terminator
|
||||
|
||||
return baseSize + stringByteCount;
|
||||
}
|
||||
|
||||
private int CalculateSize(JToken t)
|
||||
{
|
||||
switch (t.Type)
|
||||
{
|
||||
case JTokenType.Object:
|
||||
{
|
||||
int bases = 4;
|
||||
foreach (JProperty p in t)
|
||||
{
|
||||
bases += CalculateSize(p);
|
||||
}
|
||||
bases += 1;
|
||||
return bases;
|
||||
}
|
||||
case JTokenType.Array:
|
||||
{
|
||||
int bases = 4;
|
||||
int index = 0;
|
||||
foreach (JToken c in t)
|
||||
{
|
||||
bases += 1;
|
||||
bases += CalculateSize(index.ToString(CultureInfo.InvariantCulture));
|
||||
bases += CalculateSize(c);
|
||||
index++;
|
||||
}
|
||||
bases += 1;
|
||||
return bases;
|
||||
}
|
||||
case JTokenType.Property:
|
||||
JProperty property = (JProperty)t;
|
||||
int ss = 1;
|
||||
ss += CalculateSize(property.Name);
|
||||
ss += CalculateSize(property.Value);
|
||||
return ss;
|
||||
case JTokenType.Integer:
|
||||
return 4;
|
||||
case JTokenType.Float:
|
||||
return 8;
|
||||
case JTokenType.String:
|
||||
string s = (string)t;
|
||||
|
||||
return CalculateSizeWithLength(s, true);
|
||||
case JTokenType.Boolean:
|
||||
return 1;
|
||||
case JTokenType.Null:
|
||||
case JTokenType.Undefined:
|
||||
return 0;
|
||||
case JTokenType.Date:
|
||||
return 8;
|
||||
case JTokenType.Bytes:
|
||||
byte[] data = (byte[])t;
|
||||
return 4 + 1 + data.Length;
|
||||
case JTokenType.Raw:
|
||||
// hacky
|
||||
return 12;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("t", "Unexpected token when writing BSON: {0}".FormatWith(CultureInfo.InvariantCulture, t.Type));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the end.
|
||||
/// </summary>
|
||||
@@ -281,10 +169,11 @@ namespace Newtonsoft.Json.Bson
|
||||
protected override void WriteEnd(JsonToken token)
|
||||
{
|
||||
base.WriteEnd(token);
|
||||
RemoveParent();
|
||||
|
||||
if (Top == 0)
|
||||
{
|
||||
WriteToken(Token);
|
||||
_writer.WriteToken(_root);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,7 +226,269 @@ namespace Newtonsoft.Json.Bson
|
||||
|
||||
// hack to update the writer state
|
||||
AutoComplete(JsonToken.Undefined);
|
||||
AddValue(new JRaw(value), JsonToken.Raw);
|
||||
AddValue(value, BsonType.Oid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the beginning of a Json array.
|
||||
/// </summary>
|
||||
public override void WriteStartArray()
|
||||
{
|
||||
base.WriteStartArray();
|
||||
|
||||
AddParent(new BsonArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the beginning of a Json object.
|
||||
/// </summary>
|
||||
public override void WriteStartObject()
|
||||
{
|
||||
base.WriteStartObject();
|
||||
|
||||
AddParent(new BsonObject());
|
||||
}
|
||||
|
||||
public override void WritePropertyName(string name)
|
||||
{
|
||||
base.WritePropertyName(name);
|
||||
|
||||
_propertyName = name;
|
||||
}
|
||||
|
||||
private void AddParent(BsonToken container)
|
||||
{
|
||||
AddToken(container);
|
||||
_parent = container;
|
||||
}
|
||||
|
||||
private void RemoveParent()
|
||||
{
|
||||
_parent = _parent.Parent;
|
||||
}
|
||||
|
||||
private void AddValue(object value, BsonType type)
|
||||
{
|
||||
AddToken(new BsonValue(value, type));
|
||||
}
|
||||
|
||||
internal void AddToken(BsonToken token)
|
||||
{
|
||||
if (_parent != null)
|
||||
{
|
||||
if (_parent is BsonObject)
|
||||
{
|
||||
((BsonObject)_parent).Add(_propertyName, token);
|
||||
_propertyName = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
((BsonArray)_parent).Add(token);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_parent = token;
|
||||
_root = token;
|
||||
}
|
||||
}
|
||||
|
||||
#region WriteValue methods
|
||||
/// <summary>
|
||||
/// Writes a null value.
|
||||
/// </summary>
|
||||
public override void WriteNull()
|
||||
{
|
||||
base.WriteNull();
|
||||
AddValue(null, BsonType.Null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an undefined value.
|
||||
/// </summary>
|
||||
public override void WriteUndefined()
|
||||
{
|
||||
base.WriteUndefined();
|
||||
AddValue(null, BsonType.Undefined);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a <see cref="String"/> value.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="String"/> value to write.</param>
|
||||
public override void WriteValue(string value)
|
||||
{
|
||||
base.WriteValue(value);
|
||||
AddToken(new BsonString(value ?? string.Empty, true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a <see cref="Int32"/> value.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="Int32"/> value to write.</param>
|
||||
public override void WriteValue(int value)
|
||||
{
|
||||
base.WriteValue(value);
|
||||
AddValue(value, BsonType.Integer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a <see cref="UInt32"/> value.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="UInt32"/> value to write.</param>
|
||||
public override void WriteValue(uint value)
|
||||
{
|
||||
if (value > int.MaxValue)
|
||||
throw new JsonWriterException("Value is too large to fit in a signed 32 bit integer. BSON does not support unsigned values.");
|
||||
|
||||
base.WriteValue(value);
|
||||
AddValue(value, BsonType.Integer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a <see cref="Int64"/> value.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="Int64"/> value to write.</param>
|
||||
public override void WriteValue(long value)
|
||||
{
|
||||
base.WriteValue(value);
|
||||
AddValue(value, BsonType.Long);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a <see cref="UInt64"/> value.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="UInt64"/> value to write.</param>
|
||||
public override void WriteValue(ulong value)
|
||||
{
|
||||
if (value > long.MaxValue)
|
||||
throw new JsonWriterException("Value is too large to fit in a signed 64 bit integer. BSON does not support unsigned values.");
|
||||
|
||||
base.WriteValue(value);
|
||||
AddValue(value, BsonType.Long);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a <see cref="Single"/> value.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="Single"/> value to write.</param>
|
||||
public override void WriteValue(float value)
|
||||
{
|
||||
base.WriteValue(value);
|
||||
AddValue(value, BsonType.Number);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a <see cref="Double"/> value.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="Double"/> value to write.</param>
|
||||
public override void WriteValue(double value)
|
||||
{
|
||||
base.WriteValue(value);
|
||||
AddValue(value, BsonType.Number);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a <see cref="Boolean"/> value.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="Boolean"/> value to write.</param>
|
||||
public override void WriteValue(bool value)
|
||||
{
|
||||
base.WriteValue(value);
|
||||
AddValue(value, BsonType.Boolean);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a <see cref="Int16"/> value.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="Int16"/> value to write.</param>
|
||||
public override void WriteValue(short value)
|
||||
{
|
||||
base.WriteValue(value);
|
||||
AddValue(value, BsonType.Integer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a <see cref="UInt16"/> value.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="UInt16"/> value to write.</param>
|
||||
public override void WriteValue(ushort value)
|
||||
{
|
||||
base.WriteValue(value);
|
||||
AddValue(value, BsonType.Integer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a <see cref="Char"/> value.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="Char"/> value to write.</param>
|
||||
public override void WriteValue(char value)
|
||||
{
|
||||
base.WriteValue(value);
|
||||
AddToken(new BsonString(value.ToString(), true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a <see cref="Byte"/> value.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="Byte"/> value to write.</param>
|
||||
public override void WriteValue(byte value)
|
||||
{
|
||||
base.WriteValue(value);
|
||||
AddValue(value, BsonType.Integer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a <see cref="SByte"/> value.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="SByte"/> value to write.</param>
|
||||
public override void WriteValue(sbyte value)
|
||||
{
|
||||
base.WriteValue(value);
|
||||
AddValue(value, BsonType.Integer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a <see cref="Decimal"/> value.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="Decimal"/> value to write.</param>
|
||||
public override void WriteValue(decimal value)
|
||||
{
|
||||
base.WriteValue(value);
|
||||
AddValue(value, BsonType.Number);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a <see cref="DateTime"/> value.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="DateTime"/> value to write.</param>
|
||||
public override void WriteValue(DateTime value)
|
||||
{
|
||||
base.WriteValue(value);
|
||||
AddValue(value, BsonType.Date);
|
||||
}
|
||||
|
||||
#if !PocketPC && !NET20
|
||||
/// <summary>
|
||||
/// Writes a <see cref="DateTimeOffset"/> value.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="DateTimeOffset"/> value to write.</param>
|
||||
public override void WriteValue(DateTimeOffset value)
|
||||
{
|
||||
base.WriteValue(value);
|
||||
AddValue(value, BsonType.Date);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Writes a <see cref="T:Byte[]"/> value.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="T:Byte[]"/> value to write.</param>
|
||||
public override void WriteValue(byte[] value)
|
||||
{
|
||||
base.WriteValue(value);
|
||||
AddValue(value, BsonType.Binary);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Bson\BsonBinaryType.cs" />
|
||||
<Compile Include="Bson\BsonBinaryWriter.cs" />
|
||||
<Compile Include="Bson\BsonObjectId.cs" />
|
||||
<Compile Include="Bson\BsonReader.cs" />
|
||||
<Compile Include="Bson\BsonType.cs" />
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Bson\BsonBinaryType.cs" />
|
||||
<Compile Include="Bson\BsonBinaryWriter.cs" />
|
||||
<Compile Include="Bson\BsonObjectId.cs" />
|
||||
<Compile Include="Bson\BsonReader.cs" />
|
||||
<Compile Include="Bson\BsonType.cs" />
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Bson\BsonBinaryType.cs" />
|
||||
<Compile Include="Bson\BsonBinaryWriter.cs" />
|
||||
<Compile Include="Bson\BsonObjectId.cs" />
|
||||
<Compile Include="Bson\BsonReader.cs" />
|
||||
<Compile Include="Bson\BsonType.cs" />
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Bson\BsonBinaryType.cs" />
|
||||
<Compile Include="Bson\BsonBinaryWriter.cs" />
|
||||
<Compile Include="Bson\BsonReader.cs" />
|
||||
<Compile Include="Bson\BsonType.cs" />
|
||||
<Compile Include="Bson\BsonWriter.cs" />
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json.Utilities;
|
||||
using System.Globalization;
|
||||
@@ -43,7 +44,13 @@ namespace Newtonsoft.Json.Serialization
|
||||
|
||||
int IEqualityComparer<object>.GetHashCode(object obj)
|
||||
{
|
||||
#if !PocketPC
|
||||
// put objects in a bucket based on their reference
|
||||
return RuntimeHelpers.GetHashCode(obj);
|
||||
#else
|
||||
// put all objects in the same bucket so ReferenceEquals is called on all
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -199,15 +199,18 @@ namespace Newtonsoft.Json.Serialization
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckForCircularReference(memberValue, property.ReferenceLoopHandling))
|
||||
if (!CheckForCircularReference(memberValue, property.ReferenceLoopHandling, contract))
|
||||
return;
|
||||
|
||||
writer.WritePropertyName(propertyName);
|
||||
SerializeValue(writer, memberValue, memberConverter, contract);
|
||||
}
|
||||
|
||||
private bool CheckForCircularReference(object value, ReferenceLoopHandling? referenceLoopHandling)
|
||||
private bool CheckForCircularReference(object value, ReferenceLoopHandling? referenceLoopHandling, JsonContract contract)
|
||||
{
|
||||
if (value == null || contract is JsonPrimitiveContract)
|
||||
return true;
|
||||
|
||||
if (SerializeStack.IndexOf(value) != -1)
|
||||
{
|
||||
switch (referenceLoopHandling.GetValueOrDefault(Serializer.ReferenceLoopHandling))
|
||||
@@ -358,7 +361,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!CheckForCircularReference(value, null))
|
||||
if (!CheckForCircularReference(value, null, contract))
|
||||
return;
|
||||
|
||||
SerializeStack.Add(value);
|
||||
@@ -416,7 +419,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!CheckForCircularReference(value, null))
|
||||
if (!CheckForCircularReference(value, null, contract))
|
||||
continue;
|
||||
|
||||
SerializeValue(writer, value, null, valueContract);
|
||||
@@ -503,7 +506,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!CheckForCircularReference(value, null))
|
||||
if (!CheckForCircularReference(value, null, contract))
|
||||
continue;
|
||||
|
||||
writer.WritePropertyName(propertyName);
|
||||
|
||||
@@ -31,6 +31,17 @@ namespace Newtonsoft.Json.Utilities
|
||||
{
|
||||
internal class MathUtils
|
||||
{
|
||||
public static int IntLength(int i)
|
||||
{
|
||||
if (i < 0)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
if (i == 0)
|
||||
return 1;
|
||||
|
||||
return (int)Math.Floor(Math.Log10(i)) + 1;
|
||||
}
|
||||
|
||||
public static int HexToInt(char h)
|
||||
{
|
||||
if ((h >= '0') && (h <= '9'))
|
||||
|
||||
Reference in New Issue
Block a user