// Squawk messenger. // Copyright (C) 2019 Yury Gubich // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef DATABASE_CACHE_HPP #define DATABASE_CACHE_HPP #include "cache.h" #include "exceptions.h" template DataBase::Cache::Cache(const std::string& p_name, DataBase* parent): DataBase::Table(p_name, parent), mode(new Mode), cache(new std::map()), abscent(new std::set()), sizeDifference(new uint32_t) { *mode = Mode::nothing; *sizeDifference = 0; } template DataBase::Cache::~Cache() { delete sizeDifference; delete mode; delete cache; delete abscent; } template void DataBase::Cache::addRecord(const K& key, const V& value) { if (!DataBase::Table::db->opened) { throw Closed("addRecord", DataBase::Table::db->name, DataBase::Table::name); } if (cache->count(key) > 0) { throw Exist(DataBase::_Table::toString(key), DataBase::Table::db->name, DataBase::Table::name); } Table::addRecord(key, value); cache->insert(std::make_pair(key, value)); } template void DataBase::Cache::changeRecord(const K& key, const V& value) { if (!DataBase::Table::db->opened) { throw Closed("changeRecord", DataBase::Table::db->name, DataBase::Table::name); } if (*mode == Mode::full) { typename std::map::iterator itr = cache->find(key); if (itr != cache->end()) { throw NotFound(DataBase::_Table::toString(key), DataBase::Table::db->name, DataBase::Table::name); } Table::changeRecord(key, value); itr->second = value; } else { if (abscent->count(key) > 0) { throw NotFound(DataBase::_Table::toString(key), DataBase::Table::db->name, DataBase::Table::name); } try { Table::changeRecord(key, value); typename std::map::iterator itr = cache->find(key); if (itr != cache->end()) { itr->second = value; } else { cache->insert(std::make_pair(key, value)); handleMode(); } } catch (const NotFound& error) { abscent->insert(key); throw error; } } } template V DataBase::Cache::getRecord(const K& key) const { if (!DataBase::Table::db->opened) { throw Closed("getRecord", DataBase::Table::db->name, DataBase::Table::name); } typename std::map::const_iterator itr = cache->find(key); if (itr != cache->end()) { return itr->second; } if (*mode == Mode::full || abscent->count(key) != 0) { throw NotFound(DataBase::_Table::toString(key), DataBase::Table::db->name, DataBase::Table::name); } try { V value = Table::getRecord(key); cache->insert(std::make_pair(key, value)); handleMode(); return value; } catch (const NotFound& error) { abscent->insert(key); throw error; } } template std::map DataBase::Cache::readAll() const { if (!DataBase::Table::db->opened) { throw Closed("readAll", DataBase::Table::db->name, DataBase::Table::name); } if (*mode != Mode::full) { //there is a room for optimization *mode = Mode::full; //I can read and deserialize only those values *cache = DataBase::Table::readAll(); //that are missing in the cache abscent->clear(); *sizeDifference = 0; } return *cache; } template void DataBase::Cache::removeRecord(const K& key) { if (!DataBase::Table::db->opened) { throw Closed("removeRecord", DataBase::Table::db->name, DataBase::Table::name); } typename std::pair::const_iterator, bool> pair = abscent->insert(key); if (!pair.second) { throw NotFound(DataBase::_Table::toString(key), DataBase::Table::db->name, DataBase::Table::name); } Table::removeRecord(key); if (cache->erase(key) == 0) { //if it was not cached and we are now in size mode then the sizeDifference would decrease handleMode(); } if (*mode != Mode::full) { abscent->insert(key); } } template uint32_t DataBase::Cache::count() const { switch (*mode) { case Mode::nothing: { uint32_t sz = DataBase::Table::count(); *sizeDifference = sz - cache->size(); if (sz == 0) { *mode = Mode::full; abscent->clear(); } else { *mode = Mode::size; } return sz; } case Mode::size: return cache->size() + *sizeDifference; case Mode::full: return cache->size(); } } template void DataBase::Cache::handleMode() const { if (*mode == Mode::size) { --(*sizeDifference); if (*sizeDifference == 0) { *mode = Mode::full; abscent->clear(); } } } template int DataBase::Cache::drop(MDB_txn * transaction) { int res = DataBase::Table::drop(transaction); cache->clear(); abscent->clear(); *mode = Mode::full; *sizeDifference = 0; return res; } #endif //DATABASE_CACHE_HPP