summaryrefslogtreecommitdiff
path: root/nejdb
diff options
context:
space:
mode:
Diffstat (limited to 'nejdb')
-rw-r--r--nejdb/Ejdb.SON/BSONArray.cs6
-rw-r--r--nejdb/Ejdb.SON/BSONDocument.cs191
-rw-r--r--nejdb/Ejdb.SON/BSONIterator.cs4
-rw-r--r--nejdb/Ejdb.SON/BSONOid.cs11
-rw-r--r--nejdb/Ejdb.SON/BSONValue.cs47
-rw-r--r--nejdb/Ejdb.Tests/TestBSON.cs60
6 files changed, 277 insertions, 42 deletions
diff --git a/nejdb/Ejdb.SON/BSONArray.cs b/nejdb/Ejdb.SON/BSONArray.cs
index 6d66215..b05db90 100644
--- a/nejdb/Ejdb.SON/BSONArray.cs
+++ b/nejdb/Ejdb.SON/BSONArray.cs
@@ -25,6 +25,12 @@ namespace Ejdb.SON {
}
}
+ public object this[int key] {
+ get {
+ return GetObjectValue(key.ToString());
+ }
+ }
+
public BSONDocument SetNull(int idx) {
return base.SetNull(idx.ToString());
}
diff --git a/nejdb/Ejdb.SON/BSONDocument.cs b/nejdb/Ejdb.SON/BSONDocument.cs
index 2784288..fd4c657 100644
--- a/nejdb/Ejdb.SON/BSONDocument.cs
+++ b/nejdb/Ejdb.SON/BSONDocument.cs
@@ -27,16 +27,43 @@ namespace Ejdb.SON {
/// BSON document deserialized data wrapper.
/// </summary>
[Serializable]
- public class BSONDocument : IBSONValue {
+ public class BSONDocument : IBSONValue, IEnumerable<BSONValue> {
Dictionary<string, BSONValue> _fields;
readonly List<BSONValue> _fieldslist;
-
+ int? _cachedhash;
+
+ /// <summary>
+ /// BSON Type this document.
+ /// </summary>
+ /// <remarks>
+ /// Type can be either <see cref="Ejdb.SON.BSONType.OBJECT"/> or <see cref="Ejdb.SON.BSONType.ARRAY"/>
+ /// </remarks>
+ /// <value>The type of the BSON.</value>
public virtual BSONType BSONType {
get {
return BSONType.OBJECT;
}
}
+ /// <summary>
+ /// Gets the document keys.
+ /// </summary>
+ public ICollection<string> Keys {
+ get {
+ CheckFields();
+ return _fields.Keys;
+ }
+ }
+
+ /// <summary>
+ /// Gets count of document keys.
+ /// </summary>
+ public int KeysCount {
+ get {
+ return _fieldslist.Count;
+ }
+ }
+
public BSONDocument() {
this._fields = null;
this._fieldslist = new List<BSONValue>();
@@ -64,6 +91,78 @@ namespace Ejdb.SON {
}
}
+ public BSONDocument(BSONDocument doc) : this() {
+ foreach (var bv in doc) {
+ Add((BSONValue) bv.Clone());
+ }
+ }
+
+ public IEnumerator<BSONValue> GetEnumerator() {
+ return _fieldslist.GetEnumerator();
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
+ return GetEnumerator();
+ }
+
+ /// <summary>
+ /// Convert BSON document object into BSON binary data byte array.
+ /// </summary>
+ /// <returns>The byte array.</returns>
+ public byte[] ToByteArray() {
+ byte[] res;
+ using (var ms = new MemoryStream()) {
+ Serialize(ms);
+ res = ms.ToArray();
+ }
+ return res;
+ }
+
+ public string ToDebugDataString() {
+ return BitConverter.ToString(ToByteArray());
+ }
+
+ /// <summary>
+ /// Gets the field value.
+ /// </summary>
+ /// <returns>The BSON value.</returns>
+ /// <see cref="Ejdb.SON.BSONValue"/>
+ /// <param name="key">Document field name</param>
+ public BSONValue GetBSONValue(string key) {
+ CheckFields();
+ BSONValue ov;
+ if (_fields.TryGetValue(key, out ov)) {
+ return ov;
+ } else {
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Gets the field value object.
+ /// </summary>
+ /// <returns>The object value.</returns>
+ /// <see cref="Ejdb.SON.BSONValue"/>
+ /// <param name="key">Key.</param>
+ /// <returns>Key object </c> or <c>null</c> if the key is not exists or value type is either
+ /// <see cref="Ejdb.SON.BSONType.NULL"/> or <see cref="Ejdb.SON.BSONType.UNDEFINED"/></returns>
+ public object GetObjectValue(string key) {
+ var bv = GetBSONValue(key);
+ return bv != null ? bv.Value : null;
+ }
+
+ /// <summary>
+ /// Gets the <see cref="Ejdb.SON.BSONDocument"/> with the specified key.
+ /// </summary>
+ /// <param name="key">Key.</param>
+ /// <returns>Key object </c> or <c>null</c> if the key is not exists or value type is either
+ /// <see cref="Ejdb.SON.BSONType.NULL"/> or <see cref="Ejdb.SON.BSONType.UNDEFINED"/></returns>
+ public object this[string key] {
+ get {
+ return GetObjectValue(key);
+ }
+ }
+
public BSONDocument SetNull(string key) {
return SetBSONValue(new BSONValue(BSONType.NULL, key));
}
@@ -149,15 +248,28 @@ namespace Ejdb.SON {
}
public BSONValue DropValue(string key) {
- var bv = this[key];
+ var bv = GetBSONValue(key);
if (bv == null) {
return bv;
}
+ _cachedhash = null;
_fields.Remove(key);
_fieldslist.RemoveAll(x => x.Key == key);
return bv;
}
+ /// <summary>
+ /// Removes all data from document.
+ /// </summary>
+ 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;
@@ -189,16 +301,57 @@ namespace Ejdb.SON {
}
os.Flush();
}
- //.//////////////////////////////////////////////////////////////////
- // Private staff
- //.//////////////////////////////////////////////////////////////////
- internal BSONValue this[string key] {
- get {
- return GetBSONValue(key);
+
+ public override bool Equals(object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (ReferenceEquals(this, obj)) {
+ return true;
+ }
+ if (!(obj is BSONDocument)) {
+ return false;
+ }
+ BSONDocument d1 = this;
+ BSONDocument 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) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public override int GetHashCode() {
+ if (_cachedhash != null) {
+ return (int) _cachedhash;
+ }
+ unchecked {
+ int hash = 1;
+ foreach (var bv in _fieldslist) {
+ hash = (hash * 31) + bv.GetHashCode();
+ }
+ _cachedhash = hash;
}
+ return (int) _cachedhash;
+ }
+
+ public static bool operator ==(BSONDocument d1, BSONDocument d2) {
+ return Equals(d1, d2);
}
+ public static bool operator !=(BSONDocument d1, BSONDocument d2) {
+ return !(d1 == d2);
+ }
+ //.//////////////////////////////////////////////////////////////////
+ // Private staff
+ //.//////////////////////////////////////////////////////////////////
internal BSONDocument Add(BSONValue bv) {
+ _cachedhash = null;
_fieldslist.Add(bv);
if (_fields != null) {
_fields[bv.Key] = bv;
@@ -206,31 +359,21 @@ namespace Ejdb.SON {
return this;
}
- internal BSONValue GetBSONValue(string key) {
- CheckFields();
- return _fields[key];
- }
-
internal BSONDocument SetBSONValue(BSONValue val) {
+ _cachedhash = null;
CheckFields();
- var ov = _fields[val.Key];
- if (ov != null) {
+ BSONValue ov;
+ if (_fields.TryGetValue(val.Key, out ov)) {
ov.Key = val.Key;
ov.BSONType = val.BSONType;
ov.Value = val.Value;
} else {
_fieldslist.Add(val);
- _fields[val.Key] = val;
+ _fields.Add(val.Key, val);
}
return this;
}
- internal object GetObjectValue(string key) {
- CheckFields();
- var bv = _fields[key];
- return bv != null ? bv.Value : null;
- }
-
protected virtual void CheckKey(string key) {
}
@@ -340,7 +483,7 @@ namespace Ejdb.SON {
protected void WriteTypeAndKey(BSONValue bv, ExtBinaryWriter bw) {
bw.Write((byte) bv.BSONType);
- bw.WriteBSONString(bv.Key);
+ bw.WriteCString(bv.Key);
}
protected void CheckFields() {
diff --git a/nejdb/Ejdb.SON/BSONIterator.cs b/nejdb/Ejdb.SON/BSONIterator.cs
index a3373d8..6821baf 100644
--- a/nejdb/Ejdb.SON/BSONIterator.cs
+++ b/nejdb/Ejdb.SON/BSONIterator.cs
@@ -55,7 +55,7 @@ namespace Ejdb.SON {
throw new IOException("Input stream must be seekable");
}
this._input = new ExtBinaryReader(input);
- this._ctype = BSONType.EOO;
+ this._ctype = BSONType.UNKNOWN;
this._doclen = _input.ReadInt32();
if (this._doclen < 5) {
Dispose();
@@ -94,7 +94,7 @@ namespace Ejdb.SON {
if (_ctype == BSONType.EOO) {
return BSONType.EOO;
}
- if (_ctype != BSONType.UNKNOWN) {
+ if (!_entryDataSkipped && _ctype != BSONType.UNKNOWN) {
SkipData();
}
byte bv = _input.ReadByte();
diff --git a/nejdb/Ejdb.SON/BSONOid.cs b/nejdb/Ejdb.SON/BSONOid.cs
index 192d3da..9059792 100644
--- a/nejdb/Ejdb.SON/BSONOid.cs
+++ b/nejdb/Ejdb.SON/BSONOid.cs
@@ -91,6 +91,12 @@ namespace Ejdb.SON {
}
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);
}
@@ -102,10 +108,9 @@ namespace Ejdb.SON {
}
public static bool operator ==(BSONOid a, BSONOid b) {
- if (ReferenceEquals(a, b)) {
+ if (ReferenceEquals(a, b))
return true;
- }
- if (a == null || b == null) {
+ if ((object) a == null || (object) b == null) {
return false;
}
return a.Equals(b);
diff --git a/nejdb/Ejdb.SON/BSONValue.cs b/nejdb/Ejdb.SON/BSONValue.cs
index f73c62a..9f3c77f 100644
--- a/nejdb/Ejdb.SON/BSONValue.cs
+++ b/nejdb/Ejdb.SON/BSONValue.cs
@@ -21,7 +21,7 @@ namespace Ejdb.SON {
/// BSON field value.
/// </summary>
[Serializable]
- public sealed class BSONValue : IBSONValue {
+ public sealed class BSONValue : IBSONValue, ICloneable {
/// <summary>
/// BSON.Type
@@ -47,9 +47,54 @@ namespace Ejdb.SON {
public BSONValue(BSONType type, string key) : this(type, key, null) {
}
+ public override bool Equals(object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (ReferenceEquals(this, obj)) {
+ return true;
+ }
+ if (obj.GetType() != typeof(BSONValue)) {
+ return false;
+ }
+ BSONValue other = (BSONValue) obj;
+ if (BSONType != other.BSONType || Key != other.Key) {
+ return false;
+ }
+ if (Value != null) {
+ return Value.Equals(other.Value);
+ } else {
+ return (Value == other.Value);
+ }
+ }
+
+ public static bool operator ==(BSONValue v1, BSONValue v2) {
+ if (ReferenceEquals(v1, v2)) {
+ return true;
+ }
+ if ((object) v1 == null || (object) v2 == null) {
+ return false;
+ }
+ return v1.Equals(v2);
+ }
+
+ public static bool operator !=(BSONValue v1, BSONValue v2) {
+ return !(v1 == v2);
+ }
+
+ 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);
}
+
+ public object Clone() {
+ return new BSONValue(this.BSONType, this.Key, this.Value);
+ }
}
}
diff --git a/nejdb/Ejdb.Tests/TestBSON.cs b/nejdb/Ejdb.Tests/TestBSON.cs
index 1cb9422..f9ac5a8 100644
--- a/nejdb/Ejdb.Tests/TestBSON.cs
+++ b/nejdb/Ejdb.Tests/TestBSON.cs
@@ -25,24 +25,60 @@ namespace Ejdb.Tests {
[Test]
public void TestSerializeEmpty() {
- MemoryStream ms = new MemoryStream();
BSONDocument doc = new BSONDocument();
- doc.Serialize(ms);
- string hv = TestUtils.ToHexString(ms.ToArray());
- ms.Close();
- Assert.AreEqual("05-00-00-00-00", hv);
+ Assert.AreEqual("05-00-00-00-00", doc.ToDebugDataString());
}
[Test]
- public void TestSerializeSimple() {
- MemoryStream ms = new MemoryStream();
+ public void TestSerialize1() {
+ byte[] bdata;
BSONDocument doc = new BSONDocument();
- doc.Serialize(ms);
- string hv = TestUtils.ToHexString(ms.ToArray());
- ms.Close();
- Console.WriteLine("HV=" + hv);
- }
+ doc.SetNumber("0", 1);
+ //0C-00-00-00 len
+ //10 type
+ //30-00 key
+ //01-00-00-00 int val
+ //00 zero term
+ bdata = doc.ToByteArray();
+ Assert.AreEqual("0C-00-00-00-10-30-00-01-00-00-00-00", doc.ToDebugDataString());
+ Assert.AreEqual(bdata.Length, (int) Convert.ToByte(doc.ToDebugDataString().Substring(0, 2), 16));
+
+ BSONDocument doc2 = new BSONDocument(doc.ToByteArray());
+ Assert.AreEqual(1, doc2.KeysCount);
+ int c = 0;
+ foreach (BSONValue bv in doc2) {
+ c++;
+ Assert.IsNotNull(bv);
+ Assert.AreEqual(BSONType.INT, bv.BSONType);
+ Assert.AreEqual("0", bv.Key);
+ Assert.AreEqual(1, bv.Value);
+ }
+ Assert.That(c > 0);
+ doc2.SetNumber("0", 2);
+ Assert.AreEqual(1, doc2.KeysCount);
+ object ival = doc2["0"];
+ Assert.IsInstanceOfType(typeof(int), ival);
+ Assert.AreEqual(2, ival);
+
+ doc2.SetNumber("1", Int32.MaxValue);
+ //13-00-00-00
+ //10
+ //30-00
+ //02-00-00-00
+ //10-31-00
+ //FF-FF-FF-7F
+ //00
+ Assert.AreEqual("13-00-00-00-10-30-00-02-00-00-00-10-31-00-FF-FF-FF-7F-00",
+ doc2.ToDebugDataString());
+ doc2 = new BSONDocument(doc2);
+ Assert.AreEqual("13-00-00-00-10-30-00-02-00-00-00-10-31-00-FF-FF-FF-7F-00",
+ doc2.ToDebugDataString());
+
+ doc2 = new BSONDocument(doc2.ToByteArray());
+ Assert.AreEqual("13-00-00-00-10-30-00-02-00-00-00-10-31-00-FF-FF-FF-7F-00",
+ doc2.ToDebugDataString());
+ }
}
}