-New improved BSON writer

-Performance improvement for reference loop checking
This commit is contained in:
JamesNK
2010-03-24 10:43:41 +00:00
parent 064e5cb8ed
commit 6ce972156d
13 changed files with 806 additions and 240 deletions
@@ -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));
}
}
}
}
+8 -8
View File
@@ -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;
+372 -221
View File
@@ -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'))