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.Collections;
11 using Antmicro.Migrant;
12 using System.Linq;
13 using Antmicro.Migrant.Hooks;
14 
15 namespace Antmicro.Renode.Utilities.Collections
16 {
17     public class SerializableWeakDictionary<TKey, TValue> : IDictionary<TKey, TValue>
18     {
SerializableWeakDictionary()19         public SerializableWeakDictionary()
20         {
21             sync = new object();
22             Clear();
23         }
24 
Add(TKey key, TValue value)25         public void Add(TKey key, TValue value)
26         {
27             lock(sync)
28             {
29                 List<TKey> currentKeys;
30                 List<TValue> currentValues;
31                 ObtainKeysAndValues(out currentKeys, out currentValues);
32                 if(currentKeys.Contains(key))
33                 {
34                     throw new InvalidOperationException(string.Format("Key '{0}' already exists.", key));
35                 }
36                 keys.Add(new WeakReference(key));
37                 values.Add(new WeakReference(value));
38             }
39         }
40 
ContainsKey(TKey key)41         public bool ContainsKey(TKey key)
42         {
43             lock(sync)
44             {
45                 return keys.Select(x => x.Target).Where(x => x != null).Any(x => x.Equals(key));
46             }
47         }
48 
Remove(TKey key)49         public bool Remove(TKey key)
50         {
51             lock(sync)
52             {
53                 List<TKey> currentKeys;
54                 List<TValue> currentValues;
55                 ObtainKeysAndValues(out currentKeys, out currentValues);
56                 var index = currentKeys.IndexOf(key);
57                 if(index == -1)
58                 {
59                     return false;
60                 }
61                 keys.RemoveAt(index);
62                 values.RemoveAt(index);
63                 return true;
64             }
65         }
66 
TryGetValue(TKey key, out TValue value)67         public bool TryGetValue(TKey key, out TValue value)
68         {
69             lock(sync)
70             {
71                 List<TKey> currentKeys;
72                 List<TValue> currentValues;
73                 ObtainKeysAndValues(out currentKeys, out currentValues);
74                 var index = currentKeys.IndexOf(key);
75                 if(index == -1)
76                 {
77                     value = default(TValue);
78                     return false;
79                 }
80                 value = currentValues[index];
81                 return true;
82             }
83         }
84 
85         public TValue this[TKey key]
86         {
87             get
88             {
89                 TValue value;
90                 if(!TryGetValue(key, out value))
91                 {
92                     throw new KeyNotFoundException();
93                 }
94                 return value;
95             }
96             set
97             {
98                 lock(sync)
99                 {
100                     List<TKey> currentKeys;
101                     List<TValue> currentValues;
102                     ObtainKeysAndValues(out currentKeys, out currentValues);
103                     var index = currentKeys.IndexOf(key);
104                     if(index == -1)
105                     {
106                         Add(key, value);
107                         return;
108                     }
109                     values[index] = new WeakReference(value);
110                 }
111             }
112         }
113 
114         public ICollection<TKey> Keys
115         {
116             get
117             {
118                 List<TKey> currentKeys;
119                 List<TValue> currentValues;
120                 ObtainKeysAndValues(out currentKeys, out currentValues);
121                 return currentKeys;
122             }
123         }
124 
125         public ICollection<TValue> Values
126         {
127             get
128             {
129                 List<TKey> currentKeys;
130                 List<TValue> currentValues;
131                 ObtainKeysAndValues(out currentKeys, out currentValues);
132                 return currentValues;
133             }
134         }
135 
Add(KeyValuePair<TKey, TValue> item)136         public void Add(KeyValuePair<TKey, TValue> item)
137         {
138             Add(item.Key, item.Value);
139         }
140 
Clear()141         public void Clear()
142         {
143             lock(sync)
144             {
145                 keys = new List<WeakReference>();
146                 values = new List<WeakReference>();
147             }
148         }
149 
Contains(KeyValuePair<TKey, TValue> item)150         public bool Contains(KeyValuePair<TKey, TValue> item)
151         {
152             lock(sync)
153             {
154                 List<TKey> currentKeys;
155                 List<TValue> currentValues;
156                 ObtainKeysAndValues(out currentKeys, out currentValues);
157                 var index = currentKeys.IndexOf(item.Key);
158                 if(index == -1)
159                 {
160                     return false;
161                 }
162                 return currentValues[index].Equals(item.Value);
163             }
164         }
165 
CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)166         public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
167         {
168             throw new NotImplementedException();
169         }
170 
Remove(KeyValuePair<TKey, TValue> item)171         public bool Remove(KeyValuePair<TKey, TValue> item)
172         {
173             throw new NotImplementedException();
174         }
175 
176         public int Count
177         {
178             get
179             {
180                 List<TKey> currentKeys;
181                 List<TValue> currentValues;
182                 ObtainKeysAndValues(out currentKeys, out currentValues);
183                 return currentKeys.Count;
184             }
185         }
186 
187         public bool IsReadOnly
188         {
189             get
190             {
191                 return false;
192             }
193         }
194 
195 
GetEnumerator()196         public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
197         {
198             List<TKey> currentKeys;
199             List<TValue> currentValues;
200             ObtainKeysAndValues(out currentKeys, out currentValues);
201             for(var i = 0; i < currentKeys.Count; i++)
202             {
203                 yield return new KeyValuePair<TKey, TValue>(currentKeys[i], currentValues[i]);
204             }
205         }
206 
207 
IEnumerable.GetEnumerator()208         IEnumerator IEnumerable.GetEnumerator()
209         {
210             return GetEnumerator();
211         }
212 
GetOrCreateValue(TKey key, TValue value)213         public TValue GetOrCreateValue(TKey key, TValue value)
214         {
215             lock(sync)
216             {
217                 List<TKey> currentKeys;
218                 List<TValue> currentValues;
219                 ObtainKeysAndValues(out currentKeys, out currentValues);
220                 var index = currentKeys.IndexOf(key);
221                 if(index == -1)
222                 {
223                     Add(key, value);
224                     return value;
225                 }
226                 return currentValues[index];
227             }
228         }
229 
ObtainKeysAndValues(out List<TKey> currentKeys, out List<TValue> currentValues)230         private void ObtainKeysAndValues(out List<TKey> currentKeys, out List<TValue> currentValues)
231         {
232             lock(sync)
233             {
234                 currentKeys = new List<TKey>();
235                 currentValues = new List<TValue>();
236                 for(var i = 0; i < keys.Count; i++)
237                 {
238                     // if the key OR value is not available, the whole entry is not available
239                     var key = keys[i].Target;
240                     var value = values[i].Target;
241                     if(key == null || value == null)
242                     {
243                         keys.RemoveAt(i);
244                         values.RemoveAt(i);
245                         i--;
246                         continue;
247                     }
248                     currentKeys.Add((TKey)key);
249                     currentValues.Add((TValue)value);
250                 }
251             }
252         }
253 
254         [PreSerialization]
BeforeSerialization()255         private void BeforeSerialization()
256         {
257             ObtainKeysAndValues(out serializedKeys, out serializedValues);
258         }
259 
260         [PostSerialization]
AfterSerialization()261         private void AfterSerialization()
262         {
263             serializedKeys = null;
264             serializedValues = null;
265         }
266 
267         [PostDeserialization]
AfterDeserialization()268         private void AfterDeserialization()
269         {
270             keys = serializedKeys.Select(x => new WeakReference(x)).ToList();
271             values = serializedValues.Select(x => new WeakReference(x)).ToList();
272             AfterSerialization();
273         }
274 
275         private object sync;
276 
277         [Transient]
278         private List<WeakReference> keys;
279 
280         [Transient]
281         private List<WeakReference> values;
282 
283         private List<TKey> serializedKeys;
284         private List<TValue> serializedValues;
285     }
286 }
287 
288