diff options
Diffstat (limited to 'nejdb')
-rw-r--r-- | nejdb/Ejdb.SON/BSONArray.cs | 6 | ||||
-rw-r--r-- | nejdb/Ejdb.SON/BSONDocument.cs | 191 | ||||
-rw-r--r-- | nejdb/Ejdb.SON/BSONIterator.cs | 4 | ||||
-rw-r--r-- | nejdb/Ejdb.SON/BSONOid.cs | 11 | ||||
-rw-r--r-- | nejdb/Ejdb.SON/BSONValue.cs | 47 | ||||
-rw-r--r-- | nejdb/Ejdb.Tests/TestBSON.cs | 60 |
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()); + } } } |