#include #include #include #include namespace phosg { template LRUSet::Item::Item(size_t size) : prev(nullptr), next(nullptr), key(nullptr), size(size) {} template LRUSet::LRUSet() : head(nullptr), tail(nullptr), items(), total_size(0) {} template LRUSet::~LRUSet() { // don't need to do anything - the items and keys will be destroyed by the // unordered_map destructor } template bool LRUSet::after_emplace( const std::pair::iterator, bool>& emplace_ret, size_t size) { auto& k = emplace_ret.first->first; auto& i = emplace_ret.first->second; if (emplace_ret.second) { // item was inserted i.key = &k; i.size = size; this->total_size += size; this->link_item(&i); return true; } else { // item already existed. the key must match already; just update the size // and move the item to the front of the lru ssize_t size_delta = static_cast(size) - static_cast(i.size); i.size = size; this->total_size += size_delta; this->unlink_item(&i); this->link_item(&i); return false; } } template bool LRUSet::insert(const K& key, size_t size) { auto emplace_ret = this->items.emplace(std::piecewise_construct, std::make_tuple(key), std::make_tuple(size)); return this->after_emplace(emplace_ret, size); } template bool LRUSet::emplace(K&& key, size_t size) { auto emplace_ret = this->items.emplace(std::piecewise_construct, std::forward_as_tuple(std::move(key)), std::forward_as_tuple(size)); return this->after_emplace(emplace_ret, size); } template bool LRUSet::erase(const K& k) { auto item_it = this->items.find(k); if (item_it == this->items.end()) { return false; } Item& item = item_it->second; this->unlink_item(&item); this->total_size -= item.size; this->items.erase(item_it); return true; } template void LRUSet::clear() { this->head = nullptr; this->tail = nullptr; this->items.clear(); this->total_size = 0; } template bool LRUSet::change_size(const K& k, size_t new_size) { try { Item& i = this->items.at(k); this->total_size += new_size - i.size; i.size = new_size; return true; } catch (const std::out_of_range& e) { return false; } } template bool LRUSet::touch(const K& k, ssize_t new_size) { try { Item& i = this->items.at(k); if (this->head != &i) { this->unlink_item(&i); this->link_item(&i); } if (new_size >= 0) { this->total_size += new_size - i.size; i.size = new_size; } return true; } catch (const std::out_of_range& e) { return false; } } template void LRUSet::unlink_item(Item* i) { if (this->head == i) { this->head = i->next; } if (this->tail == i) { this->tail = i->prev; } if (i->prev) { i->prev->next = i->next; } if (i->next) { i->next->prev = i->prev; } i->prev = nullptr; i->next = nullptr; } template void LRUSet::link_item(Item* i) { i->next = this->head; if (this->head) { this->head->prev = i; } this->head = i; if (!this->tail) { this->tail = i; } } template size_t LRUSet::size() const { return total_size; } template size_t LRUSet::count() const { return this->items.size(); } template std::pair LRUSet::evict_object() { Item* i = this->tail; if (!i) { throw std::out_of_range("nothing to evict"); } this->unlink_item(i); std::pair ret = std::make_pair(*i->key, i->size); this->items.erase(ret.first); this->total_size -= ret.second; return ret; } template std::pair LRUSet::peek() { Item* i = this->tail; if (!i) { throw std::out_of_range("set is empty"); } return std::make_pair(*i->key, i->size); } template void LRUSet::swap(LRUSet& other) { Item* this_head = this->head; Item* this_tail = this->tail; size_t this_total_size = this->total_size; this->items.swap(other.items); this->head = other.head; this->tail = other.tail; this->total_size = other.total_size; other.head = this_head; other.tail = this_tail; other.total_size = this_total_size; } } // namespace phosg