1 /** 2 Copyright: Copyright (c) 2016-2018 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 module voxelman.container.cache; 7 8 // entries are sorted based on last access time. 9 // successfull gets bring entries to the front of list, 10 // thus preventing them from being replaces on put 11 // LRU (least recently used) are dropped when cache is filled. 12 struct Cache(Key, Value, uint maxEntries) 13 { 14 enum lastEntryIndex = maxEntries - 1; 15 16 // returns null if key is not found. 17 // gives entry max priority 18 Value* get(Key key) 19 { 20 static bool searchPred(Entry a, Key b) 21 { 22 return a.key == b; 23 } 24 import std.algorithm : countUntil; 25 ptrdiff_t entry = countUntil!searchPred(entries[0..numUsed], key); 26 27 if (entry == -1) 28 return null; 29 else 30 { 31 bringEntryToFront(entry); 32 return &values[entries[0].valueIndex]; 33 } 34 } 35 36 private void bringEntryToFront(size_t index) 37 { 38 Entry temp = entries[index]; 39 for(ptrdiff_t i = index-1; i>=0; --i) 40 { 41 entries[i+1] = entries[i]; 42 } 43 entries[0] = temp; 44 } 45 46 void put(Key key, Value val) 47 { 48 Value* valPtr = put(key); 49 *valPtr = val; 50 } 51 52 Value* put(Key key) 53 { 54 if (numUsed == maxEntries) 55 { 56 auto index = entries[lastEntryIndex].valueIndex; 57 entries[lastEntryIndex] = Entry(key, index); 58 bringEntryToFront(lastEntryIndex); 59 return &values[index]; 60 } 61 else 62 { 63 entries[numUsed] = Entry(key, numUsed); 64 auto res = &values[numUsed]; 65 bringEntryToFront(numUsed); 66 ++numUsed; 67 return res; 68 } 69 } 70 71 void toString()(scope void delegate(const(char)[]) sink) const 72 { 73 import std.format : formattedWrite; 74 sink.formattedWrite("Cache!(%s, %s, %s)(", Key, Value, maxEntries); 75 foreach(i; 0..numUsed) 76 { 77 auto e = entries[i]; 78 sink.formattedWrite("%s:%s, ", e.key, values[e.valueIndex]); 79 } 80 sink.formattedWrite(")"); 81 } 82 83 static struct Entry 84 { 85 Key key; 86 size_t valueIndex; 87 } 88 89 Entry[maxEntries] entries; 90 Value[maxEntries] values; 91 size_t numUsed; 92 } 93 94 unittest 95 { 96 Cache!(int, string, 3) cache; 97 98 //cache.writeln; 99 cache.put(1, "1"); 100 //cache.writeln; 101 cache.put(2, "2"); 102 //cache.writeln; 103 cache.put(3, "3"); 104 //cache.writeln; 105 cache.put(4, "4"); 106 //cache.writeln; 107 assert(cache.get(1) is null); 108 109 assert(*cache.get(4) == "4"); 110 //cache.writeln; 111 112 assert(cache.get(5) is null); 113 114 cache.put(5, "5"); 115 //cache.writeln; 116 assert(cache.get(2) is null); 117 118 cache.get(3); 119 //cache.writeln; 120 assert(cache.entries[0].key == 3); 121 }