UXCache
UXCache is keyed storage that cannot grow without bound. Set a capacity,
and when a write would exceed it the least-recently-used entry is evicted.
#use <UXKit> // or #import "UXCache.xc"Overview
Section titled “Overview”UXCache* c = new UXCache();c.setCapacity(3);
c.set((u8*)"a", (Object*)thumbA);c.set((u8*)"b", (Object*)thumbB);c.set((u8*)"c", (Object*)thumbC);
c.get((u8*)"a"); // hit — and now "a" is the most recentc.set((u8*)"d", (Object*)thumbD); // over capacity: "b" is evictedThumbnail, decoded-image and parsed-resource caches use it to stay bounded. It is a pure data structure with no platform code, so it is testable.
The default capacity is 16.
What counts as a use
Section titled “What counts as a use”Every get and every set marks its entry most-recently-used
by stamping it with a monotonic counter. The victim is always the entry with the
oldest stamp.
c.get((u8*)"a"); // "a" is now newest; something else becomes the victimcontains does not mark recency:
Keys are strings, compared by content, so a key built at run time matches a literal. You need this when the key is a filename or a URL.
Setting an existing key updates in place: the value is replaced and the entry is re-stamped, and no second entry appears. Repeated writes to one key do not consume capacity.
A miss is null
Section titled “A miss is null”Object* v = c.get((u8*)"gone"); // 0As a result there is no way to cache a null value: storing 0 is
indistinguishable from not having the key. To remember that a lookup found
nothing, cache a marker object instead of null.
UXNull exists for this.
Cost, and what it is sized for
Section titled “Cost, and what it is sized for”Lookup is a linear scan, and each eviction is another scan to find the oldest entry. There is no hash and no linked list.
For a capacity in the tens, typical of a thumbnail cache, this is faster than the alternatives and much simpler. It is the wrong shape for thousands of entries: the scan dominates, and a hash plus an intrusive LRU list would suit better.
setCapacity clamps to a minimum of 1, so a cache cannot discard
everything. Shrinking the capacity evicts immediately.
Topics
Section titled “Topics”setCapacity · set · get · contains · remove · removeAll · count · find · evictToFit
setCapacity
Section titled “setCapacity”void setCapacity(i32 n)The maximum number of entries. Clamped to at least 1, and evicts down to fit immediately.
void set(u8* key, Object* value)Store, replacing an existing key in place. Marks most-recently-used, then evicts if over capacity. See the caution about the key pointer.
Object* get(u8* key)Fetch, or 0. Marks most-recently-used on a hit.
contains
Section titled “contains”bool contains(u8* key)Presence only. Does not mark recency. See above.
remove
Section titled “remove”void remove(u8* key)Drop one entry. A no-op if it is not there.
removeAll
Section titled “removeAll”void removeAll(void)Empty the cache. The cache holds values strongly, so this releases them.
i32 count(void)Entries currently held. Never more than the capacity.
UXCacheEntry* find(u8* key)The UXCacheEntry, or null, without
marking recency. The other methods are built on this lookup.
evictToFit
Section titled “evictToFit”void evictToFit(void)Evict least-recently-used entries until within capacity. set and
setCapacity call it for you.
Example
Section titled “Example”cache count=3 a=1 b=0 c=1 d=1after peek+set: c=0 e=1miss=0re-set same key: 3 -> 3 value=E2after setCapacity(1): count=1Capacity 3, keys a b c inserted in order. Getting a made it newest, so
adding d evicted b. a was oldest by insertion but not by use. Then
contains("c") did not save c, and e pushed it out. Re-setting e changed
its value without changing the count.
The program is website/site/examples/uxkit/collections.xc. The doc-examples
gate compiles it, and the listing above is its output.
Conforms to
Section titled “Conforms to”- A plain class (not an
Objectsubclass)
See also
Section titled “See also”UXCacheEntry: one key/value/stamp rowUXKeyValueStore: keyed storage that persists instead of evictingUXNull: the marker to cache when the answer is “nothing”