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