blob: 43e342cb7479dd662c09d085cf424ba464582a21 [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once
namespace JsUtil
{
template <typename T, uint size, class TAllocator>
class CircularBuffer
{
public:
CircularBuffer():
writeIndex(0),
filled(false)
{
}
void Clear()
{
this->writeIndex = 0;
this->filled = false;
}
void Add(const T& value)
{
if (!Contains(value))
{
entries[writeIndex] = value;
uint nextIndex = (writeIndex + 1) % size;
if (nextIndex < writeIndex && !filled)
{
filled = true;
}
writeIndex = nextIndex;
}
}
bool Contains(const T& value)
{
for (uint i = 0; i < GetMaxIndex(); i++)
{
if (DefaultComparer<T>::Equals(entries[i], value))
{
return true;
}
}
return false;
}
uint GetMaxIndex()
{
return (filled ? size : writeIndex);
}
const T& Item(uint index)
{
Assert(index < GetMaxIndex());
return entries[index];
}
#ifdef VERBOSE_EVAL_MAP
void Dump()
{
Output::Print(_u("Length: %d, writeIndex: %d, filled: %d\n"), size, writeIndex, filled);
for (uint i = 0; i < GetMaxIndex(); i++)
{
Output::Print(_u("Item %d: %s\n"), i, entries[i].str.GetBuffer());
}
Output::Flush();
}
#endif
bool IsEmpty()
{
return (writeIndex == 0 && !filled);
}
int GetCount()
{
if (!filled) return writeIndex;
return size;
}
private:
Field(uint) writeIndex;
Field(bool) filled;
Field(T, TAllocator) entries[size];
};
template <class TKey, int MRUSize, class TAllocator = Recycler>
class MRURetentionPolicy
{
public:
typedef CircularBuffer<TKey, MRUSize, TAllocator> TMRUStoreType;
MRURetentionPolicy(TAllocator* allocator)
{
store = AllocatorNew(TAllocator, allocator, TMRUStoreType);
}
void NotifyAdd(const TKey& key)
{
this->store->Add(key);
}
bool CanEvict(const TKey& key)
{
return !store->Contains(key);
}
void DumpKeepAlives()
{
store->Dump();
}
private:
Field(TMRUStoreType*, TAllocator) store;
};
template <
class TKey,
class TValue,
class TAllocator,
class SizePolicy,
class CacheRetentionPolicy,
template <typename ValueOrKey> class Comparer = DefaultComparer,
template <typename K, typename V> class Entry = SimpleDictionaryEntry
>
class Cache
{
private:
typedef BaseDictionary<TKey, TValue, TAllocator, SizePolicy, Comparer, Entry> TCacheStoreType;
typedef typename TCacheStoreType::AllocatorType AllocatorType;
class CacheStore : public TCacheStoreType
{
public:
CacheStore(AllocatorType* allocator, int capacity) : TCacheStoreType(allocator, capacity), inAdd(false) {};
bool IsInAdd()
{
return this->inAdd;
}
int Add(const TKey& key, const TValue& value)
{
AutoRestoreValue<bool> var(&this->inAdd, true);
return __super::Add(key, value);
}
void SetIsInAdd(bool value) {inAdd = value; }
private:
bool inAdd;
};
public:
typedef TKey KeyType;
typedef TValue ValueType;
typedef void (*OnItemEvictedCallback)(const TKey& key, TValue value);
Cache(AllocatorType * allocator, int capacity = 0):
cachePolicyType(allocator)
{
this->cacheStore = AllocatorNew(AllocatorType, allocator, CacheStore, allocator, capacity);
}
int Add(const TKey& key, const TValue& value)
{
int index = this->cacheStore->Add(key, value);
this->cachePolicyType.NotifyAdd(key);
return index;
}
void SetIsInAdd(bool value) {this->cacheStore->SetIsInAdd(value); }
void NotifyAdd(const TKey& key)
{
this->cachePolicyType.NotifyAdd(key);
}
bool TryGetValue(const TKey& key, TValue* value)
{
return cacheStore->TryGetValue(key, value);
}
bool TryGetReference(const TKey& key, const TValue** value, int* index)
{
return cacheStore->TryGetReference(key, value, index);
}
bool TryGetValueAndRemove(const TKey& key, TValue* value)
{
return cacheStore->TryGetValueAndRemove(key, value);
}
TKey const& GetKeyAt(const int& index)
{
return cacheStore->GetKeyAt(index);
}
template <class Fn>
void Clean(Fn callback)
{
if (!this->cacheStore->IsInAdd())
{
// Queue up items to be removed
// TODO: Don't use Contains since that's linear- store pointers to the eval map key instead, and set a bit indicating that its in the dictionary?
cacheStore->MapAndRemoveIf([this, callback](const typename CacheStore::EntryType &entry) {
if (this->cachePolicyType.CanEvict(entry.Key()) || CONFIG_FLAG(ForceCleanCacheOnCollect))
{
callback(entry.Key(), entry.Value());
if (!CONFIG_FLAG(ForceCleanCacheOnCollect))
{
return true;
}
}
return false;
});
if (CONFIG_FLAG(ForceCleanCacheOnCollect))
{
this->cacheStore->Clear();
Assert(this->cacheStore->Count() == 0);
}
}
}
template <class Fn>
void CleanAll(Fn callback)
{
Assert(!this->cacheStore->IsInAdd());
cacheStore->MapAndRemoveIf([this, callback](const CacheStore::EntryType &entry) -> bool {
callback(entry.Key(), entry.Value());
return true;
});
}
void DumpKeepAlives()
{
cachePolicyType.DumpKeepAlives();
}
private:
Field(CacheStore*) cacheStore;
Field(CacheRetentionPolicy) cachePolicyType;
};
}