1 // 2 // Copyright (c) 2010-2018 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System; 9 using System.Collections.Generic; 10 using System.Runtime.CompilerServices; 11 using System.Linq; 12 using Antmicro.Migrant; 13 using Antmicro.Migrant.Hooks; 14 15 namespace Antmicro.Renode.Utilities.Collections 16 { 17 public class SerializableWeakKeyDictionary<TKey, TValue> : IDictionary<TKey, TValue> where TKey : class where TValue : class 18 { 19 #region IDictionary implementation 20 Add(TKey key, TValue value)21 public void Add(TKey key, TValue value) 22 { 23 lock(sync) 24 { 25 wl.Add(new WeakReference(key)); 26 cwt.Add(key, value); 27 } 28 } 29 ContainsKey(TKey key)30 public bool ContainsKey(TKey key) 31 { 32 lock(sync) 33 { 34 return wl.Any(x => 35 { 36 TKey lkey; 37 return TryGetTarget(x, out lkey) && lkey != null && lkey.Equals(key); 38 }); 39 } 40 } 41 Remove(TKey key)42 public bool Remove(TKey key) 43 { 44 lock(sync) 45 { 46 wl.RemoveAll(x => 47 { 48 TKey lkey; 49 return TryGetTarget(x, out lkey) && lkey.Equals(key); 50 }); 51 return cwt.Remove(key); 52 } 53 } 54 TryGetValue(TKey key, out TValue value)55 public bool TryGetValue(TKey key, out TValue value) 56 { 57 lock(sync) 58 { 59 return cwt.TryGetValue(key, out value); 60 } 61 } 62 63 public TValue this[TKey index] 64 { 65 get 66 { 67 lock(sync) 68 { 69 TValue value; 70 if(cwt.TryGetValue(index, out value)) 71 { 72 return value; 73 } 74 throw new KeyNotFoundException(); 75 } 76 } 77 set 78 { 79 lock(sync) 80 { 81 if(ContainsKey(index)) 82 { 83 cwt.Remove(index); 84 cwt.Add(index, value); 85 } 86 else 87 { 88 Add(index, value); 89 } 90 } 91 } 92 } 93 94 public ICollection<TKey> Keys 95 { 96 get 97 { 98 lock(sync) 99 { 100 return wl.Select(x => 101 { 102 TKey target; 103 return TryGetTarget(x, out target) ? target : null; 104 }).Where(x => x != null).ToList(); 105 } 106 } 107 } 108 109 public ICollection<TValue> Values 110 { 111 get 112 { 113 lock(sync) 114 { 115 return wl.Select(x => 116 { 117 TKey target; 118 TValue value; 119 return TryGetTarget(x, out target) && cwt.TryGetValue(target, out value) ? value : null; 120 }).Where(x => x != null).ToList(); 121 } 122 } 123 } 124 125 #endregion 126 127 #region ICollection implementation 128 Add(KeyValuePair<TKey, TValue> item)129 public void Add(KeyValuePair<TKey, TValue> item) 130 { 131 throw new NotImplementedException(); 132 } 133 Clear()134 public void Clear() 135 { 136 throw new NotImplementedException(); 137 } 138 Contains(KeyValuePair<TKey, TValue> item)139 public bool Contains(KeyValuePair<TKey, TValue> item) 140 { 141 throw new NotImplementedException(); 142 } 143 CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)144 public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) 145 { 146 throw new NotImplementedException(); 147 } 148 Remove(KeyValuePair<TKey, TValue> item)149 public bool Remove(KeyValuePair<TKey, TValue> item) 150 { 151 throw new NotImplementedException(); 152 } 153 154 public int Count { get { return wl.Count; } } 155 156 public bool IsReadOnly { get { return false; } } 157 158 #endregion 159 160 #region IEnumerable implementation 161 GetEnumerator()162 public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() 163 { 164 var result = new List<KeyValuePair<TKey, TValue>>(); 165 lock(sync) 166 { 167 foreach(var weakKey in wl) 168 { 169 TKey key; 170 TValue value; 171 if(TryGetTarget(weakKey, out key) && cwt.TryGetValue(key, out value)) 172 { 173 result.Add(new KeyValuePair<TKey, TValue>(key, value)); 174 } 175 } 176 } 177 178 for(var i = 0; i < result.Count; i++) 179 { 180 yield return result[i]; 181 } 182 } 183 184 #endregion 185 186 #region IEnumerable implementation 187 System.Collections.IEnumerable.GetEnumerator()188 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 189 { 190 return GetEnumerator(); 191 } 192 193 #endregion 194 SerializableWeakKeyDictionary()195 public SerializableWeakKeyDictionary() 196 { 197 cwt = new ConditionalWeakTable<TKey, TValue>(); 198 wl = new List<WeakReference>(); 199 sync = new object(); 200 } 201 202 [PreSerialization] BeforeSerialization()203 private void BeforeSerialization() 204 { 205 keys = new List<TKey>(); 206 values = new List<TValue>(); 207 208 lock(sync) 209 { 210 foreach (var x in wl) 211 { 212 TKey target; 213 TValue value; 214 if(TryGetTarget(x, out target) && cwt.TryGetValue(target, out value)) 215 { 216 keys.Add(target); 217 values.Add(value); 218 } 219 } 220 } 221 } 222 223 [PostDeserialization] PostDeserialization()224 private void PostDeserialization() 225 { 226 lock(sync) 227 { 228 for(int i = 0; i < keys.Count; i++) 229 { 230 Add(keys[i], values[i]); 231 } 232 } 233 234 keys = null; 235 values = null; 236 } 237 238 private bool TryGetTarget<T>(WeakReference wref, out T result) where T : class 239 { 240 result = wref.Target as T; 241 return (result != null); 242 } 243 244 [Constructor] 245 private readonly ConditionalWeakTable<TKey, TValue> cwt; 246 [Constructor] 247 private readonly List<WeakReference> wl; 248 private readonly object sync; 249 250 private List<TKey> keys; 251 private List<TValue> values; 252 } 253 } 254 255