1 //
2 // Copyright (c) 2010-2019 Antmicro
3 //
4 //  This file is licensed under the MIT License.
5 //  Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Collections.Generic;
9 
10 namespace Antmicro.Renode.Utilities.Collections
11 {
12     public class SimpleCache
13     {
Get(T1 parameter, Func<T1, R> generator)14         public R Get<T1, R>(T1 parameter, Func<T1, R> generator)
15         {
16             if(generator == null)
17             {
18                 throw new ArgumentNullException(nameof(generator));
19             }
20             var obj = new CacheObject<T1, Object, Func<T1, R>>(generator, parameter);
21             if(simpleCache.TryGetValue(obj, out var result))
22             {
23                 this.CacheHits = this.CacheHits + 1;
24                 return (R)result;
25             }
26 
27             this.CacheMisses = this.CacheMisses + 1;
28             var generated = generator(parameter);
29             simpleCache[obj] = generated;
30             return generated;
31         }
32 
Get(T1 parameterT1, T2 parameterT2, Func<T1, T2, R> generator)33         public R Get<T1, T2, R>(T1 parameterT1, T2 parameterT2, Func<T1, T2, R> generator)
34         {
35             if(generator == null)
36             {
37                throw new ArgumentNullException(nameof(generator));
38             }
39             var obj = new CacheObject<T1, T2, Func<T1, T2, R>>(generator, parameterT1, parameterT2);
40             if(simpleCache.TryGetValue(obj, out var result))
41             {
42                 this.CacheHits = this.CacheHits + 1;
43                 return (R)result;
44             }
45             this.CacheMisses = this.CacheMisses + 1;
46             var generated = generator(parameterT1, parameterT2);
47             simpleCache[obj] = generated;
48             return generated;
49         }
50 
ClearCache()51         public void ClearCache()
52         {
53             CacheMisses = 0;
54             CacheHits = 0;
55             simpleCache.Clear();
56         }
57 
58         public ulong CacheMisses { get; private set; }
59 
60         public ulong CacheHits { get; private set; }
61 
62         public int CacheSize { get { return simpleCache.Count; } }
63 
64         private struct CacheObject<T1, T2, R>
65         {
CacheObjectAntmicro.Renode.Utilities.Collections.SimpleCache.CacheObject66             public CacheObject(R z, T1 x = default(T1), T2 y = default(T2))
67             {
68                 this.parameterT1 = x;
69                 this.parameterT2 = y;
70                 this.generator = z;
71             }
72 
EqualsAntmicro.Renode.Utilities.Collections.SimpleCache.CacheObject73             public override bool Equals(Object obj)
74             {
75                 if(!(obj is CacheObject<T1, T2, R> cacheObj))
76                 {
77                     return false;
78                 }
79 
80                 var isEqualT1 = EqualityComparer<T1>.Default.Equals(parameterT1, cacheObj.parameterT1);
81                 var isEqualT2 = EqualityComparer<T2>.Default.Equals(parameterT2, cacheObj.parameterT2);
82                 var isEqualR = EqualityComparer<R>.Default.Equals(generator, cacheObj.generator);
83 
84                 return isEqualT1 && isEqualT2 && isEqualR;
85             }
86 
87             //https://docs.microsoft.com/en-us/dotnet/api/system.object.gethashcode?view=netframework-4.7.2&viewFallbackFrom=netf
GetHashCodeAntmicro.Renode.Utilities.Collections.SimpleCache.CacheObject88             public override int GetHashCode()
89             {
90                 var isDefaultT1 = EqualityComparer<T1>.Default.Equals(parameterT1, default(T1));
91                 var isDefaultT2 = EqualityComparer<T2>.Default.Equals(parameterT2, default(T2));
92 
93                 var hash = generator.GetHashCode();
94 
95                 if(!isDefaultT1)
96                 {
97                     hash ^= ShiftAndWrap(parameterT1.GetHashCode(), 2);
98                 }
99 
100                 if(!isDefaultT2)
101                 {
102                     hash ^= ShiftAndWrap(parameterT2.GetHashCode(), 4);
103                 }
104 
105                 return hash;
106             }
107 
ShiftAndWrapAntmicro.Renode.Utilities.Collections.SimpleCache.CacheObject108             private int ShiftAndWrap(int value, int positions)
109             {
110                 positions = positions & 0x1F;
111 
112                 // Save the existing bit pattern, but interpret it as an unsigned integer.
113                 uint number = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
114                 // Preserve the bits to be discarded.
115                 uint wrapped = number >> (32 - positions);
116                 // Shift and wrap the discarded bits.
117                 return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) | wrapped), 0);
118             }
119 
120             private readonly T1 parameterT1;
121             private readonly T2 parameterT2;
122             private readonly R generator;
123         }
124 
125         private readonly Dictionary<object, object> simpleCache = new Dictionary<object, object>();
126     }
127 }
128