Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id CAFCC200B81 for ; Tue, 13 Sep 2016 18:31:23 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id C982B160AD2; Tue, 13 Sep 2016 16:31:23 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id E93CC160AAA for ; Tue, 13 Sep 2016 18:31:21 +0200 (CEST) Received: (qmail 42444 invoked by uid 500); 13 Sep 2016 16:31:20 -0000 Mailing-List: contact commits-help@ignite.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@ignite.apache.org Delivered-To: mailing list commits@ignite.apache.org Received: (qmail 42435 invoked by uid 99); 13 Sep 2016 16:31:20 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 13 Sep 2016 16:31:20 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 83D21DFCC0; Tue, 13 Sep 2016 16:31:20 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: ptupitsyn@apache.org To: commits@ignite.apache.org Message-Id: <696782bc97e2411ebab13bbf8fc93c7d@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: ignite git commit: Merge session state item collection classes Date: Tue, 13 Sep 2016 16:31:20 +0000 (UTC) archived-at: Tue, 13 Sep 2016 16:31:24 -0000 Repository: ignite Updated Branches: refs/heads/ignite-3199-1 815cde40c -> 11e69b482 Merge session state item collection classes Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/11e69b48 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/11e69b48 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/11e69b48 Branch: refs/heads/ignite-3199-1 Commit: 11e69b482bc7d9b18cad9ffe6b21e641bfc61a65 Parents: 815cde4 Author: Pavel Tupitsyn Authored: Tue Sep 13 19:31:08 2016 +0300 Committer: Pavel Tupitsyn Committed: Tue Sep 13 19:31:08 2016 +0300 ---------------------------------------------------------------------- .../Apache.Ignite.AspNet.csproj | 3 +- .../Impl/IgniteSessionStateItemCollection.cs | 492 ++++++++++++++++-- .../Impl/IgniteSessionStateStoreData.cs | 10 +- .../Impl/KeyValueDirtyTrackedCollection.cs | 507 ------------------- .../Apache.Ignite.Core.Tests.csproj | 1 - .../IgniteSessionStateItemCollectionTest.cs | 176 ++++++- .../KeyValueDirtyTrackedCollectionTest.cs | 260 ---------- 7 files changed, 608 insertions(+), 841 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/11e69b48/modules/platforms/dotnet/Apache.Ignite.AspNet/Apache.Ignite.AspNet.csproj ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet/Apache.Ignite.AspNet.csproj b/modules/platforms/dotnet/Apache.Ignite.AspNet/Apache.Ignite.AspNet.csproj index b17c9f3..1ac452f 100644 --- a/modules/platforms/dotnet/Apache.Ignite.AspNet/Apache.Ignite.AspNet.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.AspNet/Apache.Ignite.AspNet.csproj @@ -51,9 +51,8 @@ - - + http://git-wip-us.apache.org/repos/asf/ignite/blob/11e69b48/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/IgniteSessionStateItemCollection.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/IgniteSessionStateItemCollection.cs b/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/IgniteSessionStateItemCollection.cs index 946e277..1e17bcb 100644 --- a/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/IgniteSessionStateItemCollection.cs +++ b/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/IgniteSessionStateItemCollection.cs @@ -19,49 +19,90 @@ namespace Apache.Ignite.AspNet.Impl { using System; using System.Collections; + using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + using System.IO; + using System.Linq; + using System.Runtime.Serialization.Formatters.Binary; using System.Web.SessionState; + using Apache.Ignite.Core.Binary; using Apache.Ignite.Core.Impl.Common; /// - /// Wrapper for . + /// Binarizable key-value collection with dirty item tracking. /// internal class IgniteSessionStateItemCollection : ISessionStateItemCollection { - /** Wrapped collection */ - private readonly KeyValueDirtyTrackedCollection _collection; + /** */ + private readonly Dictionary _dict; + + /** */ + private readonly List _list; + + /** Indicates where this is a new collection, not a deserialized old one. */ + private readonly bool _isNew; + + /** Indicates that this instance is a diff. */ + private readonly bool _isDiff; + + /** Removed keys. Hash set because keys can be removed multiple times. */ + private HashSet _removedKeys; + + /** Indicates that entire collection is dirty and can't be written as a diff. */ + private bool _dirtyAll; /// /// Initializes a new instance of the class. /// - /// The collection. - public IgniteSessionStateItemCollection(KeyValueDirtyTrackedCollection collection) + /// The binary reader. + internal IgniteSessionStateItemCollection(IBinaryRawReader reader) { - Debug.Assert(collection != null); + Debug.Assert(reader != null); + + _isDiff = reader.ReadBoolean(); + + var count = reader.ReadInt(); + + _dict = new Dictionary(count); + _list = new List(count); + + for (var i = 0; i < count; i++) + { + var key = reader.ReadString(); + + var valBytes = reader.ReadByteArray(); + + if (valBytes != null) + { + var entry = new Entry(key, true, valBytes); + + _dict[key] = _list.Count; - // TODO: Merge classes - _collection = collection; + _list.Add(entry); + } + else + AddRemovedKey(key); + } + + _isNew = false; } /// - /// Gets the collection. + /// Initializes a new instance of the class. /// - public KeyValueDirtyTrackedCollection Collection + public IgniteSessionStateItemCollection() { - get { return _collection; } + _dict = new Dictionary(); + _list = new List(); + _isNew = true; } /** */ - public IEnumerator GetEnumerator() - { - // This should return only keys. - return _collection.GetKeys().GetEnumerator(); - } - - /** */ - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Validated.")] + [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", + Justification = "Validation is present.")] public void CopyTo(Array array, int index) { IgniteArgumentCheck.NotNull(array, "array"); @@ -69,20 +110,27 @@ namespace Apache.Ignite.AspNet.Impl "The number of elements in the source collection is greater than the available space " + "from specified index to the end of the array."); - foreach (var key in _collection.GetKeys()) + foreach (var key in GetKeys()) array.SetValue(key, index++); } /** */ + public IEnumerator GetEnumerator() + { + // This should return only keys. + return GetKeys().GetEnumerator(); + } + + /** */ public int Count { - get { return _collection.Count; } + get { return _dict.Count; } } /** */ public object SyncRoot { - get { return _collection; } + get { return _list; } } /** */ @@ -92,48 +140,404 @@ namespace Apache.Ignite.AspNet.Impl } /** */ - public void Remove(string name) + public object this[string key] { - _collection.Remove(name); + get + { + var entry = GetEntry(key); + + if (entry == null) + return null; + + SetDirtyOnRead(entry); + + return entry.Value; + } + set + { + var entry = GetOrCreateDirtyEntry(key); + + entry.Value = value; + } } /** */ - public void RemoveAt(int index) + public object this[int index] { - _collection.RemoveAt(index); + get + { + var entry = _list[index]; + + SetDirtyOnRead(entry); + + return entry.Value; + } + set + { + var entry = _list[index]; + + entry.IsDirty = true; + + entry.Value = value; + } } /** */ - public void Clear() + public NameObjectCollectionBase.KeysCollection Keys { - _collection.Clear(); + get { return new NameObjectCollection(this).Keys; } } + /** */ - public object this[string name] + public bool Dirty { - get { return _collection[name]; } - set { _collection[name] = value; } + get { return _dirtyAll || _list.Any(x => x.IsDirty); } + set { _dirtyAll = value; } } - /** */ - public object this[int index] + /// + /// Gets or sets a value indicating whether only dirty changed things should be serialized. + /// + public bool WriteChangesOnly { get; set; } + + /// + /// Gets the keys. + /// + public IEnumerable GetKeys() { - get { return _collection[index]; } - set { _collection[index] = value; } + return _list.Select(x => x.Key); } - /** */ - public NameObjectCollectionBase.KeysCollection Keys + /// + /// Writes this object to the given writer. + /// + /// Writer. + public void WriteBinary(IBinaryRawWriter writer) { - get { return new NameObjectCollection(this).Keys; } + IgniteArgumentCheck.NotNull(writer, "writer"); + + if (_isDiff) + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, + "Cannot serialize incomplete {0}.", GetType())); + + if (_isNew || _dirtyAll || !WriteChangesOnly || (_removedKeys == null && _list.All(x => x.IsDirty))) + { + // Write in full mode. + writer.WriteBoolean(false); + writer.WriteInt(_list.Count); + + foreach (var entry in _list) + { + writer.WriteString(entry.Key); + + // Write as byte array to enable partial deserialization. + writer.WriteByteArray(entry.GetBytes()); + } + } + else + { + // Write in diff mode. + writer.WriteBoolean(true); + + var removed = GetRemovedKeys(); + + var count = _list.Count(x => x.IsDirty) + (removed == null ? 0 : removed.Count); + + writer.WriteInt(count); // reserve count + + // Write removed keys as [key + null]. + if (removed != null) + { + foreach (var removedKey in removed) + { + writer.WriteString(removedKey); + writer.WriteByteArray(null); + } + } + + // Write dirty items. + foreach (var entry in _list) + { + if (!entry.IsDirty) + continue; + + writer.WriteString(entry.Key); + + // Write as byte array to enable partial deserialization. + writer.WriteByteArray(entry.GetBytes()); + } + } } - /** */ - public bool Dirty + /// + /// Gets the removed keys. + /// + private ICollection GetRemovedKeys() + { + if (_removedKeys == null) + return null; + + // Filter out existing keys. + var removed = new HashSet(_removedKeys); + + foreach (var entry in _list) + removed.Remove(entry.Key); + + return removed; + } + + /// + /// Removes the specified key. + /// + public void Remove(string key) + { + var index = GetIndex(key); + + if (index < 0) + return; + + var entry = _list[index]; + Debug.Assert(key == entry.Key); + + _list.RemoveAt(index); + _dict.Remove(key); + + // Update all indexes. + for (var i = 0; i < _list.Count; i++) + _dict[_list[i].Key] = i; + + if (entry.IsInitial) + AddRemovedKey(key); + } + + /// + /// Removes at specified index. + /// + public void RemoveAt(int index) + { + var entry = _list[index]; + + _list.RemoveAt(index); + _dict.Remove(entry.Key); + + if (entry.IsInitial) + AddRemovedKey(entry.Key); + } + + /// + /// Clears this instance. + /// + public void Clear() + { + foreach (var entry in _list) + { + if (entry.IsInitial) + AddRemovedKey(entry.Key); + } + + _list.Clear(); + _dict.Clear(); + + _dirtyAll = true; + } + + /// + /// Applies the changes. + /// + public void ApplyChanges(IgniteSessionStateItemCollection changes) + { + var removed = changes._removedKeys; + + if (removed != null) + { + foreach (var key in removed) + Remove(key); + } + else if (!changes._isDiff) + { + // Not a diff: replace all. + Clear(); + } + + foreach (var changedEntry in changes._list) + { + var entry = GetOrCreateDirtyEntry(changedEntry.Key); + + // Copy without deserialization. + changedEntry.CopyTo(entry); + } + } + + /// + /// Adds the removed key. + /// + private void AddRemovedKey(string key) + { + Debug.Assert(!_isNew); + + if (_removedKeys == null) + _removedKeys = new HashSet(); + + _removedKeys.Add(key); + } + + /// + /// Gets or creates an entry. + /// + private Entry GetOrCreateDirtyEntry(string key) + { + var entry = GetEntry(key); + + if (entry == null) + { + entry = new Entry(key, false, null); + + _dict[key] = _list.Count; + _list.Add(entry); + } + + entry.IsDirty = true; + + return entry; + } + + /// + /// Gets the entry. + /// + private Entry GetEntry(string key) + { + IgniteArgumentCheck.NotNull(key, "key"); + + int index; + + return !_dict.TryGetValue(key, out index) ? null : _list[index]; + } + + /// + /// Gets the index. + /// + private int GetIndex(string key) + { + int index; + + return !_dict.TryGetValue(key, out index) ? -1 : index; + } + + /// + /// Sets the dirty on read. + /// + private static void SetDirtyOnRead(Entry entry) + { + var type = entry.Value.GetType(); + + if (IsImmutable(type)) + return; + + entry.IsDirty = true; + } + + /// + /// Determines whether the specified type is immutable. + /// + private static bool IsImmutable(Type type) { - get { return _collection.IsDirty; } - set { _collection.IsDirty = value; } + type = Nullable.GetUnderlyingType(type) ?? type; // Unwrap nullable. + + if (type.IsPrimitive) + return true; + + if (type == typeof(string) || type == typeof(DateTime) || type == typeof(Guid) || type == typeof(decimal)) + return true; + + return false; + } + + /// + /// Inner entry. + /// + private class Entry + { + /** */ + public readonly bool IsInitial; + + /** */ + public readonly string Key; + + /** */ + public bool IsDirty; + + /** */ + private object _value; + + /** */ + private bool _isDeserialized; + + /// + /// Initializes a new instance of the class. + /// + public Entry(string key, bool isInitial, object value) + { + Debug.Assert(key != null); + + Key = key; + IsInitial = isInitial; + _isDeserialized = !isInitial; + _value = value; + } + + /// + /// Gets or sets the value. + /// + public object Value + { + get + { + if (!_isDeserialized) + { + using (var stream = new MemoryStream((byte[])_value)) + { + _value = new BinaryFormatter().Deserialize(stream); + } + + _isDeserialized = true; + } + + return _value; + } + set + { + _value = value; + _isDeserialized = true; + } + } + + /// + /// Copies contents to another entry. + /// + public void CopyTo(Entry entry) + { + Debug.Assert(entry != null); + + entry._isDeserialized = _isDeserialized; + entry._value = _value; + } + + /// + /// Gets the bytes. + /// + public byte[] GetBytes() + { + if (!_isDeserialized) + return (byte[]) _value; + + using (var stream = new MemoryStream()) + { + new BinaryFormatter().Serialize(stream, _value); + + return stream.ToArray(); + } + } } /// @@ -153,4 +557,4 @@ namespace Apache.Ignite.AspNet.Impl } } } -} \ No newline at end of file +} http://git-wip-us.apache.org/repos/asf/ignite/blob/11e69b48/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/IgniteSessionStateStoreData.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/IgniteSessionStateStoreData.cs b/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/IgniteSessionStateStoreData.cs index bdd619c..44b0633 100644 --- a/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/IgniteSessionStateStoreData.cs +++ b/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/IgniteSessionStateStoreData.cs @@ -33,7 +33,7 @@ namespace Apache.Ignite.AspNet.Impl /// /// The reader. public IgniteSessionStateStoreData(IBinaryRawReader reader) : base( - new IgniteSessionStateItemCollection(new KeyValueDirtyTrackedCollection(reader)), + new IgniteSessionStateItemCollection(reader), DeserializeStaticObjects(reader.ReadByteArray()), reader.ReadInt()) { LockNodeId = reader.ReadGuid(); @@ -47,7 +47,7 @@ namespace Apache.Ignite.AspNet.Impl /// Writer. public void WriteBinary(IBinaryRawWriter writer) { - ((IgniteSessionStateItemCollection)Items).Collection.WriteBinary(writer); + ((IgniteSessionStateItemCollection)Items).WriteBinary(writer); writer.WriteByteArray(SerializeStaticObjects()); writer.WriteInt(Timeout); @@ -76,8 +76,8 @@ namespace Apache.Ignite.AspNet.Impl /// public bool WriteChangesOnly { - get { return ((IgniteSessionStateItemCollection) Items).Collection.WriteChangesOnly; } - set { ((IgniteSessionStateItemCollection) Items).Collection.WriteChangesOnly = value; } + get { return ((IgniteSessionStateItemCollection) Items).WriteChangesOnly; } + set { ((IgniteSessionStateItemCollection) Items).WriteChangesOnly = value; } } /// @@ -86,7 +86,7 @@ namespace Apache.Ignite.AspNet.Impl /// The static objects. /// The timeout. public IgniteSessionStateStoreData(HttpStaticObjectsCollection staticObjects, int timeout) - : base(new IgniteSessionStateItemCollection(new KeyValueDirtyTrackedCollection()), staticObjects, timeout) + : base(new IgniteSessionStateItemCollection(), staticObjects, timeout) { // No-op. } http://git-wip-us.apache.org/repos/asf/ignite/blob/11e69b48/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/KeyValueDirtyTrackedCollection.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/KeyValueDirtyTrackedCollection.cs b/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/KeyValueDirtyTrackedCollection.cs deleted file mode 100644 index e70cea4..0000000 --- a/modules/platforms/dotnet/Apache.Ignite.AspNet/Impl/KeyValueDirtyTrackedCollection.cs +++ /dev/null @@ -1,507 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace Apache.Ignite.AspNet.Impl -{ - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Apache.Ignite.Core.Binary; - using Apache.Ignite.Core.Impl.Common; - - /// - /// Binarizable key-value collection with dirty item tracking. - /// - internal class KeyValueDirtyTrackedCollection - { - /** */ - private readonly Dictionary _dict; - - /** */ - private readonly List _list; - - /** Indicates where this is a new collection, not a deserialized old one. */ - private readonly bool _isNew; - - /** Indicates that this instance is a diff. */ - private readonly bool _isDiff; - - /** Removed keys. Hash set because keys can be removed multiple times. */ - private HashSet _removedKeys; - - /** Indicates that entire collection is dirty and can't be written as a diff. */ - private bool _dirtyAll; - - /// - /// Initializes a new instance of the class. - /// - /// The binary reader. - internal KeyValueDirtyTrackedCollection(IBinaryRawReader reader) - { - Debug.Assert(reader != null); - - _isDiff = reader.ReadBoolean(); - - var count = reader.ReadInt(); - - _dict = new Dictionary(count); - _list = new List(count); - - for (var i = 0; i < count; i++) - { - var key = reader.ReadString(); - - var valBytes = reader.ReadByteArray(); - - if (valBytes != null) - { - var entry = new Entry(key, true, valBytes); - - _dict[key] = _list.Count; - - _list.Add(entry); - } - else - AddRemovedKey(key); - } - - _isNew = false; - } - - /// - /// Initializes a new instance of the class. - /// - public KeyValueDirtyTrackedCollection() - { - _dict = new Dictionary(); - _list = new List(); - _isNew = true; - } - - /// - /// Gets the number of elements contained in the . - /// - public int Count - { - get { return _dict.Count; } - } - - /// - /// Gets or sets the value with the specified key. - /// - public object this[string key] - { - get - { - var entry = GetEntry(key); - - if (entry == null) - return null; - - SetDirtyOnRead(entry); - - return entry.Value; - } - set - { - var entry = GetOrCreateDirtyEntry(key); - - entry.Value = value; - } - } - - /// - /// Gets or sets the value at the specified index. - /// - public object this[int index] - { - get - { - var entry = _list[index]; - - SetDirtyOnRead(entry); - - return entry.Value; - } - set - { - var entry = _list[index]; - - entry.IsDirty = true; - - entry.Value = value; - } - } - - /// - /// Gets or sets a value indicating whether this instance is dirty. - /// - public bool IsDirty - { - get { return _dirtyAll || _list.Any(x => x.IsDirty); } - set { _dirtyAll = value; } - } - - /// - /// Gets or sets a value indicating whether only dirty changed things should be serialized. - /// - public bool WriteChangesOnly { get; set; } - - /// - /// Gets the keys. - /// - public IEnumerable GetKeys() - { - return _list.Select(x => x.Key); - } - - /// - /// Writes this object to the given writer. - /// - /// Writer. - public void WriteBinary(IBinaryRawWriter writer) - { - IgniteArgumentCheck.NotNull(writer, "writer"); - - if (_isDiff) - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, - "Cannot serialize incomplete {0}.", GetType())); - - if (_isNew || _dirtyAll || !WriteChangesOnly || (_removedKeys == null && _list.All(x => x.IsDirty))) - { - // Write in full mode. - writer.WriteBoolean(false); - writer.WriteInt(_list.Count); - - foreach (var entry in _list) - { - writer.WriteString(entry.Key); - - // Write as byte array to enable partial deserialization. - writer.WriteByteArray(entry.GetBytes()); - } - } - else - { - // Write in diff mode. - writer.WriteBoolean(true); - - var removed = GetRemovedKeys(); - - var count = _list.Count(x => x.IsDirty) + (removed == null ? 0 : removed.Count); - - writer.WriteInt(count); // reserve count - - // Write removed keys as [key + null]. - if (removed != null) - { - foreach (var removedKey in removed) - { - writer.WriteString(removedKey); - writer.WriteByteArray(null); - } - } - - // Write dirty items. - foreach (var entry in _list) - { - if (!entry.IsDirty) - continue; - - writer.WriteString(entry.Key); - - // Write as byte array to enable partial deserialization. - writer.WriteByteArray(entry.GetBytes()); - } - } - } - - /// - /// Gets the removed keys. - /// - private ICollection GetRemovedKeys() - { - if (_removedKeys == null) - return null; - - // Filter out existing keys. - var removed = new HashSet(_removedKeys); - - foreach (var entry in _list) - removed.Remove(entry.Key); - - return removed; - } - - /// - /// Removes the specified key. - /// - public void Remove(string key) - { - var index = GetIndex(key); - - if (index < 0) - return; - - var entry = _list[index]; - Debug.Assert(key == entry.Key); - - _list.RemoveAt(index); - _dict.Remove(key); - - // Update all indexes. - for (var i = 0; i < _list.Count; i++) - _dict[_list[i].Key] = i; - - if (entry.IsInitial) - AddRemovedKey(key); - } - - /// - /// Removes at specified index. - /// - public void RemoveAt(int index) - { - var entry = _list[index]; - - _list.RemoveAt(index); - _dict.Remove(entry.Key); - - if (entry.IsInitial) - AddRemovedKey(entry.Key); - } - - /// - /// Clears this instance. - /// - public void Clear() - { - foreach (var entry in _list) - { - if (entry.IsInitial) - AddRemovedKey(entry.Key); - } - - _list.Clear(); - _dict.Clear(); - - _dirtyAll = true; - } - - /// - /// Applies the changes. - /// - public void ApplyChanges(KeyValueDirtyTrackedCollection changes) - { - var removed = changes._removedKeys; - - if (removed != null) - { - foreach (var key in removed) - Remove(key); - } - else if (!changes._isDiff) - { - // Not a diff: replace all. - Clear(); - } - - foreach (var changedEntry in changes._list) - { - var entry = GetOrCreateDirtyEntry(changedEntry.Key); - - // Copy without deserialization. - changedEntry.CopyTo(entry); - } - } - - /// - /// Adds the removed key. - /// - private void AddRemovedKey(string key) - { - Debug.Assert(!_isNew); - - if (_removedKeys == null) - _removedKeys = new HashSet(); - - _removedKeys.Add(key); - } - - /// - /// Gets or creates an entry. - /// - private Entry GetOrCreateDirtyEntry(string key) - { - var entry = GetEntry(key); - - if (entry == null) - { - entry = new Entry(key, false, null); - - _dict[key] = _list.Count; - _list.Add(entry); - } - - entry.IsDirty = true; - - return entry; - } - - /// - /// Gets the entry. - /// - private Entry GetEntry(string key) - { - IgniteArgumentCheck.NotNull(key, "key"); - - int index; - - return !_dict.TryGetValue(key, out index) ? null : _list[index]; - } - - /// - /// Gets the index. - /// - private int GetIndex(string key) - { - int index; - - return !_dict.TryGetValue(key, out index) ? -1 : index; - } - - /// - /// Sets the dirty on read. - /// - private static void SetDirtyOnRead(Entry entry) - { - var type = entry.Value.GetType(); - - if (IsImmutable(type)) - return; - - entry.IsDirty = true; - } - - /// - /// Determines whether the specified type is immutable. - /// - private static bool IsImmutable(Type type) - { - type = Nullable.GetUnderlyingType(type) ?? type; // Unwrap nullable. - - if (type.IsPrimitive) - return true; - - if (type == typeof(string) || type == typeof(DateTime) || type == typeof(Guid) || type == typeof(decimal)) - return true; - - return false; - } - - /// - /// Inner entry. - /// - private class Entry - { - /** */ - public readonly bool IsInitial; - - /** */ - public readonly string Key; - - /** */ - public bool IsDirty; - - /** */ - private object _value; - - /** */ - private bool _isDeserialized; - - /// - /// Initializes a new instance of the class. - /// - public Entry(string key, bool isInitial, object value) - { - Debug.Assert(key != null); - - Key = key; - IsInitial = isInitial; - _isDeserialized = !isInitial; - _value = value; - } - - /// - /// Gets or sets the value. - /// - public object Value - { - get - { - if (!_isDeserialized) - { - using (var stream = new MemoryStream((byte[])_value)) - { - _value = new BinaryFormatter().Deserialize(stream); - } - - _isDeserialized = true; - } - - return _value; - } - set - { - _value = value; - _isDeserialized = true; - } - } - - /// - /// Copies contents to another entry. - /// - public void CopyTo(Entry entry) - { - Debug.Assert(entry != null); - - entry._isDeserialized = _isDeserialized; - entry._value = _value; - } - - /// - /// Gets the bytes. - /// - public byte[] GetBytes() - { - if (!_isDeserialized) - return (byte[]) _value; - - using (var stream = new MemoryStream()) - { - new BinaryFormatter().Serialize(stream, _value); - - return stream.ToArray(); - } - } - } - } -} http://git-wip-us.apache.org/repos/asf/ignite/blob/11e69b48/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj index 5e54115..12fa03a 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj @@ -71,7 +71,6 @@ - http://git-wip-us.apache.org/repos/asf/ignite/blob/11e69b48/modules/platforms/dotnet/Apache.Ignite.Core.Tests/AspNet/IgniteSessionStateItemCollectionTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/AspNet/IgniteSessionStateItemCollectionTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/AspNet/IgniteSessionStateItemCollectionTest.cs index 9f8f34d..45c2326 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/AspNet/IgniteSessionStateItemCollectionTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/AspNet/IgniteSessionStateItemCollectionTest.cs @@ -18,9 +18,11 @@ namespace Apache.Ignite.Core.Tests.AspNet { using System; + using System.IO; using System.Linq; using Apache.Ignite.AspNet.Impl; - using Apache.Ignite.Core.Impl.Collections; + using Apache.Ignite.Core.Impl.Binary; + using Apache.Ignite.Core.Impl.Binary.IO; using NUnit.Framework; /// @@ -34,28 +36,33 @@ namespace Apache.Ignite.Core.Tests.AspNet [Test] public void TestEmpty() { - var col = new IgniteSessionStateItemCollection(new KeyValueDirtyTrackedCollection()); + var col1 = new IgniteSessionStateItemCollection(); + var col2 = SerializeDeserialize(col1); - // Check empty. - Assert.IsFalse(col.Dirty); - Assert.IsFalse(col.IsSynchronized); - Assert.AreEqual(0, col.Count); - Assert.IsNotNull(col.SyncRoot); - Assert.IsEmpty(col); - Assert.IsEmpty(col.OfType().ToArray()); - Assert.IsEmpty(col.Keys); - Assert.IsNotNull(col.SyncRoot); + foreach (var col in new[] { col1, col2 }) + { + Assert.IsFalse(col.Dirty); + Assert.IsFalse(col.IsSynchronized); + Assert.AreEqual(0, col.Count); + Assert.IsNotNull(col.SyncRoot); + Assert.IsEmpty(col); + Assert.IsEmpty(col.OfType().ToArray()); + Assert.IsEmpty(col.Keys); + Assert.IsNotNull(col.SyncRoot); - col.Clear(); - col.Remove("key"); - Assert.Throws(() => col.RemoveAt(0)); + Assert.IsNull(col["key"]); + Assert.Throws(() => col[0] = "x"); + Assert.Throws(() => Assert.AreEqual(0, col[0])); + Assert.Throws(() => col.RemoveAt(0)); - Assert.IsNull(col["key"]); - Assert.Throws(() => Assert.IsNull(col[0])); + col.Clear(); + col.Remove("test"); - var keys = new[] {"1"}; - col.CopyTo(keys, 0); - Assert.AreEqual(new[] {"1"}, keys); + Assert.AreEqual(0, col.Count); + + col.Dirty = true; + Assert.IsTrue(col.Dirty); + } } /// @@ -64,8 +71,7 @@ namespace Apache.Ignite.Core.Tests.AspNet [Test] public void TestModification() { - var innerCol = new KeyValueDirtyTrackedCollection(); - var col = new IgniteSessionStateItemCollection(innerCol); + var col = new IgniteSessionStateItemCollection(); // Populate and check. col["key"] = "val"; @@ -131,8 +137,134 @@ namespace Apache.Ignite.Core.Tests.AspNet Assert.AreEqual(0, col.Count); // Set dirty. - var col1 = new IgniteSessionStateItemCollection(innerCol) {Dirty = true}; + var col1 = new IgniteSessionStateItemCollection {Dirty = true}; Assert.IsTrue(col1.Dirty); } + + /// + /// Tests dirty tracking. + /// + [Test] + public void TestApplyChanges() + { + Func getCol = () => + { + var res = new IgniteSessionStateItemCollection(); + + res["1"] = 1; + res["2"] = 2; + res["3"] = 3; + + return res; + }; + + var col = getCol(); + + var col0 = SerializeDeserialize(col); + + Assert.AreEqual(3, col0.Count); + + col0.Remove("1"); + col0["2"] = 22; + col0["4"] = 44; + + // Apply non-serialized changes. + col.ApplyChanges(col0); + + Assert.AreEqual(3, col.Count); + Assert.AreEqual(null, col["1"]); + Assert.AreEqual(22, col["2"]); + Assert.AreEqual(3, col["3"]); + Assert.AreEqual(44, col["4"]); + + // Apply serialized changes without WriteChangesOnly. + col = getCol(); + col.ApplyChanges(SerializeDeserialize(col0)); + + Assert.AreEqual(3, col.Count); + Assert.AreEqual(null, col["1"]); + Assert.AreEqual(22, col["2"]); + Assert.AreEqual(3, col["3"]); + Assert.AreEqual(44, col["4"]); + + // Apply serialized changes with WriteChangesOnly. + col0.WriteChangesOnly = true; + + col = getCol(); + col.ApplyChanges(SerializeDeserialize(col0)); + + Assert.AreEqual(3, col.Count); + Assert.AreEqual(null, col["1"]); + Assert.AreEqual(22, col["2"]); + Assert.AreEqual(3, col["3"]); + Assert.AreEqual(44, col["4"]); + + // Remove key then add back. + col0.Remove("2"); + col0.Remove("3"); + col0["2"] = 222; + + col = getCol(); + col.ApplyChanges(SerializeDeserialize(col0)); + + Assert.AreEqual(2, col.Count); + Assert.AreEqual(222, col["2"]); + Assert.AreEqual(44, col["4"]); + + // Remove all. + col0 = SerializeDeserialize(getCol()); + col0.WriteChangesOnly = true; + col0.Clear(); + + col = getCol(); + col.ApplyChanges(SerializeDeserialize(col0)); + + Assert.AreEqual(0, col.Count); + + // Add to empty. + col0["-1"] = -1; + col0["-2"] = -2; + + col = getCol(); + col.ApplyChanges(SerializeDeserialize(col0)); + + Assert.AreEqual(2, col.Count); + Assert.AreEqual(-1, col0["-1"]); + Assert.AreEqual(-2, col0["-2"]); + + // Remove initial key, then add it back, then remove again. + col0 = SerializeDeserialize(getCol()); + col0.WriteChangesOnly = true; + + col0.Remove("1"); + col0.Remove("2"); + col0["1"] = "111"; + col0.Remove("1"); + + col = getCol(); + col.ApplyChanges(SerializeDeserialize(col0)); + + Assert.AreEqual(1, col.Count); + Assert.AreEqual(3, col["3"]); + } + + /// + /// Serializes and deserializes back an instance. + /// + private static IgniteSessionStateItemCollection SerializeDeserialize(IgniteSessionStateItemCollection data) + { + var marsh = BinaryUtils.Marshaller; + + using (var stream = new BinaryHeapStream(128)) + { + var writer = marsh.StartMarshal(stream); + + data.WriteBinary(writer.GetRawWriter()); + + stream.Seek(0, SeekOrigin.Begin); + + return new IgniteSessionStateItemCollection(marsh.StartUnmarshal(stream)); + } + } } } http://git-wip-us.apache.org/repos/asf/ignite/blob/11e69b48/modules/platforms/dotnet/Apache.Ignite.Core.Tests/AspNet/KeyValueDirtyTrackedCollectionTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/AspNet/KeyValueDirtyTrackedCollectionTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/AspNet/KeyValueDirtyTrackedCollectionTest.cs deleted file mode 100644 index 9c8947b..0000000 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/AspNet/KeyValueDirtyTrackedCollectionTest.cs +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace Apache.Ignite.Core.Tests.AspNet -{ - using System; - using System.IO; - using System.Linq; - using Apache.Ignite.AspNet.Impl; - using Apache.Ignite.Core.Impl.Binary; - using Apache.Ignite.Core.Impl.Binary.IO; - using NUnit.Framework; - - /// - /// Tests for - /// - public class KeyValueDirtyTrackedCollectionTest - { - /// - /// Tests the empty collection. - /// - [Test] - public void TestEmpty() - { - var col1 = new KeyValueDirtyTrackedCollection(); - var col2 = SerializeDeserialize(col1); - - foreach (var col in new[] {col1, col2}) - { - Assert.AreEqual(0, col.Count); - Assert.IsFalse(col.IsDirty); - Assert.IsEmpty(col.GetKeys()); - - Assert.IsNull(col["key"]); - Assert.Throws(() => col[0] = "x"); - Assert.Throws(() => Assert.AreEqual(0, col[0])); - Assert.Throws(() => col.RemoveAt(0)); - - col.Clear(); - col.Remove("test"); - - Assert.AreEqual(0, col.Count); - - col.IsDirty = true; - Assert.IsTrue(col.IsDirty); - } - } - - /// - /// Tests the modification. - /// - [Test] - public void TestModification() - { - var col = new KeyValueDirtyTrackedCollection(); - - // Populate and check. - col["key"] = "val"; - col["1"] = 1; - - Assert.AreEqual("val", col["key"]); - Assert.AreEqual(1, col["1"]); - - Assert.AreEqual(2, col.Count); - Assert.IsTrue(col.IsDirty); - Assert.AreEqual(new[] {"key", "1"}, col.GetKeys().ToArray()); - - // Modify using index. - col[0] = "val1"; - col[1] = 2; - - Assert.AreEqual("val1", col["key"]); - Assert.AreEqual(2, col["1"]); - - // Modify using key. - col["1"] = 3; - col["key"] = "val2"; - - Assert.AreEqual("val2", col["key"]); - Assert.AreEqual(3, col["1"]); - - // Serialize. - var col0 = SerializeDeserialize(col); - - Assert.AreEqual(col.GetKeys(), col0.GetKeys()); - Assert.AreEqual(col.GetKeys().Select(x => col[x]), col0.GetKeys().Select(x => col0[x])); - - // Remove. - col["2"] = 2; - col["3"] = 3; - - col.Remove("invalid"); - Assert.AreEqual(4, col.Count); - - col.Remove("1"); - - Assert.AreEqual(new[] { "key", "2", "3" }, col.GetKeys()); - Assert.AreEqual(null, col["1"]); - - Assert.AreEqual("val2", col["key"]); - Assert.AreEqual("val2", col[0]); - - Assert.AreEqual(2, col["2"]); - Assert.AreEqual(2, col[1]); - - Assert.AreEqual(3, col["3"]); - Assert.AreEqual(3, col[2]); - - // RemoveAt. - col0.RemoveAt(0); - Assert.AreEqual(new[] { "1" }, col0.GetKeys()); - - // Clear. - Assert.AreEqual(3, col.Count); - - col.Clear(); - Assert.AreEqual(0, col.Count); - } - - /// - /// Tests dirty tracking. - /// - [Test] - public void TestApplyChanges() - { - Func getCol = () => - { - var res = new KeyValueDirtyTrackedCollection(); - - res["1"] = 1; - res["2"] = 2; - res["3"] = 3; - - return res; - }; - - var col = getCol(); - - var col0 = SerializeDeserialize(col); - - Assert.AreEqual(3, col0.Count); - - col0.Remove("1"); - col0["2"] = 22; - col0["4"] = 44; - - // Apply non-serialized changes. - col.ApplyChanges(col0); - - Assert.AreEqual(3, col.Count); - Assert.AreEqual(null, col["1"]); - Assert.AreEqual(22, col["2"]); - Assert.AreEqual(3, col["3"]); - Assert.AreEqual(44, col["4"]); - - // Apply serialized changes without WriteChangesOnly. - col = getCol(); - col.ApplyChanges(SerializeDeserialize(col0)); - - Assert.AreEqual(3, col.Count); - Assert.AreEqual(null, col["1"]); - Assert.AreEqual(22, col["2"]); - Assert.AreEqual(3, col["3"]); - Assert.AreEqual(44, col["4"]); - - // Apply serialized changes with WriteChangesOnly. - col0.WriteChangesOnly = true; - - col = getCol(); - col.ApplyChanges(SerializeDeserialize(col0)); - - Assert.AreEqual(3, col.Count); - Assert.AreEqual(null, col["1"]); - Assert.AreEqual(22, col["2"]); - Assert.AreEqual(3, col["3"]); - Assert.AreEqual(44, col["4"]); - - // Remove key then add back. - col0.Remove("2"); - col0.Remove("3"); - col0["2"] = 222; - - col = getCol(); - col.ApplyChanges(SerializeDeserialize(col0)); - - Assert.AreEqual(2, col.Count); - Assert.AreEqual(222, col["2"]); - Assert.AreEqual(44, col["4"]); - - // Remove all. - col0 = SerializeDeserialize(getCol()); - col0.WriteChangesOnly = true; - col0.Clear(); - - col = getCol(); - col.ApplyChanges(SerializeDeserialize(col0)); - - Assert.AreEqual(0, col.Count); - - // Add to empty. - col0["-1"] = -1; - col0["-2"] = -2; - - col = getCol(); - col.ApplyChanges(SerializeDeserialize(col0)); - - Assert.AreEqual(2, col.Count); - Assert.AreEqual(-1, col0["-1"]); - Assert.AreEqual(-2, col0["-2"]); - - // Remove initial key, then add it back, then remove again. - col0 = SerializeDeserialize(getCol()); - col0.WriteChangesOnly = true; - - col0.Remove("1"); - col0.Remove("2"); - col0["1"] = "111"; - col0.Remove("1"); - - col = getCol(); - col.ApplyChanges(SerializeDeserialize(col0)); - - Assert.AreEqual(1, col.Count); - Assert.AreEqual(3, col["3"]); - } - - /// - /// Serializes and deserializes back an instance. - /// - private static KeyValueDirtyTrackedCollection SerializeDeserialize(KeyValueDirtyTrackedCollection data) - { - var marsh = BinaryUtils.Marshaller; - - using (var stream = new BinaryHeapStream(128)) - { - var writer = marsh.StartMarshal(stream); - - data.WriteBinary(writer.GetRawWriter()); - - stream.Seek(0, SeekOrigin.Begin); - - return new KeyValueDirtyTrackedCollection(marsh.StartUnmarshal(stream)); - } - } - } -}