diff --git a/Ejdb.BSON/BSONArray.cs b/Ejdb.BSON/BSONArray.cs
index b45ab02..b195227 100644
--- a/Ejdb.BSON/BSONArray.cs
+++ b/Ejdb.BSON/BSONArray.cs
@@ -14,238 +14,101 @@
// Boston, MA 02111-1307 USA.
// ============================================================================================
using System;
-using System.IO;
+using System.Collections;
+using System.Globalization;
namespace Ejdb.BSON {
[Serializable]
- public class BSONArray : BSONDocument {
-
- public override BSONType BSONType {
- get {
- return BSONType.ARRAY;
- }
- }
-
- public object this[int key] {
- get {
- return GetObjectValue(key.ToString());
- }
- }
-
- public BSONArray() {
- }
-
- public BSONArray(BSONUndefined[] arr) {
- for (var i = 0; i < arr.Length; ++i) {
- SetUndefined(i);
- }
- }
-
- public BSONArray(BSONull[] arr) {
- for (var i = 0; i < arr.Length; ++i) {
- SetNull(i);
- }
- }
-
- public BSONArray(ushort[] arr) {
- for (var i = 0; i < arr.Length; ++i) {
- SetNumber(i, (int) arr[i]);
- }
- }
-
- public BSONArray(uint[] arr) {
- for (var i = 0; i < arr.Length; ++i) {
- SetNumber(i, (long) arr[i]);
- }
- }
-
- public BSONArray(ulong[] arr) {
- for (var i = 0; i < arr.Length; ++i) {
- SetNumber(i, (long) arr[i]);
- }
- }
-
- public BSONArray(short[] arr) {
- for (var i = 0; i < arr.Length; ++i) {
- SetNumber(i, (int) arr[i]);
- }
- }
-
- public BSONArray(string[] arr) {
- for (var i = 0; i < arr.Length; ++i) {
- SetString(i, arr[i]);
- }
- }
-
- public BSONArray(int[] arr) {
- for (var i = 0; i < arr.Length; ++i) {
- SetNumber(i, arr[i]);
- }
- }
-
- public BSONArray(long[] arr) {
- for (var i = 0; i < arr.Length; ++i) {
- SetNumber(i, arr[i]);
- }
- }
-
- public BSONArray(float[] arr) {
- for (var i = 0; i < arr.Length; ++i) {
- SetNumber(i, arr[i]);
- }
- }
-
- public BSONArray(double[] arr) {
- for (var i = 0; i < arr.Length; ++i) {
- SetNumber(i, arr[i]);
- }
- }
-
- public BSONArray(bool[] arr) {
- for (var i = 0; i < arr.Length; ++i) {
- SetBool(i, arr[i]);
- }
- }
-
- public BSONArray(BSONOid[] arr) {
- for (var i = 0; i < arr.Length; ++i) {
- SetOID(i, arr[i]);
- }
- }
-
- public BSONArray(DateTime[] arr) {
- for (var i = 0; i < arr.Length; ++i) {
- SetDate(i, arr[i]);
- }
- }
-
- public BSONArray(BSONDocument[] arr) {
- for (var i = 0; i < arr.Length; ++i) {
- SetObject(i, arr[i]);
- }
- }
-
- public BSONArray(BSONArray[] arr) {
- for (var i = 0; i < arr.Length; ++i) {
- SetArray(i, arr[i]);
- }
- }
-
- public BSONArray(BSONRegexp[] arr) {
- for (var i = 0; i < arr.Length; ++i) {
- SetRegexp(i, arr[i]);
- }
- }
-
- public BSONArray(BSONTimestamp[] arr) {
- for (var i = 0; i < arr.Length; ++i) {
- SetTimestamp(i, arr[i]);
- }
- }
-
- public BSONArray(BSONCodeWScope[] arr) {
- for (var i = 0; i < arr.Length; ++i) {
- SetCodeWScope(i, arr[i]);
- }
- }
-
- public BSONArray(BSONBinData[] arr) {
- for (var i = 0; i < arr.Length; ++i) {
- SetBinData(i, arr[i]);
- }
- }
-
- public BSONDocument SetNull(int idx) {
- return base.SetNull(idx.ToString());
- }
-
- public BSONDocument SetUndefined(int idx) {
- return base.SetUndefined(idx.ToString());
- }
-
- public BSONDocument SetMaxKey(int idx) {
- return base.SetMaxKey(idx.ToString());
- }
-
- public BSONDocument SetMinKey(int idx) {
- return base.SetMinKey(idx.ToString());
- }
-
- public BSONDocument SetOID(int idx, string oid) {
- return base.SetOID(idx.ToString(), oid);
- }
-
- public BSONDocument SetOID(int idx, BSONOid oid) {
- return base.SetOID(idx.ToString(), oid);
- }
-
- public BSONDocument SetBool(int idx, bool val) {
- return base.SetBool(idx.ToString(), val);
- }
-
- public BSONDocument SetNumber(int idx, int val) {
- return base.SetNumber(idx.ToString(), val);
- }
-
- public BSONDocument SetNumber(int idx, long val) {
- return base.SetNumber(idx.ToString(), val);
- }
-
- public BSONDocument SetNumber(int idx, double val) {
- return base.SetNumber(idx.ToString(), val);
- }
-
- public BSONDocument SetNumber(int idx, float val) {
- return base.SetNumber(idx.ToString(), val);
- }
-
- public BSONDocument SetString(int idx, string val) {
- return base.SetString(idx.ToString(), val);
- }
-
- public BSONDocument SetCode(int idx, string val) {
- return base.SetCode(idx.ToString(), val);
- }
-
- public BSONDocument SetSymbol(int idx, string val) {
- return base.SetSymbol(idx.ToString(), val);
- }
-
- public BSONDocument SetDate(int idx, DateTime val) {
- return base.SetDate(idx.ToString(), val);
- }
-
- public BSONDocument SetRegexp(int idx, BSONRegexp val) {
- return base.SetRegexp(idx.ToString(), val);
- }
-
- public BSONDocument SetBinData(int idx, BSONBinData val) {
- return base.SetBinData(idx.ToString(), val);
- }
-
- public BSONDocument SetObject(int idx, BSONDocument val) {
- return base.SetDocument(idx.ToString(), val);
- }
-
- public BSONDocument SetArray(int idx, BSONArray val) {
- return base.SetArray(idx.ToString(), val);
- }
-
- public BSONDocument SetTimestamp(int idx, BSONTimestamp val) {
- return base.SetTimestamp(idx.ToString(), val);
- }
-
- public BSONDocument SetCodeWScope(int idx, BSONCodeWScope val) {
- return base.SetCodeWScope(idx.ToString(), val);
- }
-
- protected override void CheckKey(string key) {
- int idx;
- if (key == null || !int.TryParse(key, out idx) || idx < 0) {
- throw new InvalidBSONDataException(string.Format("Invalid array key: {0}", key));
- }
- }
+ public class BsonArray : BsonDocument {
+
+
+ public BsonArray()
+ {
+ }
+
+ public BsonArray(BsonDocument document)
+ {
+ var iterator = new BsonIterator(document);
+ while (iterator.Next() != BsonType.EOO)
+ Add(iterator.CurrentKey, iterator.FetchCurrentValue());
+ }
+
+ public BsonArray(IEnumerable objects)
+ {
+ AddRange(objects);
+ }
+
+ public override BsonType BSONType
+ {
+ get
+ {
+ return BsonType.ARRAY;
+ }
+ }
+
+ public object this[int key]
+ {
+ get { return GetObjectValue(key.ToString()); }
+ }
+
+ public void SetMaxKey(int idx)
+ {
+ Add(BsonValue.GetMaxKey());
+ }
+
+ public void SetMinKey(int idx)
+ {
+ Add(BsonValue.GetMinKey());
+ }
+
+ ///
+ /// Adds multiple elements to the array.
+ ///
+ /// A list of values to add to the array.
+ /// The array (so method calls can be chained).
+ public BsonArray AddRange(IEnumerable values)
+ {
+ if (values != null)
+ {
+ foreach (var value in values)
+ Add(BsonValue.ValueOf(value));
+ }
+
+ return this;
+ }
+
+ ///
+ /// Adds an element to the array.
+ ///
+ /// The value to add to the array.
+ /// The array (so method calls can be chained).
+ public BsonArray Add(BsonValue value)
+ {
+ if (value != null)
+ {
+ var key = Keys.Count.ToString(CultureInfo.InvariantCulture);
+ Add(key, value);
+ }
+
+ return this;
+ }
+
+ public BsonArray Add(object value)
+ {
+ if (value != null)
+ {
+ var bsonValue = BsonValue.ValueOf(value);
+ return Add(bsonValue);
+ }
+
+ return this;
+ }
+
+ public int Count
+ {
+ get { return KeysCount; }
+ }
}
}
diff --git a/Ejdb.BSON/BSONBinData.cs b/Ejdb.BSON/BSONBinData.cs
index 7aa9e45..ae836fc 100644
--- a/Ejdb.BSON/BSONBinData.cs
+++ b/Ejdb.BSON/BSONBinData.cs
@@ -19,7 +19,7 @@
namespace Ejdb.BSON {
[Serializable]
- public sealed class BSONBinData {
+ public sealed class BsonBinData {
readonly byte _subtype;
readonly byte[] _data;
@@ -35,13 +35,13 @@ public byte[] Data {
}
}
- public BSONBinData(byte subtype, byte[] data) {
+ public BsonBinData(byte subtype, byte[] data) {
_subtype = subtype;
_data = new byte[data.Length];
Array.Copy(data, _data, data.Length);
}
- internal BSONBinData(byte subtype, int len, BinaryReader input) {
+ internal BsonBinData(byte subtype, int len, BinaryReader input) {
_subtype = subtype;
_data = input.ReadBytes(len);
}
diff --git a/Ejdb.BSON/BSONCodeWScope.cs b/Ejdb.BSON/BSONCodeWScope.cs
index 8ece275..3f26129 100644
--- a/Ejdb.BSON/BSONCodeWScope.cs
+++ b/Ejdb.BSON/BSONCodeWScope.cs
@@ -18,13 +18,13 @@
namespace Ejdb.BSON {
[Serializable]
- public sealed class BSONCodeWScope : BSONDocument {
+ public sealed class BsonCodeWScope : BsonDocument {
readonly string _code;
- public override BSONType BSONType {
+ public override BsonType BSONType {
get {
- return BSONType.CODEWSCOPE;
+ return BsonType.CODEWSCOPE;
}
}
@@ -34,13 +34,13 @@ public string Code {
}
}
- public BSONDocument Scope {
+ public BsonDocument Scope {
get {
return this;
}
}
- public BSONCodeWScope(string code) {
+ public BsonCodeWScope(string code) {
this._code = code;
}
@@ -51,10 +51,10 @@ public override bool Equals(object obj) {
if (ReferenceEquals(this, obj)) {
return true;
}
- if (!(obj is BSONCodeWScope)) {
+ if (!(obj is BsonCodeWScope)) {
return false;
}
- BSONCodeWScope cw = (BSONCodeWScope) obj;
+ BsonCodeWScope cw = (BsonCodeWScope) obj;
if (_code != cw._code) {
return false;
}
diff --git a/Ejdb.BSON/BSONConstants.cs b/Ejdb.BSON/BSONConstants.cs
index a686af2..38e9ed6 100644
--- a/Ejdb.BSON/BSONConstants.cs
+++ b/Ejdb.BSON/BSONConstants.cs
@@ -20,9 +20,10 @@ namespace Ejdb.BSON {
///
/// Various BSON processing constants and shared values.
///
- public static class BSONConstants {
+ public static class BsonConstants {
- static BSONConstants() {
+ static BsonConstants()
+ {
Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
}
@@ -30,6 +31,32 @@ static BSONConstants() {
/// Gets or sets the epoch.
///
public static DateTime Epoch { get; private set; }
+
+ ///
+ /// The name of the id field
+ ///
+ public const string Id = "_id";
+
+ ///
+ /// Converts a DateTime to UTC (with special handling for MinValue and MaxValue).
+ ///
+ /// A DateTime.
+ /// The DateTime in UTC.
+ public static DateTime ToUniversalTime(DateTime dateTime)
+ {
+ if (dateTime == DateTime.MinValue)
+ {
+ return DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
+ }
+ else if (dateTime == DateTime.MaxValue)
+ {
+ return DateTime.SpecifyKind(DateTime.MaxValue, DateTimeKind.Utc);
+ }
+ else
+ {
+ return dateTime.ToUniversalTime();
+ }
+ }
}
}
diff --git a/Ejdb.BSON/BSONDocument.cs b/Ejdb.BSON/BSONDocument.cs
index 14ed3ae..5e77a9d 100644
--- a/Ejdb.BSON/BSONDocument.cs
+++ b/Ejdb.BSON/BSONDocument.cs
@@ -16,12 +16,7 @@
using System;
using System.Collections.Generic;
using System.IO;
-using System.Text;
-using System.Diagnostics;
using Ejdb.IO;
-using Ejdb.BSON;
-using Ejdb.Utils;
-using System.Reflection;
using System.Linq;
namespace Ejdb.BSON {
@@ -30,82 +25,34 @@ namespace Ejdb.BSON {
/// BSON document deserialized data wrapper.
///
[Serializable]
- public class BSONDocument : IBSONValue, IEnumerable, ICloneable {
- static Dictionary> TYPE_SETTERS =
- new Dictionary> {
- {typeof(bool), (d, k, v) => d.SetBool(k, (bool) v)},
- {typeof(bool[]), (d, k, v) => d.SetArray(k, new BSONArray((bool[]) v))},
- {typeof(byte), (d, k, v) => d.SetNumber(k, (int) v)},
- {typeof(sbyte), (d, k, v) => d.SetNumber(k, (int) v)},
- {typeof(ushort), (d, k, v) => d.SetNumber(k, (int) v)},
- {typeof(ushort[]), (d, k, v) => d.SetArray(k, new BSONArray((ushort[]) v))},
- {typeof(short), (d, k, v) => d.SetNumber(k, (int) v)},
- {typeof(short[]), (d, k, v) => d.SetArray(k, new BSONArray((short[]) v))},
- {typeof(uint), (d, k, v) => d.SetNumber(k, (int) v)},
- {typeof(uint[]), (d, k, v) => d.SetArray(k, new BSONArray((uint[]) v))},
- {typeof(int), (d, k, v) => d.SetNumber(k, (int) v)},
- {typeof(int[]), (d, k, v) => d.SetArray(k, new BSONArray((int[]) v))},
- {typeof(ulong), (d, k, v) => d.SetNumber(k, (long) v)},
- {typeof(ulong[]), (d, k, v) => d.SetArray(k, new BSONArray((ulong[]) v))},
- {typeof(long), (d, k, v) => d.SetNumber(k, (long) v)},
- {typeof(long[]), (d, k, v) => d.SetArray(k, new BSONArray((long[]) v))},
- {typeof(float), (d, k, v) => d.SetNumber(k, (float) v)},
- {typeof(float[]), (d, k, v) => d.SetArray(k, new BSONArray((float[]) v))},
- {typeof(double), (d, k, v) => d.SetNumber(k, (double) v)},
- {typeof(double[]), (d, k, v) => d.SetArray(k, new BSONArray((double[]) v))},
- {typeof(char), (d, k, v) => d.SetString(k, v.ToString())},
- {typeof(string), (d, k, v) => d.SetString(k, (string) v)},
- {typeof(string[]), (d, k, v) => d.SetArray(k, new BSONArray((string[]) v))},
- {typeof(BSONOid), (d, k, v) => d.SetOID(k, (BSONOid) v)},
- {typeof(BSONOid[]), (d, k, v) => d.SetArray(k, new BSONArray((BSONOid[]) v))},
- {typeof(BSONRegexp), (d, k, v) => d.SetRegexp(k, (BSONRegexp) v)},
- {typeof(BSONRegexp[]), (d, k, v) => d.SetArray(k, new BSONArray((BSONRegexp[]) v))},
- {typeof(BSONValue), (d, k, v) => d.SetBSONValue((BSONValue) v)},
- {typeof(BSONTimestamp), (d, k, v) => d.SetTimestamp(k, (BSONTimestamp) v)},
- {typeof(BSONTimestamp[]), (d, k, v) => d.SetArray(k, new BSONArray((BSONTimestamp[]) v))},
- {typeof(BSONCodeWScope), (d, k, v) => d.SetCodeWScope(k, (BSONCodeWScope) v)},
- {typeof(BSONCodeWScope[]), (d, k, v) => d.SetArray(k, new BSONArray((BSONCodeWScope[]) v))},
- {typeof(BSONBinData), (d, k, v) => d.SetBinData(k, (BSONBinData) v)},
- {typeof(BSONBinData[]), (d, k, v) => d.SetArray(k, new BSONArray((BSONBinData[]) v))},
- {typeof(BSONDocument), (d, k, v) => d.SetDocument(k, (BSONDocument) v)},
- {typeof(BSONDocument[]), (d, k, v) => d.SetArray(k, new BSONArray((BSONDocument[]) v))},
- {typeof(BSONArray), (d, k, v) => d.SetArray(k, (BSONArray) v)},
- {typeof(BSONArray[]), (d, k, v) => d.SetArray(k, new BSONArray((BSONArray[]) v))},
- {typeof(DateTime), (d, k, v) => d.SetDate(k, (DateTime) v)},
- {typeof(DateTime[]), (d, k, v) => d.SetArray(k, new BSONArray((DateTime[]) v))},
- {typeof(BSONUndefined), (d, k, v) => d.SetUndefined(k)},
- {typeof(BSONUndefined[]), (d, k, v) => d.SetArray(k, new BSONArray((BSONUndefined[]) v))},
- {typeof(BSONull), (d, k, v) => d.SetNull(k)},
- {typeof(BSONull[]), (d, k, v) => d.SetArray(k, new BSONArray((BSONull[]) v))}
- };
-
- readonly List _fieldslist;
-
+ public class BsonDocument : IBsonValue, IEnumerable, ICloneable
+ {
[NonSerializedAttribute]
- Dictionary _fields;
+ Dictionary _fields;
[NonSerializedAttribute]
- int? _cachedhash;
+ private int? _cachedhash;
///
/// BSON Type this document.
///
///
- /// Type can be either or
+ /// Type can be either or
///
/// The type of the BSON.
- public virtual BSONType BSONType {
+ public virtual BsonType BSONType {
get {
- return BSONType.OBJECT;
+ return BsonType.OBJECT;
}
}
///
/// Gets the document keys.
///
- public ICollection Keys {
- get {
- CheckFields();
+ public ICollection Keys
+ {
+ get
+ {
return _fields.Keys;
}
}
@@ -115,41 +62,54 @@ public ICollection Keys {
///
public int KeysCount {
get {
- return _fieldslist.Count;
+ return _fields.Count;
}
}
- public BSONDocument() {
- this._fields = null;
- this._fieldslist = new List();
+ public BsonDocument() {
+ _fields = new Dictionary();
}
- public BSONDocument(BSONIterator it) : this() {
- while (it.Next() != BSONType.EOO) {
- Add(it.FetchCurrentValue());
+ public BsonDocument(BsonIterator it)
+ : this()
+ {
+ while (it.Next() != BsonType.EOO)
+ {
+ var value = it.FetchCurrentValue();
+ Add(it.CurrentKey, value);
}
}
- public BSONDocument(BSONIterator it, string[] fields) : this() {
+ ///
+ /// Initializes a new instance of the BsonDocument class and adds new elements from a dictionary of key/value pairs.
+ ///
+ /// A dictionary to initialize the document from.
+ public BsonDocument(Dictionary dictionary)
+ {
+ foreach (var entry in dictionary)
+ Add(entry.Key, BsonValue.ValueOf(entry.Value));
+ }
+
+ public BsonDocument(BsonIterator it, string[] fields) : this() {
Array.Sort(fields);
- BSONType bt;
+ BsonType bt;
int ind = -1;
int nfc = 0;
- foreach (string f in fields) {
- if (f != null) {
+ foreach (string f in fields)
+ {
+ if (f != null)
nfc++;
- }
}
- while ((bt = it.Next()) != BSONType.EOO) {
+ while ((bt = it.Next()) != BsonType.EOO) {
if (nfc < 1) {
continue;
}
string kk = it.CurrentKey;
if ((ind = Array.IndexOf(fields, kk)) != -1) {
- Add(it.FetchCurrentValue());
+ Add(it.CurrentKey, it.FetchCurrentValue());
fields[ind] = null;
nfc--;
- } else if (bt == BSONType.OBJECT || bt == BSONType.ARRAY) {
+ } else if (bt == BsonType.OBJECT || bt == BsonType.ARRAY) {
string[] narr = null;
for (var i = 0; i < fields.Length; ++i) {
var f = fields[i];
@@ -168,10 +128,10 @@ public BSONDocument(BSONIterator it, string[] fields) : this() {
}
}
if (narr != null) {
- BSONIterator nit = new BSONIterator(it);
- BSONDocument ndoc = new BSONDocument(nit, narr);
+ BsonIterator nit = new BsonIterator(it);
+ BsonDocument ndoc = new BsonDocument(nit, narr);
if (ndoc.KeysCount > 0) {
- Add(new BSONValue(bt, kk, ndoc));
+ Add(kk, new BsonValue(bt, ndoc));
}
}
}
@@ -179,33 +139,40 @@ public BSONDocument(BSONIterator it, string[] fields) : this() {
it.Dispose();
}
- public BSONDocument(byte[] bsdata) : this() {
- using (BSONIterator it = new BSONIterator(bsdata)) {
- while (it.Next() != BSONType.EOO) {
- Add(it.FetchCurrentValue());
- }
- }
+ public BsonDocument(byte[] bsdata)
+ : this()
+ {
+ using (var iterator = new BsonIterator(bsdata))
+ {
+ while (iterator.Next() != BsonType.EOO)
+ Add(iterator.CurrentKey, iterator.FetchCurrentValue());
}
-
- public BSONDocument(Stream bstream) : this() {
- using (BSONIterator it = new BSONIterator(bstream)) {
- while (it.Next() != BSONType.EOO) {
- Add(it.FetchCurrentValue());
- }
- }
}
- public BSONDocument(BSONDocument doc) : this() {
- foreach (var bv in doc) {
- Add((BSONValue) bv.Clone());
- }
+ public BsonDocument(Stream bstream)
+ : this()
+ {
+ using (var iterator = new BsonIterator(bstream))
+ {
+ while (iterator.Next() != BsonType.EOO)
+ Add(iterator.CurrentKey, iterator.FetchCurrentValue());
}
-
- public IEnumerator GetEnumerator() {
- return _fieldslist.GetEnumerator();
}
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
+ public BsonDocument(BsonDocument doc)
+ : this()
+ {
+ foreach (var bv in doc._fields)
+ Add(bv.Key, (BsonValue) bv.Value.Clone());
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ foreach (var entry in _fields)
+ yield return new BsonValueWithKey(entry.Key, entry.Value, entry.Value.BSONType);
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return GetEnumerator();
}
@@ -213,16 +180,19 @@ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
/// Convert BSON document object into BSON binary data byte array.
///
/// The byte array.
- public byte[] ToByteArray() {
+ public byte[] ToByteArray()
+ {
byte[] res;
- using (var ms = new MemoryStream()) {
+ using (var ms = new MemoryStream())
+ {
Serialize(ms);
res = ms.ToArray();
}
return res;
}
- public string ToDebugDataString() {
+ public string ToDebugDataString()
+ {
return BitConverter.ToString(ToByteArray());
}
@@ -230,11 +200,10 @@ public string ToDebugDataString() {
/// Gets the field value.
///
/// The BSON value.
- ///
+ ///
/// Document field name
- public BSONValue GetBSONValue(string key) {
- CheckFields();
- BSONValue ov;
+ public BsonValue GetBsonValue(string key) {
+ BsonValue ov;
if (_fields.TryGetValue(key, out ov)) {
return ov;
} else {
@@ -249,10 +218,20 @@ public BSONValue GetBSONValue(string key) {
/// Hierarchical field paths are NOT supported. Use [] operator instead.
///
/// BSON document key
- ///
- public object GetObjectValue(string key) {
- var bv = GetBSONValue(key);
- return bv != null ? bv.Value : null;
+ ///
+ public object GetObjectValue(string key)
+ {
+ var bv = GetBsonValue(key);
+ return bv != null ? _UnwrapValue(bv.Value) : null;
+ }
+
+ private object _UnwrapValue(object value)
+ {
+ var array = value as BsonArray;
+ if (array == null)
+ return value;
+
+ return array.Select(x => _UnwrapValue(x.Value)).ToArray();
}
///
@@ -261,158 +240,47 @@ public object GetObjectValue(string key) {
/// true if this document has the specified key; otherwise, false.
/// Key.
public bool HasKey(string key) {
- return (GetBSONValue(key) != null);
+ return (GetBsonValue(key) != null);
}
///
- /// Gets the with the specified key.
+ /// Gets the with the specified key.
///
///
/// Getter for hierarchical field paths are supported.
///
/// Key.
/// Key object or null if the key is not exists or value type is either
- /// or
- public object this[string key] {
- get {
- int ind;
- if ((ind = key.IndexOf(".", StringComparison.Ordinal)) == -1) {
- return GetObjectValue(key);
- } else {
- string prefix = key.Substring(0, ind);
- BSONDocument doc = GetObjectValue(prefix) as BSONDocument;
- if (doc == null || key.Length < ind + 2) {
- return null;
- }
- return doc[key.Substring(ind + 1)];
+ /// or
+ public object this[string key]
+ {
+ get
+ {
+ int ind;
+ if ((ind = key.IndexOf(".", StringComparison.Ordinal)) == -1)
+ return GetObjectValue(key);
+
+ string prefix = key.Substring(0, ind);
+ var doc = GetObjectValue(prefix) as BsonDocument;
+ if (doc == null || key.Length < ind + 2) {
+ return null;
}
+ return doc[key.Substring(ind + 1)];
}
- set {
- object v = value;
- if (v == null) {
- SetNull(key);
- return;
- }
- Action setter;
- Type vtype = v.GetType();
- TYPE_SETTERS.TryGetValue(vtype, out setter);
- if (setter == null) {
- if (vtype.IsAnonymousType()) {
- setter = SetAnonType;
- } else {
- throw new Exception(string.Format("Unsupported value type: {0} for doc[key] assign operation", v.GetType()));
- }
- }
- setter(this, key, v);
+ set
+ {
+ var bsonValue = BsonValue.ValueOf(value);
+ Add(key, bsonValue);
}
}
- public static void SetAnonType(BSONDocument doc, string key, object val) {
- BSONDocument ndoc = (key == null) ? doc : new BSONDocument();
- Type vtype = val.GetType();
- foreach (PropertyInfo pi in vtype.GetProperties()) {
- if (!pi.CanRead) {
- continue;
- }
- ndoc[pi.Name] = pi.GetValue(val, null);
- }
- if (key != null) {
- doc.SetDocument(key, ndoc);
- }
- }
-
- public BSONDocument SetNull(string key) {
- return SetBSONValue(new BSONValue(BSONType.NULL, key));
- }
-
- public BSONDocument SetUndefined(string key) {
- return SetBSONValue(new BSONValue(BSONType.UNKNOWN, key));
- }
-
- public BSONDocument SetMaxKey(string key) {
- return SetBSONValue(new BSONValue(BSONType.MAXKEY, key));
- }
-
- public BSONDocument SetMinKey(string key) {
- return SetBSONValue(new BSONValue(BSONType.MINKEY, key));
- }
-
- public BSONDocument SetOID(string key, string oid) {
- return SetBSONValue(new BSONValue(BSONType.OID, key, new BSONOid(oid)));
- }
-
- public BSONDocument SetOID(string key, BSONOid oid) {
- return SetBSONValue(new BSONValue(BSONType.OID, key, oid));
- }
-
- public BSONDocument SetBool(string key, bool val) {
- return SetBSONValue(new BSONValue(BSONType.BOOL, key, val));
- }
-
- public BSONDocument SetNumber(string key, int val) {
- return SetBSONValue(new BSONValue(BSONType.INT, key, val));
- }
-
- public BSONDocument SetNumber(string key, long val) {
- return SetBSONValue(new BSONValue(BSONType.LONG, key, val));
- }
-
- public BSONDocument SetNumber(string key, double val) {
- return SetBSONValue(new BSONValue(BSONType.DOUBLE, key, val));
- }
-
- public BSONDocument SetNumber(string key, float val) {
- return SetBSONValue(new BSONValue(BSONType.DOUBLE, key, val));
- }
-
- public BSONDocument SetString(string key, string val) {
- return SetBSONValue(new BSONValue(BSONType.STRING, key, val));
- }
-
- public BSONDocument SetCode(string key, string val) {
- return SetBSONValue(new BSONValue(BSONType.CODE, key, val));
- }
-
- public BSONDocument SetSymbol(string key, string val) {
- return SetBSONValue(new BSONValue(BSONType.SYMBOL, key, val));
- }
-
- public BSONDocument SetDate(string key, DateTime val) {
- return SetBSONValue(new BSONValue(BSONType.DATE, key, val));
- }
-
- public BSONDocument SetRegexp(string key, BSONRegexp val) {
- return SetBSONValue(new BSONValue(BSONType.REGEX, key, val));
- }
-
- public BSONDocument SetBinData(string key, BSONBinData val) {
- return SetBSONValue(new BSONValue(BSONType.BINDATA, key, val));
- }
-
- public BSONDocument SetDocument(string key, BSONDocument val) {
- return SetBSONValue(new BSONValue(BSONType.OBJECT, key, val));
- }
-
- public BSONDocument SetArray(string key, BSONArray val) {
- return SetBSONValue(new BSONValue(BSONType.ARRAY, key, val));
- }
-
- public BSONDocument SetTimestamp(string key, BSONTimestamp val) {
- return SetBSONValue(new BSONValue(BSONType.TIMESTAMP, key, val));
- }
-
- public BSONDocument SetCodeWScope(string key, BSONCodeWScope val) {
- return SetBSONValue(new BSONValue(BSONType.CODEWSCOPE, key, val));
- }
-
- public BSONValue DropValue(string key) {
- var bv = GetBSONValue(key);
+ public BsonValue DropValue(string key) {
+ var bv = GetBsonValue(key);
if (bv == null) {
return bv;
}
_cachedhash = null;
_fields.Remove(key);
- _fieldslist.RemoveAll(x => x.Key == key);
return bv;
}
@@ -421,266 +289,257 @@ public BSONValue DropValue(string key) {
///
public void Clear() {
_cachedhash = null;
- _fieldslist.Clear();
- if (_fields != null) {
- _fields.Clear();
- _fields = null;
- }
- }
-
- public void Serialize(Stream os) {
- if (os.CanSeek) {
- long start = os.Position;
- os.Position += 4; //skip int32 document size
- using (var bw = new ExtBinaryWriter(os, Encoding.UTF8, true)) {
- foreach (BSONValue bv in _fieldslist) {
- WriteBSONValue(bv, bw);
- }
- bw.Write((byte) 0x00);
- long end = os.Position;
- os.Position = start;
- bw.Write((int) (end - start));
- os.Position = end; //go to the end
- }
- } else {
- byte[] darr;
- var ms = new MemoryStream();
- using (var bw = new ExtBinaryWriter(ms)) {
- foreach (BSONValue bv in _fieldslist) {
- WriteBSONValue(bv, bw);
- }
- darr = ms.ToArray();
- }
- using (var bw = new ExtBinaryWriter(os, Encoding.UTF8, true)) {
- bw.Write(darr.Length + 4/*doclen*/ + 1/*0x00*/);
- bw.Write(darr);
- bw.Write((byte) 0x00);
- }
- }
- os.Flush();
- }
-
- public override bool Equals(object obj) {
- if (obj == null) {
+ _fields.Clear();
+ }
+
+ public void Serialize(Stream stream)
+ {
+ if (stream.CanSeek)
+ {
+ _WriteToSeekableStream(stream);
+ }
+ else
+ {
+ var ms = new MemoryStream();
+ _WriteToSeekableStream(ms);
+ byte[] bytes = ms.ToArray();
+ stream.Write(bytes, 0, bytes.Length);
+ }
+ stream.Flush();
+ }
+
+ private void _WriteToSeekableStream(Stream stream)
+ {
+ long start = stream.Position;
+ stream.Position += 4; //skip int32 document size
+ using (var bw = new ExtBinaryWriter(stream, true))
+ {
+ foreach (var bv in _fields)
+ WriteBsonValue(bv.Key, bv.Value, bw);
+
+ bw.Write((byte) 0x00);
+ long end = stream.Position;
+ stream.Position = start;
+ bw.Write((int) (end - start));
+ stream.Position = end; //go to the end
+ }
+ }
+
+ public override bool Equals(object obj) {
+ if (obj == null)
return false;
- }
- if (ReferenceEquals(this, obj)) {
+
+ if (ReferenceEquals(this, obj))
return true;
- }
- if (!(obj is BSONDocument)) {
+
+ if (!(obj is BsonDocument))
return false;
- }
- BSONDocument d1 = this;
- BSONDocument d2 = ((BSONDocument) obj);
- if (d1.KeysCount != d2.KeysCount) {
+
+ var d1 = this;
+ var d2 = ((BsonDocument) obj);
+
+ if (d1.KeysCount != d2.KeysCount)
return false;
- }
- foreach (BSONValue bv1 in d1._fieldslist) {
- BSONValue bv2 = d2.GetBSONValue(bv1.Key);
- if (bv1 != bv2) {
+
+ foreach (var bv1 in d1._fields)
+ {
+ var bv2 = d2.GetBsonValue(bv1.Key);
+ if (bv1.Value != bv2)
return false;
- }
}
+
return true;
}
- public override int GetHashCode() {
- if (_cachedhash != null) {
+ public override int GetHashCode()
+ {
+ if (_cachedhash != null)
return (int) _cachedhash;
- }
- unchecked {
+
+ unchecked
+ {
int hash = 1;
- foreach (var bv in _fieldslist) {
- hash = (hash * 31) + bv.GetHashCode();
+ foreach (var bv in _fields) {
+ hash = (hash * 31) + bv.Key.GetHashCode() + bv.Value.GetHashCode();
}
_cachedhash = hash;
}
return (int) _cachedhash;
}
- public static bool operator ==(BSONDocument d1, BSONDocument d2) {
+ public static bool operator ==(BsonDocument d1, BsonDocument d2) {
return Equals(d1, d2);
}
- public static bool operator !=(BSONDocument d1, BSONDocument d2) {
+ public static bool operator !=(BsonDocument d1, BsonDocument d2) {
return !(d1 == d2);
}
- public static BSONDocument ValueOf(object val) {
- if (val == null) {
- return new BSONDocument();
- }
- Type vtype = val.GetType();
- if (val is BSONDocument) {
- return (BSONDocument) val;
- } else if (vtype == typeof(byte[])) {
- return new BSONDocument((byte[]) val);
- } else if (vtype.IsAnonymousType()) {
- BSONDocument doc = new BSONDocument();
- SetAnonType(doc, null, val);
- return doc;
- }
- throw new InvalidCastException(string.Format("Unsupported cast type: {0}", vtype));
+ public static BsonDocument ValueOf(object val)
+ {
+ if (val == null)
+ return new BsonDocument();
+
+ var vtype = val.GetType();
+
+ if (val is BsonDocument)
+ return (BsonDocument) val;
+
+ if (vtype == typeof (byte[]))
+ return new BsonDocument((byte[]) val);
+
+ return BsonValue.GetDocumentForCustomClassObject(val);
}
public object Clone() {
- return new BSONDocument(this);
+ return new BsonDocument(this);
}
- public override string ToString() {
+ public override string ToString()
+ {
return string.Format("[{0}: {1}]", GetType().Name,
- string.Join(", ", from bv in _fieldslist select bv.ToString()));
+ string.Join(", ", from bv in _fields select bv.Value.ToStringWithKey(bv.Key)));
}
//.//////////////////////////////////////////////////////////////////
// Private staff
//.//////////////////////////////////////////////////////////////////
- internal BSONDocument Add(BSONValue bv) {
- _cachedhash = null;
- _fieldslist.Add(bv);
- if (_fields != null) {
- _fields[bv.Key] = bv;
- }
- return this;
- }
- internal BSONDocument SetBSONValue(BSONValue val) {
+
+
+ public BsonDocument Add(string key, BsonValue val)
+ {
_cachedhash = null;
- CheckFields();
- if (val.BSONType == BSONType.STRING && val.Key == "_id") {
- val = new BSONValue(BSONType.OID, val.Key, new BSONOid((string) val.Value));
- }
- BSONValue ov;
- if (_fields.TryGetValue(val.Key, out ov)) {
- ov.Key = val.Key;
+
+ if (val.BSONType == BsonType.STRING && key == BsonConstants.Id)
+ val = new BsonValue(BsonType.OID, new BsonOid((string)val.Value));
+
+ BsonValue ov;
+ if (_fields.TryGetValue(key, out ov))
+ {
ov.BSONType = val.BSONType;
ov.Value = val.Value;
- } else {
- _fieldslist.Add(val);
- _fields.Add(val.Key, val);
}
- return this;
- }
+ else
+ _fields.Add(key, val);
- protected virtual void CheckKey(string key) {
- }
-
- protected void WriteBSONValue(BSONValue bv, ExtBinaryWriter bw) {
- BSONType bt = bv.BSONType;
- switch (bt) {
- case BSONType.EOO:
- break;
- case BSONType.NULL:
- case BSONType.UNDEFINED:
- case BSONType.MAXKEY:
- case BSONType.MINKEY:
- WriteTypeAndKey(bv, bw);
- break;
- case BSONType.OID:
- {
- WriteTypeAndKey(bv, bw);
- BSONOid oid = (BSONOid) bv.Value;
- Debug.Assert(oid._bytes.Length == 12);
- bw.Write(oid._bytes);
- break;
- }
- case BSONType.STRING:
- case BSONType.CODE:
- case BSONType.SYMBOL:
- WriteTypeAndKey(bv, bw);
- bw.WriteBSONString((string) bv.Value);
- break;
- case BSONType.BOOL:
- WriteTypeAndKey(bv, bw);
- bw.Write((bool) bv.Value);
- break;
- case BSONType.INT:
- WriteTypeAndKey(bv, bw);
- bw.Write((int) bv.Value);
- break;
- case BSONType.LONG:
- WriteTypeAndKey(bv, bw);
- bw.Write((long) bv.Value);
- break;
- case BSONType.ARRAY:
- case BSONType.OBJECT:
- {
- BSONDocument doc = (BSONDocument) bv.Value;
- WriteTypeAndKey(bv, bw);
- doc.Serialize(bw.BaseStream);
- break;
- }
- case BSONType.DATE:
- {
- DateTime dt = (DateTime) bv.Value;
- var diff = dt.ToLocalTime() - BSONConstants.Epoch;
- long time = (long) Math.Floor(diff.TotalMilliseconds);
- WriteTypeAndKey(bv, bw);
- bw.Write(time);
- break;
- }
- case BSONType.DOUBLE:
- WriteTypeAndKey(bv, bw);
- bw.Write((double) bv.Value);
- break;
- case BSONType.REGEX:
- {
- BSONRegexp rv = (BSONRegexp) bv.Value;
- WriteTypeAndKey(bv, bw);
- bw.WriteCString(rv.Re ?? "");
- bw.WriteCString(rv.Opts ?? "");
- break;
- }
- case BSONType.BINDATA:
- {
- BSONBinData bdata = (BSONBinData) bv.Value;
- WriteTypeAndKey(bv, bw);
- bw.Write(bdata.Data.Length);
- bw.Write(bdata.Subtype);
- bw.Write(bdata.Data);
- break;
- }
- case BSONType.DBREF:
- //Unsupported DBREF!
- break;
- case BSONType.TIMESTAMP:
- {
- BSONTimestamp ts = (BSONTimestamp) bv.Value;
- WriteTypeAndKey(bv, bw);
- bw.Write(ts.Inc);
- bw.Write(ts.Ts);
- break;
- }
- case BSONType.CODEWSCOPE:
- {
- BSONCodeWScope cw = (BSONCodeWScope) bv.Value;
- WriteTypeAndKey(bv, bw);
- using (var cwwr = new ExtBinaryWriter(new MemoryStream())) {
- cwwr.WriteBSONString(cw.Code);
- cw.Scope.Serialize(cwwr.BaseStream);
- byte[] cwdata = ((MemoryStream) cwwr.BaseStream).ToArray();
- bw.Write(cwdata.Length);
- bw.Write(cwdata);
- }
- break;
- }
- default:
- throw new InvalidBSONDataException("Unknown entry type: " + bt);
- }
+ return this;
}
- protected void WriteTypeAndKey(BSONValue bv, ExtBinaryWriter bw) {
+ public BsonDocument Add(string key, object value)
+ {
+ return Add(key, BsonValue.ValueOf(value));
+ }
+
+ protected void WriteBsonValue(string key, BsonValue bv, ExtBinaryWriter bw)
+ {
+ BsonType bt = bv.BSONType;
+ switch (bt)
+ {
+ case BsonType.EOO:
+ break;
+ case BsonType.NULL:
+ case BsonType.UNDEFINED:
+ case BsonType.MAXKEY:
+ case BsonType.MINKEY:
+ WriteTypeAndKey(key, bv, bw);
+ break;
+ case BsonType.OID:
+ {
+ WriteTypeAndKey(key, bv, bw);
+ var oid = (BsonOid)bv.Value;
+ bw.Write(oid.ToByteArray());
+ break;
+ }
+ case BsonType.STRING:
+ case BsonType.CODE:
+ case BsonType.SYMBOL:
+ WriteTypeAndKey(key, bv, bw);
+ bw.WriteBSONString((string)bv.Value);
+ break;
+ case BsonType.BOOL:
+ WriteTypeAndKey(key, bv, bw);
+ bw.Write((bool)bv.Value);
+ break;
+ case BsonType.INT:
+ WriteTypeAndKey(key, bv, bw);
+ bw.Write((int)bv.Value);
+ break;
+ case BsonType.LONG:
+ WriteTypeAndKey(key, bv, bw);
+ bw.Write((long)bv.Value);
+ break;
+ case BsonType.ARRAY:
+ case BsonType.OBJECT:
+ {
+ BsonDocument doc = (BsonDocument)bv.Value;
+ WriteTypeAndKey(key, bv, bw);
+ doc.Serialize(bw.BaseStream);
+ break;
+ }
+ case BsonType.DATE:
+ {
+ DateTime dt = (DateTime)bv.Value;
+ var diff = dt.ToUniversalTime() - BsonConstants.Epoch;
+ long time = (long)Math.Floor(diff.TotalMilliseconds);
+ WriteTypeAndKey(key, bv, bw);
+ bw.Write(time);
+ break;
+ }
+ case BsonType.DOUBLE:
+ WriteTypeAndKey(key, bv, bw);
+ bw.Write((double)bv.Value);
+ break;
+ case BsonType.REGEX:
+ {
+ BsonRegexp rv = (BsonRegexp)bv.Value;
+ WriteTypeAndKey(key, bv, bw);
+ bw.WriteCString(rv.Re ?? "");
+ bw.WriteCString(rv.Opts ?? "");
+ break;
+ }
+ case BsonType.BINDATA:
+ {
+ BsonBinData bdata = (BsonBinData)bv.Value;
+ WriteTypeAndKey(key, bv, bw);
+ bw.Write(bdata.Data.Length);
+ bw.Write(bdata.Subtype);
+ bw.Write(bdata.Data);
+ break;
+ }
+ case BsonType.DBREF:
+ //Unsupported DBREF!
+ break;
+ case BsonType.TIMESTAMP:
+ {
+ BsonTimestamp ts = (BsonTimestamp)bv.Value;
+ WriteTypeAndKey(key, bv, bw);
+ bw.Write(ts.Inc);
+ bw.Write(ts.Ts);
+ break;
+ }
+ case BsonType.CODEWSCOPE:
+ {
+ BsonCodeWScope cw = (BsonCodeWScope)bv.Value;
+ WriteTypeAndKey(key, bv, bw);
+ using (var cwwr = new ExtBinaryWriter(new MemoryStream()))
+ {
+ cwwr.WriteBSONString(cw.Code);
+ cw.Scope.Serialize(cwwr.BaseStream);
+ byte[] cwdata = ((MemoryStream)cwwr.BaseStream).ToArray();
+ bw.Write(cwdata.Length);
+ bw.Write(cwdata);
+ }
+ break;
+ }
+ default:
+ throw new InvalidBSONDataException("Unknown entry type: " + bt);
+ }
+ }
+
+ protected void WriteTypeAndKey(string key, BsonValue bv, ExtBinaryWriter bw)
+ {
bw.Write((byte) bv.BSONType);
- bw.WriteCString(bv.Key);
- }
-
- protected void CheckFields() {
- if (_fields != null) {
- return;
- }
- _fields = new Dictionary(Math.Max(_fieldslist.Count + 1, 32));
- foreach (var bv in _fieldslist) {
- _fields.Add(bv.Key, bv);
- }
+ bw.WriteCString(key);
}
- }
+ }
}
diff --git a/Ejdb.BSON/BSONIterator.cs b/Ejdb.BSON/BSONIterator.cs
index ac5ee26..2ff3527 100644
--- a/Ejdb.BSON/BSONIterator.cs
+++ b/Ejdb.BSON/BSONIterator.cs
@@ -1,364 +1,400 @@
-// ============================================================================================
-// .NET API for EJDB database library http://ejdb.org
-// Copyright (C) 2012-2013 Softmotions Ltd
-//
-// This file is part of EJDB.
-// EJDB is free software; you can redistribute it and/or modify it under the terms of
-// the GNU Lesser General Public License as published by the Free Software Foundation; either
-// version 2.1 of the License or any later version. EJDB is distributed in the hope
-// that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
-// License for more details.
-// You should have received a copy of the GNU Lesser General Public License along with EJDB;
-// if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
-// Boston, MA 02111-1307 USA.
-// ============================================================================================
-using System;
-using System.Text;
-using System.Diagnostics;
-using System.IO;
-using Ejdb.IO;
-using System.Collections.Generic;
-
-namespace Ejdb.BSON {
-
- public sealed class BSONIterator : IDisposable, IEnumerable {
- ExtBinaryReader _input;
-
- bool _closeOnDispose = true;
-
- bool _disposed;
-
- int _doclen;
-
- BSONType _ctype = BSONType.UNKNOWN;
-
- string _entryKey;
-
- int _entryLen;
-
- bool _entryDataSkipped;
-
- BSONValue _entryDataValue;
-
- ///
- /// Returns true if this is disposed.
- ///
- public bool Disposed {
- get {
- return _disposed;
- }
- }
-
- ///
- /// Gets a value indicating whether this is empty.
- ///
- public bool Empty {
- get {
- return (_ctype == BSONType.EOO);
- }
- }
-
- ///
- /// Gets the length of the document in bytes represented by this iterator.
- ///
- public int DocumentLength {
- get { return _doclen; }
- private set { _doclen = value; }
- }
-
- ///
- /// Gets the current document key pointed by this iterator.
- ///
- public string CurrentKey {
- get { return _entryKey; }
- }
-
- public BSONIterator() { //empty iterator
- this._ctype = BSONType.EOO;
- }
-
- public BSONIterator(BSONDocument doc) : this(doc.ToByteArray()) {
- }
-
- public BSONIterator(byte[] bbuf) : this(new MemoryStream(bbuf)) {
- }
-
- public BSONIterator(Stream input) {
- if (!input.CanRead) {
- Dispose();
- throw new IOException("Input stream must be readable");
- }
- if (!input.CanSeek) {
- Dispose();
- throw new IOException("Input stream must be seekable");
- }
- this._input = new ExtBinaryReader(input);
- this._ctype = BSONType.UNKNOWN;
- this._doclen = _input.ReadInt32();
- if (this._doclen < 5) {
- Dispose();
- throw new InvalidBSONDataException("Unexpected end of BSON document");
- }
- }
-
- BSONIterator(ExtBinaryReader input, int doclen) {
- this._input = input;
- this._doclen = doclen;
- if (this._doclen < 5) {
- Dispose();
- throw new InvalidBSONDataException("Unexpected end of BSON document");
- }
- }
-
- internal BSONIterator(BSONIterator it) : this(it._input, it._entryLen + 4) {
- _closeOnDispose = false;
- it._entryDataSkipped = true;
- }
-
- ~BSONIterator() {
- Dispose();
- }
-
- public void Dispose() {
- _disposed = true;
- if (_closeOnDispose && _input != null) {
- _input.Close();
- _input = null;
- }
- }
-
- void CheckDisposed() {
- if (Disposed) {
- throw new ObjectDisposedException("BSONIterator");
- }
- }
-
- public IEnumerator GetEnumerator() {
- while (Next() != BSONType.EOO) {
- yield return _ctype;
- }
- }
-
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
- return GetEnumerator();
- }
-
- public IEnumerable Values() {
- while (Next() != BSONType.EOO) {
- yield return FetchCurrentValue();
- }
- }
-
- public BSONDocument ToBSONDocument(params string[] fields) {
- if (fields.Length > 0) {
- return new BSONDocument(this, fields);
- } else {
- return new BSONDocument(this);
- }
- }
-
- public BSONType Next() {
- CheckDisposed();
- if (_ctype == BSONType.EOO) {
- return BSONType.EOO;
- }
- if (!_entryDataSkipped && _ctype != BSONType.UNKNOWN) {
- SkipData();
- }
- byte bv = _input.ReadByte();
- if (!Enum.IsDefined(typeof(BSONType), bv)) {
- throw new InvalidBSONDataException("Unknown bson type: " + bv);
- }
- _entryDataSkipped = false;
- _entryDataValue = null;
- _entryKey = null;
- _ctype = (BSONType) bv;
- _entryLen = 0;
- if (_ctype != BSONType.EOO) {
- ReadKey();
- }
- switch (_ctype) {
- case BSONType.EOO:
- Dispose();
- return BSONType.EOO;
- case BSONType.UNDEFINED:
- case BSONType.NULL:
- case BSONType.MAXKEY:
- case BSONType.MINKEY:
- _entryLen = 0;
- break;
- case BSONType.BOOL:
- _entryLen = 1;
- break;
- case BSONType.INT:
- _entryLen = 4;
- break;
- case BSONType.LONG:
- case BSONType.DOUBLE:
- case BSONType.TIMESTAMP:
- case BSONType.DATE:
- _entryLen = 8;
- break;
- case BSONType.OID:
- _entryLen = 12;
- break;
- case BSONType.STRING:
- case BSONType.CODE:
- case BSONType.SYMBOL:
- _entryLen = _input.ReadInt32();
- break;
- case BSONType.DBREF:
- //Unsupported DBREF!
- _entryLen = 12 + _input.ReadInt32();
- break;
- case BSONType.BINDATA:
- _entryLen = 1 + _input.ReadInt32();
- break;
- case BSONType.OBJECT:
- case BSONType.ARRAY:
- case BSONType.CODEWSCOPE:
- _entryLen = _input.ReadInt32() - 4;
- Debug.Assert(_entryLen > 0);
- break;
- case BSONType.REGEX:
- _entryLen = 0;
- break;
- default:
- throw new InvalidBSONDataException("Unknown entry type: " + _ctype);
- }
- return _ctype;
- }
-
- public BSONValue FetchCurrentValue() {
- CheckDisposed();
- if (_entryDataSkipped) {
- return _entryDataValue;
- }
- _entryDataSkipped = true;
- switch (_ctype) {
- case BSONType.EOO:
- case BSONType.UNDEFINED:
- case BSONType.NULL:
- case BSONType.MAXKEY:
- case BSONType.MINKEY:
- _entryDataValue = new BSONValue(_ctype, _entryKey);
- break;
- case BSONType.OID:
- Debug.Assert(_entryLen == 12);
- _entryDataValue = new BSONValue(_ctype, _entryKey, new BSONOid(_input));
- break;
- case BSONType.STRING:
- case BSONType.CODE:
- case BSONType.SYMBOL:
- {
- Debug.Assert(_entryLen - 1 >= 0);
- string sv = Encoding.UTF8.GetString(_input.ReadBytes(_entryLen - 1));
- _entryDataValue = new BSONValue(_ctype, _entryKey, sv);
- var rb = _input.ReadByte();
- Debug.Assert(rb == 0x00); //trailing zero byte
- break;
- }
- case BSONType.BOOL:
- _entryDataValue = new BSONValue(_ctype, _entryKey, _input.ReadBoolean());
- break;
- case BSONType.INT:
- _entryDataValue = new BSONValue(_ctype, _entryKey, _input.ReadInt32());
- break;
- case BSONType.OBJECT:
- case BSONType.ARRAY:
- {
- BSONDocument doc = (_ctype == BSONType.OBJECT ? new BSONDocument() : new BSONArray());
- BSONIterator sit = new BSONIterator(this);
- while (sit.Next() != BSONType.EOO) {
- doc.Add(sit.FetchCurrentValue());
- }
- _entryDataValue = new BSONValue(_ctype, _entryKey, doc);
- break;
- }
- case BSONType.DOUBLE:
- _entryDataValue = new BSONValue(_ctype, _entryKey, _input.ReadDouble());
- break;
- case BSONType.LONG:
- _entryDataValue = new BSONValue(_ctype, _entryKey, _input.ReadInt64());
- break;
- case BSONType.DATE:
- _entryDataValue = new BSONValue(_ctype, _entryKey,
- BSONConstants.Epoch.AddMilliseconds(_input.ReadInt64()));
- break;
- case BSONType.TIMESTAMP:
- {
- int inc = _input.ReadInt32();
- int ts = _input.ReadInt32();
- _entryDataValue = new BSONValue(_ctype, _entryKey,
- new BSONTimestamp(inc, ts));
- break;
- }
- case BSONType.REGEX:
- {
- string re = _input.ReadCString();
- string opts = _input.ReadCString();
- _entryDataValue = new BSONValue(_ctype, _entryKey,
- new BSONRegexp(re, opts));
- break;
- }
- case BSONType.BINDATA:
- {
- byte subtype = _input.ReadByte();
- BSONBinData bd = new BSONBinData(subtype, _entryLen - 1, _input);
- _entryDataValue = new BSONValue(_ctype, _entryKey, bd);
- break;
- }
- case BSONType.DBREF:
- {
- //Unsupported DBREF!
- SkipData(true);
- _entryDataValue = new BSONValue(_ctype, _entryKey);
- break;
- }
- case BSONType.CODEWSCOPE:
- {
- int cwlen = _entryLen + 4;
- Debug.Assert(cwlen > 5);
- int clen = _input.ReadInt32(); //code length
- string code = Encoding.UTF8.GetString(_input.ReadBytes(clen));
- BSONCodeWScope cw = new BSONCodeWScope(code);
- BSONIterator sit = new BSONIterator(_input, _input.ReadInt32());
- while (sit.Next() != BSONType.EOO) {
- cw.Add(sit.FetchCurrentValue());
- }
- _entryDataValue = new BSONValue(_ctype, _entryKey, cw);
- break;
- }
- }
- return _entryDataValue;
- }
- //.//////////////////////////////////////////////////////////////////
- // Private staff
- //.//////////////////////////////////////////////////////////////////
- internal void SkipData(bool force = false) {
- if (_entryDataSkipped && !force) {
- return;
- }
- _entryDataValue = null;
- _entryDataSkipped = true;
- if (_ctype == BSONType.REGEX) {
- _input.SkipCString();
- _input.SkipCString();
- Debug.Assert(_entryLen == 0);
- } else if (_entryLen > 0) {
- long cpos = _input.BaseStream.Position;
- if ((cpos + _entryLen) != _input.BaseStream.Seek(_entryLen, SeekOrigin.Current)) {
- throw new IOException("Inconsitent seek within input BSON stream");
- }
- _entryLen = 0;
- }
- }
-
- string ReadKey() {
- _entryKey = _input.ReadCString();
- return _entryKey;
- }
- }
-}
-
+// ============================================================================================
+// .NET API for EJDB database library http://ejdb.org
+// Copyright (C) 2012-2013 Softmotions Ltd
+//
+// This file is part of EJDB.
+// EJDB is free software; you can redistribute it and/or modify it under the terms of
+// the GNU Lesser General Public License as published by the Free Software Foundation; either
+// version 2.1 of the License or any later version. EJDB is distributed in the hope
+// that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+// License for more details.
+// You should have received a copy of the GNU Lesser General Public License along with EJDB;
+// if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA.
+// ============================================================================================
+using System;
+using System.Linq;
+using System.Text;
+using System.Diagnostics;
+using System.IO;
+using Ejdb.IO;
+using System.Collections.Generic;
+
+namespace Ejdb.BSON {
+
+ public sealed class BsonIterator : IDisposable, IEnumerable
+ {
+ private static readonly Encoding STRICT_ENCODING = new UTF8Encoding(false, true);
+
+ ExtBinaryReader _input;
+
+ bool _closeOnDispose = true;
+
+ bool _disposed;
+
+ int _doclen;
+
+ BsonType _ctype = BsonType.UNKNOWN;
+
+ string _entryKey;
+
+ int _entryLen;
+
+ bool _entryDataSkipped;
+
+ BsonValue _entryDataValue;
+
+ ///
+ /// Returns true if this is disposed.
+ ///
+ public bool Disposed {
+ get {
+ return _disposed;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether this is empty.
+ ///
+ public bool Empty {
+ get {
+ return (_ctype == BsonType.EOO);
+ }
+ }
+
+ ///
+ /// Gets the length of the document in bytes represented by this iterator.
+ ///
+ public int DocumentLength {
+ get { return _doclen; }
+ private set { _doclen = value; }
+ }
+
+ ///
+ /// Gets the current document key pointed by this iterator.
+ ///
+ public string CurrentKey {
+ get { return _entryKey; }
+ }
+
+ public BsonIterator() { //empty iterator
+ this._ctype = BsonType.EOO;
+ }
+
+ public BsonIterator(BsonDocument doc) : this(doc.ToByteArray()) {
+ }
+
+ public BsonIterator(byte[] bbuf) : this(new MemoryStream(bbuf)) {
+ }
+
+ public BsonIterator(Stream input) {
+ if (!input.CanRead) {
+ Dispose();
+ throw new IOException("Input stream must be readable");
+ }
+ if (!input.CanSeek) {
+ Dispose();
+ throw new IOException("Input stream must be seekable");
+ }
+ this._input = new ExtBinaryReader(input);
+ this._ctype = BsonType.UNKNOWN;
+ this._doclen = _input.ReadInt32();
+ if (this._doclen < 5) {
+ Dispose();
+ throw new InvalidBSONDataException("Unexpected end of BSON document");
+ }
+ }
+
+ BsonIterator(ExtBinaryReader input, int doclen) {
+ this._input = input;
+ this._doclen = doclen;
+ if (this._doclen < 5) {
+ Dispose();
+ throw new InvalidBSONDataException("Unexpected end of BSON document");
+ }
+ }
+
+ internal BsonIterator(BsonIterator it) : this(it._input, it._entryLen + 4) {
+ _closeOnDispose = false;
+ it._entryDataSkipped = true;
+ }
+
+ ~BsonIterator() {
+ Dispose();
+ }
+
+ public void Dispose() {
+ _disposed = true;
+ if (_closeOnDispose && _input != null) {
+ _input.Close();
+ _input = null;
+ }
+ }
+
+ void CheckDisposed() {
+ if (Disposed) {
+ throw new ObjectDisposedException("BsonIterator");
+ }
+ }
+
+ public IEnumerator GetEnumerator() {
+ while (Next() != BsonType.EOO) {
+ yield return _ctype;
+ }
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
+ return GetEnumerator();
+ }
+
+ public IEnumerable Values() {
+ while (Next() != BsonType.EOO) {
+ yield return FetchCurrentValue();
+ }
+ }
+
+ public BsonDocument ToBsonDocument(params string[] fields)
+ {
+ if (fields.Length > 0)
+ return new BsonDocument(this, fields);
+
+ return new BsonDocument(this);
+ }
+
+ public T To()
+ {
+ var classMap = BsonClassSerialization.LookupClassMap(typeof (T));
+ var obj = classMap.Creator();
+
+ while (Next() != BsonType.EOO)
+ {
+ BsonMemberMap memberMap;
+ if (classMap.AllMemberMaps.TryGetValue(CurrentKey, out memberMap))
+ {
+ var value = FetchCurrentValue();
+ memberMap.Setter(obj, value.Value);
+ }
+ else
+ Debug.Fail(string.Format("Could not find a property '{0}' on type {1}", CurrentKey, typeof (T).Name));
+ }
+
+ return (T) obj;
+ }
+
+ public BsonType Next() {
+ CheckDisposed();
+ if (_ctype == BsonType.EOO) {
+ return BsonType.EOO;
+ }
+ if (!_entryDataSkipped && _ctype != BsonType.UNKNOWN) {
+ SkipData();
+ }
+ byte bv = _input.ReadByte();
+ if (!Enum.IsDefined(typeof(BsonType), bv)) {
+ throw new InvalidBSONDataException("Unknown bson type: " + bv);
+ }
+ _entryDataSkipped = false;
+ _entryDataValue = null;
+ _entryKey = null;
+ _ctype = (BsonType) bv;
+ _entryLen = 0;
+ if (_ctype != BsonType.EOO) {
+ ReadKey();
+ }
+ switch (_ctype) {
+ case BsonType.EOO:
+ Dispose();
+ return BsonType.EOO;
+ case BsonType.UNDEFINED:
+ case BsonType.NULL:
+ case BsonType.MAXKEY:
+ case BsonType.MINKEY:
+ _entryLen = 0;
+ break;
+ case BsonType.BOOL:
+ _entryLen = 1;
+ break;
+ case BsonType.INT:
+ _entryLen = 4;
+ break;
+ case BsonType.LONG:
+ case BsonType.DOUBLE:
+ case BsonType.TIMESTAMP:
+ case BsonType.DATE:
+ _entryLen = 8;
+ break;
+ case BsonType.OID:
+ _entryLen = 12;
+ break;
+ case BsonType.STRING:
+ case BsonType.CODE:
+ case BsonType.SYMBOL:
+ _entryLen = _input.ReadInt32();
+ break;
+ case BsonType.DBREF:
+ //Unsupported DBREF!
+ _entryLen = 12 + _input.ReadInt32();
+ break;
+ case BsonType.BINDATA:
+ _entryLen = 1 + _input.ReadInt32();
+ break;
+ case BsonType.OBJECT:
+ case BsonType.ARRAY:
+ case BsonType.CODEWSCOPE:
+ _entryLen = _input.ReadInt32() - 4;
+ Debug.Assert(_entryLen > 0);
+ break;
+ case BsonType.REGEX:
+ _entryLen = 0;
+ break;
+ default:
+ throw new InvalidBSONDataException("Unknown entry type: " + _ctype);
+ }
+ return _ctype;
+ }
+
+ public BsonValue FetchCurrentValue() {
+ CheckDisposed();
+ if (_entryDataSkipped) {
+ return _entryDataValue;
+ }
+ _entryDataSkipped = true;
+ switch (_ctype) {
+ case BsonType.EOO:
+ case BsonType.UNDEFINED:
+ case BsonType.NULL:
+ case BsonType.MAXKEY:
+ case BsonType.MINKEY:
+ _entryDataValue = new BsonValue(_ctype, _entryKey);
+ break;
+ case BsonType.OID:
+ Debug.Assert(_entryLen == 12);
+ var bytes = _input.ReadBytes(12);
+ _entryDataValue = new BsonValue(_ctype, new BsonOid(bytes));
+ break;
+ case BsonType.STRING:
+ case BsonType.CODE:
+ case BsonType.SYMBOL:
+ {
+ if (_entryLen <= 0)
+ {
+ var message = string.Format("Invalid string length: {0} (the length includes the null terminator so it must be greater than or equal to 1).", _entryLen);
+ throw new BsonSerializationException(message);
+ }
+
+ string sv = STRICT_ENCODING.GetString(_input.ReadBytes(_entryLen - 1));
+ _entryDataValue = new BsonValue(_ctype, (object) sv);
+ var finalByte = _input.ReadByte();
+
+ if (finalByte != 0x00)
+ throw new BsonSerializationException("String is missing null terminator.");
+
+ break;
+ }
+ case BsonType.BOOL:
+ _entryDataValue = new BsonValue(_ctype, (object) _input.ReadBoolean());
+ break;
+ case BsonType.INT:
+ _entryDataValue = new BsonValue(_ctype, (object) _input.ReadInt32());
+ break;
+ case BsonType.OBJECT:
+ case BsonType.ARRAY:
+ {
+ BsonDocument doc = (_ctype == BsonType.OBJECT ? new BsonDocument() : new BsonArray());
+ BsonIterator sit = new BsonIterator(this);
+ while (sit.Next() != BsonType.EOO)
+ {
+ doc.Add(sit.CurrentKey, sit.FetchCurrentValue());
+ }
+ _entryDataValue = new BsonValue(_ctype, doc);
+ break;
+ }
+ case BsonType.DOUBLE:
+ _entryDataValue = new BsonValue(_ctype, _input.ReadDouble());
+ break;
+ case BsonType.LONG:
+ _entryDataValue = new BsonValue(_ctype, _input.ReadInt64());
+ break;
+ case BsonType.DATE:
+ var milliseconds = _input.ReadInt64();
+ var dateTime = BsonConstants.Epoch.AddMilliseconds(milliseconds);
+ _entryDataValue = new BsonValue(_ctype, dateTime);
+ break;
+ case BsonType.TIMESTAMP:
+ {
+ int inc = _input.ReadInt32();
+ int ts = _input.ReadInt32();
+ _entryDataValue = new BsonValue(_ctype,
+ new BsonTimestamp(inc, ts));
+ break;
+ }
+ case BsonType.REGEX:
+ {
+ string re = _input.ReadCString();
+ string opts = _input.ReadCString();
+ _entryDataValue = new BsonValue(_ctype,
+ new BsonRegexp(re, opts));
+ break;
+ }
+ case BsonType.BINDATA:
+ {
+ byte subtype = _input.ReadByte();
+ BsonBinData bd = new BsonBinData(subtype, _entryLen - 1, _input);
+ _entryDataValue = new BsonValue(_ctype, bd);
+ break;
+ }
+ case BsonType.DBREF:
+ {
+ //Unsupported DBREF!
+ SkipData(true);
+ _entryDataValue = new BsonValue(_ctype, _entryKey);
+ break;
+ }
+ case BsonType.CODEWSCOPE:
+ {
+ int cwlen = _entryLen + 4;
+ Debug.Assert(cwlen > 5);
+ int clen = _input.ReadInt32(); //code length
+ string code = STRICT_ENCODING.GetString(_input.ReadBytes(clen));
+ BsonCodeWScope cw = new BsonCodeWScope(code);
+ BsonIterator sit = new BsonIterator(_input, _input.ReadInt32());
+ while (sit.Next() != BsonType.EOO)
+ {
+ cw.Add(sit.CurrentKey, sit.FetchCurrentValue());
+ }
+ _entryDataValue = new BsonValue(_ctype, (object) cw);
+ break;
+ }
+ }
+ return _entryDataValue;
+ }
+ //.//////////////////////////////////////////////////////////////////
+ // Private staff
+ //.//////////////////////////////////////////////////////////////////
+ internal void SkipData(bool force = false) {
+ if (_entryDataSkipped && !force) {
+ return;
+ }
+ _entryDataValue = null;
+ _entryDataSkipped = true;
+ if (_ctype == BsonType.REGEX) {
+ _input.SkipCString();
+ _input.SkipCString();
+ Debug.Assert(_entryLen == 0);
+ } else if (_entryLen > 0) {
+ long cpos = _input.BaseStream.Position;
+ if ((cpos + _entryLen) != _input.BaseStream.Seek(_entryLen, SeekOrigin.Current)) {
+ throw new IOException("Inconsitent seek within input BSON stream");
+ }
+ _entryLen = 0;
+ }
+ }
+
+ string ReadKey() {
+ _entryKey = _input.ReadCString();
+ return _entryKey;
+ }
+ }
+}
+
diff --git a/Ejdb.BSON/BSONOid.cs b/Ejdb.BSON/BSONOid.cs
index 9c3a835..50dfe7e 100644
--- a/Ejdb.BSON/BSONOid.cs
+++ b/Ejdb.BSON/BSONOid.cs
@@ -1,140 +1,593 @@
-// ============================================================================================
-// .NET API for EJDB database library http://ejdb.org
-// Copyright (C) 2012-2013 Softmotions Ltd
-//
-// This file is part of EJDB.
-// EJDB is free software; you can redistribute it and/or modify it under the terms of
-// the GNU Lesser General Public License as published by the Free Software Foundation; either
-// version 2.1 of the License or any later version. EJDB is distributed in the hope
-// that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
-// License for more details.
-// You should have received a copy of the GNU Lesser General Public License along with EJDB;
-// if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
-// Boston, MA 02111-1307 USA.
-// ============================================================================================
-using System;
-using System.IO;
-
-namespace Ejdb.BSON {
-
- [Serializable]
- public sealed class BSONOid : IComparable, IBSONValue {
-
- internal byte[] _bytes;
- string _cachedString;
-
- public BSONType BSONType {
- get {
- return BSONType.OID;
- }
- }
-
- BSONOid() {
- }
-
- public BSONOid(string val) {
- ParseOIDString(val);
- }
-
- public BSONOid(byte[] val) {
- _bytes = new byte[12];
- Array.Copy(val, _bytes, 12);
- }
-
- public BSONOid(BinaryReader reader) {
- _bytes = reader.ReadBytes(12);
- }
-
- bool IsValidOid(string oid) {
- var i = 0;
- for (; i < oid.Length &&
- ((oid[i] >= 0x30 && oid[i] <= 0x39) || (oid[i] >= 0x61 && oid[i] <= 0x66));
- ++i) {
- }
- return (i == 24);
- }
-
- void ParseOIDString(string val) {
- if (!IsValidOid(val)) {
- throw new ArgumentException("Invalid oid string");
- }
- var vlen = val.Length;
- _bytes = new byte[vlen / 2];
- for (var i = 0; i < vlen; i += 2) {
- _bytes[i / 2] = Convert.ToByte(val.Substring(i, 2), 16);
- }
- }
-
- public int CompareTo(BSONOid other) {
- if (ReferenceEquals(other, null)) {
- return 1;
- }
- var obytes = other._bytes;
- for (var x = 0; x < _bytes.Length; x++) {
- if (_bytes[x] < obytes[x]) {
- return -1;
- }
- if (_bytes[x] > obytes[x]) {
- return 1;
- }
- }
- return 0;
- }
-
- public byte[] ToBytes() {
- var b = new byte[12];
- Array.Copy(_bytes, b, 12);
- return b;
- }
-
- public override string ToString() {
- if (_cachedString == null) {
- _cachedString = BitConverter.ToString(_bytes).Replace("-", "").ToLower();
- }
- return _cachedString;
- }
-
- public override bool Equals(object obj) {
- if (obj == null) {
- return false;
- }
- if (ReferenceEquals(this, obj)) {
- return true;
- }
- if (obj is BSONOid) {
- return (CompareTo((BSONOid) obj) == 0);
- }
- return false;
- }
-
- public override int GetHashCode() {
- return ToString().GetHashCode();
- }
-
- public static bool operator ==(BSONOid a, BSONOid b) {
- if (ReferenceEquals(a, b))
- return true;
- if ((object) a == null || (object) b == null) {
- return false;
- }
- return a.Equals(b);
- }
-
- public static bool operator !=(BSONOid a, BSONOid b) {
- return !(a == b);
- }
-
- public static bool operator >(BSONOid a, BSONOid b) {
- return a.CompareTo(b) > 0;
- }
-
- public static bool operator <(BSONOid a, BSONOid b) {
- return a.CompareTo(b) < 0;
- }
-
- public static implicit operator BSONOid(string val) {
- return new BSONOid(val);
- }
- }
+using System;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Security;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading;
+
+namespace Ejdb.BSON
+{
+ ///
+ /// Represents an ObjectId (see also BsonObjectId).
+ ///
+ [Serializable]
+ public class BsonOid : IComparable, IEquatable, IConvertible
+ {
+ // private static fields
+ private static BsonOid __emptyInstance = default(BsonOid);
+ private static int __staticMachine;
+ private static short __staticPid;
+ private static int __staticIncrement; // high byte will be masked out when generating new ObjectId
+
+ // private fields
+ // we're using 14 bytes instead of 12 to hold the ObjectId in memory but unlike a byte[] there is no additional object on the heap
+ // the extra two bytes are not visible to anyone outside of this class and they buy us considerable simplification
+ // an additional advantage of this representation is that it will serialize to JSON without any 64 bit overflow problems
+ private int _timestamp;
+ private int _machine;
+ private short _pid;
+ private int _increment;
+
+ // static constructor
+ static BsonOid()
+ {
+ __staticMachine = GetMachineHash();
+ __staticIncrement = (new Random()).Next();
+
+ try
+ {
+ __staticPid = (short)GetCurrentProcessId(); // use low order two bytes only
+ }
+ catch (SecurityException)
+ {
+ __staticPid = 0;
+ }
+ }
+
+ // constructors
+ ///
+ /// Initializes a new instance of the ObjectId class.
+ ///
+ /// The bytes.
+ public BsonOid(byte[] bytes)
+ {
+ if (bytes == null)
+ {
+ throw new ArgumentNullException("bytes");
+ }
+ Unpack(bytes, out _timestamp, out _machine, out _pid, out _increment);
+ }
+
+ ///
+ /// Initializes a new instance of the ObjectId class.
+ ///
+ /// The timestamp (expressed as a DateTime).
+ /// The machine hash.
+ /// The PID.
+ /// The increment.
+ public BsonOid(DateTime timestamp, int machine, short pid, int increment)
+ : this(GetTimestampFromDateTime(timestamp), machine, pid, increment)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the ObjectId class.
+ ///
+ /// The timestamp.
+ /// The machine hash.
+ /// The PID.
+ /// The increment.
+ public BsonOid(int timestamp, int machine, short pid, int increment)
+ {
+ if ((machine & 0xff000000) != 0)
+ {
+ throw new ArgumentOutOfRangeException("machine", "The machine value must be between 0 and 16777215 (it must fit in 3 bytes).");
+ }
+ if ((increment & 0xff000000) != 0)
+ {
+ throw new ArgumentOutOfRangeException("increment", "The increment value must be between 0 and 16777215 (it must fit in 3 bytes).");
+ }
+
+ _timestamp = timestamp;
+ _machine = machine;
+ _pid = pid;
+ _increment = increment;
+ }
+
+ ///
+ /// Initializes a new instance of the ObjectId class.
+ ///
+ /// The value.
+ public BsonOid(string value)
+ {
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ Unpack(_ParseHexString(value), out _timestamp, out _machine, out _pid, out _increment);
+ }
+
+ private static byte[] _ParseHexString(string val)
+ {
+ if (!IsValidOid(val))
+ {
+ var message = string.Format("'{0}' is not a valid 24 digit hex string.", val);
+ throw new FormatException(message);
+ }
+
+ return _UnsafeParseHexString(val);
+ }
+
+ private static byte[] _UnsafeParseHexString(string val)
+ {
+ var vlen = val.Length;
+ var bytes = new byte[vlen/2];
+ for (var i = 0; i < vlen; i += 2)
+ bytes[i/2] = Convert.ToByte(val.Substring(i, 2), 16);
+
+ return bytes;
+ }
+
+ private static bool IsValidOid(string oid)
+ {
+ var i = 0;
+ for (; i < oid.Length && (
+ (oid[i] >= '0' && oid[i] <= '9') ||
+ (oid[i] >= 'a' && oid[i] <= 'f') ||
+ (oid[i] >= 'A' && oid[i] <= 'F')); ++i)
+ {
+ }
+
+ return (i == 24);
+ }
+
+ // public static properties
+ ///
+ /// Gets an instance of ObjectId where the value is empty.
+ ///
+ public static BsonOid Empty
+ {
+ get { return __emptyInstance; }
+ }
+
+ // public properties
+ ///
+ /// Gets the timestamp.
+ ///
+ public int Timestamp
+ {
+ get { return _timestamp; }
+ }
+
+ ///
+ /// Gets the machine.
+ ///
+ public int Machine
+ {
+ get { return _machine; }
+ }
+
+ ///
+ /// Gets the PID.
+ ///
+ public short Pid
+ {
+ get { return _pid; }
+ }
+
+ ///
+ /// Gets the increment.
+ ///
+ public int Increment
+ {
+ get { return _increment; }
+ }
+
+ ///
+ /// Gets the creation time (derived from the timestamp).
+ ///
+ public DateTime CreationTime
+ {
+ get { return BsonConstants.Epoch.AddSeconds(_timestamp); }
+ }
+
+ // public operators
+ ///
+ /// Compares two ObjectIds.
+ ///
+ /// The first ObjectId.
+ /// The other ObjectId
+ /// True if the first ObjectId is less than the second ObjectId.
+ public static bool operator <(BsonOid lhs, BsonOid rhs)
+ {
+ return lhs.CompareTo(rhs) < 0;
+ }
+
+ ///
+ /// Compares two ObjectIds.
+ ///
+ /// The first ObjectId.
+ /// The other ObjectId
+ /// True if the first ObjectId is less than or equal to the second ObjectId.
+ public static bool operator <=(BsonOid lhs, BsonOid rhs)
+ {
+ return lhs.CompareTo(rhs) <= 0;
+ }
+
+ ///
+ /// Compares two ObjectIds.
+ ///
+ /// The first ObjectId.
+ /// The other ObjectId.
+ /// True if the two ObjectIds are equal.
+ public static bool operator ==(BsonOid lhs, BsonOid rhs)
+ {
+ return lhs.Equals(rhs);
+ }
+
+ ///
+ /// Compares two ObjectIds.
+ ///
+ /// The first ObjectId.
+ /// The other ObjectId.
+ /// True if the two ObjectIds are not equal.
+ public static bool operator !=(BsonOid lhs, BsonOid rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+ ///
+ /// Compares two ObjectIds.
+ ///
+ /// The first ObjectId.
+ /// The other ObjectId
+ /// True if the first ObjectId is greather than or equal to the second ObjectId.
+ public static bool operator >=(BsonOid lhs, BsonOid rhs)
+ {
+ return lhs.CompareTo(rhs) >= 0;
+ }
+
+ ///
+ /// Compares two ObjectIds.
+ ///
+ /// The first ObjectId.
+ /// The other ObjectId
+ /// True if the first ObjectId is greather than the second ObjectId.
+ public static bool operator >(BsonOid lhs, BsonOid rhs)
+ {
+ return lhs.CompareTo(rhs) > 0;
+ }
+
+ // public static methods
+ ///
+ /// Generates a new ObjectId with a unique value.
+ ///
+ /// An ObjectId.
+ public static BsonOid GenerateNewId()
+ {
+ return GenerateNewId(GetTimestampFromDateTime(DateTime.UtcNow));
+ }
+
+ ///
+ /// Generates a new ObjectId with a unique value (with the timestamp component based on a given DateTime).
+ ///
+ /// The timestamp component (expressed as a DateTime).
+ /// An ObjectId.
+ public static BsonOid GenerateNewId(DateTime timestamp)
+ {
+ return GenerateNewId(GetTimestampFromDateTime(timestamp));
+ }
+
+ ///
+ /// Generates a new ObjectId with a unique value (with the given timestamp).
+ ///
+ /// The timestamp component.
+ /// An ObjectId.
+ public static BsonOid GenerateNewId(int timestamp)
+ {
+ int increment = Interlocked.Increment(ref __staticIncrement) & 0x00ffffff; // only use low order 3 bytes
+ return new BsonOid(timestamp, __staticMachine, __staticPid, increment);
+ }
+
+ ///
+ /// Packs the components of an ObjectId into a byte array.
+ ///
+ /// The timestamp.
+ /// The machine hash.
+ /// The PID.
+ /// The increment.
+ /// A byte array.
+ public static byte[] Pack(int timestamp, int machine, short pid, int increment)
+ {
+ if ((machine & 0xff000000) != 0)
+ {
+ throw new ArgumentOutOfRangeException("machine", "The machine value must be between 0 and 16777215 (it must fit in 3 bytes).");
+ }
+ if ((increment & 0xff000000) != 0)
+ {
+ throw new ArgumentOutOfRangeException("increment", "The increment value must be between 0 and 16777215 (it must fit in 3 bytes).");
+ }
+
+ byte[] bytes = new byte[12];
+ bytes[0] = (byte)(timestamp >> 24);
+ bytes[1] = (byte)(timestamp >> 16);
+ bytes[2] = (byte)(timestamp >> 8);
+ bytes[3] = (byte)(timestamp);
+ bytes[4] = (byte)(machine >> 16);
+ bytes[5] = (byte)(machine >> 8);
+ bytes[6] = (byte)(machine);
+ bytes[7] = (byte)(pid >> 8);
+ bytes[8] = (byte)(pid);
+ bytes[9] = (byte)(increment >> 16);
+ bytes[10] = (byte)(increment >> 8);
+ bytes[11] = (byte)(increment);
+ return bytes;
+ }
+
+ ///
+ /// Parses a string and creates a new ObjectId.
+ ///
+ /// The string value.
+ /// A ObjectId.
+ public static BsonOid Parse(string s)
+ {
+ if (s == null)
+ throw new ArgumentNullException("s");
+
+
+ var bytes = _ParseHexString(s);
+ return new BsonOid(bytes);
+ }
+
+ ///
+ /// Tries to parse a string and create a new ObjectId.
+ ///
+ /// The string value.
+ /// The new ObjectId.
+ /// True if the string was parsed successfully.
+ public static bool TryParse(string oidString, out BsonOid bsonOid)
+ {
+ // don't throw ArgumentNullException if oidString is null
+ if (oidString != null && oidString.Length == 24)
+ {
+ if (IsValidOid(oidString))
+ {
+ byte[] bytes = _UnsafeParseHexString(oidString);
+ bsonOid = new BsonOid(bytes);
+ return true;
+ }
+ }
+
+ bsonOid = default(BsonOid);
+ return false;
+ }
+
+ ///
+ /// Unpacks a byte array into the components of an ObjectId.
+ ///
+ /// A byte array.
+ /// The timestamp.
+ /// The machine hash.
+ /// The PID.
+ /// The increment.
+ public static void Unpack(byte[] bytes, out int timestamp, out int machine, out short pid, out int increment)
+ {
+ if (bytes == null)
+ {
+ throw new ArgumentNullException("bytes");
+ }
+ if (bytes.Length != 12)
+ {
+ throw new ArgumentOutOfRangeException("bytes", "Byte array must be 12 bytes long.");
+ }
+ timestamp = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
+ machine = (bytes[4] << 16) + (bytes[5] << 8) + bytes[6];
+ pid = (short)((bytes[7] << 8) + bytes[8]);
+ increment = (bytes[9] << 16) + (bytes[10] << 8) + bytes[11];
+ }
+
+ // private static methods
+ ///
+ /// Gets the current process id. This method exists because of how CAS operates on the call stack, checking
+ /// for permissions before executing the method. Hence, if we inlined this call, the calling method would not execute
+ /// before throwing an exception requiring the try/catch at an even higher level that we don't necessarily control.
+ ///
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static int GetCurrentProcessId()
+ {
+ return Process.GetCurrentProcess().Id;
+ }
+
+ private static int GetMachineHash()
+ {
+ var hostName = Environment.MachineName; // use instead of Dns.HostName so it will work offline
+ var md5 = MD5.Create();
+ var hash = md5.ComputeHash(Encoding.UTF8.GetBytes(hostName));
+ return (hash[0] << 16) + (hash[1] << 8) + hash[2]; // use first 3 bytes of hash
+ }
+
+ private static int GetTimestampFromDateTime(DateTime timestamp)
+ {
+ return (int)Math.Floor((BsonConstants.ToUniversalTime(timestamp) - BsonConstants.Epoch).TotalSeconds);
+ }
+
+ // public methods
+ ///
+ /// Compares this ObjectId to another ObjectId.
+ ///
+ /// The other ObjectId.
+ /// A 32-bit signed integer that indicates whether this ObjectId is less than, equal to, or greather than the other.
+ public int CompareTo(BsonOid other)
+ {
+ int r = _timestamp.CompareTo(other._timestamp);
+ if (r != 0) { return r; }
+ r = _machine.CompareTo(other._machine);
+ if (r != 0) { return r; }
+ r = _pid.CompareTo(other._pid);
+ if (r != 0) { return r; }
+ return _increment.CompareTo(other._increment);
+ }
+
+ ///
+ /// Compares this ObjectId to another ObjectId.
+ ///
+ /// The other ObjectId.
+ /// True if the two ObjectIds are equal.
+ public bool Equals(BsonOid rhs)
+ {
+ return
+ _timestamp == rhs._timestamp &&
+ _machine == rhs._machine &&
+ _pid == rhs._pid &&
+ _increment == rhs._increment;
+ }
+
+ ///
+ /// Compares this ObjectId to another object.
+ ///
+ /// The other object.
+ /// True if the other object is an ObjectId and equal to this one.
+ public override bool Equals(object obj)
+ {
+ if (obj is BsonOid)
+ {
+ return Equals((BsonOid)obj);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Gets the hash code.
+ ///
+ /// The hash code.
+ public override int GetHashCode()
+ {
+ int hash = 17;
+ hash = 37 * hash + _timestamp.GetHashCode();
+ hash = 37 * hash + _machine.GetHashCode();
+ hash = 37 * hash + _pid.GetHashCode();
+ hash = 37 * hash + _increment.GetHashCode();
+ return hash;
+ }
+
+ ///
+ /// Converts the ObjectId to a byte array.
+ ///
+ /// A byte array.
+ public byte[] ToByteArray()
+ {
+ return Pack(_timestamp, _machine, _pid, _increment);
+ }
+
+ ///
+ /// Returns a string representation of the value.
+ ///
+ /// A string representation of the value.
+ public override string ToString()
+ {
+ var bytes = Pack(_timestamp, _machine, _pid, _increment);
+ return BitConverter.ToString(bytes).Replace("-", "").ToLower();
+ }
+
+ // explicit IConvertible implementation
+ TypeCode IConvertible.GetTypeCode()
+ {
+ return TypeCode.Object;
+ }
+
+ bool IConvertible.ToBoolean(IFormatProvider provider)
+ {
+ throw new InvalidCastException();
+ }
+
+ byte IConvertible.ToByte(IFormatProvider provider)
+ {
+ throw new InvalidCastException();
+ }
+
+ char IConvertible.ToChar(IFormatProvider provider)
+ {
+ throw new InvalidCastException();
+ }
+
+ DateTime IConvertible.ToDateTime(IFormatProvider provider)
+ {
+ throw new InvalidCastException();
+ }
+
+ decimal IConvertible.ToDecimal(IFormatProvider provider)
+ {
+ throw new InvalidCastException();
+ }
+
+ double IConvertible.ToDouble(IFormatProvider provider)
+ {
+ throw new InvalidCastException();
+ }
+
+ short IConvertible.ToInt16(IFormatProvider provider)
+ {
+ throw new InvalidCastException();
+ }
+
+ int IConvertible.ToInt32(IFormatProvider provider)
+ {
+ throw new InvalidCastException();
+ }
+
+ long IConvertible.ToInt64(IFormatProvider provider)
+ {
+ throw new InvalidCastException();
+ }
+
+ sbyte IConvertible.ToSByte(IFormatProvider provider)
+ {
+ throw new InvalidCastException();
+ }
+
+ float IConvertible.ToSingle(IFormatProvider provider)
+ {
+ throw new InvalidCastException();
+ }
+
+ string IConvertible.ToString(IFormatProvider provider)
+ {
+ return ToString();
+ }
+
+ object IConvertible.ToType(Type conversionType, IFormatProvider provider)
+ {
+ switch (Type.GetTypeCode(conversionType))
+ {
+ case TypeCode.String:
+ return ((IConvertible)this).ToString(provider);
+ case TypeCode.Object:
+ if (conversionType == typeof (BsonOid))
+ return this;
+ if (conversionType == typeof (BsonValue))
+ return BsonValue.Create(this);
+ break;
+ }
+
+ throw new InvalidCastException();
+ }
+
+ ushort IConvertible.ToUInt16(IFormatProvider provider)
+ {
+ throw new InvalidCastException();
+ }
+
+ uint IConvertible.ToUInt32(IFormatProvider provider)
+ {
+ throw new InvalidCastException();
+ }
+
+ ulong IConvertible.ToUInt64(IFormatProvider provider)
+ {
+ throw new InvalidCastException();
+ }
+ }
}
diff --git a/Ejdb.BSON/BSONRegexp.cs b/Ejdb.BSON/BSONRegexp.cs
index 9ec6c8b..8434694 100644
--- a/Ejdb.BSON/BSONRegexp.cs
+++ b/Ejdb.BSON/BSONRegexp.cs
@@ -21,15 +21,15 @@ namespace Ejdb.BSON {
/// BSON Regexp complex value.
///
[Serializable]
- public sealed class BSONRegexp : IBSONValue {
+ public sealed class BsonRegexp : IBsonValue {
readonly string _re;
readonly string _opts;
- public BSONType BSONType {
+ public BsonType BSONType {
get {
- return BSONType.REGEX;
+ return BsonType.REGEX;
}
}
@@ -45,13 +45,13 @@ public string Opts {
}
}
- BSONRegexp() {
+ BsonRegexp() {
}
- public BSONRegexp(string re) : this(re, "") {
+ public BsonRegexp(string re) : this(re, "") {
}
- public BSONRegexp(string re, string opts) {
+ public BsonRegexp(string re, string opts) {
this._re = re;
this._opts = opts;
}
@@ -63,10 +63,10 @@ public override bool Equals(object obj) {
if (ReferenceEquals(this, obj)) {
return true;
}
- if (!(obj is BSONRegexp)) {
+ if (!(obj is BsonRegexp)) {
return false;
}
- BSONRegexp other = (BSONRegexp) obj;
+ BsonRegexp other = (BsonRegexp) obj;
return (_re == other._re && _opts == other._opts);
}
@@ -77,7 +77,7 @@ public override int GetHashCode() {
}
public override string ToString() {
- return string.Format("[BSONRegexp: re={0}, opts={1}]", _re, _opts);
+ return string.Format("[BsonRegexp: re={0}, opts={1}]", _re, _opts);
}
}
}
diff --git a/Ejdb.BSON/BSONTimestamp.cs b/Ejdb.BSON/BSONTimestamp.cs
index e0cf39a..b5fce64 100644
--- a/Ejdb.BSON/BSONTimestamp.cs
+++ b/Ejdb.BSON/BSONTimestamp.cs
@@ -21,22 +21,22 @@ namespace Ejdb.BSON {
/// BSON Timestamp complex value.
///
[Serializable]
- public sealed class BSONTimestamp : IBSONValue {
+ public sealed class BsonTimestamp : IBsonValue {
readonly int _inc;
readonly int _ts;
- BSONTimestamp() {
+ BsonTimestamp() {
}
- public BSONTimestamp(int inc, int ts) {
+ public BsonTimestamp(int inc, int ts) {
this._inc = inc;
this._ts = ts;
}
- public BSONType BSONType {
+ public BsonType BSONType {
get {
- return BSONType.TIMESTAMP;
+ return BsonType.TIMESTAMP;
}
}
@@ -59,10 +59,10 @@ public override bool Equals(object obj) {
if (ReferenceEquals(this, obj)) {
return true;
}
- if (!(obj is BSONTimestamp)) {
+ if (!(obj is BsonTimestamp)) {
return false;
}
- BSONTimestamp other = (BSONTimestamp) obj;
+ BsonTimestamp other = (BsonTimestamp) obj;
return (_inc == other._inc && _ts == other._ts);
}
@@ -73,7 +73,7 @@ public override int GetHashCode() {
}
public override string ToString() {
- return string.Format("[BSONTimestamp: inc={0}, ts={1}]", _inc, _ts);
+ return string.Format("[BsonTimestamp: inc={0}, ts={1}]", _inc, _ts);
}
}
}
diff --git a/Ejdb.BSON/BSONType.cs b/Ejdb.BSON/BSONType.cs
index e7c7498..d74d965 100644
--- a/Ejdb.BSON/BSONType.cs
+++ b/Ejdb.BSON/BSONType.cs
@@ -17,7 +17,7 @@ namespace Ejdb.BSON {
/** BSON types according to the bsonspec (http://bsonspec.org/) */
- public enum BSONType : byte {
+ public enum BsonType : byte {
UNKNOWN = 0xfe,
EOO = 0x00,
DOUBLE = 0x01,
diff --git a/Ejdb.BSON/BSONUndefined.cs b/Ejdb.BSON/BSONUndefined.cs
index 2ea8e3a..596aed4 100644
--- a/Ejdb.BSON/BSONUndefined.cs
+++ b/Ejdb.BSON/BSONUndefined.cs
@@ -17,13 +17,13 @@
namespace Ejdb.BSON {
- public sealed class BSONUndefined : IBSONValue {
+ public sealed class BsonUndefined : IBsonValue {
- public static BSONUndefined VALUE = new BSONUndefined();
+ public static BsonUndefined VALUE = new BsonUndefined();
- public BSONType BSONType {
+ public BsonType BSONType {
get {
- return BSONType.UNDEFINED;
+ return BsonType.UNDEFINED;
}
}
@@ -34,7 +34,7 @@ public override bool Equals(object obj) {
if (ReferenceEquals(this, obj)) {
return true;
}
- if (!(obj is BSONUndefined)) {
+ if (!(obj is BsonUndefined)) {
return false;
}
return true;
@@ -45,7 +45,7 @@ public override int GetHashCode() {
}
public override string ToString() {
- return "[BSONUndefined]";
+ return "[BsonUndefined]";
}
}
}
diff --git a/Ejdb.BSON/BSONValue.cs b/Ejdb.BSON/BSONValue.cs
index c8a6183..aafccb1 100644
--- a/Ejdb.BSON/BSONValue.cs
+++ b/Ejdb.BSON/BSONValue.cs
@@ -14,6 +14,7 @@
// Boston, MA 02111-1307 USA.
// ============================================================================================
using System;
+using System.Collections.Generic;
namespace Ejdb.BSON {
@@ -21,54 +22,50 @@ namespace Ejdb.BSON {
/// BSON field value.
///
[Serializable]
- public sealed class BSONValue : IBSONValue, ICloneable {
+ public sealed class BsonValue : IBsonValue, ICloneable
+ {
///
/// BSON.Type
///
- public BSONType BSONType { get; internal set; }
-
- ///
- /// BSON field key.
- ///
- public string Key { get; internal set; }
+ public BsonType BSONType { get; internal set; }
///
/// Deserialized BSON field value.
///
public object Value { get; internal set; }
- public BSONValue(BSONType type, string key, object value) {
- this.BSONType = type;
- this.Key = key;
- this.Value = value;
+ public BsonValue(BsonType type, object value)
+ {
+ BSONType = type;
+ Value = value;
}
- public BSONValue(BSONType type, string key) : this(type, key, null) {
+ public BsonValue(BsonType type) : this(type, null) {
}
- public override bool Equals(object obj) {
- if (obj == null) {
+ public override bool Equals(object obj)
+ {
+ if (obj == null)
return false;
- }
- if (ReferenceEquals(this, obj)) {
+
+ if (ReferenceEquals(this, obj))
return true;
- }
- if (obj.GetType() != typeof(BSONValue)) {
+
+ if (obj.GetType() != typeof(BsonValue))
return false;
- }
- BSONValue other = (BSONValue) obj;
- if (BSONType != other.BSONType || Key != other.Key) {
+
+ BsonValue other = (BsonValue) obj;
+ if (BSONType != other.BSONType)
return false;
- }
- if (Value != null) {
+
+ if (Value != null)
return Value.Equals(other.Value);
- } else {
- return (Value == other.Value);
- }
+
+ return (Value == other.Value);
}
- public static bool operator ==(BSONValue v1, BSONValue v2) {
+ public static bool operator ==(BsonValue v1, BsonValue v2) {
if (ReferenceEquals(v1, v2)) {
return true;
}
@@ -78,23 +75,205 @@ public override bool Equals(object obj) {
return v1.Equals(v2);
}
- public static bool operator !=(BSONValue v1, BSONValue v2) {
+ public static bool operator !=(BsonValue v1, BsonValue v2)
+ {
return !(v1 == v2);
}
- public override int GetHashCode() {
+ public override int GetHashCode()
+ {
unchecked {
return BSONType.GetHashCode() ^ (Value != null ? Value.GetHashCode() : 0);
}
}
public override string ToString() {
- return string.Format("[BSONValue: BSONType={0}, Key={1}, Value={2}]", BSONType, Key, Value);
+ return String.Format("[BsonValue: BsonType={0}, Value={1}]", BSONType, Value);
+ }
+
+ public string ToStringWithKey(string key)
+ {
+ return String.Format("[BsonValue: BsonType={0}, Key={1}, Value={2}]", BSONType, key, Value);
+ }
+
+ public object Clone()
+ {
+ return new BsonValue(this.BSONType, this.Value);
+ }
+
+ public static BsonValue ValueOf(object v)
+ {
+ if (v == null)
+ return GetNull();
+
+ var arrayValue = v as Array;
+ if (arrayValue != null)
+ return Create(new BsonArray(arrayValue));
+
+ Func