using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Security; using System.Security.Permissions; using System.Runtime.CompilerServices; namespace System.Data.Linq { using System.Data.Linq.Mapping; using System.Data.Linq.Provider; internal abstract class IdentityManager { internal abstract object InsertLookup(MetaType type, object instance); internal abstract bool RemoveLike(MetaType type, object instance); internal abstract object Find(MetaType type, object[] keyValues); internal abstract object FindLike(MetaType type, object instance); internal static IdentityManager CreateIdentityManager(bool asReadOnly) { if (asReadOnly) { return new ReadOnlyIdentityManager(); } else { return new StandardIdentityManager(); } } class StandardIdentityManager : IdentityManager { Dictionary caches; IdentityCache currentCache; MetaType currentType; internal StandardIdentityManager() { this.caches = new Dictionary(); } internal override object InsertLookup(MetaType type, object instance) { this.SetCurrent(type); return this.currentCache.InsertLookup(instance); } internal override bool RemoveLike(MetaType type, object instance) { this.SetCurrent(type); return this.currentCache.RemoveLike(instance); } internal override object Find(MetaType type, object[] keyValues) { this.SetCurrent(type); return this.currentCache.Find(keyValues); } internal override object FindLike(MetaType type, object instance) { this.SetCurrent(type); return this.currentCache.FindLike(instance); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private void SetCurrent(MetaType type) { type = type.InheritanceRoot; if (this.currentType != type) { if (!this.caches.TryGetValue(type, out this.currentCache)) { KeyManager km = GetKeyManager(type); this.currentCache = (IdentityCache)Activator.CreateInstance( typeof(IdentityCache<,>).MakeGenericType(type.Type, km.KeyType), BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new object[] { km }, null ); this.caches.Add(type, this.currentCache); } this.currentType = type; } } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] static KeyManager GetKeyManager(MetaType type) { int n = type.IdentityMembers.Count; MetaDataMember mm = type.IdentityMembers[0]; KeyManager km = (KeyManager)Activator.CreateInstance( typeof(SingleKeyManager<,>).MakeGenericType(type.Type, mm.Type), BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new object[] { mm.StorageAccessor, 0 }, null ); for (int i = 1; i < n; i++) { mm = type.IdentityMembers[i]; km = (KeyManager) Activator.CreateInstance( typeof(MultiKeyManager<,,>).MakeGenericType(type.Type, mm.Type, km.KeyType), BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new object[] { mm.StorageAccessor, i, km }, null ); } return km; } #region Nested type definitions // These types are internal rather than private to work around // CLR bug #117419 related to type visibility under partial trust // in nested class scenarios. internal abstract class KeyManager { internal abstract Type KeyType { get; } } internal abstract class KeyManager : KeyManager { internal abstract K CreateKeyFromInstance(T instance); internal abstract bool TryCreateKeyFromValues(object[] values, out K k); internal abstract IEqualityComparer Comparer { get; } } internal class SingleKeyManager : KeyManager { bool isKeyNullAssignable; MetaAccessor accessor; int offset; IEqualityComparer comparer; internal SingleKeyManager(MetaAccessor accessor, int offset) { this.accessor = accessor; this.offset = offset; this.isKeyNullAssignable = System.Data.Linq.SqlClient.TypeSystem.IsNullAssignable(typeof(V)); } internal override V CreateKeyFromInstance(T instance) { return this.accessor.GetValue(instance); } internal override bool TryCreateKeyFromValues(object[] values, out V v) { object o = values[this.offset]; if (o == null && !this.isKeyNullAssignable) { v = default(V); return false; } v = (V)o; return true; } internal override Type KeyType { get { return typeof(V); } } internal override IEqualityComparer Comparer { get { if (this.comparer == null) { this.comparer = EqualityComparer.Default; } return this.comparer; } } } internal class MultiKeyManager : KeyManager> { MetaAccessor accessor; int offset; KeyManager next; IEqualityComparer> comparer; internal MultiKeyManager(MetaAccessor accessor, int offset, KeyManager next) { this.accessor = accessor; this.next = next; this.offset = offset; } internal override MultiKey CreateKeyFromInstance(T instance) { return new MultiKey( this.accessor.GetValue(instance), this.next.CreateKeyFromInstance(instance) ); } internal override bool TryCreateKeyFromValues(object[] values, out MultiKey k) { System.Diagnostics.Debug.Assert(this.offset < values.Length, "offset is outside the bounds of the values array"); object o = values[this.offset]; if (o == null && typeof(V1).IsValueType) { k = default(MultiKey); return false; } V2 v2; if (!this.next.TryCreateKeyFromValues(values, out v2)) { k = default(MultiKey); return false; } k = new MultiKey((V1)o, v2); return true; } internal override Type KeyType { get { return typeof(MultiKey); } } internal override IEqualityComparer> Comparer { get { if (this.comparer == null) { this.comparer = new MultiKey.Comparer(EqualityComparer.Default, next.Comparer); } return this.comparer; } } } internal struct MultiKey { T1 value1; T2 value2; internal MultiKey(T1 value1, T2 value2) { this.value1 = value1; this.value2 = value2; } internal class Comparer : IEqualityComparer>, IEqualityComparer { IEqualityComparer comparer1; IEqualityComparer comparer2; internal Comparer(IEqualityComparer comparer1, IEqualityComparer comparer2) { this.comparer1 = comparer1; this.comparer2 = comparer2; } public bool Equals(MultiKey x, MultiKey y) { return this.comparer1.Equals(x.value1, y.value1) && this.comparer2.Equals(x.value2, y.value2); } public int GetHashCode(MultiKey x) { return this.comparer1.GetHashCode(x.value1) ^ this.comparer2.GetHashCode(x.value2); } bool IEqualityComparer.Equals(object x, object y) { return this.Equals((MultiKey)x, (MultiKey)y); } int IEqualityComparer.GetHashCode(object x) { return this.GetHashCode((MultiKey)x); } } } internal abstract class IdentityCache { internal abstract object Find(object[] keyValues); internal abstract object FindLike(object instance); internal abstract object InsertLookup(object instance); internal abstract bool RemoveLike(object instance); } internal class IdentityCache : IdentityCache { int[] buckets; Slot[] slots; int count; int freeList; KeyManager keyManager; IEqualityComparer comparer; public IdentityCache(KeyManager keyManager) { this.keyManager = keyManager; this.comparer = keyManager.Comparer; buckets = new int[7]; slots = new Slot[7]; freeList = -1; } internal override object InsertLookup(object instance) { T value = (T)instance; K key = this.keyManager.CreateKeyFromInstance(value); Find(key, ref value, true); return value; } internal override bool RemoveLike(object instance) { T value = (T)instance; K key = this.keyManager.CreateKeyFromInstance(value); int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; int bucket = hashCode % buckets.Length; int last = -1; for (int i = buckets[bucket] - 1; i >= 0; last = i, i = slots[i].next) { if (slots[i].hashCode == hashCode && comparer.Equals(slots[i].key, key)) { if (last < 0) { buckets[bucket] = slots[i].next + 1; } else { slots[last].next = slots[i].next; } slots[i].hashCode = -1; slots[i].value = default(T); slots[i].next = freeList; freeList = i; return true; } } return false; } internal override object Find(object[] keyValues) { K key; if (this.keyManager.TryCreateKeyFromValues(keyValues, out key)) { T value = default(T); if (Find(key, ref value, false)) return value; } return null; } internal override object FindLike(object instance) { T value = (T)instance; K key = this.keyManager.CreateKeyFromInstance(value); if (Find(key, ref value, false)) return value; return null; } bool Find(K key, ref T value, bool add) { int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF; for (int i = buckets[hashCode % buckets.Length] - 1; i >= 0; i = slots[i].next) { if (slots[i].hashCode == hashCode && comparer.Equals(slots[i].key, key)) { value = slots[i].value; return true; } } if (add) { int index; if (freeList >= 0) { index = freeList; freeList = slots[index].next; } else { if (count == slots.Length) Resize(); index = count; count++; } int bucket = hashCode % buckets.Length; slots[index].hashCode = hashCode; slots[index].key = key; slots[index].value = value; slots[index].next = buckets[bucket] - 1; buckets[bucket] = index + 1; } return false; } void Resize() { int newSize = checked(count * 2 + 1); int[] newBuckets = new int[newSize]; Slot[] newSlots = new Slot[newSize]; Array.Copy(slots, 0, newSlots, 0, count); for (int i = 0; i < count; i++) { int bucket = newSlots[i].hashCode % newSize; newSlots[i].next = newBuckets[bucket] - 1; newBuckets[bucket] = i + 1; } buckets = newBuckets; slots = newSlots; } internal struct Slot { internal int hashCode; internal K key; internal T value; internal int next; } } #endregion } /// /// This is the noop implementation used when object tracking is disabled. /// class ReadOnlyIdentityManager : IdentityManager { internal ReadOnlyIdentityManager() { } internal override object InsertLookup(MetaType type, object instance) { return instance; } internal override bool RemoveLike(MetaType type, object instance) { return false; } internal override object Find(MetaType type, object[] keyValues) { return null; } internal override object FindLike(MetaType type, object instance) { return null; } } } }