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